Sunday, April 10, 2016

PC Bot

The PC in PC Bot means Politically Correct – because it reacts to every event. During its lifetime, a (Microsoft Bot Framework) bot can have many different types of messages, in addition to a normal text message from the user. You can know when a user or bot is added or removed, when a conversation ends, and a couple other message types. This post explains PC Bot and discusses a few interesting aspects of message type handling.
Note: PC Bot doesn’t overreact, as you would expect of proper botiquette.

What are the Message Types?

The Bot Framework documents several Message Types. A lot of folks would probably feel like I do that the names are pretty descriptive. To handle these messages, I created a MessageHandler class, containing the following React method:
        public Message React(Message eventMessage)
        {
            Message responseMessage = null;

            BotEvent eventType = GetBotEvent(eventMessage.Type);

            switch (eventType)
            {
                case BotEvent.BotAddedToConversation:
                    responseMessage = ReactToBotAdded(eventMessage);
                    break;
                case BotEvent.BotRemovedFromConversation:
                    responseMessage = ReactToBotRemoved(eventMessage);
                    break;
                case BotEvent.DeleteUserData:
                    responseMessage = ReactToDeleteUser(eventMessage);
                    break;
                case BotEvent.EndOfConversation:
                    responseMessage = ReactToEndConversation(eventMessage);
                    break;
                case BotEvent.Message:
                    responseMessage = ReactToMessage(eventMessage);
                    break;
                case BotEvent.Ping:
                    responseMessage = ReactToPing(eventMessage);
                    break;
                case BotEvent.UserAddedToConversation:
                    responseMessage = ReactToUserAdded(eventMessage);
                    break;
                case BotEvent.UserRemovedFromConversation:
                    responseMessage = ReactToUserRemoved(eventMessage);
                    break;
                case BotEvent.Unknown:
                default:
                    responseMessage = ReactToUnknown(eventMessage);
                    break;
            }

            responseMessage.Text += 
                ", JSON: " + JsonConvert.SerializeObject(eventMessage);

            return responseMessage;
        }
The listing above contains a switch statement with a case for each message type. Although this is a demo, I showed a little bit of defensive programming, by adding the BotEvent.Unknown case with a fall-through to the default case. Each case calls a method, specifically for that message type. I’ll explain the JSON part of the message, at the bottom of the method in an upcoming section.
Notice that each of the cases are an enum member. The BotEvent enum isn’t part of the Bot Framework – I added it myself. That’s why the React method calls the GetBotEvent method, shown below, ahead of the switch statement.
        BotEvent GetBotEvent(string eventType)
        {
            BotEvent botEvent;

            if (!Enum.TryParse<BotEvent>(eventType, out botEvent))
                botEvent = BotEvent.Unknown;

            return botEvent;
        }
The GetBotEvent method above tries to convert the message type from a string to a BotEvent type. If that doesn’t work, it returns BotEvent.Unknown. I could have simply created case statements as strings, but if you prefer a more strongly typed approach, this is one.
You might ask, “Why aren’t the messages already strongly typed?”. The reason has to do with the fact that the Bot Framework is cross-platform. As long as you give it a compatible interface (e.g. Post endpoint with appropriately formatted JSON content and message types), the Bot Framework is okay with that. In other platforms, strongly typed could mean something very different, but strings are well-known. A perfect example of another platform is node.js, for which the Bot Framework already has a Node.js SDK.

Handling Message Types

The messages called in the switch case statements receive a request message and return a response message, containing acknowledgement and some diagnostic details. Here’s an example of methods to handle UserAddedToConversation and UserRemovedFromConversation messages:
        Message ReactToUserAdded(Message eventMessage)
        {
            return eventMessage.CreateReplyMessage(
                $"User Added From - Address: {eventMessage.From.Address}, Name: {eventMessage.From.Name}");
        }

        Message ReactToUserRemoved(Message eventMessage)
        {
            return eventMessage.CreateReplyMessage(
                $"User removed From - Address: {eventMessage.From.Address}, Name: {eventMessage.From.Name}");
        }
Each method uses the CreateReplyMessage method to build a response message, but notice the interpolated string expressions. They’re reading the From property of the eventMessage, which contains additional information. Some bots might want to keep track of who they’re speaking with for proper conversation context and this would be an easy way to do so. There are many other properties of the Message type to obtain information on the particular event.
Additionally, you can also collect state for debugging and diagnostics.

Debugging?

To test your logic on most of these messages, you can use the Bot Framework Emulator, shown below:
image
Tip: When you’re working with multiple bots, it’s convenient to change the bot’s port number. The port number, in the emulator, for PC Bot is 3977 (not the default 3978).
The gray toolbar, above the JSON window, has a drop-down list with various message types. Select the message you want and click the Send button. In the figure, I selected the User Removed From Conversation message type. The chat window shows the response from the bot, which corresponds to the ReactToUserRemoved method response above.
You also see a lot of JSON in the response message, repeated below, which came from the bottom of the React method, before returning the responseMessage:
            responseMessage.Text += 
                ", JSON: " + JsonConvert.SerializeObject(eventMessage);
You see, the Bot Framework Message type serializes to JSON very easily. One problem this solves is when learning how to work with a new channel. There are only a few channels available as I write this blog post, but there will be more in the future. Each channel has it’s own unique characteristics. The Bot Framework accounts for this via a channelData property where you can read meta-data specific to that channel. The emulator doesn’t have this property, but the Slack channel and others do. You need a way to see what the channel is sending your bot. You could say, “Well, I’ll just read the documentation.” and you would be right, until you’re not. Documentation gets old, changes, and sometimes doesn’t exist and being able to inspect those values can be helpful.

Using MessageHandler

Here’s how I use the MessageHandler class:
        public async Task<Message> Post([FromBody]Message message)
        {
            return new MessageHandler().React(message);
        }
Because this bot is PC, it will react to every message.
@JoeMayo

No comments:

Post a Comment