Monday, May 15, 2017

Azure Bot Service: Fooling around with PromptDialog

In a previous post I started on my journey to build a bot to handle some IT automation tasks I have been working on.  If you have been following along, you should now have a basic bot that you can test locally.  The goal of this post is to play around a bit with PromptDialog.

The prompt dialog seems like an effective and easy way to get some basic information from a user.  There are several samples that show how to use this.  At it's basic, it seems like you can use a prompt dialog to get a pick from a list of choices, a long, an int, a boolean, etc.  Here is some sample code to work with that prompts the user for a choice.

                    PromptDialog.Choice(
                        context,
                        this.AfterChoiceSelected,
                        new[] { Option1, Option2, Option3, Quit },
                        "How can I help you today?",
                        $"I'm sorry {activity.From.Name}, but I didn't understand that, can you try again?",
                        attempts: 2,
                        promptStyle: PromptStyle.PerLine,
                        descriptions: new[] { Option1, Option2, Option3, Quit }
                        );

Most of the above is pretty standard.  The second parameter passed in is a callback that should be called when the prompt is finished.  Here is a sample of my code.


           
        private async Task AfterChoiceSelected(IDialogContext context, IAwaitable<string> result)
        {
            try
            {
                var selection = await result;
                this.selectedChoice = selection;

                if (this.selectedChoice.Equals(Quit))
                {
                    await context.PostAsync("Quitting... Have a nice day!");
                    context.Done(this);
                    return;
                }
                
                PromptDialog.Confirm(
                    context,
                    this.AfterConfirm,
                    prompt: $"Are you sure you want to {selection}",
                    retry:$"Sorry {context.Activity.From.Name}, I didn't understand that",
                    attempts: 2,
                    promptStyle: PromptStyle.PerLine
                );
            }
            catch (Exception)
            {

                throw;
            }
        }

The other thing worth noting is the promptStyle parameter.

Okay, so now that you have the code, lets chat about a couple of things.

There is no way to quit a prompt dialog, at least that I could find....

When you kick off a prompt dialog as part of a bot interaction, it takes over control of the conversation.  As the state is carried forward automatically in the bot, the only way to exit the prompt dialog is to either pick a valid selection or exhaust the number of attempts.  While playing around, I added a "quit" option that allows the user to type quit to exit the conversation.

You should play around with promptstyle in your different channels

The prompt style parameter is a pretty interesting and neat feature.  Essentially, you are telling the channel abstraction that you are dealing with to render the choice list.  Auto doesn't always work in all cases.  Here is an example of my options in slack:

 
 As you can see, it isn't really usable since the options are cut off.  When I switched it to "PerLine" it showed up like this:

That is much more usable.  I also took a look at the "keyboard" option.  While this didn't seem to do anything material in slack, there was a cool experience in the emulator where "buttons" would show up as the responses.


Pretty cool.  Ultimately, I would think a "per channel" approach to rendering might be a better way to go if you get into complex channel visual requirements.

There is no default "confirm" in PromptDialog.Choice

While it completely makes sense, there is no "confirm" option in the prompt dialog choice object.  So essentially, once you select the option, it fires your call back.  It is up to you then to process the logic as required.  From a usability perspective, you might want to add this step.  It is pretty simple and requires two key pieces.

The first is the PromptDialog.Confirm object which allows you to prompt the user for a yes/no.  The second is the fact that you need to maintain state between interactions with the bot.  The former is shown in the code above, and the latter is easy given that the entire dialog object is serialized into state automatically.  Here is what it looks like in slack.

There is fuzzy matching on picking a number for the option, but not on the words

One thing that is kind of cool (and shown in the image above) is the user can be a little "loose" with how to reference the choices by number.  So they can use "the first one" or "first" or "one" and it will all map to the same option.  This doesn't work, at least in my testing, when trying to match the display of the option.  You have to type out the entire option if you want to select it by name.

Well, prompt dialog seems to be doing it's thing.  I know that I could tie this into LUIS and do some cool things with the natural language processing, but for my use case, this simple prompt dialog does the trick.