Search code examples
c#botframework

How to call custom dialogs in MessageController for custom events


Problem statement:
I want to call dialogs from MessageController when custom events are sent to the bot.

Setup:
I have a bot build using Microsoft Bot Framework [v3.15.3]
I have a set of custom events that are sent to the bot from external systems to notify the bot adn ask it to perform actions.
[ Example, MarkUserAsOfflineInBackEndStore ShowExternalActionCompletedMessageToUser]

My users connect to the bot using a web portal that has a webchat connect. This also sends custom events to the bot to notify the bot about user actions
[Example UserClickedOnLogoutFromSite , userNavigatedToDifferentPage]

For these events also the bot has to take some actions.

Problem Statement:
From my message controller I have to redirect to different dialog based on different events that come in. My current setup is below:

 using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
 {
     if (activity.Type == ActivityTypes.Event)
     {
         var eventDialog = GetEventDialog(scope, activity);
         if (eventDialog != null)
         {
              await Conversation.SendAsync(activity, () => eventDialog).ConfigureAwait(false);
         }
     }
     else
     {
          await Conversation.SendAsync(activity, () => scope.Resolve<RootDialog>()).ConfigureAwait(false);
     }
}

I want the user ot be able to talk to the bot without error irrespective of the events that are happening. This means that If the bot is waiting for user input using context.Wait(...) the events should not cause unexpected behavior.

I explored calling dialogs using context.Call but could not find a recommended way to get the context reference in messagecontroller.

Looking for suggestion on how to setup the code here. In my basic scenario,when i send messages one at a time and one event at a time with no waits, it seems to be working correctly. But with complex dialog i get "Sorry my bot code is having an error".


Solution

  • Your issue seems to be based on a common misunderstanding of the MakeRoot delegate in Conversation.SendAsync. The delegate cannot be used to control which dialog the activity is sent to. The reason you have to pass a delegate to begin with instead of just a dialog is because in most cases no new dialog should be constructed. The idea behind Conversation.SendAsync is to send the activity to whatever dialog is on top of the stack. From the documentation:

    The MakeRoot factory method is invoked for new conversations only, because existing conversations have the dialog stack and state serialized in the IMessageActivity data.

    While I don't know the details of what you're trying to do, I presume you should be able to respond to most events in a way that does not require the use of a dialog. If you're sure you want to send those events to a dialog, you need to keep in mind that there's only one dialog stack and only one dialog on top of the stack, which means you'd need to make sure all of your dialogs are able to handle all possible events gracefully. Please have a look at the documentation to better understand dialogs and dialog flow: https://learn.microsoft.com/en-us/azure/bot-service/dotnet/bot-builder-dotnet-manage-conversation-flow?view=azure-bot-service-3.0