在node.js上针对Facebook Messenger的聊天机器人开发

该材料的译文(我们今天将向您介绍该译文)致力于开发Facebook Messenger聊天机器人。 与用户交流的名为Aww Bot的机器人将向他们发送可爱的猫和狗的图片。



开始使用


首先,通过填写必填字段在Facebook上创建一个页面。 此页面适用于机器人。 此外, 我们将创建一个 Facebook应用程序,然后在“添加产品”页面上,将Messenger产品连接到该应用程序。 接下来,我们将在Messenger设置页面上找到自己。 在这里,您需要找到“令牌生成”部分,在其中-在“页面”列表中选择机器人页面。 之后,将询问我们有关权限的信息,并创建一个访问令牌。 该机器人将使用此令牌来调用Facebook Messenger API,这将使其与用户进行通信。

Web服务器设置


我们将使用node.js和express.js创建一个HTTP服务器。 运行以下命令:

npm install express body-parser request config --save 

将以下代码添加到index.js ,以使您可以创建简单的HTTP服务器:

 'use strict'; let express = require('express'),   bodyParser = require('body-parser'),   app = express(); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.listen(8989, () => console.log('Example app listening on port 8989!')); app.get('/', (req, res) => res.send('Hello World!')); 

现在,如果您启动服务器并使用浏览器访问http://127.0.0.1:8989 ,则会看到一个包含服务器响应的页面Hello World!

HTTPS和本地开发环境


在继续使用Webhook技术之前,我们需要为开发环境配置HTTPS。 如果您使用自签名SSL证书,则Messenger不会接受用于向我们的服务器发送通知的Webhook地址。 可以从Let's Encrypt获得免费证书。 但是,在这里,您只能获得一个域的证书,而不能获得IP地址的证书。 我们将使用ngrok服务,该服务将允许您通过使用HTTPS的公共URL来组织对本地服务器的访问。

Ngrok设置


ngrok容易。 您只需要从项目站点下载压缩存档 ,解压缩并运行以下命令:

 ./ngrok http 80 

切记在路由器的WAN设置中将端口80重定向到8989。 结果, ngrok将为本地服务器创建公共HTTP和HTTPS地址。

使用Webhook通知


该Messenger使用Webhook技术进行身份验证并将有关事件的通知发送到您的应用程序。 从编程的角度来看,所有这些都归结为用于处理HTTP请求的常用回调函数,该HTTP请求将接收有关事件的数据,例如聊天机器人接收到的消息。 为了解析GET和POST请求,我们将使用body-parser模块。

将以下路由添加到应用程序。 处理Webhook验证请求是必需的。

 //   GET-  webhook app.get('/webhook', (req, res) => {   //  .    ,       let VERIFY_TOKEN = "SOMETHING_RANDOM";   //      let mode = req.query['hub.mode'];   let token = req.query['hub.verify_token'];   let challenge = req.query['hub.challenge'];   // ,     mode  token   if (mode && token) {       //   mode  token       if (mode === 'subscribe' && token === VERIFY_TOKEN) {           //   challenge             console.log('WEBHOOK_VERIFIED');           res.status(200).send(challenge);       } else {           //   '403 Forbidden'                res.sendStatus(403);       }   } }); 

现在,您需要打开Messenger设置,在此处找到Webhooks部分,并使用Webhook通知配置应用程序集成。 在设置页面的“回调URL”字段中,输入从ngrok收到的我们的HTTPS URL。 验证令牌(代码中存在的一个令牌,代表我们创建的随机字符串)必须放在“验证令牌”字段中。 之后,如果您有用于处理Webhook通知的URL可用,并且验证令牌与代码中的一个相匹配,那么您应该能够通过单击“验证并保存”按钮来验证并保存设置。


配置应用程序的令牌和URL以接收Webhook通知

保存后,从下拉列表中选择页面并订阅页面事件。

现在创建一个POST路由来处理来自Messenger的POST事件。 将以下代码添加到应用程序。

 //     webhook app.post('/webhook', (req, res) => {   let body = req.body;   if (body.object === 'page') {       // ,               body.entry.forEach(function(entry) {           //  entry.messaging  ,            //     ,    0           let webhook_event = entry.messaging[0];           console.log(webhook_event);           //  PSID            let sender_psid = webhook_event.sender.id;           console.log('Sender PSID: ' + sender_psid);           //  ,  , message   postback,           //     -           if (webhook_event.message) {               console.log(webhook_event.message)           } else if (webhook_event.postback) {               console.log(webhook_event.postback)           }       });       //  '200 OK'            res.status(200).send('EVENT_RECEIVED');   } else {       //  '404 Not Found',      ,           res.sendStatus(404);   } }); 

