Having a botframework chatbot up and running and responding to user messages is one thing, but how can you send a new message to bring the user back into the conversation if they haven’t just sent a new message for you to reply to?
The botframework documentation and other tutorials will point you towards using Azure Functions and the new ActivityType.Trigger
to handle this which, although being a great use case for Azure Functions, make the underlying implementation harder to understand. It also means you couldn’t easily implement this on AWS, for example.
In this article I’ll show you how to easily implement Proactive Botframework Messaging just using BotFramework fundamentals.
Creating a ResumptionCookie
The key to this is something called the ResumptionCookie
; using this we can save pertinent information from an incoming message, and subsequently create a new message and send it as a sort of “belated reply” to the original message.
First, let’s create a ResumptionCookie
from the original message and save it somewhere so we can get it back again later:
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
var resume = new ResumptionCookie(message);
// Persist this ResumptionCookie somewhere
var data = JsonConvert.SerializeObject(resume);
_resumptionRepo.Save(data);
...
}
Let’s assume that the object has been saved somewhere in the “_resumptionRepo”; I like Azure, so I’ll push it into Azure Table Storage as JSON. The resumption cookie looks like this when serialized for a locally running bot using the botframework channel emulator:
{
"address": {
"botId": "m53jjbfil0kgbmnlm",
"channelId": "emulator",
"userId": "default-user",
"conversationId": "ngef9nnf7j941290",
"serviceUrl": "http://localhost:8546"
},
"userName": "User",
"isTrustedServiceUrl": true,
"isGroup": false
}
Nothing too scary going on there, right?
Using the ResumptionCookie
If you want to send a message to that user at some point later on, you can take your saved ResumptionCookie
, retrieve the original message using the GetMessage()
method, and create a reply.
Firstly retrieve it from your repo – in my case I serialized it as JSON into Azure table storage, so it will be a dynamic
JObject
when I bring it back out:
dynamic resumeData = JsonConvert.DeserializeObject(resumeJson);
Theoretically I should be able to just do something like this:
var resume = resumeData as ResumptionCookie;
or even like this:
ResumptionCookie resume
= JsonConvert.DeserializeObject<ResumptionCookie>(resumeJson);
but there’s no “locale” in the serialized version so it fails to cast.
Now let’s retrieve the constructor info from that object:
string botId = resumeData.address.botId;
string channelId = resumeData.address.channelId;
string userId = resumeData.address.userId;
string conversationId = resumeData.address.conversationId;
string serviceUrl = resumeData.address.serviceUrl;
string userName = resumeData.userName;
bool isGroup = resumeData.isGroup;
Then we can use this to create a new ResumptionCookie
instance, manually passing in the “locale”:
var resume = new ResumptionCookie(
new Address(botId, channelId, userId, conversationId, serviceUrl),
userName, isGroup, "en_GB");
From here we can retrieve the original message and cast as an “Activity”:
var messageactivity = (Activity) resume.GetMessage();
Then create a reply:
var reply = messageactivity.CreateReply();
reply.Text = "Hey! I have more *exciting news*! Come back!";
Finally we can create a new ConnectorClient
from the original message’s ServiceUrl
and send the new message to it:
var client = new ConnectorClient(new Uri(messageactivity.ServiceUrl));
await client.Conversations.ReplyToActivityAsync(reply);
And that’s it! Easy, right? 🙂
Summary
Hopefully you can now see how easy it can be to implement proactive botframework messaging. How you initiate this message is up to you; perhaps from an event on your backend, or on a schedule using something like an Observable.Interval
from System.Reactive.Linq
.
I personally use the Observable.Interval
approach for proactive messaging when a user’s reply takes a significant amount of time to process a response; I can now say something like “gotcha – I’ll work on that and let you know when I’m done”, regularly checking the status of the background task and only responding when there’s something to give to the user.
Let me know how you implement this; I’d love to know.
Hi Robin,
Thanks for the great walk thru on proactive bots! I’m currently working on a LUIS bots using Azure bot services, and how do I make the proactive message? Is the walk thru above applicable on the LUIS template provided in Azure bot services?
Thanks!
I’ve found using the ‘string ResumptionCookie.GZipSerialize()’ extension method and its corresponding static ‘ResumptionCookie.GZipDeserialize(string)’ to be even handier. No arsing about with recreating the ResumptionCookie from your dynamic object!
Nice! I haven’t tried that, thanks for the tip
Do you think it is possible to start a new dialog from a trigger?
I know how to send a message, but I would like to start a dialog, triggered from an azure queue by a function.
Would be great if you can share a Git code.
how do I make the proactive message?
Get the original message or the resumption cookie:
var messageactivity = (Activity) resume.GetMessage();
Create a reply for that message:
var reply = messageactivity.CreateReply();
reply.Text = "Hey! I have more *exciting news*! Come back!";
Send the reply!
var client = new ConnectorClient(new Uri(messageactivity.ServiceUrl));
await client.Conversations.ReplyToActivityAsync(reply);
HI, Robin
Could you also tell is it possible to send a proactive message to skype group? is there any difference with single contact?
Thanks a lot!
ResumptionCookie is obsolete. perhaps you should update the post to mention this.
https://docs.microsoft.com/en-us/dotnet/api/microsoft.bot.builder.dialogs.resumptioncookie?view=botbuilder-3.12.2.4