Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to create Proactive Bot with Microsoft Bot Framework

0.00/5 (No votes)
17 Mar 2017 1  
This article will guide you step by step on how to create a Proactive Bot with Microsoft Bot Framework.

Introduction

This article explains how to create a proactive bot using Microsoft Bot Framework. The nice example is given at Microsoft Bot framework documentation here. https://docs.botframework.com/en-us/azure-bot-service/templates/proactive/

What is a Proactive Bot and Proactive Messages

A proactive bot is a bot which sends the messages to the user proactively. I mean, without user interaction for information. i.e. Alarm Bot.

To achieve this functionality,Bot framework uses proactive messages. examples of Proactive messages are Discount Notifications, latest items notifications etc. which comes to your chatbot automatically on some timely basis.

PreRequisites for Proactive Bot

To start developing you will need to following things.

  1. Bot deplyoed on bot framework and configured on any channel i.e. skype.
  2. Directline Channel configured on the Bot Framework.
  3. Azure account with permissions of creating Azure functions and Azure Database.

Background

The article is divided in two sections. In section 1 will show how to create a basic proactive bot message and in another section 2 will show how to manage it with good user interface with azure database.

Section#1

Basic Proactive Bot

Basically to send a proactive message to bot we need to trigger the bot for the messages. The bot can be triggered in many ways, but the easiest way to trigger a bot to use an Azure Function. By using Azure Functions we can set the out put to the Bot (This will be explained later in this blog).

So to begin with first create the bot solution in visual studio. And name it as ProactiveBot.

Than create a class file named ProactiveBusiness.cs, than add the following code snippet in your ProactiveBusiness.cs class.

[Serializable]
public class ProactiveBusiness : IDialog<object>
{
        public async Task StartAsync(IDialogContext context)
        {
            context.Wait(MessageReceivedAsync);
            
        }
        private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
        {

        }
}

Your message controller will look like this.

public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
        {
            if (activity != null)
            {
                try
                {
                    switch (activity.GetActivityType())
                    {
                        case ActivityTypes.Message:
                            activity.Text = activity.RemoveRecipientMention();
                            await Conversation.SendAsync(activity, () => new ProactiveBusiness());                            
                            break;                       
                    }
                }
                catch (Exception ex)
                {

                }
            }
            var response = Request.CreateResponse(HttpStatusCode.OK);
            return response;
        }

Now we need a mediator which stores the data and pass to Bot. We can store the data to the AzureQueue storage to start proactive messaging.

Here, to receive the proactive messages from the user we need the message along with ResumptionCookie of the user. By using which we can identify the user and send the proactive message. So, we will save the message with ResumptionCookie and the text in the json format in the queue.

To do so, first create a class QueueMessage for the message in the ProactiveBusiness.cs class as below.

[Serializable]
public class QueueMessage
{
        public ResumptionCookie ResumptionCookie;
        public string MessageText;
}

than add the message and the resumption cookie in the QueueMessage as shown below.

QueueMessage queueMessage;

private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
    var msg = await argument;

    ResumptionCookie resCookie = new ResumptionCookie(msg);
    queueMessage = new QueueMessage
    {
        ResumptionCookie = resCookie,
        MessageText = msg.Text
    };
}

Now we will add this message to AzureQueue. Below are the steps to show hot to add message to AzureQueue. Good example is given at here

First, you need to add the nuget package to use the AzureStorage. Below is the link for it.

Microsoft Azure Storage Client Library for .NET

Create Azure Storage Account

Login to Azure and Create storage account as shown below.

After Creating the storage navigate to keys section and copy the storage account name and key (we will need this key to create a connection string).

our connection string will be in below format as per the above information.

DefaultEndpointsProtocol=https;AccountName=<storage-account-name>;AccountKey=<account-key>

Now we need to put this storage account connection string in our appconfig section in web.config file of bot solution as shown below.

<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=<stroge-account-name>;AccountKey=<account-key>" />

write the following code to store the message to the queue

CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));

// Create the queue client.
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

// Retrieve a reference to a container.
CloudQueue queue = queueClient.GetQueueReference("myqueue");

// Create the queue if it doesn't already exist
queue.CreateIfNotExists();
            
CloudQueueMessage messageNew = new CloudQueueMessage(JsonConvert.SerializeObject(queueMessage));
queue.AddMessage(messageNew);

Final code will look like this to store the current message to Azure Queue Storage.

(Sometimes it happen that the data stored in queue will not ber retrived, this is because of the timeout mechanism of queue)

References

using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

using Microsoft.Azure; // Namespace for CloudConfigurationManager
using Microsoft.WindowsAzure.Storage; // Namespace for CloudStorageAccount

