Chat bot development for Facebook Messenger at node.js

The material, the translation of which we present to your attention today, is devoted to the development of chat bot for Facebook Messenger. The bot, which is called Aww Bot, communicating with users, will send them pictures of cute cats and dogs.



Beginning of work


Let's start by creating a page on Facebook by filling in the required fields. This page is for bot. In addition, we will create a Facebook application, then, on the Add a Product page, we will connect the Messenger product to the application. Next, we will be on the settings page of the messenger. Here you need to find the section Token Generation, in it - select the bot page in the Page list. After that, we will be asked for permissions and an access token will be created. The bot will use this token to make calls to the Facebook Messenger API, which will allow it to communicate with users.

Web server setup


We will use node.js and express.js to create an HTTP server. Run the following command:

npm install express body-parser request config --save 

Add to index.js following code that will allow you to create a simple HTTP server:

 '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!')); 

Now, if you start the server and go, using the browser, at http://127.0.0.1:8989 , you can see the page with the server's response - Hello World! .

HTTPS and local development environment


Before moving to working with Webhook technology, we need to configure HTTPS for the development environment. The messenger will not accept the Webhook address used to send notifications to our server if you use a self-signed SSL certificate. Free certificate can be obtained from Let's Encrypt . Here, however, you can get a certificate only for the domain, and not for the IP address. We will use the ngrok service, which will allow access to the local server through a public URL that uses HTTPS.

Ngrok setup


ngrok easy. You just need to download a compressed archive from the project site, unpack it and execute the following command:

 ./ngrok http 80 

Do not forget to redirect port 80 to 8989 in the WAN settings of your router. As a result, ngrok will create public HTTP and HTTPS addresses for the local server.

Work with Webhook notifications


The messenger uses Webhook technology for authentication and to send notifications about events to your application. From a programming point of view, it all comes down to the work of ordinary callback functions for processing HTTP requests that will receive data about events, such as messages received by a chat bot. To parse GET and POST requests, we will use the body-parser module.

Add the following route to the application. It is needed to process Webhook verification requests.

 //   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);       }   } }); 

Now you need to open the settings of the messenger, find the Webhooks section there and configure the integration of the application with Webhook notifications. On the settings page, in the Callback URL field, you must enter our HTTPS URL, obtained from ngrok. The verification token (the one that is present in the code and represents the random string we created) must be placed in the Verify Token field. After that, you should be able to verify and save the settings by clicking the Verify and Save button, if your URL for processing Webhook notifications is available, and the verification token corresponds to the one in the code.


Setting a token and URL for the application to receive Webhook notifications

After saving, select your page from the drop-down list and subscribe to the page events.

Now create a POST route for handling POST events from the messenger. Add the following code to the application.

 //     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);   } }); 

We set up the application so that it handles two types of events - message and postback . In order to check the operation of the Webhook-notification mechanism, open the messenger and send a bot message to the page. If everything works as it should, the PSID of the sender, the event information and the message content will be logged. Now we will write handler functions for events of interest to us.

 //   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'){   } } 

The handleMessage() method is responsible for handling incoming messages, and the handlePostback() method is responsible for handling handlePostback() inbound events. Update the existing code by adding calls to these methods:

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

Now, when we receive a message or postback , the data will be sent to the appropriate handler along with the sender's PSID.

Setting the welcome screen and postback events to start a dialogue with the bot


When a new user starts a conversation with a bot, the Get Started button is displayed in the chat window. You can set up your own postback event for this situation. For example, set a message for the user, which describes the bot and how to communicate with it. In order to customize your own greeting, run this curl command in the terminal:

 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" 

We set up Aww Bot to display a message asking the user if he is ready to see the cutest cats and dogs. To set up a postback event, issue this command in a terminal:

 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" 

Here’s what a chat session with a bot looks like.


Start screen

Application setup


We will use the npm configuration module to store the page access token in a separate configuration file. Create the config directory in our project and the default.json file in it. In this file, you must add the access token to the page and make an entry about this file in .gitignore .

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

We will get a page access token in the callSendAPI() method using the config.get('facebook.page.access_token') command config.get('facebook.page.access_token') .

Handle start event


Here is the code to handle the start event.

 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);   } } 

Create a method askTemplate() , which will return a properly prepared response object for the messenger API. The callSendAPI() method will send a message to the user. Add the following methods to the application:

 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);       }   }); } 

We send the user a message containing two buttons and text. When the user selects what he needs by clicking the appropriate button, a request with the data of the postback event will be sent to our Webhook-address and we will process it.


The user is prompted to select the type of images he is interested in.

Handling your own postback events


Update the code for the postback event handler postback :

 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);   }   //   } 

When a user clicks on the Cats button, our address used to process Webhook notifications will receive a request with a postback event containing CAT_PICS data. Selecting the Dogs option will send a postback event with DOG_PICS data. We added another method to the system, imageTemplate() , which returns a message containing a link to the image of a cat or dog.

Creating a simple API that returns links to images


Let's write a simple API to return links to the images of cats or dogs that will be used in messages sent by the bot to users. Create a pics.js file and add the following code to it:

 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'   ] }; 

Now connect it in the application.

 images = require('./pics'); 

Add to the code the following method used to generate a message containing a link to the image.

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

In the process of user interaction with the bot, images are alternately extracted from the array and sent as bot answers to the user. After sending the last image, we return to the top of the list.

Let's add to the project the following code intended for storing and processing data about users communicating with the bot.

 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]; } 

We store the PSID of each of the users communicating with the bot, as a key in the users object. If there is no user record yet, create a new record. We will update the image number information each time a user requests a picture of a cat or dog. Then we return the absolute path to the image that will be used in the message template. Next, we send a message with the image as a response to the postback event, generated when the user selects the type of image of interest.

 //  ,    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); } 

In addition, after sending the image, we pass to the callSendAPI() method a callSendAPI() function to send the user a new question about which images he is interested in. If successful, we call this function. This scheme of work, taking into account the asynchronous nature of callback functions, allows the user to receive a message asking for the next image after the message with the image requested earlier was sent to him.


Communication with the bot

Results


Here is the repository of this project. There, in the readme.md file, you can find instructions for installing and configuring the bot. In order for other people to communicate with your bot, your Facebook application must be approved. Up to this point, only administrators and testers of your application can talk to the bot. Here is a video demonstrating the process of communicating with the bot.

Dear readers! Do you plan to create bots for Facebook Messenger?

Source: https://habr.com/ru/post/414081/


All Articles