Создание Whatsapp бота на Node.js. Полное руководство

Репозиторий с исходником бота

Введение

Сейчас мы расскажем, как создать WhatsApp бота на Node JS, используя наш шлюз API WhatsApp

Бот будет реагировать на команды, поступающие ему в виде обычных сообщений в WhatsApp и отвечать на них. В нашем примере мы постараемся охватить самый частоиспользуемый и необходимый в разработке функционал:

  • Реагирование и ответ на команду
  • Вывод ID текущего чата (в ЛС или в беседе)
  • Вывод имени того, кто общается с ботом
  • Отправка файлов различных форматов (pdf, jpg, doc, mp3 и др.)
  • Отправка голосовых сообщений (файлов *.ogg)
  • Отправка геопозиции (координаты локации)
  • Создание конференции (группы) с ботом

Обратите внимание: чтобы бот работал, телефон должен быть всегда подключен к интернету и не должен использоваться для Whatsapp Web

Подготовительная работа

Авторизация Whatsapp через QR код

В самом начале, сразу свяжем whatsapp с нашим скриптом, чтобы по мере написания кода - проверять его работу. Для этого переходим в личный кабинет и получаем там QR-код. Далее открываем WhatsApp на мобильном телефоне, заходим в Настройки -> WhatsApp Web -> Сканируем QR-код.

Возможности сервиса Chat-Api позволяют установить вебхук, который будет передавать информацию о новых сообщениях (и не только) Вашему веб-серверу. Чтобы сервер вызывал наш скрипт при новых сообщениях, нужно указать WebHook URL. WebHook URL – это ссылка, куда будут посылаться, методом POST, JSON–данные с информацией о входящих сообщениях или уведомлениях. Соответственно, чтобы бот работал нам требуется сервер – который эти данные будет принимать и обрабатывать.

Мы будем разворачивать веб-сервер на Вашем компьютере/VPS/VDS (далее – машина), соответственно, именно его нужно будет указать в личном кабинете. Итак, в настройках аккаунта мы указываем адрес для WebHook’a, например https://субдомен.домен.ru/webhook или если Вы будете использовать отдельный домен, это будет выглядеть примерно так: https://домен.ru/webhook.

Нажимаем «Сохранить». Готово, теперь по нашему адресу будут приходить все уведомления о новых сообщениях, нам останется только их обрабатывать.

Получить бесплатный доступ к WhatsApp API

Разворачиваем бота у себя

Мы рекомендуем начать с клонирования нашего Git репозитория с последующим изменением под себя.

Итак, создаём папку WhatsAppBot, клонируем гитом (или просто скачиваем) в неё файлы из репозитория. Первым делом мы установим необходимые зависимости для нашего бота командой npm install – набор библиотек, с помощью которых он будет работать.

Далее заходим в конфиг (файл config.js) и указываем в кавычках свой url адрес для запросов и токен (взять их можно в личном кабинете). Получается так:

                        module.exports = {
    // URL адрес для обращений к API
    apiUrl: "https://eu14.chat-api.com/instance12345/",
    // Токен для работы с API из личного кабинета
    token: "xb1s668fphzmr9m"
}
                    

Сохраняем файл и пишем в терминале node index.js – этой командой мы запустим веб-сервер для обработки запросов. Если Вы всё сделали верно – бот будет работать.

Разбор кода и написание Whatsapp бота с нуля

Если Вы решили написать бота с нуля – сейчас расскажем о том, как это сделать.

Пропускаем описание установки Node.JS и NPM (предполагая, что Вы с этим уже знакомы, раз читаете этот гайд).

Итак, создаём папку WhatsAppBot, заходим в неё и открываем терминал. Инициализируем проект командой npm init, заполняем данные или просто несколько раз нажимаем Enter до появления надписи Is this OK? (yes), подтверждаем создание ещё одним нажатием Enter.

Далее создаём наш главный файл – index.js. Он будет содержать всю основную логику работы бота. Создаём ещё и config.js – туда мы вынесем основные параметры, которые могут быть динамическими (чтобы для их исправления не залезать в основной файл).

