Bot In Action – A More Advanced Hello World

Whenever I talk about Bots, I always think of those Sci-fi movies where you can have those super complex and intelligent digital beings. When I heard that the Bot Framework is out, I wanted to try it and it wasn’t that easy to learn and develop. I know I am a bit late in this blog, so the words might be new but the technology is not that recent.

Let's start coding then.

First off, you need to follow this post to set up the environment and the template for the bot. So, when you create a new bot project, you will be given some template files on which you can work directly. This is what we are going to do. I will directly use those templates and change them to more meaningful and understandable code.

If you see the template generated files, you will observe that the code is divided into two main parts - Controller and Dialog. The Controller will send the message that will come from the user via the bot interface and it will send it to the dialog. The dialog will process the message and will reply accordingly. We can have multiple dialogs which you can consider as routes to a website.

How we will work.

MessageController

No change here. So, we will use the same file as the template generated.

  1. using System.Net;  
  2. using System.Net.Http;  
  3. using System.Threading.Tasks;  
  4. using Microsoft.Bot.Builder.Dialogs;  
  5. using Microsoft.Bot.Connector;  
  6. using Microsoft.AspNetCore.Mvc;  
  7.   
  8. namespace BotTry1.Controllers  
  9. {  
  10.     [Route("api/[controller]")]  
  11.     [BotAuthentication]  
  12.     public class MessagesController : Controller  
  13.     {  
  14.         /// <summary>  
  15.         /// POST: api/Messages  
  16.         /// Receive a message from a user and reply to it  
  17.         /// </summary>  
  18.         [HttpPost]  
  19.         public async Task<HttpResponseMessage> Post([FromBody]Activity activity)  
  20.         {  
  21.             if (activity?.Type == ActivityTypes.Message)  
  22.             {  
  23.                 await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());  
  24.             }  
  25.             else  
  26.             {  
  27.                 HandleSystemMessage(activity);  
  28.             }  
  29.   
  30.             return new HttpResponseMessage(HttpStatusCode.OK);  
  31.         }  
  32.   
  33.         private Activity HandleSystemMessage(Activity message)  
  34.         {  
  35.             if (message.Type == ActivityTypes.DeleteUserData)  
  36.             {  
  37.                 // Implement user deletion here  
  38.                 // If we handle user deletion, return a real message  
  39.             }  
  40.             else if (message.Type == ActivityTypes.ConversationUpdate)  
  41.             {  
  42.                 // Handle conversation state changes, like members being added and removed  
  43.                 // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info  
  44.                 // Not available in all channels  
  45.             }  
  46.             else if (message.Type == ActivityTypes.ContactRelationUpdate)  
  47.             {  
  48.                 // Handle add/remove from contact lists  
  49.                 // Activity.From + Activity.Action represent what happened  
  50.             }  
  51.             else if (message.Type == ActivityTypes.Typing)  
  52.             {  
  53.                 // Handle knowing tha the user is typing  
  54.             }  
  55.             else if (message.Type == ActivityTypes.Ping)  
  56.             {  
  57.             }  
  58.   
  59.             return null;  
  60.         }  
  61.     }  
  62. }  

RootDialog

Here, we will instruct the dialog to get user profile information. Do remember that this class needs to be serializable. This class is called to ensure the user profile- either by loading an existing profile or creating a new one if stored. And then, return the context back to the root dialog.

  1. using System;  
  2. using System.Threading.Tasks;  
  3. using Microsoft.Bot.Builder.Dialogs;  
  4. using Microsoft.Bot.Connector;  
  5.   
  6. namespace BotTry1.Dialogs  
  7. {  
  8.     [Serializable]  
  9.     public class RootDialog : IDialog<object>  
  10.     {  
  11.         public Task StartAsync(IDialogContext context)  
  12.         {  
  13.             context.Wait(MessageReceivedAsync);  
  14.   
  15.             return Task.CompletedTask;  
  16.         }  
  17.   
  18.         private Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)  
  19.         {  
  20.             context.Call<UserProfile>(new EnsureUserProfile(), ProfileEnsured);  
  21.             return Task.CompletedTask;  
  22.         }  
  23.   
  24.         private async Task ProfileEnsured(IDialogContext context, IAwaitable<UserProfile> result)  
  25.         {  
  26.             var profile = await result;  
  27.             context.UserData.SetValue("profile", profile);  
  28.             await context.PostAsync($@"Hello {profile.Name} we love {profile.Company}");  
  29.             context.Wait(MessageReceivedAsync);  
  30.         }  
  31.     }  
  32. }  

EnsureProfileDialog

This dialog will get the user profile from the user. You can also notice that this class is serializable. Now, this class ensures that the user has entered the related information. The first entry point will be the StartAsync from which we will pass the context. After that, we will try to get the user profile via the profile id to see if the user is new or a returning user.

If the user is a returning user, then we will just load the existing profile but if the profile is not found, then we will have to create a new profile. We will prompt the user to enter the name and company name. Once we have that information, we will return the control to RootDialog.
  1. using System.Threading.Tasks;  
  2. using Microsoft.Bot.Builder.Dialogs;  
  3. using System;  
  4.   
  5. namespace BotTry1.Dialogs  
  6. {  
  7.     [Serializable]  
  8.     class EnsureUserProfile : IDialog<UserProfile>  
  9.     {  
  10.         public Task StartAsync(IDialogContext context)  
  11.         {  
  12.             EnsureNameEntered(context);  
  13.             return Task.CompletedTask;  
  14.         }  
  15.   
  16.         private UserProfile _userProfile;  
  17.         private void EnsureNameEntered(IDialogContext context)  
  18.         {  
  19.             var hasProfile = context.UserData.TryGetValue("profile"out _userProfile);  
  20.             if (!hasProfile)  
  21.             {  
  22.                 _userProfile = new UserProfile();  
  23.             }  
  24.             if (string.IsNullOrEmpty(_userProfile.Name))  
  25.             {  
  26.                 PromptDialog.Text(context, NameEntered, "Please enter name");  
  27.   
  28.             }  
  29.             else  
  30.                 EnsureCompanyName(context);  
  31.   
  32.         }  
  33.   
  34.         private void EnsureCompanyName(IDialogContext context)  
  35.         {  
  36.             if (string.IsNullOrWhiteSpace(_userProfile.Company))  
  37.             {  
  38.                 PromptDialog.Text(context, CompanyNameEntered, @"What is your company  name");  
  39.   
  40.             }  
  41.             else  
  42.                 context.Done(_userProfile);  
  43.         }  
  44.   
  45.         private async Task CompanyNameEntered(IDialogContext context, IAwaitable<string> result)  
  46.         {  
  47.             _userProfile.Company = await result;  
  48.             context.Done(_userProfile);  
  49.         }  
  50.   
  51.         private async Task NameEntered(IDialogContext context, IAwaitable<string> result)  
  52.         {  
  53.             _userProfile.Name = await result;  
  54.             EnsureCompanyName(context);  
  55.   
  56.         }  
  57.     }  
  58. }  
UserProfile

This serializable data structure will hold the actual profile information. Consider this as the reference data structure that will hold our user profile.
  1. using System;  
  2.   
  3. namespace BotTry1.Dialogs  
  4. {  
  5.     [Serializable]  
  6.     public  class UserProfile  
  7.     {  
  8.         public string Name { getset; }  
  9.         public string  Company { getset; }  
  10.     }  
  11. }  
Ebook Download
View all
Learn
View all