Introduction
If you’re not sure what a Bot is, then check out What is Microsoft Bot Framework Overview.
A Bot doesn’t have to be intelligent and/or understand complex input. Although this sounds the most exciting aspects of using Bots we could also just use a Bot like an API into our services/applications.
What Bots do generally have is some simple user interface for a user to interact with the Bot. We’ve probably heard of or used a “chat bot” in the guise of a text input box which the user types into, but the bot could take input from buttons as well or other control/input mechanisms.
Prerequisites
Whilst we can write our Bot code from scratch we may as well use the Bot items templates which can be downloaded from http://aka.ms/bf-bc-vstemplate
Cortana skills templates can be downloaded from https://aka.ms/bf-cortanaskill-template but are not required for this post.
Once downloaded, place the zip files (for these templates) into %USERPROFILE%\Documents\Visual Studio 2017\Templates\ProjectTemplates\Visual C#.
Now when you start/restart Visual Studio 2017 these templates will be available in New | Project.
Next up you might like to download the Bot framework emulator from Microsoft/BotFramework-Emulator on github. I’m using the botframework-emulator-Setup-3.5.31 version for this and other posts.
Writing my first Bot
- Create New | Project and select Bot application (mine’s named TestBot)
- Build the project to pull in all the NuGet packages
At this point we have an ASP.NET application or more specifically we’ve created an ASP.NET Web API. The key areas of interest for us (at this point) is the Controllers/MessageController.cs file and the Dialogs/RootDialog.cs file.
At this point the template has created an Echo bot. We can run up this Bot application and you’ll be presented with a web page stating we need to register our Bot. Ofcourse we’d probably prefer to test things out first on our local machine without this extra requirement. We can run the Bot Framework Channel Emulator (as mentioned in the prerequisites section). So let’s see our Bot in action
- Run you Bot application
- Run the Bot emulator
- In the emulator type the following http://localhost:3979/api/messages (obviously replace the localhost:3979 with whatever host/port you are running your Bot application from).
- If all worked, simply type some message into the “Type your message…” textbox and the Bot your respond with “You sent
which was
If you click on either the message or response, the emulator will display the JSON request and response data.
What’s the code doing?
We’ve seen that we can communicate with our Bot application but what’s really happening?
Obviously the Bot framework handles a lot of the communications for us. I’m not going to go too in depth with this side of things for the simple reason that, at the moment, I don’t know enough about how it works to confidently state how everything fits together. So hopefully I’ll get around to revisiting this question at another time.
I mentioned that MessagesController.cs was a key file. This is where we received messages and also where system messages can be handled. Think of it as the entry point to your application. In the template echo Bot we have this code
if (activity.Type == ActivityTypes.Message) { await Conversation.SendAsync(activity, () => new Dialogs.RootDialog()); }
When a Message type is received (types can be Message, Event, Ping and more, see ActivityTypes).
If you take a look at Dialogs/RootDialog.cs within your solution you’ll see that this implements the IDialog<> interface which requires the Task StartAsync(IDialogContext context) to be implemented. This method basically calls the method MessagedReceivedAsync method (shown below) which is where our “echo” comes from
private async Task MessageReceivedAsync( IDialogContext context, IAwaitable<object> result) { var activity = await result as Activity; // calculate something for us to return int length = (activity.Text ?? string.Empty).Length; // return our reply to the user await context.PostAsync( $"You sent {activity.Text} which was {length} characters"); context.Wait(MessageReceivedAsync); }
Making the Bot a little more useful
We’ve looked at generating a echo Bot which is a great starting point but let’s now start code something of our own.
You’ll notice from the code
int length = (activity.Text ?? string.Empty).Length;
This shows that the activity is supplied with the text sent to the Bot via the message input. Obviously in a more complicated interaction, one might send this text to a NPL service, such as Luis or slightly more simply, use RegEx or ofcrouse creating your own parser. But if our Bot was just designed to respond to simple commands, be they sent as messages via a user textual input or from a button press, then the MessageReceivedAsync code could be turned into the equivalent of a switch statement.
Let’s rewrite the MessageReceivedAsync method to act as a basic calculator where we have three commands, using ? to list the commands, and the add and subtract commands, both of which take 2 parameters (we could easily extend these to more parameters or switch to using operators but let’s start simple).
Simply remove the existing MessageReceivedAsync method and replace with the following
private async Task MessageReceivedAsync( IDialogContext context, IAwaitable<object> result) { var activity = await result as Activity; string response = "I do not understand this command"; var command = activity .Text .ToLower() .Split(new []{' '}, StringSplitOptions.RemoveEmptyEntries); switch (command[0]) { case "?": response = ListCommands(); break; case "add": response = Add(command[1], command[2]); break; case "subtract": response = Subtract(command[1], command[2]); break; } await context.PostAsync(response); context.Wait(MessageReceivedAsync); } private string ListCommands() { return "? => list commands\n\nadd a b => adds a and b\n\nsubtract a b => subtract a from b\n\n"; } private string Add(string a, string b) { double v1, v2; if (Double.TryParse(a, out v1) && Double.TryParse(b, out v2)) { return (v1 + v2).ToString(); } return "Non numeric detected"; } private string Subtract(string a, string b) { double v1, v2; if (Double.TryParse(a, out v1) && Double.TryParse(b, out v2)) { return (v2 - v1).ToString(); } return "Non numeric detected"; }
Obviously we’re light on error handling code etc. but you get the idea.
Note: In the ListCommands, to output a new line (i.e. display the list of commands in a multi-line response using the emulator) we need to use two newline characters. We could call context.PostAsync multiple times but this would appear as different message responses.