机器人技能和消息 API





0/5 (0投票)
在本帖中,我们将讨论如何连接 Twilio 和 PubNub 的消息 API、Imgur 的照片共享服务以及机器人化的 JavaScript 代码,为 Misty 构建一个照片亭技能。
消息服务为人类通过我们已有的设备与可编程机器人进行交互奠定了基础。这种交互感觉就像魔法,但这是任何会编码的人都可以实现的魔法。为了向您展示我的意思,我们只需要看看我们在 Twilio SIGNAL 2019 上演示的 Misty 照片亭技能。
当此技能运行时,您可以发送短信要求您的 Misty 机器人为您拍照。当 Misty 收到您的短信时,她会停止正在做的事情,转过身看着您,然后用她头盔中的摄像头捕捉您的肖像。然后,她会通过 MMS 将照片直接发送回您的手机。
为了展示这一切如何运作,本文将揭示 Misty 照片亭技能幕后的工作原理,从您的手指触摸“发送”到您的肖像回到您的设备。它检查了该技能组合的每个服务之间的链接,并解释了一些控制 Misty 响应的 JavaScript 代码的关键部分。
让我们从 Twilio Autopilot 开始,这是您的短信首先访问的服务。
短信 -> Twilio Autopilot
为了引起 Misty 的注意,用户会向连接到 Twilio 的 Autopilot 服务的电话号码发送短信。如果您不熟悉 Autopilot,Twilio 是这样描述它的:
Autopilot 是一个会话式 AI 平台,可使用自然语言理解和完整的 Autopilot Actions 可编程性来构建全渠道机器人和虚拟助手。
Twilio
当您发送短信到这个特殊号码时,Twilio 会将您的消息转发给与唯一 Autopilot“机器人”关联的可编程消息通道。您创建的每个 Autopilot 机器人都可以执行一个或多个“任务”(即当您的机器人收到特定类型的消息时触发的操作)。您可以在 Twilio 控制台中自定义任务,以编程它们在触发时应执行的操作。
对于照片亭技能,我们的 Autopilot 机器人的核心功能位于一个名为 `take_picture` 的任务中。我们“训练”我们的机器人,当它收到以下短语之一时触发 `take_picture` 任务:
下一步是编程当 `take_picture` 任务触发时机器人执行的操作。可能的动作包括向用户发送消息、监听响应或收集和存储信息。或者,如果内置操作不够,您可以将任务编程为重定向到其他服务。这就是我们在照片亭技能中所做的。当 `take_picture` 任务触发时,它会调用一个重定向来运行 Twilio Function。
Autopilot -> Twilio Function
Twilio Functions 是用于处理入站 Twilio 通信的无服务器函数。这些函数是快速将您在 Twilio 中处理的通信引入到网络的其他部分(机器人如 Misty 非常擅长的一种通信方式)的一种快捷方式。
我们在照片亭技能中使用的 Twilio Function 会做几件事。首先,它将用户的电话号码分配给一个变量。然后,它使用 Twilio API 向用户回复一条短信:“该摆姿势了”。最后,它将用户的电话号码(以及他们要求的操作性质)POST 到 Misty 正在监听新消息的 PubNub 通道。(下一节有更多详细信息)。
照片亭技能的 Twilio Function 代码大致如下:
exports.handler = function(context, event, callback) { // Saves phone number to contact variable var contact = event.UserIdentifier || "19294421336"; // Sends SMS to user const replySms = {"actions": [{"say": "[••] Time to Pose"}]}; const replyErrorSms = {"actions": [{"say": "[••] Oops an error occured, Could you please try again.."}]}; const axios = require('axios') // The PubNub URL includes publish/subscribe keys, a // channel name (similar to the name of a chatroom), // and a client name (a unique name identifying this // device in the PubNub channel). axios.post('https://ps.pndsn.com/publish/<publish-key>/<subscribe-key>/0/<channel-name>/0?store=0&uuid=<client-name>', { 'phNumber': contact, 'type': 'photo' }) .then((res) => { console.log(res); callback(null, replySms); }) .catch((error) => { console.log(error); callback(null, replyErrorSms); }); };
设置了 Twilio Function 和 Autopilot Bot 后,我们就可以查看 PubNub 了,该服务通知 Misty 拍照。
Twilio Function -> PubNub
让机器人响应短信非常酷。更酷的是什么?当机器人几乎没有延迟地响应时。这就是 PubNub 的用武之地。
PubNub 提供了一个实时消息 API,开发人员可以通过 HTTP 通信协议进行利用,从而实现各种机器之间的快速通信。 当您使用 PubNub 的消息 API 时,您会创建一个数据通道——有点像设备的聊天室——多个设备(如 Twilio 服务器和机器人)可以订阅和发布消息。
虽然 PubNub 为多种语言提供了 SDK,但在照片亭技能中,我们仅使用基本的 HTTP 请求即可顺利运行。当您在 PubNub 中创建新的“应用程序”时,您会获得用于发布和订阅该应用程序的唯一 API 密钥。要发布数据(如我们在上面的 Twilio Function 中所做的那样),我们会向以下地址发送一个 POST 请求:
https://ps.pndsn.com/publish/<publish-key>/<subscribe-key>/0/<channel-name>/0?store=0&uuid=<client-name>
您会注意到 PubNub URL 包含发布/订阅密钥、通道名称(类似于聊天室名称)以及客户端名称(一个唯一名称,用于在 PubNub 通道中标识此设备)。发送此请求时,我们会传递一个 JSON 正文,其中包含我们要发布的消息。在我们的例子中,该消息类似于:
{ 'phNumber': contact, 'type': 'photo' }
您可以阅读有关此内容的更多信息,请参阅 PubNub 开发者文档,但总体而言,此请求会将消息发布到我们为照片亭技能创建的 PubNub 应用程序。外部设备(如可编程机器人)监听该应用程序可以读取这些消息并自行使用它们。
PubNub -> Misty
在我们查看机器人技能代码之前,了解我们所说的“技能”是什么很有帮助。在 Misty 的机器人世界中,技能是您在机器人本地运行的 JavaScript 代码。每个技能都需要一个 JavaScript 代码文件,其中包含 Misty 运行技能时执行的 JavaScript,以及一个 JSON 元文件,其中包含有关技能的其他信息。
Misty 的板载 JavaScript API 提供了订阅传感器数据和其他事件的方法,您可以在技能代码中定义处理这些数据的回调。此 API 还包括用于移动机器人、使用其传感器、播放声音和发送 Web 请求等命令的方法。最后一点是 Misty 在照片亭技能中从 PubNub 获取数据的方式。
为了获得快速响应,操作照片亭技能的机器人应该在有人给它发送短信之前就已开启并运行代码。当技能运行时,Misty 通过定期向我们的 PubNub 通道发送请求来监听新的短信通知。我们在代码中通过 JavaScript API 的 `misty.SendExternalRequest()` 方法来执行此操作。
Misty 发送的每个请求如果在 20 秒后仍未收到响应,则会超时,并且不能保证有人会在该时间范围内发送消息给 Misty。我们在技能中通过将订阅请求与发布空消息到 PubNub 通道的请求配对来解决此问题,该请求以循环方式运行以保持通信线路畅通。当我们的 Twilio 机器人将消息转发到 PubNub 时,Misty 会将其返回给我们的技能,并将其传递到 `_pubNubSubscribe()` 回调函数中。
在我们照片亭技能的 JavaScript 文件中,该代码大致如下(您也可以在 GitHub 上找到 仅提取的 PubNub 功能示例)。
// Calls the keepActive() function every 15 seconds misty.RegisterTimerEvent("keepActive", 15000, true); // Sends a publish request to work around timeouts function _keepActive() { misty.SendExternalRequest("POST", "https://ps.pndsn.com/publish/<publish-key>/<subscribe-key/0/<channel-name>/myCallback", null, null, "{}", false, false, "", "application/json"); } // Gets the message Twilio sends to PubNub and passes // the response into the _pubNubSubscribe callback function misty.SendExternalRequest("GET", "https://ps.pndsn.com/subscribe/<subscribe-key>/<channel-name>/0/0?uuid=<client-id>", null, null, "{}", false, false, "", "application/json","_pubNubSubscribe"); // Extracts the phone number and runs the function that // has Misty take a picture function _pubNubSubscribe(data) { outputExt(data.Result.ResponseObject.Data); }
`outputExt()` 函数(如上所示)从 Twilio Function 发送的消息中提取电话号码和 `type` 参数的值。 Misty 存储电话号码并检查 `type` 的值是否等于 `photo`。如果等于,她会运行一段代码,让她移动头部(和摄像头)面向用户,更改其显示图像,并播放声音以告知用户发生了什么。下面是一个示例:
if (data != [] && data.type == 'photo') { // Saves the user's contact info misty.Set("contact", (data.phNumber).toString(), false); // Changes display image, sets head position, and plays // sounds to show she's taking a picture misty.DisplayImage("DefaultEyes_SystemCamera.jpg"); misty.Pause(100); misty.PlayAudio("DefaultSounds_Awe3.wav", 100); misty.Set("pictureMode", true, false); misty.MoveHeadPosition(0, 0, 0, 45); misty.Pause(3000); misty.DisplayImage("DefaultEyes_SystemFlash.jpg"); misty.ChangeLED(255, 255, 255); misty.PlayAudio("DefaultSounds_SystemCameraShutter.wav", 100); // Snaps a portrait! By default, this method passes // a base64-encoded string with the image data for the // picture into the _TakePicture() callback function. misty.TakePicture("Photobooth", 375, 812, false, true); misty.Pause(200); misty.DisplayImage("DefaultEyes_SystemCamera.jpg"); misty.ChangeLED(140, 0, 255); misty.Pause(500); misty.DisplayImage("DefaultEyes_Joy2.jpg"); }
Misty -> Imgur
当您对 Misty 进行编码以拍照时,您可以将照片数据的 base64 编码字符串传递到回调函数中进行进一步处理。默认情况下,这些回调函数使用与 `misty.TakePicture()` 方法相同的名称,前面加上下划线:`_TakePicture()`。 在我们的技能中,我们使用此 `_TakePicture()` 回调函数将包含照片数据的 base64 编码字符串传递到 `uploadImage()` 函数中。此回调函数如下所示:
function _TakePicture(data) { var base64String = data.Result.Base64; uploadImage(base64String); }
当我们调用 `uploadImage()` 函数时,Misty 会将照片 POST 到一个私人 Imgur 相册。我们可以使用多个图片共享服务来托管这些图片,但 Imgur 的 API 有两点使其成为照片亭技能的理想选择。第一点:它接受 base64 编码的字符串,第二点:它在响应正文中返回上传图片的 URL。通过将返回的 URL 传递回 Twilio 的 MMS API,Misty 可以直接将图片发送给请求它的人。
用于管理照片亭技能中此功能的代码大致如下:
function uploadImage(imageData) { // Sets up the JSON body for uploading the picture var jsonBody = { 'image': imageData, 'type' : 'base64', 'album': '<album-name> }; // Uploads the picture to a private album; then, passes Imgur // response data into the _imageUploadResponse() callback misty.SendExternalRequest("POST", "https://api.imgur.com/3/image", "Bearer", "<bearer-token>", JSON.stringify(jsonBody), false, false, "", "application/json", "_imageUploadResponse"); } function _imageUploadResponse(responseData) { // Saves the URL misty.Set("imageLink", JSON.parse(responseData.Result.ResponseObject.Data).data.link, false); // Runs the code to send the picture sendPicture(); }
Misty -> MMS
图片进入我们的 Imgur 相册后,只剩最后一步:将图片发送到想要它的手机。这也通过 Twilio API 进行。在我们的 JavaScript 技能代码中,我们使用 `SendPicture()` 函数来 POST 一个请求,该请求包含发送原始文本的人的联系信息以及指向其上传图片的 URL。
当我们调用 `sendPicture()` 函数时,Misty 会向 Twilio API 发送一个请求,该请求会将给定 URL 的图片发送到我们用户的消息收件箱。过程大致如下:
function sendPicture() { misty.Debug("Sending Image to User"); // Sets up thee JSON body for the Twilio SMS API. // Includes the phone number of the recipient and // the URL for their photograph on Imgur var jsonBody = { 'Body': '[••] Greetings from Misty!', 'From': '<number-to-send-from>', 'To': misty.Get("contact"), 'MediaUrl': misty.Get("imageLink") }; // Sends a request to the Twilio API with our account // credentials to send the picture to the person who asked for it var credentials = "<base64-encoded-Twilio-credentials>" misty.SendExternalRequest("POST", "https://api.twilio.com/2010-04-01/Accounts/<account-id>/Messages.json", "Basic", credentials, JSON.stringify(jsonBody), false, false, "", "application/x-www-form-urlencoded"); }
所以,总结一下:
在本帖中,我们讨论了如何连接 Twilio 和 PubNub 的消息 API、Imgur 的照片共享服务以及机器人化的 JavaScript 代码,为 Misty 构建一个照片亭技能。当我们使用此技能时:
- 有人向我们的 Twilio 电话号码发送消息
- Twilio 将消息传递给我们的 Twilio Autopilot 机器人
- 我们的 Autopilot 机器人读取消息,识别任务,并重定向到我们的 Twilio Function
- Twilio Function 将用户的电话号码 POST 到我们的 PubNub 通道
- Misty 一直在运行照片亭技能,并从 PubNub 拉取消息
- Misty 重新定位摄像头并拍照
- Misty 将照片上传到私人 Imgur 相册,并调用 Twilio API 将其作为 MMS 发送给我们的用户
给您的机器人发短信是让它做某事的一种相当社交的方式。当机器人用它最喜欢的人的照片回复时?这简直太亲切了。
加入 Misty 社区,看看其他开发者正在教 Misty 做什么。