我们将应用程序设置为可以处理两种类型的事件: messagepostback 。 为了检查Webhook通知机制的操作,请打开Messenger并将消息发送到bot页面。 如果一切正常,将记录发件人的PSID,事件信息和消息内容。 现在,我们将为我们感兴趣的事件编写处理程序函数。

 //   message const handleMessage = (sender_psid, received_message) => {   let response;   if (received_message.text) {   } } //   postback const handlePostback = (sender_psid, received_postback) => {   let response;   //    postback   let payload = received_postback.payload;   if(payload === 'GET_STARTED'){   } } 

handleMessage()方法负责处理传入的消息, handlePostback()方法handlePostback()处理传入的postback事件。 通过添加对以下方法的调用来更新现有代码:

 //   //     - if (webhook_event.message) {   handleMessage(sender_psid, webhook_event.message); } else if (webhook_event.postback) {   handlePostback(sender_psid, webhook_event.postback); } 

现在,当我们接收到messagepostback ,数据将与发送者的PSID一起传输到相应的处理程序。

配置欢迎屏幕和回发事件以启动与机器人的对话


当新用户开始与漫游器进行对话时,聊天窗口中将显示“入门”按钮。 您可以针对这种情况配置自己的回发事件。 例如,为用户设置一条描述机器人以及如何与机器人通信的消息。 要配置自己的问候语,请在终端中运行以下curl命令:

 curl -X POST -H "Content-Type: application/json" -d '{ "greeting": [   {     "locale":"default",     "text":"Hello {{user_first_name}}! Are you ready to see the cutests cats and dogs"   } ] }' "https://graph.facebook.com/v2.6/me/messenger_profile?access_token=YOUR_PAGE_ACCESS_TOKEN" 

我们设置了Aww Bot,以便它显示一条消息,询问用户是否准备好看最可爱的猫和狗。 为了配置回发事件,请在终端中执行以下命令:

 curl -X POST -H "Content-Type: application/json" -d '{ "get_started": {"payload": "GET_STARTED"} }' "https://graph.facebook.com/v2.6/me/messenger_profile?access_token=YOUR_PAGE_ACCESS_TOKEN" 

这是与机器人的聊天会话的样子。


入门屏幕

应用设置


我们将使用npm配置模块将页面访问令牌存储在单独的配置文件中。 在我们的项目中创建config目录,并在其中创建default.json文件。 在此文件中,您需要将访问令牌添加到页面并将此文件记录在.gitignore

 { "facebook": {   "page": {     "access_token": "PAGE_ACCESS_TOKEN"   } } } 

我们将使用config.get('facebook.page.access_token')命令在callSendAPI()方法中获取页面访问令牌。

处理开始事件


