Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Terminate all dialogs and exit conversation in MS Bot Framework when the user types "exit", "quit" etc

People also ask

How do you end a conversation in bot framework?

endDialogAsync() ends the current dialog on the stack returning control to the parent dialog, if present, or to the turn handler. Additionally, it can be called from anywhere the dialog context is accessible. Best practice is to call it at the end of every dialog.

Which method is used to close a dialog?

The DOM Dialog close() method is used to close the dialog. The Dialog element is accessed by getElementById().

What are dialogs in bot framework?

What is a dialog in Bot framework? Basically dialog is a class, which allows Bot developer to logically separate various areas of Bot functionality and guide conversational flow.


PROBLEM BREAKDOWN

From my understanding of your question, what you want to achieve is to reset the dialog stack without completely destroy the bot state.


FACTS (from what I read from github repository)

  1. How the framework save the dialog stack is as below:

BotDataStore > BotData > DialogStack

  1. BotFramework is using AutoFac as an DI container
  2. DialogModule is their Autofac module for dialog components

HOW TO DO

Knowing FACTS from above, my solution will be

  1. Register the dependencies so we can use in our controller:

// in Global.asax.cs
var builder = new ContainerBuilder();
builder.RegisterModule(new DialogModule());
builder.RegisterModule(new ReflectionSurrogateModule());
builder.RegisterModule(new DialogModule_MakeRoot());

var config = GlobalConfiguration.Configuration;
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(config);
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
  1. Get the Autofac Container (feel free to put anywhere in your code that you're comfortable with)

private static ILifetimeScope Container
{
    get
    {
        var config = GlobalConfiguration.Configuration;
        var resolver = (AutofacWebApiDependencyResolver)config.DependencyResolver;
        return resolver.Container;
    }
}
  1. Load the BotData in the scope
  2. Load the DialogStack
  3. Reset the DialogStack
  4. Push the new BotData back to BotDataStore

using (var scope = DialogModule.BeginLifetimeScope(Container, activity))
{
    var botData = scope.Resolve<IBotData>();
    await botData.LoadAsync(default(CancellationToken));
    var stack = scope.Resolve<IDialogStack>();
    stack.Reset();
    await botData.FlushAsync(default(CancellationToken));
}

Hope it helps.


UPDATE 1 (27/08/2016)

Thanks to @ejadib to point out, Container is already being exposed in conversation class.

We can remove step 2 in the answer above, in the end the code will look like

using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
    var botData = scope.Resolve<IBotData>();
    await botData.LoadAsync(default(CancellationToken));
    var stack = scope.Resolve<IDialogStack>();
    stack.Reset();
    await botData.FlushAsync(default(CancellationToken));
}

Here is a horribly ugly hack that works. It basically deletes all user data (which you might actually need) and this causes the conversation to restart.

If someone knows a better way, without deleting user data, please please share.

    public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        try
        {
            if (activity.Type == ActivityTypes.Message)
            {
                //if the user types certain messages, quit all dialogs and start over
                string msg = activity.Text.ToLower().Trim();
                if (msg == "start over" || msg == "exit" || msg == "quit" || msg == "done" || msg =="start again" || msg == "restart" || msg == "leave" || msg == "reset")
                {
                    //This is where the conversation gets reset!
                    activity.GetStateClient().BotState.DeleteStateForUser(activity.ChannelId, activity.From.Id);
                }

                //and even if we reset everything, show the welcome message again
                BotUtils.SendTyping(activity); //send "typing" indicator upon each message received
                await Conversation.SendAsync(activity, () => new RootDialog());
            }
            else
            {
                HandleSystemMessage(activity);
            }
        }

I know this is a little old, but I had the same problem and the posted solutions are no longer the best approaches.

I'm not sure since what version this is available, but on 3.8.1 you can register IScorable services that can be triggered anywhere in the dialog.

There is a sample code that shows how it works, and it does have a "cancel" global command handler:

https://github.com/Microsoft/BotBuilder-Samples/tree/master/CSharp/core-GlobalMessageHandlers

A part of the code will look like this:

protected override async Task PostAsync(IActivity item, string state, CancellationToken token)
{
    this.task.Reset();
}