Botframework-solutions: Bot.Builder.Community.Adapters.Alexa not working with the VA

Created on 19 Sep 2019  ·  3Comments  ·  Source: microsoft/botframework-solutions

What project is affected?

Virtual Assistant.

What language is this in?

C#

What happens?

BotBuilder.Community.Adapters.Alexa doesn't seem to work with the VA.

What are the steps to reproduce this issue?

Followed the instructions here:- https://github.com/BotBuilderCommunity/botbuilder-community-dotnet/tree/develop/libraries/Bot.Builder.Community.Adapters.Alexa

1) In the Startup.cs I added the following NuGet packages:

Bot.Builder.Community.Adapters.Alexa;
Bot.Builder.Community.Adapters.Alexa.Integration.AspNet.Core;
Bot.Builder.Community.Adapters.Alexa.Middleware;

Before this code

            // Configure bot
            services.AddTransient<IBot, DialogBot<MainDialog>>();

I added:

            // Registering the AlexaHttpAdapter
            services.AddSingleton<IAlexaHttpAdapter>((sp) =>
            {
                var alexaHttpAdapter = new AlexaHttpAdapter(validateRequests: true)
                {
                    OnTurnError = async (context, exception) =>
                    {
                        await context.SendActivityAsync("Sorry, something went wrong. Please try again later." + context + " Exception:" + exception);
                    },
                    ShouldEndSessionByDefault = true,
                    ConvertBotBuilderCardsToAlexaCards = false,
                };
                alexaHttpAdapter.Use(new AlexaIntentRequestToMessageActivityMiddleware());
                return alexaHttpAdapter;
            });

            services.AddSingleton<AlexaIntentRequestToMessageActivityMiddleware>();

2) Created an Alexa controller:

using Bot.Builder.Community.Adapters.Alexa.Integration.AspNet.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace GuideDogs_VA.Controllers
{
    [Route("api/skillrequests")]
    [ApiController]
    public class AlexaBotController : ControllerBase
    {
        private readonly IAlexaHttpAdapter _adapter;
        private readonly IBot _bot;

        public AlexaBotController(IAlexaHttpAdapter adapter, IBot bot)
        {
            _adapter = adapter;
            _bot = bot;
        }

        [HttpPost]
        public async Task PostAsync()
        {
            await _adapter.ProcessAsync(Request, Response, _bot);
        }
    }
}

3) Created an Alexa Skill using the following JSON:

{
    "interactionModel": {
        "languageModel": {
            "invocationName": "guide dogs",
            "intents": [
                {
                    "name": "GetUserIntent",
                    "slots": [
                        {
                            "name": "phrase",
                            "type": "phrase"
                        }
                    ],
                    "samples": [
                        "{phrase}"
                    ]
                },
                {
                    "name": "AMAZON.FallbackIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.NavigateHomeIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": []
                }
            ],
            "types": [
                {
                    "name": "phrase",
                    "values": [
                        {
                            "name": {
                                "value": "hi there Alexa"
                            }
                        },
                        {
                            "name": {
                                "value": "you are just going to repeat what I said aren't you"
                            }
                        },
                        {
                            "name": {
                                "value": "what colour is the sky?"
                            }
                        }
                    ]
                }
            ]
        }
    }
}

What were you expecting to happen?

When testing the Alexa Skill through Alexa's developer console or via an Alexa device, for it to respond, but unfortunately it ends up calling OnEventSync() even though the activity type is of type message.

Can you share any logs, error output, etc.?

System.NullReferenceException thrown from the MainDialog.cs file:

image

Any screenshots or additional context?

image

