如何使用 Microsoft Bot Framework 创建主动式 Bot






4.71/5 (7投票s)
本文将指导您一步一步地创建Microsoft Bot Framework的主动型机器人。
引言
本文解释了如何使用Microsoft Bot Framework创建主动型机器人。在Microsoft Bot Framework文档中提供了一个很好的示例:https://docs.botframework.com/en-us/azure-bot-service/templates/proactive/
什么是主动型机器人和主动消息?
主动型机器人是指主动向用户发送消息的机器人。也就是说,在没有用户交互的情况下发送信息。例如,闹钟机器人。
为了实现此功能,Bot Framework使用了主动消息。主动消息的示例包括折扣通知、最新商品通知等,这些信息会在特定时间自动发送到您的聊天机器人。
主动型机器人的先决条件
要开始开发,您需要具备以下条件:
- 已部署到Bot Framework并在任何频道(例如Skype)上配置的机器人。
- 在Bot Framework上配置的Directline频道。
- 具有创建Azure Functions和Azure Database权限的Azure账户。
背景
本文分为两部分。在第一部分,我们将展示如何创建一个基本的主动型机器人消息;在第二部分,我们将展示如何使用Azure数据库进行良好的用户界面管理。
第一部分
基本主动型机器人
基本上,要向机器人发送主动消息,我们需要触发机器人来发送消息。机器人可以通过多种方式触发,但最简单的触发方式是使用Azure Function。通过使用Azure Functions,我们可以将输出发送到机器人(这将在后面的博客中解释)。
因此,首先在Visual Studio中创建机器人解决方案,并将其命名为ProactiveBot。
然后创建一个名为ProactiveBusiness.cs的类文件,然后将以下代码片段添加到您的ProactiveBusiness.cs类中。
[Serializable]
public class ProactiveBusiness : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
}
}
您的消息控制器将如下所示。
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;
}
现在,我们需要一个存储数据并将其传递给机器人的中介。我们可以将数据存储到Azure Queue Storage中以开始主动消息传递。
在此,为了接收用户的Proactive消息,我们需要用户的消息以及用户的ResumptionCookie
。通过使用这些,我们可以识别用户并发送Proactive消息。因此,我们将消息与ResumptionCookie和文本以json格式保存在队列中。
为此,首先在ProactiveBusiness.cs类中创建一个名为QueueMessage
的类,如下所示:
[Serializable]
public class QueueMessage
{
public ResumptionCookie ResumptionCookie;
public string MessageText;
}
然后,在QueueMessage
中添加消息和resumption cookie,如下所示:
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
};
}
现在,我们将此消息添加到AzureQueue。以下是有关如何将消息添加到AzureQueue的步骤。在此处提供了很好的示例:here
首先,您需要添加nuget包以使用AzureStorage。以下是其链接:
.NET的Microsoft Azure Storage客户端库
创建Azure存储账户
登录Azure并创建存储账户,如下所示。
创建存储后,导航到“密钥”部分,并复制存储账户名称和密钥(我们将需要此密钥来创建连接字符串)。
根据上述信息,我们的连接字符串将采用以下格式。
DefaultEndpointsProtocol=https;AccountName=<storage-account-name>;AccountKey=<account-key>
现在,我们需要将此存储账户连接字符串放在机器人解决方案的web.config文件中appconfig部分,如下所示。
<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=<stroge-account-name>;AccountKey=<account-key>" />
编写以下代码以将消息存储到队列中。
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);
将当前消息存储到Azure Queue Storage的最终代码将如下所示。
(有时会出现存储在队列中的数据无法检索的情况,这是由于队列的超时机制)。
参考文献
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;
代码
[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);
}
}
创建Azure Functions
我们将数据存储到Azure队列的任务已完成。现在,我们将创建一个队列触发的Azure函数。
当向队列中添加任何项目时,此函数将自动触发。我们将把函数输出设置为我们的Azure函数中的机器人。
为此,请创建一个函数应用,并为其命名。在此,在创建函数应用时,请确保选择您已创建的用于存储队列数据的存储账户。
创建函数应用后,导航到它,并从“创建自定义函数”部分创建QueueTrigger
函数。这里将提供函数列表,我们需要选择QueueTriggered-CSharp
。
选择它后,向下滚动页面,并在“为函数命名”部分提供QueueTriggeredFunction
。此外,在Queue Name中,请输入我们创建的队列名称,即myqueue。在存储账户连接部分,我们需要提供到我们存储账户的连接字符串。
如果您已经创建了连接字符串,则选择它,或者您也可以稍后添加(暂时从任何可用的连接字符串中保存)。
将连接字符串添加到Azure Function
要添加连接字符串,请转到您刚刚创建的函数应用的函数应用设置。
在此,“开发”部分下方将有“应用程序设置”。点击“应用程序设置”旁边的“配置应用程序设置”按钮。
这将打开设置部分。那里将有不同的部分,例如“常规设置”、“调试”、“应用程序设置”等。在这里,我们需要转到“应用程序设置”,并添加我们的存储账户的密钥。在key文本框中提供key的名称,即StorageConnectionString
,以及连接字符串的值,正如我们在上面的代码中所使用的。即DefaultEndpointsProtocol=https;AccountName=<stroge-account-name>;AccountKey=<account-key>
,然后保存页面。存储连接字符串将在一段时间后填充到应用程序的连接字符串部分。
一旦连接字符串填充完毕,点击我们的QueueTriggered
函数,然后点击“集成”部分。这里有一个下拉列表用于选择连接字符串,选择StorageConnectionString
并保存。
将函数输出设置为Bot Framework
现在,我们需要将输出设置为Bot Framework。为此,请转到我们的QueueTriggeredFunction
并点击其下方的“集成”部分。在这里,我们将在右侧找到“输出”部分。我们需要点击“+新建输出”,这将打开下方的输出设置面板。在这里,我们需要提供三项内容:
1. Bot参数名称 2. 发送者ID 3. Direct Line密钥
1. Bot参数名称:此变量将用于存储机器人数据,并将值返回给BotFramework。我们将名称命名为message。
2. 发送者ID:这是我们的机器人的ID,将用于在Bot Framework上识别机器人。
3. Directline密钥:我们的机器人将从Direct Line接收数据,所以在这里我们需要配置我们机器人的Direct Line客户端的密钥。要设置Directline密钥,首先需要激活Bot Framework上的Directline频道并检索密钥。然后,我们需要在Directline Key部分提供该密钥。
代码
设置好输入和输出后,从左侧导航栏转到“开发”部分。这里提供了我们可以编写代码和编写自己逻辑的区域。以下代码用于读取队列中的数据并将其作为输出提供给Bot Framework。
以下代码应粘贴到QueueTriggered
函数中。
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;
}
在这里,myQueueItem
变量包含来自队列的数据,并将保存到message变量中。然后它会将message变量返回给Bot Framework。
这将创建我们机器人中的一个触发器。现在,我们需要编写代码来处理触发器并显示适当的消息。
接收触发的消息
以下是显示来自QueueTriggeredFunction
的触发文本的触发代码。我们需要将此代码写入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;
上面的代码将获取从Azure Queue触发的消息。然后它会将数据解析为JSON对象,并获取resumption cookie和消息。然后,我们基于resumption cookie创建活动,并将Hero Card发送给用户。
最终的消息控制器将如下所示。
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;
}
测试我的代码
要测试我们上面完成的代码,我们需要将代码发布到Azure,并为机器人配置Directline API(我们已在文章前面的步骤中完成)。或者,如果您知道如何使用ngrok,也可以直接在不发布到Azure的情况下进行测试。使用ngrok的链接在此:https://robinosborne.co.uk/2016/09/19/debugging-botframework-locally-using-ngrok/
要测试主动场景,请向机器人发送任何消息(我目前使用的是Skype频道)。它将返回您输入的消息以及通知卡。
(首次触发活动可能需要一些时间,因为它使用Direct Line API,请耐心等待,并喝杯咖啡。很多时候,我们可能需要发送多条消息来触发活动)。
太棒了!我们已经完成了主动型机器人的配置。现在,对于任何通知,您都可以将消息添加到队列中,并带有resumption cookie,它将被传递给您的用户。
#第二部分将很快发布。