Материал, перевод которого мы сегодня представляем вашему вниманию, посвящён разработке чат-бота для Facebook Messenger. Бот, который называется Aww Bot, общаясь с пользователями, будет отправлять им картинки симпатичных котов и собак.
Начнём с создания страницы на Facebook, заполнив необходимые поля. Эта страница предназначена для бота. Кроме того, создадим Facebook-приложение, после чего, на странице Add a Product, подключим к приложению продукт Messenger. Далее, мы окажемся на странице настройки мессенджера. Здесь надо найти раздел Token Generation, в нём — выбрать страницу бота в списке Page. После этого нас спросят о разрешениях и будет создан токен доступа. Бот будет использовать этот токен для выполнения вызовов к API Facebook Messenger, что позволит ему общаться с пользователями.
Мы, для создания HTTP-сервера, будем использовать node.js и express.js. Выполним следующую команду:
Добавим в
Теперь, если запустить сервер и перейти, с помощь браузера, по адресу
Прежде чем переходить к работе с технологией Webhook, нам нужно настроить HTTPS для окружения разработки. Мессенджер не примет адрес Webhook, используемый для отправки уведомлений нашему серверу, если вы используете самоподписанный SSL-сертификат. Бесплатный сертификат можно получить от Let’s Encrypt. Тут, правда, можно получить сертификат только на домен, а не на IP-адрес. Мы воспользуемся сервисом ngrok, который позволит организовать доступ к локальному серверу через общедоступный URL, который использует HTTPS.
Настроить
Не забудьте перенаправить порт 80 на 8989 в WAN-настройках вашего маршрутизатора. В результате
Мессенджер использует технологию Webhook для аутентификации и для передачи уведомлений о происходящих событиях вашему приложению. С точки зрения программирования всё это сводится к работе обычных функций обратного вызова для обработки HTTP-запросов, которые будут получать данные о событиях, вроде полученных чат-ботом сообщений. Для разбора GET и POST-запросов мы будем использовать модуль
Добавим в приложение следующий маршрут. Он нужен для обработки Webhook-запросов на верификацию.
Теперь надо открыть настройки мессенджера, найти там раздел Webhooks и настроить интеграцию приложения с Webhook-уведомлениями. На странице настроек, в поле Callback URL, надо ввести наш HTTPS URL, полученный от ngrok. Токен верификации (тот, который присутствует в коде и представляет собой созданную нами случайную строку) надо поместить в поле Verify Token. После этого у вас должно получиться верифицировать и сохранить настройки, нажав на кнопку Verify and Save, если ваш URL для обработки Webhook-уведомлений доступен, и токен верификации соответствует тому, который имеется в коде.
Настройка токена и URL для получения приложением Webhook-уведомлений
После сохранения выберите вашу страницу из выпадающего списка и подпишитесь на события страницы.
Теперь создайте POST-маршрут для обработки POST-событий от мессенджера. Добавьте в приложение следующий код.
Мы настроили приложение таким образом, чтобы оно обрабатывало два типа событий —
Метод
Теперь, когда мы получаем события
Когда новый пользователь начинает беседу с ботом, в окне чата выводится кнопка Get Started. Можно настроить собственное postback-событие для этой ситуации. Например, задать выдачу сообщения для пользователя, которое описывает бота и то, как с ним общаться. Для того чтобы настроить собственное приветствие, выполните эту команду
Мы настроили Aww Bot так, чтобы он выводил сообщение, спрашивая пользователя о том, готов ли он увидеть самых симпатичных кошек и собак. Для того чтобы настроить postback-событие, выполните в терминале эту команду:
Вот как выглядит сеанс начала чата с ботом.
Экран начала работы
Мы будем использовать модуль конфигурации npm для хранения токена доступа к странице в отдельном конфигурационном файле. Создадим директорию
Мы будем получать токен доступа к странице в методе
Вот код обработки события начала работы.
Создадим метод
Мы отправляем пользователю сообщение, содержащее две кнопки и текст. Когда пользователь выберет то, что ему нужно, щёлкнув по соответствующей кнопке, на наш Webhook-адрес будет отправлен запрос с данными события
Пользователю предлагается выбрать интересующий его вид изображений
Обновим код функции-обработчика события
Когда пользователь щёлкает по кнопке
Напишем простое API для возврата ссылок на изображения кошек или собак, которые будут использоваться в сообщениях, отправляемых ботом пользователям. Создадим файл
Теперь подключим его в приложении.
Добавим в код следующий метод, используемый для формирования сообщения, содержащего ссылку на изображение.
В процессе взаимодействия пользователя с ботом изображения поочерёдно извлекаются из массива и отправляются в виде ответов бота пользователю. После отправки последнего изображения мы возвращаемся к началу списка.
Добавим в проект следующий код, предназначенный для хранения и обработки данных о пользователях, общающихся с ботом.
Мы храним PSID каждого из пользователей, общающихся с ботом, в виде ключа в объекте
Кроме того, после отправки изображения, мы передаём методу
Общение с ботом
Вот репозиторий этого проекта. Там, в файле
Уважаемые читатели! Планируете ли вы создавать ботов для Facebook Messenger?
Начало работы
Начнём с создания страницы на Facebook, заполнив необходимые поля. Эта страница предназначена для бота. Кроме того, создадим Facebook-приложение, после чего, на странице Add a Product, подключим к приложению продукт Messenger. Далее, мы окажемся на странице настройки мессенджера. Здесь надо найти раздел Token Generation, в нём — выбрать страницу бота в списке Page. После этого нас спросят о разрешениях и будет создан токен доступа. Бот будет использовать этот токен для выполнения вызовов к API Facebook Messenger, что позволит ему общаться с пользователями.
Настройка веб-сервера
Мы, для создания HTTP-сервера, будем использовать node.js и express.js. Выполним следующую команду:
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 для окружения разработки. Мессенджер не примет адрес Webhook, используемый для отправки уведомлений нашему серверу, если вы используете самоподписанный SSL-сертификат. Бесплатный сертификат можно получить от Let’s Encrypt. Тут, правда, можно получить сертификат только на домен, а не на IP-адрес. Мы воспользуемся сервисом ngrok, который позволит организовать доступ к локальному серверу через общедоступный URL, который использует HTTPS.
Настройка ngrok
Настроить
ngrok
несложно. Нужно лишь загрузить сжатый архив с сайта проекта, распаковать его и выполнить следующую команду:./ngrok http 80
Не забудьте перенаправить порт 80 на 8989 в WAN-настройках вашего маршрутизатора. В результате
ngrok
создаст общедоступные HTTP и HTTPS-адреса для локального сервера.Работа с Webhook-уведомлениями
Мессенджер использует технологию Webhook для аутентификации и для передачи уведомлений о происходящих событиях вашему приложению. С точки зрения программирования всё это сводится к работе обычных функций обратного вызова для обработки 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);
}
}
});
Теперь надо открыть настройки мессенджера, найти там раздел Webhooks и настроить интеграцию приложения с Webhook-уведомлениями. На странице настроек, в поле Callback URL, надо ввести наш HTTPS URL, полученный от ngrok. Токен верификации (тот, который присутствует в коде и представляет собой созданную нами случайную строку) надо поместить в поле Verify Token. После этого у вас должно получиться верифицировать и сохранить настройки, нажав на кнопку Verify and Save, если ваш URL для обработки Webhook-уведомлений доступен, и токен верификации соответствует тому, который имеется в коде.
Настройка токена и URL для получения приложением Webhook-уведомлений
После сохранения выберите вашу страницу из выпадающего списка и подпишитесь на события страницы.
Теперь создайте POST-маршрут для обработки 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);
}
});
Мы настроили приложение таким образом, чтобы оно обрабатывало два типа событий —
message
и postback
. Для того чтобы проверить работу механизма Webhook-уведомлений, откройте мессенджер и отправьте странице бота сообщение. Если всё работает как надо, в лог попадут 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()
— за обработку входящих событий postback
. Обновите существующий код, добавив туда вызовы этих методов:// Проверка события
// и передача события подходящей функции-обработчику
if (webhook_event.message) {
handleMessage(sender_psid, webhook_event.message);
} else if (webhook_event.postback) {
handlePostback(sender_psid, webhook_event.postback);
}
Теперь, когда мы получаем события
message
или postback
, данные будут передаваться соответствующим обработчикам вместе с PSID отправителя.Настройка экрана приветствия и postback-события начала диалога с ботом
Когда новый пользователь начинает беседу с ботом, в окне чата выводится кнопка Get Started. Можно настроить собственное postback-событие для этой ситуации. Например, задать выдачу сообщения для пользователя, которое описывает бота и то, как с ним общаться. Для того чтобы настроить собственное приветствие, выполните эту команду
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 так, чтобы он выводил сообщение, спрашивая пользователя о том, готов ли он увидеть самых симпатичных кошек и собак. Для того чтобы настроить postback-событие, выполните в терминале эту команду:
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"
}
}
}
Мы будем получать токен доступа к странице в методе
callSendAPI()
, пользуясь командой config.get('facebook.page.access_token')
.Обработка события начала работы
Вот код обработки события начала работы.
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()
, который будет возвращать правильно подготовленный объект ответа для API мессенджера. Метод 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);
}
});
}
Мы отправляем пользователю сообщение, содержащее две кнопки и текст. Когда пользователь выберет то, что ему нужно, щёлкнув по соответствующей кнопке, на наш Webhook-адрес будет отправлен запрос с данными события
postback
и мы обработаем его.Пользователю предлагается выбрать интересующий его вид изображений
Обработка собственных событий postback
Обновим код функции-обработчика события
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);
}
// Отправим сообщение
}
Когда пользователь щёлкает по кнопке
Cats
, на наш адрес, используемый для обработки Webhook-уведомлений, поступит запрос с событием postback
, содержащим данные CAT_PICS
. Выбор варианта Dogs
приведёт к отправке события postback
с данными DOG_PICS
. Мы добавили в систему ещё один метод, 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
, вы можете найти инструкции по установке и настройке бота. Для того чтобы с вашим ботом могли пообщаться другие люди, ваше Facebook-приложение должно быть одобрено. До этого момента поговорить с ботом смогут лишь администраторы и тестировщики вашего приложения. Вот видео, демонстрирующее процесс общения с ботом.Уважаемые читатели! Планируете ли вы создавать ботов для Facebook Messenger?
Tantrido
botbuilder
и разработка такого бота упростится в разы: habr.com/post/333824 — не нужно будет изобретать велосипеды! Этот бот разрабатывался под Facebook: habr.com/post/340092ZOXEXIVO
BotBuiler не лучший вариант.
Да, он упрощает интеграцию и делает код более универсальным, но взамен получаете тонны багов, постоянно сломанная обратная совместимость и проксирование трафика через сервера MS, которые иногда дают ощутимый лаг в запросах.
Tantrido
Не сталкивался.
У нас приложение работало на Azure — всё работало реактивно.