Getting Started with Prompt Dialog using Microsoft Bot framework
Introduction:
The Bot Framework enables you to build bots that support different types of interactions with users. You can design conversations in your bot to beBot Builder SDK introduced prompt Dialogs that allow user to model conversations and manage conversation flow. The prompt is used whenever a bot needs input from the user. You can use prompts to ask a user for a series of inputs by chaining the prompts.
In this article will help you to understand how to use prompts and how you can use them to collect information from the users. We are creating sample Demo Bot for our c# corner Annual Conference 2018 registration process automation.
Prerequisite:
I have explained about Bot framework Installation, deployment and implementation the below article- Getting Started with Chatbot Using Azure Bot Service
- Getting Started with Bots Using Visual Studio 2017
- Deploying A Bot to Azure Using Visual Studio 2017
- How to Create ChatBot In Xamarin
- Getting Started with Dialog Using Microsoft Bot Framework
Create New Bot Service:
Let’s create a new bot service application using Visual Studio 2017. Open Visual Studio > Select File > Create New Project (Ctrl + Shift +N) > Select Bot applicationThe Bot application template was created with all the components and all required NuGet references installed in the solutions and add new annualplanDialog class to the project.
In this Solution, we have three main class MessagesController.cs , RootDialog.cs and AnnualPlanDialog class . Let us start discussing here.
RootDialog Class:
Step 1:
You can create / Edit the RootDialog class, create a class that is marked with the [Serializable] attribute (so the dialog can be serialized to state) and implement the IDialog interface.using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
[Serializable]
public class RootDialog : IDialog<object>
{
}
public async Task StartAsync(IDialogContext context)
{
}
/// <summary>
/// Design Title with Image and About US link
/// </summary>
/// <returns></returns>
private static Attachment GetHeroCard()
{
var heroCard = new HeroCard
{
Title = "Annual Conference 2018 Registrtion ",
Subtitle = "DELHI, 13 - 15 APRIL 2018",
Text = "The C# Corner Annual Conference 2018 is a three-day annual event for software professionals and developers. First day is exclusive for C# Corner MVPs only. The second day is open to the public, and includes presentations from many top names in the industry. The third day events are, again, exclusively for C# Corner MVPs",
Images = new List<CardImage> { new CardImage("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE5XHanlTvde5T3qHspW4utJ4QoVrFopi21t6uTrhmGKRUfdiBGq_06WVp2YIxyvP2MA0v8_Up2J-3-7aLpF9cTZCHxyXqMDc5dQCpxslZNbOQkrIhZ2FEhuBWF3bfNmrhRtrs2eRrTvk/h120/annuvalevent.PNG") },
Buttons = new List<CardAction> { new CardAction(ActionTypes.OpenUrl, "About US", value: "http://conference.c-sharpcorner.com/") }
};
return heroCard.ToAttachment();
}
public class RootDialog : IDialog<object>
{
}
Step 2:
IDialog interface has only StartAsync() methond. StartAsync() is called when the dialog becomes active. The method is passed the IDialogContext object, used to manage the conversation.public async Task StartAsync(IDialogContext context)
{
}
Step 3: Design Title and Image for Welcome:
Create a method with hero card and return the attachment. The Hero card is a multipurpose card, it is having single title, subtitle, large image, button and a "tap action “. The following code added for C# Corner annual conference 2018 registration welcome message using Bot Hero card./// <summary>
/// Design Title with Image and About US link
/// </summary>
/// <returns></returns>
private static Attachment GetHeroCard()
{
var heroCard = new HeroCard
{
Title = "Annual Conference 2018 Registrtion ",
Subtitle = "DELHI, 13 - 15 APRIL 2018",
Text = "The C# Corner Annual Conference 2018 is a three-day annual event for software professionals and developers. First day is exclusive for C# Corner MVPs only. The second day is open to the public, and includes presentations from many top names in the industry. The third day events are, again, exclusively for C# Corner MVPs",
Images = new List<CardImage> { new CardImage("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE5XHanlTvde5T3qHspW4utJ4QoVrFopi21t6uTrhmGKRUfdiBGq_06WVp2YIxyvP2MA0v8_Up2J-3-7aLpF9cTZCHxyXqMDc5dQCpxslZNbOQkrIhZ2FEhuBWF3bfNmrhRtrs2eRrTvk/h120/annuvalevent.PNG") },
Buttons = new List<CardAction> { new CardAction(ActionTypes.OpenUrl, "About US", value: "http://conference.c-sharpcorner.com/") }
};
return heroCard.ToAttachment();
}
Welcome banner its look like below
Define the enum for different type pass. it’s a prompt list item
public enum AnnuvalConferencePass
{
EarlyBird,
Regular,
DelegatePass,
CareerandJobAdvice,
}
Create a method ShowAnnuvalConferenceTicket with Prompt Dialog choice like below
public virtual async Task ShowAnnuvalConferenceTicket(IDialogContext context, IAwaitable<IMessageActivity> activity)
{
var message = await activity;
PromptDialog.Choice(
context: context,
resume: ChoiceReceivedAsync,
options: (IEnumerable<AnnuvalConferencePass>)Enum.GetValues(typeof(AnnuvalConferencePass)),
prompt: "Hi. Please Select Annuval Conference 2018 Pass :",
retry: "Selected plan not avilabel . Please try again.",
promptStyle: PromptStyle.Auto
);
}
The PropmptDialog. choice method has different parameter, you can refer below for parameter and uses
Context - user context message
Resume - its Resume handler, what next process
Options - list of prompt item
Retry - What to show on retry.
Attempts -The number of times to retry.
PromptStyle - Style of the prompt Prompt Style
Descriptions - Descriptions to display for choices.
When the user selects an option, the ChoiceReceivedAsync method will be called.
public virtual async Task ChoiceReceivedAsync(IDialogContext context, IAwaitable<AnnuvalConferencePass> activity)
{
AnnuvalConferencePass response = await activity;
context.Call<object>(new AnnualPlanDialog(response.ToString()), ChildDialogComplete);
}
if its bot conversation is completed, the ChildDialogComplete method will execute for show thanks message
public virtual async Task ChildDialogComplete(IDialogContext context, IAwaitable<object> response)
{
await context.PostAsync("Thanks for select C# Corner bot for Annual Conference 2018 Registrion .");
context.Done(this);
}
public async Task StartAsync(IDialogContext context)
{
//Show the title with background image and Details
var message = context.MakeMessage();
var attachment = GetHeroCard();
message.Attachments.Add(attachment);
await context.PostAsync(message);
// Show the list of plan
context.Wait(this.ShowAnnuvalConferenceTicket);
}
In this class , Bot will collect all the user information one by one using prompt dialog like below
namespace BotPromptDialog.Dialogs
{
[Serializable]
public class AnnualPlanDialog : IDialog<object>
{
string name;
string email;
string phone;
string plandetails;
public AnnualPlanDialog(string plan)
{
plandetails = plan;
}
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync("Thanks for Select "+ plandetails + " Plan , Can I Help for Registrtion ? ");
context.Wait(MessageReceivedAsync);
}
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> activity)
{
var response = await activity;
if (response.Text.ToLower().Contains("yes"))
{
PromptDialog.Text(
context: context,
resume: ResumeGetName,
prompt: "Please share your good name",
retry: "Sorry, I didn't understand that. Please try again."
);
}
else
{
context.Done(this);
}
}
public virtual async Task ResumeGetName(IDialogContext context, IAwaitable<string> Username)
{
string response = await Username;
name = response; ;
PromptDialog.Text(
context: context,
resume: ResumeGetEmail,
prompt: "Please share your Email ID",
retry: "Sorry, I didn't understand that. Please try again."
);
}
public virtual async Task ResumeGetEmail(IDialogContext context, IAwaitable<string> UserEmail)
{
string response = await UserEmail;
email = response; ;
PromptDialog.Text(
context: context,
resume: ResumeGetPhone,
prompt: "Please share your Mobile Number",
retry: "Sorry, I didn't understand that. Please try again."
);
}
public virtual async Task ResumeGetPhone(IDialogContext context, IAwaitable<string> mobile)
{
string response = await mobile;
phone = response;
await context.PostAsync(String.Format("Hello {0} ,Congratulation :) Your C# Corner Annual Conference 2018 Registrion Successfullly completed with Name = {0} Email = {1} Mobile Number {2} . You will get Confirmation email and SMS", name, email, phone));
context.Done(this);
}
}
}
After execute above code, the output look like below
Step 4: Custom Prompt Dialog:
The custom prompts a dialog for asking the user to select a registration plan, which he/she is interested. Like below Design .Define the enum for different type pass. it’s a prompt list item
public enum AnnuvalConferencePass
{
EarlyBird,
Regular,
DelegatePass,
CareerandJobAdvice,
}
Create a method ShowAnnuvalConferenceTicket with Prompt Dialog choice like below
public virtual async Task ShowAnnuvalConferenceTicket(IDialogContext context, IAwaitable<IMessageActivity> activity)
{
var message = await activity;
PromptDialog.Choice(
context: context,
resume: ChoiceReceivedAsync,
options: (IEnumerable<AnnuvalConferencePass>)Enum.GetValues(typeof(AnnuvalConferencePass)),
prompt: "Hi. Please Select Annuval Conference 2018 Pass :",
retry: "Selected plan not avilabel . Please try again.",
promptStyle: PromptStyle.Auto
);
}
The PropmptDialog. choice method has different parameter, you can refer below for parameter and uses
Context - user context message
Resume - its Resume handler, what next process
Options - list of prompt item
Retry - What to show on retry.
Attempts -The number of times to retry.
PromptStyle - Style of the prompt Prompt Style
Descriptions - Descriptions to display for choices.
When the user selects an option, the ChoiceReceivedAsync method will be called.
public virtual async Task ChoiceReceivedAsync(IDialogContext context, IAwaitable<AnnuvalConferencePass> activity)
{
AnnuvalConferencePass response = await activity;
context.Call<object>(new AnnualPlanDialog(response.ToString()), ChildDialogComplete);
}
if its bot conversation is completed, the ChildDialogComplete method will execute for show thanks message
public virtual async Task ChildDialogComplete(IDialogContext context, IAwaitable<object> response)
{
await context.PostAsync("Thanks for select C# Corner bot for Annual Conference 2018 Registrion .");
context.Done(this);
}
Step 3:
You can wait for a message from the conversation, call context.Wait(<method name>) and pass it the method you called when the message is received. When ShowAnnuvalConferenceTicket () is called, it's passed the dialog context and an IAwaitable of type IMessageActivity. To get the message, await the result.public async Task StartAsync(IDialogContext context)
{
//Show the title with background image and Details
var message = context.MakeMessage();
var attachment = GetHeroCard();
message.Attachments.Add(attachment);
await context.PostAsync(message);
// Show the list of plan
context.Wait(this.ShowAnnuvalConferenceTicket);
}
AnnualPlanDialog :
Create a new class file for registration prompt dialog and implement IDialog interface. In resume parameter, we can specify which dialog method to be called next after the user has responded. The response from the user is passed to the subsequent dialog methods and called to the following class.In this class , Bot will collect all the user information one by one using prompt dialog like below
namespace BotPromptDialog.Dialogs
{
[Serializable]
public class AnnualPlanDialog : IDialog<object>
{
string name;
string email;
string phone;
string plandetails;
public AnnualPlanDialog(string plan)
{
plandetails = plan;
}
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync("Thanks for Select "+ plandetails + " Plan , Can I Help for Registrtion ? ");
context.Wait(MessageReceivedAsync);
}
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> activity)
{
var response = await activity;
if (response.Text.ToLower().Contains("yes"))
{
PromptDialog.Text(
context: context,
resume: ResumeGetName,
prompt: "Please share your good name",
retry: "Sorry, I didn't understand that. Please try again."
);
}
else
{
context.Done(this);
}
}
public virtual async Task ResumeGetName(IDialogContext context, IAwaitable<string> Username)
{
string response = await Username;
name = response; ;
PromptDialog.Text(
context: context,
resume: ResumeGetEmail,
prompt: "Please share your Email ID",
retry: "Sorry, I didn't understand that. Please try again."
);
}
public virtual async Task ResumeGetEmail(IDialogContext context, IAwaitable<string> UserEmail)
{
string response = await UserEmail;
email = response; ;
PromptDialog.Text(
context: context,
resume: ResumeGetPhone,
prompt: "Please share your Mobile Number",
retry: "Sorry, I didn't understand that. Please try again."
);
}
public virtual async Task ResumeGetPhone(IDialogContext context, IAwaitable<string> mobile)
{
string response = await mobile;
phone = response;
await context.PostAsync(String.Format("Hello {0} ,Congratulation :) Your C# Corner Annual Conference 2018 Registrion Successfullly completed with Name = {0} Email = {1} Mobile Number {2} . You will get Confirmation email and SMS", name, email, phone));
context.Done(this);
}
}
}
After execute above code, the output look like below
MessagesController Class :
The RootDialog class is added to the conversation in the MessageController class via the Post() method. In the Post() method, the call to Conversation.SendAsync() creates an instance of the RootDialog, adds it to the dialog stack to make it the active dialog, calling the RootDialog.StartAsync() from Post method[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
}
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
}
}
Run Bot Application:
The emulator is a desktop application that lets we test and debug our bot on localhost. Now, you can click on "Run the application" in Visual studio and execute in the browser.- Test Application on Bot Emulator
- You can follow the below steps for test your bot application.
- Open Bot Emulator.
- Copy the above localhost url and paste it in emulator e.g. - http://localHost:3979
- You can append the /api/messages in the above url; e.g. - http://localHost:3979/api/messages.
- You won't need to specify Microsoft App ID and Microsoft App Password for localhost testing, so click on "Connect".
0 Comments