using Microsoft.WindowsAzure.Storage.Table; // Namespace for Table storage types
using Microsoft.WindowsAzure.Storage.Queue;
using Newtonsoft.Json;

Code

[Serializable]
public class QueueMessage
{
        public ResumptionCookie ResumptionCookie;
        public string MessageText;
}
   

[Serializable]
public class ProactiveBusiness : IDialog<object>
{
        public async Task StartAsync(IDialogContext context)
        {
            context.Wait(MessageReceivedAsync);

        }

        QueueMessage queueMessage;
        private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
        {
            var msg = await argument;

            ResumptionCookie resCookie = new ResumptionCookie(msg);
            queueMessage = new QueueMessage
            {
                ResumptionCookie = resCookie,
                MessageText = msg.Text
            };

            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));

            // Create the queue client.
            CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

            // Retrieve a reference to a container.
            CloudQueue queue = queueClient.GetQueueReference("myqueue");

            // Create the queue if it doesn't already exist
            queue.CreateIfNotExists();

            CloudQueueMessage messageNew = new CloudQueueMessage(JsonConvert.SerializeObject(queueMessage));
            queue.AddMessage(messageNew);
        }

}

Create Azure Functions

Our first task to store data to azure queue is complete. Now we will create a queue triggered function in azure.

This function will be triggered automatically when any item will be added int the queue. and we will set the output of function to our bot in the azure function.

To do so, Create a function app and give it any name you want. Here, while creating the function app make sure to select the Storage account which you have created to store the queue data.

After creating the function app, navigate to it and create a QueueTrigger function from the Create Custom Function section. Here the list of functions will be given and we need to select QueueTriggered-CSharp.

On selecting it scroll down the page and provide the QueueTriggeredFunction in the 'Name your Function' section. Further, in Queue Name give the name of the queue we have created i.e. myqueue . In the Storage Account connection section we need to give the connection string to our storage account.

If you already have created the connection string than select it, Or you can add it later on (for now save it from any connection string available).

Add connection string in AzureFunction

To add connection string go to the function app settings of the function app you have just created.

Here under develop section there will be Application Settings. click on configure app settings section button besides Application Settings.

This will open up the settings section . There will be different section there i.e. General Settings, Debuggin, App Settings etc.. Here we need to go to the AppSettings and add the key of our storage account. Provide the name of the key in key textbox i.e. StorageConnectionString and the value of the conection string as used in our code above. i.e. DefaultEndpointsProtocol=https;AccountName=<stroge-account-name>;AccountKey=<account-key> , and then save the page. The storage connection string will populate in the connection string section of the app in some time.

Once the connection string populates, click on our QueueTriggered Function and than click on Integrate section. Here a drop down will be there to select the conection string, select StorageConnectionString and save it

Set Output of function to Bot Framework

Now, we need to set the output to Bot framework, to do so go to our QueueTriggeredFunction and click on the Integrate Section belwo it. Here, we will find the Output Section on right side. We need to click on +New Output this will open the Output settings panel below it. Here we need to provide three things.

1. Bot Parameter Name 2. Sender Id 3. Direct Line Key

1. Bot Parameter Name: This variable will be used to store the data for bot and we willl return the value to the BotFramework. We will give name it as message

2. Sender Id: This is the Id of our Bot, will be used to identify the bot on bot framework

3. Directline Key: Our bot will receive data from direct line, so here we need to configure the key of direct line client of our bot. To set the directline key first we need to activate the directline channel on bot framework and retrive the key. Than we need to provide that key in the Directline Key section.

The Code

After setting up the input and output go to the develop section from left side navigation. Here the area is given where we can code and write our own logic. Below is the code by using which we read the data from the queue and give it as output to the bot framework

Below is the code which should be pasted in the QueueTriggered Function

using System;
using System.Net;
using System.Net.Http;
using Microsoft.Azure.WebJobs.Host;

public class BotMessage
{    
    public string Message { get; set; }
}

public static BotMessage Run(string myQueueItem, out BotMessage message, TraceWriter log)
{
    log.Info($"C# Queue trigger function process started: {myQueueItem}");
    message = new BotMessage
    { 
        Message = myQueueItem
    };
    log.Info($"C# Queue trigger function process Complete: {myQueueItem}");
    return message;    
}

Here the myQueueItem variable contains the data from the queue and it will be saved to message variable. And it will return the variable message to the bot framework.

This will create a trigger in our bot. Now we need to write the code to handle the trigger and show appropriate message.

Receive the triggered Message

Below is our trigger code to show the triggered text from QueueTriggeredFunction. We need to write this code in MessageController.cs