Response from the VA to Alexa (response generated from the Startup.cs context.SendActivityAsync:- await context.SendActivityAsync("Sorry, something went wrong. Please try again later." + context + " Exception:" + exception):

{
    "body": {
        "version": "1.0",
        "response": {
            "outputSpeech": {
                "type": "PlainText",
                "text": "Sorry, something went wrong. Please try again later.Microsoft.Bot.Builder.TurnContext Exception:System.NullReferenceException: Object reference not set to an instance of an object.\r\n   at GuideDogs_VA.Dialogs.MainDialog.OnEventAsync(DialogContext dc, CancellationToken cancellationToken) in C:\\Users\\hussel\\source\\repos\\GuideDogs-VA\\GuideDogs_VA\\Dialogs\\MainDialog.cs:line 204\r\n   at Microsoft.Bot.Builder.Solutions.Dialogs.RouterDialog.OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken)\r\n   at Microsoft.Bot.Builder.Dialogs.ComponentDialog.BeginDialogAsync(DialogContext outerDc, Object options, CancellationToken cancellationToken)\r\n   at Microsoft.Bot.Builder.Dialogs.DialogContext.BeginDialogAsync(String dialogId, Object options, CancellationToken cancellationToken)\r\n   at Microsoft.Bot.Builder.Dialogs.DialogExtensions.RunAsync(Dialog dialog, ITurnContext turnContext, IStatePropertyAccessor`1 accessor, CancellationToken cancellationToken)\r\n   at GuideDogs_VA.Bots.DialogBot`1.OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken) in C:\\Users\\hussel\\source\\repos\\GuideDogs-VA\\GuideDogs_VA\\Bots\\DialogBot.cs:line 39\r\n   at Bot.Builder.Community.Adapters.Alexa.Middleware.AlexaIntentRequestToMessageActivityMiddleware.OnTurnAsync(ITurnContext context, NextDelegate next, CancellationToken cancellationToken)\r\n   at Microsoft.Bot.Builder.MiddlewareSet.ReceiveActivityWithStatusAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken)\r\n   at Microsoft.Bot.Builder.BotAdapter.RunPipelineAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken)"
            },
            "directives": [],
            "shouldEndSession": true,
            "type": "_DEFAULT_RESPONSE"
        },
        "sessionAttributes": {}
    }
}
Bug

Most helpful comment

@samaea I have reproduced the issue. @lauren-mills was right, it is due to the RouterDialog looking for a value on the activity and treating it as an event, rather than a normal message activity.

You can add the following override to your MainDialog as a workaround. With the only change being that we are checking if the channel ID is Alexa.

After adding this change I can successfully get a response from the VA via Alexa. Hope that resolves your issue.

protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
        {
            var activity = innerDc.Context.Activity;

            if (activity.IsStartActivity())
            {
                await OnStartAsync(innerDc);
            }

            switch (activity.Type)
            {
                case ActivityTypes.Message:
                    {
                        if (activity.Value != null && activity.ChannelId != "alexa")
                        {
                            await OnEventAsync(innerDc);
                        }
                        else if (!string.IsNullOrEmpty(activity.Text))
                        {
                            var result = await innerDc.ContinueDialogAsync();

                            switch (result.Status)
                            {
                                case DialogTurnStatus.Empty:
                                    {
                                        await RouteAsync(innerDc);
                                        break;
                                    }

                                case DialogTurnStatus.Complete:
                                    {
                                        await CompleteAsync(innerDc);

                                        // End active dialog
                                        await innerDc.EndDialogAsync();
                                        break;
                                    }

                                default:
                                    {
                                        break;
                                    }
                            }
                        }

                        break;
                    }

                case ActivityTypes.Event:
                    {
                        await OnEventAsync(innerDc);
                        break;
                    }

                default:
                    {
                        await OnSystemMessageAsync(innerDc);
                        break;
                    }
            }

            return EndOfTurn;
        }

All 3 comments

This is probably caused by the default RouterDialog implementation. One option to try is overriding the ContinueDialogAsync method in MainDialog with the behavior you want.

@samaea I have reproduced the issue. @lauren-mills was right, it is due to the RouterDialog looking for a value on the activity and treating it as an event, rather than a normal message activity.

You can add the following override to your MainDialog as a workaround. With the only change being that we are checking if the channel ID is Alexa.

After adding this change I can successfully get a response from the VA via Alexa. Hope that resolves your issue.

protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
        {
            var activity = innerDc.Context.Activity;

            if (activity.IsStartActivity())
            {
                await OnStartAsync(innerDc);
            }

            switch (activity.Type)
            {
                case ActivityTypes.Message:
                    {
                        if (activity.Value != null && activity.ChannelId != "alexa")
                        {
                            await OnEventAsync(innerDc);
                        }
                        else if (!string.IsNullOrEmpty(activity.Text))
                        {
                            var result = await innerDc.ContinueDialogAsync();

                            switch (result.Status)
                            {
                                case DialogTurnStatus.Empty:
                                    {
                                        await RouteAsync(innerDc);
                                        break;
                                    }

                                case DialogTurnStatus.Complete:
                                    {
                                        await CompleteAsync(innerDc);

                                        // End active dialog
                                        await innerDc.EndDialogAsync();
                                        break;
                                    }

                                default:
                                    {
                                        break;
                                    }
                            }
                        }

                        break;
                    }

                case ActivityTypes.Event:
                    {
                        await OnEventAsync(innerDc);
                        break;
                    }

                default:
                    {
                        await OnSystemMessageAsync(innerDc);
                        break;
                    }
            }

            return EndOfTurn;
        }

Thanks @garypretty! I have an open work item for the next release to refactor RouterDialog to align more with the SDK, so I can take this feedback in for that work as well.

Was this page helpful?
0 / 5 - 0 ratings