This sample demonstrates how to control a chatbot, built on the Microsoft Bot Framework, utilizing backchannel messages. The covering note (a blog post) for this sample is here: Remotely Controlled Bots. It is recommended to read that first to understand how the backchannel messages work.
- Clone or copy the repository
- Register a bot for this sample in the bot portal
- Unfortunately registering and publishing the bot is the easiest way to get to test drive it, because the implementation is dependent on Direct Line
- Get the app ID and app password (a step in registration phase) and insert them into the Web.config
- Publish the bot (and insert the newly created endpoint to the bot portal)
- In the portal, activate Direct Line and get the first secret key
- Insert the secret key into Program.cs
- Say something to the bot so that it knows who you are (your virtual address),
you can do this on any channel the bot is on or using emulator
- If you don't say anything the bot won't know you and cannot notify you
- Run the console app (RemoteControlBotControllerSample)
- Enjoy your three notifications
The implementation consists of roughly 3 different concepts:
- Backchannel receiver (implemented in this sample by
NotificationsScorable,
see
PrepareAsync
method) - Notification type and handler (in Notifications folder)
- Message routing for delivering the notifications to (specific) users
- This message routing logic is a subset of features implemented and documented in Bot Message Routing (component) project
Let's look at two scenarios. First, when the bot receives a normal message from the user:
- A user sends a message to the bot
- The Microsoft Bot Framework translates this to an object called
Activity
, where itsText
property will contain the message
- The Microsoft Bot Framework translates this to an object called
- The received
Activity
instance is seemingly passed to the root dialog in MessagesController class - Autofac checks for registered handlers at the previous step (step 2)
- It will find two: MessageRouterScorable and NotificationsScorable
MessageRouterScorable
always stores the sender and the receiver of a message, if they have not been seen before (see Chatbots as Middlemen article to understand why)NotificationsScorable
on the other hand will look for specific backchannel messages, where theText
property of theActivity
will read"notification"
and then extracts the actual notification content from another property ofActivity
namedChannelData
- In this case no backchannel message is detected and the root dialog is invoked
- Root dialog handles the message as it is defined to
In the second scenario a backend (or other entity) sends a backchannel message to the bot according to the notifications protocol we've agreed on:
- A notification specific backchannel message is sent to the bot
- The received
Activity
instance is seemingly passed to the root dialog inMessagesController
class - however it the dialog will never receive theActivity
because of theNotificationsScorable
class, and that is because... - Autofac happens
- Now a backchannel message is detected by
NotificationScorable
, the score will exist and its value will be1.0d
(fancy way of saying the value of action double type is exactly 1), and as a result, NotificationsManager will be told to handle theActivity
instance and the root dialog is never invoked
- Now a backchannel message is detected by
NotificationsManager
usesMessageRouterManager
to deliver the notifications to desired users
As you noticed from the descriptions of the previous scenarios,
Autofac - an implementation of inversion of control
(IoC) container - makes it harder to understand the execution flow of the code
without a thorough inspection (or proper documentation). In other words, IoC
makes it more difficult to have self-documenting code. That's why I don't like
Autofac or IoC containers in general. I'd rather have the backchannel detection
(and other things) written in
MessagesController class,
before the decision to forward the Activity
instance to the root dialog is made:
WebApiConfig.MessageRouterManager.MakeSurePartiesAreTracked(activity);
string notificationData = string.Empty;
if (NotificationsManager.TryGetNotificationData(activity, out notificationData))
{
// A notification related backchannel message was detected
await NotificationsManager.SendNotificationAsync(notificationData);
}
else
{
await Conversation.SendAsync(activity, () => new RootDialog());
}
However, if you are using the Microsoft Bot Builder SDK, there is no escape from Autofac since the SDK is built using it. You can still avoid the use in the code you write. But that's my preference. You will have yours and it's neither right nor wrong. Only debatable :)
- Remotely Controlled Bots article
- Interruption Bot Sample: "Contains a bot that can handle interuptions from a proactive bot. Low Prio interruptions are stacked and only shown when a defined dialog has stopped."
- Bot Message Routing (component) project
- Intermediator Bot Sample