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.
- using System.Net;
- using System.Net.Http;
- using System.Threading.Tasks;
- using Microsoft.Bot.Builder.Dialogs;
- using Microsoft.Bot.Connector;
- using Microsoft.AspNetCore.Mvc;
-
- namespace BotTry1.Controllers
- {
- [Route("api/[controller]")]
- [BotAuthentication]
- public class MessagesController : Controller
- {
-
-
-
-
- [HttpPost]
- public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
- {
- if (activity?.Type == ActivityTypes.Message)
- {
- await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
- }
- else
- {
- HandleSystemMessage(activity);
- }
-
- return new HttpResponseMessage(HttpStatusCode.OK);
- }
-
- private Activity HandleSystemMessage(Activity message)
- {
- if (message.Type == ActivityTypes.DeleteUserData)
- {
-
-
- }
- else if (message.Type == ActivityTypes.ConversationUpdate)
- {
-
-
-
- }
- else if (message.Type == ActivityTypes.ContactRelationUpdate)
- {
-
-
- }
- else if (message.Type == ActivityTypes.Typing)
- {
-
- }
- else if (message.Type == ActivityTypes.Ping)
- {
- }
-
- return null;
- }
- }
- }
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.
- using System;
- using System.Threading.Tasks;
- using Microsoft.Bot.Builder.Dialogs;
- using Microsoft.Bot.Connector;
-
- namespace BotTry1.Dialogs
- {
- [Serializable]
- public class RootDialog : IDialog<object>
- {
- public Task StartAsync(IDialogContext context)
- {
- context.Wait(MessageReceivedAsync);
-
- return Task.CompletedTask;
- }
-
- private Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
- {
- context.Call<UserProfile>(new EnsureUserProfile(), ProfileEnsured);
- return Task.CompletedTask;
- }
-
- private async Task ProfileEnsured(IDialogContext context, IAwaitable<UserProfile> result)
- {
- var profile = await result;
- context.UserData.SetValue("profile", profile);
- await context.PostAsync($@"Hello {profile.Name} we love {profile.Company}");
- context.Wait(MessageReceivedAsync);
- }
- }
- }
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.
- using System.Threading.Tasks;
- using Microsoft.Bot.Builder.Dialogs;
- using System;
-
- namespace BotTry1.Dialogs
- {
- [Serializable]
- class EnsureUserProfile : IDialog<UserProfile>
- {
- public Task StartAsync(IDialogContext context)
- {
- EnsureNameEntered(context);
- return Task.CompletedTask;
- }
-
- private UserProfile _userProfile;
- private void EnsureNameEntered(IDialogContext context)
- {
- var hasProfile = context.UserData.TryGetValue("profile", out _userProfile);
- if (!hasProfile)
- {
- _userProfile = new UserProfile();
- }
- if (string.IsNullOrEmpty(_userProfile.Name))
- {
- PromptDialog.Text(context, NameEntered, "Please enter name");
-
- }
- else
- EnsureCompanyName(context);
-
- }
-
- private void EnsureCompanyName(IDialogContext context)
- {
- if (string.IsNullOrWhiteSpace(_userProfile.Company))
- {
- PromptDialog.Text(context, CompanyNameEntered, @"What is your company name");
-
- }
- else
- context.Done(_userProfile);
- }
-
- private async Task CompanyNameEntered(IDialogContext context, IAwaitable<string> result)
- {
- _userProfile.Company = await result;
- context.Done(_userProfile);
- }
-
- private async Task NameEntered(IDialogContext context, IAwaitable<string> result)
- {
- _userProfile.Name = await result;
- EnsureCompanyName(context);
-
- }
- }
- }
UserProfile
This serializable data structure will hold the actual profile information. Consider this as the reference data structure that will hold our user profile.
- using System;
-
- namespace BotTry1.Dialogs
- {
- [Serializable]
- public class UserProfile
- {
- public string Name { get; set; }
- public string Company { get; set; }
- }
- }