The code I built below is derived from this example on github. Here is the test code I wrote:
using Microsoft.Bot.Builder.Dialogs; using System; using System.Threading.Tasks; using Microsoft.Bot.Connector; using System.Threading; namespace TestBot.Dialogs { [Serializable] public class TimerDialog : IDialog<object> { private string fromId; private string fromName; private string toId; private string toName; private string serviceUrl; private string channelId; private string conversationId; private string message; public Task StartAsync(IDialogContext context) { context.Wait(MessageReceivedAsync); return Task.CompletedTask; } private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result) { var activity = await result; this.toId = activity.From.Id; this.toName = activity.From.Name; this.fromId = activity.Recipient.Id; this.fromName = activity.Recipient.Name; this.serviceUrl = activity.ServiceUrl; this.channelId = activity.ChannelId; this.conversationId = activity.Conversation.Id; PromptDialog.Number(context,this.AfterNumberGiven , "Set a timer for how many seconds?"); } public async Task AfterNumberGiven(IDialogContext context, IAwaitable<long> result) { var amountOfTime = await result; var t = new Timer(new TimerCallback(TimerEvent)); t.Change(amountOfTime*1000, Timeout.Infinite); this.message = $"Your {amountOfTime} second timer is up. It started {DateTime.Now}"; await context.PostAsync($"I will contact you in {amountOfTime} seconds"); context.Done(""); } public void TimerEvent(object target) { var userAccount = new ChannelAccount(this.toId, this.toName); var botAccount = new ChannelAccount(this.fromId, this.fromName); var connector = new ConnectorClient(new Uri(serviceUrl)); var message = Activity.CreateMessageActivity(); message.From = botAccount; message.Conversation = new ConversationAccount(id: this.conversationId); message.Text = this.message; message.Locale = "en-Us"; connector.Conversations.SendToConversationAsync((Activity)message); } } }
So the above code is actually quite simple. Whereas the example on github went through the trouble of storing all data in a static object, I simply stored it in this dialog itself. Based on my understanding of how state works, this dialog is essentially serialized into memory between each request. Here is what the interaction looks like in the emulator.
You will note that I was able to kick off multiple timers and have them respond differently. Okay, so here were some notes I made:
1) I wanted to know more about the data required to continue a conversation.
Here is a snippit from the debugger of my interaction. Please note that these were captured from the emulator.
The key pieces above are the channelId and the serviceURL. These probably vary greatly based on the channel being selected and I wonder what they look like in slack, etc.
2) What is the minimum set of information I need to issue a response?
In my use case, someone may send a request to my bot to kick off an automation job in a private message, but I might still want to broadcast that to the general channel in slack so that everyone knows the process of jobs. You'll note in the code sample above, I actually don't use the toId/toName in my message response. So it seems, at least in the emulator, you don't need to specify those fields.
When I tried removing the message.From property, the emulator generated a 400 error message.
Ultimately, I guess that some of this behavior will be channel specific, and I'll need to invest further time with slack on all the different options.
This was a good first step and a very basic example on playing around with proactive messages. Now that I have some understanding of what is involved, I will now have to add some external storage, and a method for Azure Automation to communicate back to the user. A future post for sure!
No comments:
Post a Comment