case ActivityTypes.Trigger:
    ITriggerActivity trigger = activity;
    var message = JsonConvert.DeserializeObject<QueueMessage>(((JObject)trigger.Value).GetValue("Message").ToString());
    if (!string.IsNullOrEmpty(message.MessageText))
    {
        var messageactivity = (Activity)message.ResumptionCookie.GetMessage();

        var client = new ConnectorClient(new Uri(messageactivity.ServiceUrl));
        activity.Text = activity.RemoveRecipientMention();

        Activity replyToConversation = messageactivity.CreateReply();
        replyToConversation.Recipient = activity.From;
        replyToConversation.Type = "message";
        
        replyToConversation.Attachments = new List<Attachment>();
        List<CardImage> cardImages = new List<CardImage>();
        cardImages.Add(new CardImage(url: "http://cdn.wonderfulengineering.com/wp-content/uploads/2016/02/iron-man-wallpaper-22.jpg"));
        List<CardAction> cardButtons = new List<CardAction>();
        CardAction plButton = new CardAction()
        {
            Value = "https://en.wikipedia.org/wiki/Iron_Man",
            Type = "openUrl",
            Title = "Ironman Wiki"
        };
        cardButtons.Add(plButton);
        HeroCard plCard = new HeroCard()
        {
            Title = message.MessageText,
            Subtitle = "Triggered Iron Man Card",
            Images = cardImages,
            Buttons = cardButtons
        };
        Attachment plAttachment = plCard.ToAttachment();
        replyToConversation.Attachments.Add(plAttachment);
        await client.Conversations.ReplyToActivityAsync(replyToConversation);
    }
    break;

The above code will get the messages triggered from Azure Queue. It will than parse the data to JSON object and get the resumption cookie and message. Than we created the activity based on the resumption cookie and send the hero card to the user.

Final messagecontroller will look like this :

public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
        {
            if (activity != null)
            {
                try
                {
                    switch (activity.GetActivityType())
                    {
                        case ActivityTypes.Message:
                            activity.Text = activity.RemoveRecipientMention();
                            await Conversation.SendAsync(activity, () => new ProactiveBusiness());                            
                            break;
                        case ActivityTypes.Trigger:
                            ITriggerActivity trigger = activity;
                            var message = JsonConvert.DeserializeObject<QueueMessage>(((JObject)trigger.Value).GetValue("Message").ToString());
                            if (!string.IsNullOrEmpty(message.MessageText))
                            {
                                var messageactivity = (Activity)message.ResumptionCookie.GetMessage();

                                var client = new ConnectorClient(new Uri(messageactivity.ServiceUrl));
                                activity.Text = activity.RemoveRecipientMention();

                                Activity replyToConversation = messageactivity.CreateReply();
                                replyToConversation.Recipient = activity.From;
                                replyToConversation.Type = "message";
                                
                                replyToConversation.Attachments = new List<Attachment>();
                                List<CardImage> cardImages = new List<CardImage>();
                                cardImages.Add(new CardImage(url: "http://cdn.wonderfulengineering.com/wp-content/uploads/2016/02/iron-man-wallpaper-22.jpg"));
                                List<CardAction> cardButtons = new List<CardAction>();
                                CardAction plButton = new CardAction()
                                {
                                    Value = "https://en.wikipedia.org/wiki/Iron_Man",
                                    Type = "openUrl",
                                    Title = "Ironman Wiki"
                                };
                                cardButtons.Add(plButton);
                                HeroCard plCard = new HeroCard()
                                {
                                    Title = message.MessageText,
                                    Subtitle = "Triggered Iron Man Card",
                                    Images = cardImages,
                                    Buttons = cardButtons
                                };
                                Attachment plAttachment = plCard.ToAttachment();
                                replyToConversation.Attachments.Add(plAttachment);
                                await client.Conversations.ReplyToActivityAsync(replyToConversation);
                            }
                            break;
                        
                    }
                }
                catch (Exception ex)
                {

                }
            }
            var response = Request.CreateResponse(HttpStatusCode.OK);
            return response;
        }

Test My Code

To test the code we have done above , we need to publish the code to azure and need to configure the directline API for the bot (which we have done in the previous steps of the article). OR If you know how to use ngrok, you can also directly check it without publishing to azure. The good link to use ngrok is given at https://robinosborne.co.uk/2016/09/19/debugging-botframework-locally-using-ngrok/

To test the proactive scenario, write any message to bot (currently I am using skype channel). and it will be returned back the text you have entered with notification card.

(The triggering activity may take some time for the first time as it uses direct line API, be patience and have a cup of coffee. Many times it may happen that we need to send multiple messages to trigger the activity.)

Hurray !! we are done configuring Proactive Bot. Now for any notifications you can add the message in the queue with resumption cookie and it will be delivered to your user.

#section 2 will be published soon.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here