Here come the bots!

2016-04-25_12-07-14

One of the more interesting announcements from the recent Microsoft //build conference, was the introduction of the Bot Framework. Demoed during the keynote, was a Domino’s Pizza Bot that allowed the speaker to order a pizza, using natural language, from on stage. Seemed pretty neat, so I decided then, that I wanted to create a bot myself. I thought that I would create a bot that would hook up to that game I can’t seem to stop playing. Luckily Destiny even has a public API, that makes it possible to pull various stats and activities about the game with some minor hoops to jump through.

 

Getting Started

 

I decided I would build my bot using Visual Studio 2015, so the first step was to Download and install the new Bot Application template into my Visual Studio templates folder. After which, I could create a new project, and have a fully functional Echo Bot starting point. A user sends a message to the bot, and the bot simply echos it back. There are a handful of system messages the bot can receive as well such as “BotAddedToConversation” or “UserAddedToConversation”. Since my bot is going to just be used to return information from the Destiny API, these are going to go unhandled.

After verifying the bot was working with the bot emulator I deployed it to an Azure backend, and registered the bot on the dev.botframework.com portal. A full guide on getting a bot started in .NET can be found here on the botframework website.

 

Understanding Language

 

One of the most interesting parts of the Domino’s Pizza Bot demo, was the natural language used to communicate with the bot. The pizza bot is able to parse out various properties from a phrase such as “send me a large pizza with pepperoni”, and know that the size is “large” and the topping is “pepperoni”. Sadly, the technology demonstrated in the keynote is not currently available, but I did come across a similar service in LUIS (Language Understanding Intelligence Service). Although not as fancy of an interface, LUIS would allow me to registered “intents” and detect “entities” within messages sent to my bot.

After registering an account on luis.ai and creating a new application, I started by making some Entities to hold detected values.

  • Statistic: This would be used to denote which piece of information to poll from a player’s Destiny stats. (such as weapon specific kills, or activities completed).
  • Activity: Player stats on Destiny activities are separated into PvE (Player versus Environment) or PvP (Player versus Player), this entity is going to be used to refine my dataset being returned when requesting player stats.
  • Vendor: I had planned on allowing my bot to poll the specific inventories of any of the vendors in the Tower.
  • PlayerPair: I wanted my bot to be able to compare the stats of different different players, so I created this Entity with two Children: “Player1” and “Player2”.
  • PlayerEntity: I am using this entity to capture the Console the player is on, and associated PSN ID or Gamertag. I later realized that I could do a player search without knowing the console.

 

With the Entities created, I could start creating Intents. At a basic level, an Intent is the output of sending input to LUIS. A message is evaluated, and the Intent that best matches, is returned to my bot, along with any recognized Entities.

I started by creating a new Intent named “Stats” that I would use to return Destiny stats about a specific player denoted by a recognized Gamertag. It has 2 required parameters, “Statistic” and “Gamertag”, with one optional parameter, “Activity”. When parameters are required, the Intent will not be recognized as a valid result unless all the required parameters are recognized as well.

 

Adding a new Intent named Stats

 

After creating an Intent, LUIS must be trained on how to recognize this. An utterance is an sample input that might be provided to the service. After entering an utterance and picking the appropriate Intent, you can select text to denote what the Entities contained in the utterance should be. Through adding a variety of utterances, and appropriating tagging them, LUIS will start to gain more confidence in its recognition.

 

Adding a new utterance
Adding a new utterance

 

In the end, I registered 12 Intents and entered over 100 different utterances. Now that my bot was registered on the botframework site, hosted on Azure, and registered with LUIS, I was finally ready to coding.

 

Building the Destiny Ghost Bot

 

Hooking up my LUIS application to my bot’s code, was surprisingly straight forward, thanks to the botbuilder SDK. Where previously my bot would receive a message, and echo back a response, now my bot will receive a message, and return back a LuisDialog object

	[BotAuthentication]
	public class MessagesController : ApiController
	{
		/// <summary>
		/// POST: api/Messages
		/// Receive a message from a user and reply to it
		/// </summary>
		public async Task<Message> Post([FromBody]Message message)
		{
			if (message.Type == "Message")
			{
				// return our reply to the user
				return await Conversation.SendAsync(message, () => new DestinyDialog());
			}
			else
			{
				return HandleSystemMessage(message);
			}
		}

The important thing to note is that the DestinyDialog class has a LuisModel attribute containing both the LUIS App ID, and a Subscription Key from Azure. Next several methods are defined, one for each Intent with a LuisIntent attribute matching it’s name as created on LUIS.

	[LuisModel("APP_ID", "SUBSCRIPTION_KEY")]
	[Serializable]
	public class DestinyDialog : LuisDialog<object>
	{
		// Get Destiny API key from https://www.bungie.net/en/User/API
		private readonly string apiKey = "API_KEY";

		public DestinyDialog(ILuisService service = null)
			: base(service)
		{
		}

		[LuisIntent("")]
		public async Task None(IDialogContext context, LuisResult result)
		{
			context.Wait(MessageReceived);
		}

		[LuisIntent("Stats")]
		public async Task GetStatForGamertag(IDialogContext context, LuisResult result)
		{
			EntityRecommendation statistic;
			if (!result.TryFindEntity("Statistic", out statistic))
			{
				// statistic is a required Entity, this should never happen
			}

Retrieving the recognized Entities is then as simple as calling TryFindEntity on the passed in LuisResult. Then I take the parameters, call into the Destiny API, and send a response back to the user. Suddenly, my bot became a whole lot more interesting.

 

Now we're talkin'
Now we’re talkin’

 

Conclusion

 

The Microsoft Bot Framework allow me to get a bot up and running very quickly, I was able to easily add natural language understanding. Not only that, but my bot is accessible across several different channels such as Skype, Slack, GroupMe and more. The bots are coming, and I for one, welcome our new bot overlords.
 
Try interacting with the bot yourself with the embeded web interface (one of the botframework channels) below
 

The Source Code for my Destiny Ghost Bot is available on GitHub.