Откроем config.js и внесём туда два параметра – apiUrl и token:

                        module.exports = {
    apiUrl: "",
    token: ""
}
                    

Не забудьте первую строку – module.exports – именно она позволит работать с этими данными из другого файла. Можем сразу заполнить данные в конфиге нашими данными из личного кабинета. Сохраняем и закрываем.

Мы будем использовать следующие зависимости (requirements):

Открываем index.js и начнём создание бота с объявления зависимостей:

                        const config = require("./config.js");
const token = config.token, apiUrl = config.apiUrl;
const app = require('express')();
const bodyParser = require('body-parser');
const fetch = require('node-fetch');
                    

Подробнее: node-fetch позволит совершать запросы к API, config подгрузит наши данные с другого файла, в переменные token и apiUrl сразу поместим данные из конфига (чтобы проще было к ним обращаться). Модуль Express нужен для разворачивания веб-сервера, а body-parser позволит удобно извлекать поток входящих запросов.

Далее объясним нашему парсеру, что работать мы будем с JSON данными:

                        app.use(bodyParser.json());
                    

Кстати, для того, чтобы узнать, как будет выглядеть принимаемый json – можно зайти в удобный раздел тестирования, который мы предоставляем в личном кабинете. В нем можно протестировать запросы и Webhook.

На всякий случай повесим полный обработчик ошибок, которые может выбросить нам prequest в процессе запросов:

                        process.on('unhandledRejection', err => {
    console.log(err)
});
                    

Ну а теперь начнём писать основной код.

Для проверки припаркованного к машине домена будем обрабатывать основную страницу (своеобразный index.html) следующим блоком:

                        app.get('/', function (req, res) {
    res.send("It's working");
}); 
                    

Проще говоря – это позволит проверить работоспособность нашего сайта – после запуска проекта при переходе на yoursite.ru - если всё сделано правильно - Вы увидите надпись “It's working" Теперь напишем функцию для общения с нашим API.

                        async function apiChatApi(method, params){
    const options = {};
    options['method'] = "POST";
    options['body'] = JSON.stringify(params);
    options['headers'] = { 'Content-Type': 'application/json' };
    
    const url = `${apiUrl}/${method}?token=${token}`; 
    
    const apiResponse = await fetch(url, options);
    const jsonResponse = await apiResponse.json();
    return jsonResponse;
}
                    

Разберём подробнее: создаём асинхронную функцию apiChatApi, которая будет принимать в себя два параметра: метод, к которому мы хотим обратиться, и объект параметров, с которым мы к этому методу обращаемся. Ну, грубо говоря, мы хотим отправить сообщение – обращаемся с методом message и передаём в объекте текст сообщения и получателя.

Внутри функции создаём объект options, который сразу пополняем двумя ключами: json и method. В первом мы передаём параметры, необходимые для API, а во втором указываем метод, с котором обращаемся и в котором хотим получить ответ.

Далее мы объявляем константу – наш url адрес для обращения к API. Он будет содержать в себе, собственно, сам url (из конфига), метод и токен, передаваемый GET запросом.

После этого направляем запрос и ответ записываем в apiResponse, который и возвращаем (в простейшем боте, кстати говоря, возврат ответа от функции, в принципе, и не потребуется – разве что для отлова ошибок)

Функция для общения с API готова. Самое время начать написание логики бота.

