Persisting data within a conversation with botframework’s dialogs

In the previous botframework article I covered the different types of responses available for the botframework. This article is going to touch on the Dialog and persisting information between subsequent messages.

So what’s a Dialog?

Dialogs can call child dialogs or send messages to a user. Dialogs are suspended when waiting for a message from the user to the bot. Dialogs are resumed when the bot receives a message from the user.

To create a Dialog, you must implement the IDialog<T> interface and make your dialog class serializable, something like this:

[Serializable]
public class HelloDialog : IDialog<object>
{
    protected int count = 1;

    public async Task StartAsync(IDialogContext context)
    {
        await context.PostAsync($"You're new!");
        context.Wait(MessageReceivedAsync);
    }

    public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
    {
        var message = await argument;
        await context.PostAsync($"I've seen you {count++} times now, {message.From.Name}");
        context.Wait(MessageReceivedAsync);
    }
}

What I’m trying to show here is how the dialog handles an initial message from a new user, versus the continuation of a conversation with that same user.

In order to wire the dialog up to the MessagesController you need to reference Microsoft.Bot.Builder.Dialogs and use the Conversation.SendAsync() method:

public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
    if (activity.Type == ActivityTypes.Message)
    {
        // needs a reference to Microsoft.Bot.Builder.Dialogs
        await Conversation.SendAsync(activity, () => new HelloDialog());
    }
    else
    {
        HandleSystemMessage(activity);
    }
    var response = Request.CreateResponse(HttpStatusCode.OK);
    return response;
}

With this implemented you should see something similar to the following conversation if you use the bot framework channel emulator:

dialog with basic state

The magic is in context.Wait:

context wait tooltip

Suspend the current dialog until the user has sent a message to the bot

So what we’ve done here is implement IDialog<T>’s StartAsync method, then tell the bot to hang around, waiting for another message in the same conversation from the same user using context.Wait.

Well, that sounds like stateful communication, right? Which sucks, since it inhibits scaling by requiring sticky sessions.

A-ha! But not quite, in this case; in something that hopefully doesn’t end up looking like viewstate, we have DialogState encoded in the context’s PrivateConversationData’s Bag:

dialogstate in the context's privateconversationdata

This will allow state to be carried around and the conversation continued without the user needing to be tied to a single instance of the bot. Nice.

Let’s play with that a bit:

public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
    int heresavalue = 1;
    if (!context.PrivateConversationData.TryGetValue<int>("countthingy", out heresavalue)) { heresavalue = 1; }

    var message = await argument;
    await context.PostAsync($"I've seen you {count++} times now, {message.From.Name} and also {heresavalue}");
    context.Wait(MessageReceivedAsync);

    context.PrivateConversationData.SetValue<int>("countthingy", heresavalue * 2);
}

Now we’re setting a variable heresavalue and doubling it for each message in the same conversation, just to show that the value is being persisted; after a few messages, it looks like this:

UDC in privateconversationdata

context.PrivateConversationData.SetValue<T> and context.PrivateConversationData.TryGetValue<T> mean we can push info into the databag and get it back again within the context of a one on one chat.

The same is also true for context.ConversationData, where the conversation is a group (i.e., the bot and more than one other user), instead of one-to-one.

Skype

What’s quite cool is how your Skype name is pulled out from message.From.Name when the bot is deployed to Skype

skype name being passed in the message

Summary

Hopefully this short article gave you an idea of how to implement a Dialog and how you can persist state within a conversation.

One thought on “Persisting data within a conversation with botframework’s dialogs

  1. Hello,

    Is there anyway to maintain some objects at the MessageController class? I am using LUIS and I want to fetch customer details when they send me the first message and then after I would like to skip the fetching function because the customer is already instantiated. However, I would need to do it at the MessageController level to keep one copy among all my dialogs.

Leave a Reply

Your email address will not be published. Required fields are marked *