这是开始事件处理代码。

 const handlePostback = (sender_psid, received_postback) => {   let response;   //   postback-   let payload = received_postback.payload;   if(payload === 'GET_STARTED'){       response = askTemplate('Are you a Cat or Dog Person?');       callSendAPI(sender_psid, response);   } } 

让我们创建一个askTemplate()方法,该方法将为Messenger Messenger返回正确准备的响应对象。 callSendAPI()方法将向用户发送一条消息。 将以下方法添加到应用程序:

 const askTemplate = (text) => {   return {       "attachment":{           "type":"template",           "payload":{               "template_type":"button",               "text": text,               "buttons":[                   {                       "type":"postback",                       "title":"Cats",                       "payload":"CAT_PICS"                   },                   {                       "type":"postback",                       "title":"Dogs",                       "payload":"DOG_PICS"                   }               ]           }       }   } } //     API Send const callSendAPI = (sender_psid, response, cb = null) => {   //      let request_body = {       "recipient": {           "id": sender_psid       },       "message": response   };   //  HTTP-  Messenger Platform   request({       "uri": "https://graph.facebook.com/v2.6/me/messages",       "qs": { "access_token": config.get('facebook.page.access_token') },       "method": "POST",       "json": request_body   }, (err, res, body) => {       if (!err) {           if(cb){               cb();           }       } else {           console.error("Unable to send message:" + err);       }   }); } 

我们向用户发送包含两个按钮和文本的消息。 当用户通过单击适当的按钮选择所需的内容时,会将请求与postback事件的数据一起发送到我们的Webhook地址,然后我们将对其进行处理。


提示用户选择他感兴趣的图像类型。

处理自定义回发事件


更新回发事件处理程序功能代码:

 const handlePostback = (sender_psid, received_postback) => {   let response;   //   postback-   let payload = received_postback.payload;   //  ,       if (payload === 'CAT_PICS') {       response = imageTemplate('cats', sender_psid);       callSendAPI(sender_psid, response, function(){           callSendAPI(sender_psid, askTemplate('Show me more'));       });   } else if (payload === 'DOG_PICS') {       response = imageTemplate('dogs', sender_psid);       callSendAPI(sender_psid, response, function(){           callSendAPI(sender_psid, askTemplate('Show me more'));       });   } else if(payload === 'GET_STARTED'){       response = askTemplate('Are you a Cat or Dog Person?');       callSendAPI(sender_psid, response);   }   //   } 

当用户单击“ Cats按钮时,带有postback事件的请求(包含CAT_PICS数据)将被CAT_PICS到我们用于处理Webhook通知的地址。 选择“ Dogs选项将发送带有DOG_PICS数据的postback事件。 我们向系统添加了另一个方法imageTemplate() ,该方法返回一条消息,其中包含指向猫或狗的图像的链接。

创建一个简单的API,返回图像链接


我们将编写一个简单的API,以返回指向猫或狗图像的链接,这些链接将在机器人发送给用户的消息中使用。 创建一个pics.js文件,并添加以下代码:

 module.exports = {   cats : [       'https://i.imgur.com/Qbg7CeM.jpg',       'https://i.imgur.com/nUzkpJY.jpg',       'https://i.imgur.com/NpDcKph.jpg',       'https://i.imgur.com/oJtSDaO.jpg',       'https://i.redd.it/82ajpsrd17111.jpg',       'https://i.redd.it/00km1d2rt0111.jpg',       'https://i.redd.it/rdbavhp0y7111.jpg',       'https://i.redd.it/5hn3mg0n98111.jpg',       'https://i.redd.it/d23pb8mta6111.jpg',       'https://i.redd.it/d2gyrwgy7oz01.jpg',       'https://i.redd.it/z4sgl84q72z01.jpg',       'https://i.redd.it/wvykzo8n1cy01.jpg'   ],   dogs : [       'https://i.redd.it/6tjihi2qe7111.jpg',       'https://i.imgur.com/etRCs56.jpg',       'https://i.redd.it/nibw50f8y4111.jpg',       'https://i.redd.it/izcvnvj1o7111.jpg',       'https://i.redd.it/eqs1g9dldz011.jpg',       'https://i.redd.it/civ9dnu9u1111.jpg',       'https://i.redd.it/kk03qwclkp011.jpg',       'https://i.redd.it/2694pupjne011.jpg',       'https://i.redd.it/qk49ls5y6oy01.jpg',       'https://i.imgur.com/oM3mKgB.jpg',       'https://i.redd.it/8kx2riaulux01.jpg'   ] }; 

现在将其连接到应用程序中。

 images = require('./pics'); 

将以下方法添加到用于编写包含图像链接的消息的代码中。

 const = imageTemplate(type, sender_id) => {   return {       "attachment":{           "type":"image",           "payload":{               "url": getImage(type, sender_id),               "is_reusable":true           }       }   } } 

在与机器人进行用户交互的过程中,图像从阵列中顺序提取,并作为机器人响应发送给用户。 发送完最后一张图像后,我们返回列表顶部。

我们将以下代码添加到项目中,旨在存储和处理有关与机器人通信的用户的数据。

 let users = {}; const = getImage(type, sender_id) => {   //       -     if(users[sender_id] === undefined){       users = Object.assign({           [sender_id] : {               'cats_count' : 0,               'dogs_count' : 0           }       }, users);   }   let count = images[type].length, //            user = users[sender_id], // ,         user_type_count = user[type+'_count'];   //          let updated_user = {       [sender_id] : Object.assign(user, {           [type+'_count'] : count === user_type_count + 1 ? 0 : user_type_count + 1       })   };   //      users = Object.assign(users, updated_user);   console.log(users);   return images[type][user_type_count]; } 

我们将与机器人通信的每个用户的PSID作为密钥存储在users对象中。 如果尚无用户记录,请创建一个新记录。 每次用户请求猫或狗的照片时,我们都会更新图像编号信息。 然后,我们将返回将在消息模板中使用的图像的绝对路径。 接下来,当用户选择感兴趣的图像类型时,我们以对postback事件的响应的形式发送带有图像的消息。

 //  ,    postback- if (payload === 'CAT_PICS') {   response = imageTemplate('cats', sender_psid);   callSendAPI(sender_psid, response, function(){       callSendAPI(sender_psid, askTemplate('Show me more'));   }); } else if (payload === 'DOG_PICS') {   response = imageTemplate('dogs', sender_psid);   callSendAPI(sender_psid, response, function(){       callSendAPI(sender_psid, askTemplate('Show me more'));   }); } else if(payload === 'GET_STARTED'){   response = askTemplate('Are you a Cat or Dog Person?');   callSendAPI(sender_psid, response); } 

另外,在发送图像之后,我们将回调函数传递给callSendAPI()方法,以向用户发送有关他感兴趣的图像的新问题。 如果成功,我们将调用此函数。 考虑到回调函数的异步特性,该工作方案允许用户在收到带有下一个图像的消息后,收到带有下一个图像问题的消息。


与机器人通讯

总结


这是该项目存储库。 在readme.md文件中,您可以找到有关安装和配置bot的说明。 为了让其他人与您的漫游器聊天,必须批准您的Facebook应用程序。 到目前为止,只有您的应用程序的管理员和测试人员才能与该机器人进行对话。 这是一个视频,演示了与机器人进行通信的过程。

亲爱的读者们! 您是否打算为Facebook Messenger创建机器人?

Source: https://habr.com/ru/post/zh-CN414081/


All Articles