I’ll be speaking at Visual Studio Live!, San Diego, October 7-11 
 I’ll be presenting the following sessions:
 W11 - Building Business Applications Using Bots
 W19 - Using The Microsoft Cognitive Custom Vision Service
 As a speaker, I can offer $400 savings on the 4-day package.
 Register here: http://bit.ly/SDSPK27Home 

Using Dialogs (MS Bot Framework V4 Preview Edition)

Jul 8

Written by:
7/8/2018 11:23 AM  RssIcon

image

The Microsoft Bot Framework V4 allows you to create intelligent bots that interact naturally wherever your users are (text/SMS to Skype, Slack, Office 365 mail and other popular services). In preview now, the Bot Builder V4 Preview SDK offers new features and is extensible with a pluggable middleware model.

In this article we will build on the example started in the following article: Saving User and Conversation Data With Azure Cosmos DB (MS Bot Framework V4 Preview Edition). In this article we will cover using Dialogs.

Tutorial Series

Other articles in this series:

Requirements

 

What Are Dialogs?

A dialog encapsulates application logic much like a function does in a standard program. It allows you to perform a specific task, such as gathering the details of a user’s profile, and then possibly reuse the code as needed. Dialogs can also be chained together in DialogSets.

The Microsoft Bot Builder SDK includes two built-in features to help you manage conversations using dialogs:

  • DialogSets - This is a collection of Dialogs. To use dialogs, you must first create a dialog set to add the dialog to. A dialog can contain only a single waterfall step, or it can consist of multiple waterfall steps.

  • Prompts - This provides the methods you can use to ask users for different types of information. For example, a text input, a multiple choice, or a date or number input. A prompt dialog uses at least two functions, one to prompt the user to input data, and another function to process and respond to the data.

 

Limitations Of The Previous Example

image

The limitation with the code created in the previous example (Saving User and Conversation Data With Azure Cosmos DB (MS Bot Framework V4 Preview Edition)) is:

  • All code logic is on the main path
  • There is no easy way to encapsulate individual logic
  • There is no way to reuse code logic
  • As the code gets bigger there will be more if..then statements, creating spaghetti code

 

Update Code To Use Dialogs

image

Open the project created in the article: Saving User and Conversation Data With Azure Cosmos DB (MS Bot Framework V4 Preview Edition) in Visual Studio 2017 (or higher).

Right-click on the project node and select Manage NuGet Packages.

image

Search for Microsoft.Bot.Builder.Dialogs.

Select include prerelease.

Click on Microsoft.Bot.Builder.Dialogs, then click the Install button.

(accept any other screens that appear and fully install the package)

image

Open the Startup.cs file.

Add the following using statement to the top of the file:

 

using System.Collections.Generic;

 

Alter the ConfigureServices method to the following:

 

   public void ConfigureServices(IServiceCollection services)
    {
        services.AddBot<EchoBot>(options =>
        {
            options.CredentialProvider = new ConfigurationCredentialProvider(Configuration);
            // The CatchExceptionMiddleware provides a top-level exception handler for your bot. 
            // Any exceptions thrown by other Middleware, or by your OnTurn method, will be 
            // caught here. To facilitate debugging, the exception is sent out, via Trace, 
            // to the emulator. Trace activities are NOT displayed to users, so in addition
            // an "Ooops" message is sent. 
            options.Middleware.Add(new CatchExceptionMiddleware<Exception>(async (context, exception) =>
            {
                await context.TraceActivity("EchoBot Exception", exception);
                await context.SendActivity("Sorry, it looks like something went wrong!");
            }));
            IStorage dataStore = new MemoryStorage();
            options.Middleware.Add(new ConversationState<Dictionary<string, object>>(dataStore));
            options.Middleware.Add(new UserState<BotUserState>(dataStore));
        });
    }

 

(this is done so we can switch to memory storage for this article - - it does not require an Azure account to run the code)

image

 

Open the EchoBot.cs file (this is the main application file for the Bot).

Add the following using statements to the top of the file:

 

using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Prompts;
using System.Collections.Generic;
using System;

 

Add a global property for a DialogSet and a constructor for the class that contains our dialog:

 

        // A property to create a global variable for DialogSet
        // This allows you to add named dialogs 
        // and then access them by name later
        private readonly DialogSet dialogs;
        // We are adding a contructor to the class
        public EchoBot()
        {
            // Create a new DialogSet
            dialogs = new DialogSet();
            // Add a dialog to the DialogSet
            // Call it "greetings"
            // Also define a WaterfallStep (a way to define a sequence of steps)
            dialogs.Add("greetings", new WaterfallStep[]
            {
                // Each step takes in a dialog context, arguments, and the next delegate.
                async (dc, args, next) =>
                {
                    // Set up state to track values durring this conversation
                    dc.ActiveDialog.State = new Dictionary<string, object>();
                    // Prompt for the users's name.  
                    // Note this is just setting up a prompt that 
                    // will be called "textPrompt"
                    // The actual prompt will be set up at the bottom of 
                    // this method
                    await dc.Prompt("textPrompt","What is your name?");
                },
                async (dc, args, next) =>
                {
                    // args contains the user response to the previous step
                    dc.ActiveDialog.State["name"] = args["Value"];
                    
                    // We can simply send a message to the user
                    await dc.Context.SendActivity($"Hi {dc.ActiveDialog.State["name"]}!");
                    // We can end the dialog at any point
                    await dc.End();
                }
            });
            // earlier we created a prompt called "textPrompt"
            // Now we will actually create that prompt (of type TextPrompt) 
            // and add it to the dialog 
            dialogs.Add("textPrompt", new Microsoft.Bot.Builder.Dialogs.TextPrompt());
        }

 

 