Выбираем, как будет называться наша страница для обработки. В моём случае пусть это будет webhook (т.е. вебхук будет отправлять запросы по адресу http://yoursite.ru/webhook). Пишем обработчик этого url:

                        app.post('/webhook', async function (req, res) {

});
                    

Внутри обработчика запишем в переменную data всё то, что получим:

                        app.post('/webhook', async function (req, res) {
    const data = req.body;

});
                    

И прогоним его через цикл for для парсинга всех сообщений:

                        app.post('/webhook', async function (req, res) {
    const data = req.body;
    for (var i in data.messages) {

    }
});
                    

Теперь запишем информацию о полученном сообщении в переменные, а также сразу отсечём информацию об исходящих сообщениях:

                        app.post('/webhook', async function (req, res) {
    const data = req.body;
    for (var i in data.messages) {
        const author = data.messages[i].author;
        const body = data.messages[i].body;
        const chatId = data.messages[i].chatId;
        const senderName = data.messages[i].senderName;

        if(data.messages[i].fromMe)return;
    }
});
                    

Итак, в author у нас теперь информация про автора сообщения, body содержит в себе текст, chatId – Id текущего чата, а senderName принял в себя имя человека, который с нами общается.

Не забудем добавить код для запуска веб-сервера в конце файла:

                        app.listen(80, function () {
    console.log('Listening on port 80..');
});
                    

А теперь можем проверить работоспособность бота, написав следующий код в цикле for после объявленных переменных:

                        console.log(senderName, author, chatId, body);
                    

Запустим бота командой node index.js и напишем бот сообщение: Test. Если всё верно, то в консоли мы увидим: Евгений 79123456789@c.us 79123456789@c.us Test

Если всё работает – двигаемся дальше. Уберём (или закомментируем) отладочную строку с console.log и подумаем, как можно обрабатывать команды.

Вариантов, на самом деле, несколько, но мы рекомендуем конструкцию if else if в связке с регулярными выражениями: это позволит, во-первых, создавать сложные команды с аргументами, во-вторых, не возиться с повторением переменных (как это было бы в случае с switch – case), а в-третьих, очень легко проверять неверно введённую команду (замыкающим else) и выдавать соответствующее сообщение.

Далее мы будем рассматривать вложенный код в for, не запутайтесь 😉

Итак, начнём с написания конструкции команд:

                        if(/help/.test(body)) {
    // Этот участок сработает, когда юзер введёт help
    } else if(/chatId/.test(body)) {
    // Этот участок сработает, когда юзер введёт chatId
    } else if(/file (pdf|jpg|doc|mp3)/.test(body)) {
    // Этот участок сработает, когда юзер введёт file pdf, file jpg, etc
    } else if(/ptt/.test(body)) {            
    // Этот участок сработает, когда юзер введёт ptt
    } else if(/geo/.test(body)) {
    // Этот участок сработает, когда юзер введёт geo
    } else if(/group/.test(body)) {
    // Этот участок сработает, когда юзер введёт group
}
                    

Ну а теперь, собственно, напишем обработчики команд. Начнём с help – тут всё проще простого:

                        const text = `${senderName}, это демо-бот для https://chat-api.com/.
Команды:
1. chatId - отобразить ID текущего чата
2. file [pdf/jpg/doc/mp3] - получить файл
3. ptt - получить голосовое сообщение
4. geo - получить локацию
5. group - создать группу с Вами и ботом`;
await apiChatApi('message', {chatId: chatId, body: text});
                    

Тут всё понятно: в переменную text мы запишем заранее заготовленный текст, разместим внутри него переменную senderName, содержащую в себе имя пользователя, который написал боту.

Ну и последняя строка – это вызов нашей функции для работы с API, в которую мы передаём метод – ‘message’ и объект с параметрами - {chatId: chatId, body: text}.

Можем запустить наш проект командой node index.js и написать боту help. Получим:

Подготовили мануал о том, как написать вацап бота на ноде

Кстати, мы советуем вам отправлять текст со всеми командами на любую непрописанную команду боту, например если человек написал нашему боту «Привет!», хотя такую команду мы не закладывали. Тогда, это стартовое сообщение всё время будет на виду у пользователя.

Теперь напишем обработчик для команды chatId. В принципе, тут тоже всё просто:

                        await apiChatApi('message', {chatId: chatId, body: chatId});
                    

Мы обращаемся к API, к методу message и просим отправить в чат chatId текст chatId.

ватсап апи

Всё просто, согласитесь? Двигаемся дальше.

На пути у нас самая сложная часть кода – функционал команды file. Для начала внимательно посмотрим на строку:

                        /file (pdf|jpg|doc|mp3)/.test(body)
                    

Я вкратце опишу её логику: мы проверяем, равняется ли body значению file + одно из значений из скобок. Например, равняется ли body значению file pdf, или значению file jpg, и так далее.

Если равняется – запускаем наш обработчик, который, первым делом с помощью функции match вычленит тип отправляемого файла в переменную fileType:

                        const fileType = body.match(/file (pdf|jpg|doc|mp3)/)[1];
                    

Т.е. в fileType у нас сейчас значение “pdf”/”jpg”/”doc”/”mp3”. Теперь заведём объект с отправляемыми данными:

                        const files = {
     doc: "https://domain.ru/tra.docx",
     jpg: "https://domain.ru/tra.jpg",
     mp3: "https://domain.ru/tra.mp3",
     pdf: "https://domain.ru/tra.pdf"
};
                    

Соответственно, он позволит нам получать url адрес файла, обращаясь к нему по индексу ключа, например:

                        files["doc"] // => "https://domain.ru/tra.docx"
files["mp3"] // => "https://domain.ru/tra.mp3"
                    

Соответственно, files[fileType] выдаст url нужного нам файла.

Осталось дело за малым – сформировать объект параметров, которые нам нужно передать в API:

                        var dataFile = {
     phone: author,
     body: files[fileType],
     filename: `Файл *.${fileType}`            
};
                    

В phone передаём автора сообщения, в body ссылку на файл (см. API), а в filename помещаем видимое название файла (я для примера помещаю слово «Файл» и его расширение).

Напишем небольшую конструкцию, которая будет добавлять в объект параметров ключ “caption” (нижняя строка текста под картинкой) только в случае, если запрашивается картинка:

                        if (fileType == "jpg") dataFile['caption'] = "Текст под фото.";
                    

И, собственно, передаём всё это в нашу функцию, указывая, что хотим вызвать метод sendFile:

                        await apiChatApi('sendFile', dataFile);
                    

Теперь реализуем обработчик для команды ptt – голосовые сообщения.

                        await apiChatApi('sendAudio', {audio: "https://domain.ru//tra.ogg", chatId: chatId});
                    

Мы обращаемся к нашей функции, передавая метод sendAudio и ключ audio с прямой ссылкой на файл в объекте параметров.

Отправка аудио бот Whatsapp

В этой статье ссылки на файлы статичны (обращаются к хостингу). Мы советуем вам передавать файлы в формате base64.

Обработчик для команды geo тоже очень прост:

                        await apiChatApi('sendLocation', {lat: 51.178843, lng: -1.826210, address: 'Стоунхендж', chatId: chatId});
                    

Вместо audio мы передаём ключи lat и lng – широту и долготу необходимого места, соответственно. В ключе “address” передаём его название. Сами координаты можно взять, например, в Гугл Картах.

Осталась команда group, создающая группу бота с человеком. Её обработчик почти не отличается от нескольких предыдущих:

                        let arrayPhones = [ author.replace("@c.us","") ];

await apiChatApi('group', {groupName: 'Группа с ботом на Node.JS', phones: arrayPhones, messageText: 'Добро пожаловать в новую группу!'});
                    

Мы создаём массив arrayPhones, в который сразу вкладываем автора, убирая в его строке @c.us, оставляя только номер. Можем вложить несколько номеров сразу.

Подробный гайд о разработке бота на node js

И выполняем запрос к функции методом group, передавая ключи groupName – название беседы, массив пользователей в phones и приветственный текст в messageText.

Поздравляю, наш бот готов!) На этом разработка обработчиков завершена. Теперь мы можем полноценно реагировать на входящие сообщения.

Вам необходимо только подставить свой токен из личного кабинета и номер инстанса.

Получить ключ API


Whatsapp бот на Node.JS

Теперь необходимо загрузить наш сервер вместе с ботом на хостинг и в качестве webhook указать ваш домен. При каждом входящем сообщение на сервер будут приходить и обрабатываться данные.

Весь код будет доступен по ссылке на гитхаб: https://github.com/chatapi/whatsapp-nodejs-bot-ru