A dialog will not actually run unless you have code to call it.

Change the OnTurn method to the following:

 

    public async Task OnTurn(ITurnContext context)
    {
        // We will use a switch statement on context.Activity.Type
        // Possible values include: 'message', 'contactRelationUpdate',
        // 'conversationUpdate', 'typing', 'ping', 'endOfConversation', 'event', 'invoke',
        // 'deleteUserData', 'messageUpdate', 'messageDelete', 'installationUpdate', 
        // 'messageReaction', 'suggestion', 'trace'
        switch (context.Activity.Type)
        {
            // This allows us to display a message when a user first connects to the Bot
            case ActivityTypes.ConversationUpdate:
                foreach (var newMember in context.Activity.MembersAdded)
                {
                    if (newMember.Id != context.Activity.Recipient.Id)
                    {
                        string strMessage = $"Hello World! {System.Environment.NewLine}";
                        strMessage += "How Are you?";
                        await context.SendActivity(strMessage);
                    }
                }
                break;
            // All Normal messages 
            case ActivityTypes.Message:
                // Get the conversation state from the turn context
                var state = ConversationState<Dictionary<string, object>>.Get(context);
                // Get the dialog context from the conversation state 
                var DialogContext = dialogs.CreateContext(context, state);
                // Continue running code for any active dialog
                // if a dialog stack exists -- if not this will do 
                // nothing
                await DialogContext.Continue();
                if (!context.Responded)
                {
                    // A dialog won't be started without calling the .begin command 
                    // passing the name of the dialog to be started
                    await DialogContext.Begin("greetings");
                }
                break;
        }
    }

 

 

Run The Updated Code

image

We can now run the application and connect to it with the Bot Framework Emulator (V4 PREVIEW) (see: Creating a Hello World! Bot (MS Bot Framework V4 Preview Edition) for information on how to do that).

image

The Bot will great the user, ask the user for their name, greet them by name… and then ask for their name again!

We now see that we need to use user state to track if we have asked the user their name, as we did in the previous article.

In addition, we need to store the user’s name.

Adding User State

We will now alter the Bot to determine if it has asked the user for their name.

If we have not asked the user for their name, we will ask the user for their name, store it in UserState, and then greet them by that name.

In addition, for any further interactions, we will repeat their name back to them with each reply.

 

Change the code in the second step of the waterfall from this:

 

                // args contains the user response to the previous step
                dc.ActiveDialog.State["name"] = args["Value"];
                    
                // We can simply send a message to the user
                await dc.Context.SendActivity($"Hi {dc.ActiveDialog.State["name"]}!");
                // We can end the dialog at any point
                await dc.End();

 

To this:

 

                // args contains the user response to the previous step
                dc.ActiveDialog.State["name"] = args["Value"];
                // Save UserName to userState
                var userState = UserState<BotUserState>.Get(dc.Context);
                userState.UserName = Convert.ToString(dc.ActiveDialog.State["name"]);
                    
                // We can simply send a message to the user
                await dc.Context.SendActivity($"Hi {userState.UserName}!");
                // We can end the dialog at any point
                await dc.End();

 

 

In the OnTurn method, add the following:

 

        // Get the user state from the turn context
        var userstate = context.GetUserState<BotUserState>();

 

Then change the if (!context.Responded) block to the following:

 

            if (!context.Responded)
            {
                if (!userstate.AskedForUserName)
                {
                    // A dialog won't be started without calling the .begin command 
                    // passing the name of the dialog to be started
                    await DialogContext.Begin("greetings");
                    // Set AskedForUserName so we don't ask again
                    userstate.AskedForUserName = true;
                }
                else
                {
                    // Echo back to the user whatever they typed.
                    await context.SendActivity(
                        $"Hello {userstate.UserName} you said '{context.Activity.Text}'");
                }
            }

 

image

Now, we can run the project, return to the Bot Emulator, click the Start Over button, and chat with the updated Bot.

 

Links

Azure Bot Service Documentation for the v4 SDK

Ask the user questions

Using dialogs to manage conversation flow

Prompt users for input

Create modular bot logic with a dialog container

Conversational AI: Best Practices for Building Bots

Download

You can download the code from the Download page