Создание Телеграм бота сейчас: бессерверный подход

  • HLCS 

Источник · Перевод автора

Тридцать лет назад я жил в Финляндии, где в то время текстовые сообщения были основным способом общения. Это не совпадение, учитывая, что именно финн Яркко Оикаринен создал глобальную чат-систему IRC, которая живет и продолжает процветать по сей день. Американцам и остальному миру понадобится еще 12/15 лет, прежде чем они перейдут на текстовые сообщения, с появлением доступных SMS.

Сегодня существует множество платформ обмена мгновенными сообщениями, таких как Signal, известными благодаря поддержке Эдварда Сноудена, швейцарской компании Wire, Facebook’s WhatsApp, очень скрытных Wickr, WeChat и многих других.

Из всех этих систем только Telegram, насчитывающий, по последним подсчетам, 200 миллионов активных пользователей в месяц, предлагает что-то, что существовало в мире IRC с момента его создания: программируемость, то есть любой может написать «бот» — программный модуль, с которым можно общаться в чате.

Думайте о ботах как о личных агентах, которые способствуют определенному интересу или деятельности

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

Недавно я решил, что хочу построить один. Для тех, кто заинтересован, в этой статье я подробно опишу, как его создать, а также предоставлю некоторые инструменты и подход для отладки и развертывания, которые сделают процесс полностью безболезненным.

И да, мой путь был нелегким, но после невероятного количества воплей и скрежета зубов, я сделал это и получил захватывающий результат.

Создание вашего первого бота

Давайте начнем с самого начала. Для создания бота вам необходимо установить приложение Telegram. Когда вы получите учетную запись, вам нужно будет попросить BotFather создать своего бота. Сначала найдите его, введя его имя в поле поиска Telegram, а затем нажмите на список:

Затем нажмите представленную кнопку Start; это отобразит команды, которые принимает BotFather. Чтобы создать своего бота, введите в текстовое поле:

/newbot

и BotFather спросит, как вы хотите назвать бота и имя пользователя, чтобы назначить его — имя пользователя должно заканчиваться суффиксом _bot.. Для этого урока мы будем называть бота «Тестовым Telegram ботом» с именем пользователя the_test_telegram_bot, (вы можете выбрать любое своё название, которое имеет для вас смысл).

Затем BotFather сгенерирует ключ API, который будет похож на приведенный ниже:

735246361: AAFLPovGMjh6lKXENaCyczHyLqwPGH0EDns

Вам нужно будет защитить этот ключ и сохранить его в секрете, так как любой с ним может контролировать вашего бота!

Теперь введите имя вашего бота в поле поиска и, когда вы найдете его, нажмите на него, а затем нажмите кнопку Start:

Пока ничего не случится. Вы можете напечатать «привет», «grüßen» или «привет и приветствие», но бот не сочтет нужным поговорить с вами.

Проблема в том, что боты являются составными. Telegram предоставляет интерфейс (фронтенд), механизм в своем приложении для ведения разговора, но вы должны предоставить информацию о поведении бота. Так что в настоящее время ваш бот «не слышит» вас и ему нечего ответить.

Мы исправим это с помощью кода

Существует множество реализаций, варьирующихся от совершенно, умопомрачительно сложных, до конструкций, которые делают очень мало, и тонны статей и учебных пособий, таких как Soham Kamani, Как сделать отзывчивого бота telegram — с которыми я начал, но это в большей степени не работает.

Существует также проблема того, на каком языке программирования описывать это поведение

Пример кода существует для PHP, Java, C #, Lua, Python, Ruby, Go, Haskell, Scala и так далее, и если вы хорошо разбираетесь в любом из них, вы можете прекратить чтение сейчас и перейти к другому учебнику.

В этой статье мы будем использовать популярный язык Javascript, реализованный в движке NodeJ, но, что более важно, развернутый на бессерверной платформе с использованием микро-сервисной архитектуры

Если вышесказанное звучит как глоток (свежего воздуха), так и есть. Для вас это означает, что для создания этого решения обычно требуется множество инфраструктуры, вам не нужно беспокоиться о том, что вам не нужен хост, с сопутствующими проблемами разместить его, как его защитить, оплатить его расходы и так далее. Вам не нужно будет выбирать или устанавливать операционную систему, или делать резервные копии и исправлять ее. Вам не нужно создавать и защищать учетные записи, устанавливать сервер приложений или веб-сервер, не нужно настраивать обратный прокси-сервер, генерировать криптографические сертификаты и выяснять, как интегрировать их в службу SSL, не нужно беспокоиться о масштабируемости (способность системы работать по мере увеличения ее использования), атаках типа «отказ в обслуживании» и хакерах, высокой доступности (когда системы гарантируют бесперебойную работу) или непрерывности бизнеса (новый термин для отказоустойчивости, то есть избыточности, необходимой для поддержания света в случай крупной катастрофы) или любой из миллиона вещей, о которых IT-системам обычно приходится беспокоиться

Так о чем конкретно беспокоиться? Бизнес-логика — то, что вы хотите сделать

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

Примечание. Работа, которую мы здесь делаем, будет выполнена на Apple OS / X. Если вы работаете в другой операционной системе, этапы работы могут отличаться!

Установка фреймворков

Во-первых, нам нужно установить кучу вещей… вещей, которые, если вы собираетесь стать разработчиком, вам все равно понадобятся. Давайте пройдем через это!

Начните с загрузки NPM / NodeJs со страницы официального скачивания.

Вам также необходимо установить клиент github, чтобы получить доступ к крупнейшему в мире хранилищу открытого исходного кода на сегодняшний день. Для этого необходимо сначала установить homebrew, который, по их собственным словам: «устанавливает то, что вам нужно, чего не делал Apple». Инструкции здесь:

Затем создайте себе аккаунт в Now, на их странице регистрации и установите инструмент интерфейса командной строки:

$ npm install -g now

Определение поведения бота

Теперь у нас должна быть вся необходимая инфраструктура …

Для создания и развертывания нашего первого бота нам не хватает только самого кода. К счастью для вас, дорогой читатель, я поместил все это в небольшой аккуратный пакет в проекте Github, который вы можете установить с помощью простой и быстрой команды ниже:

$ git clone https://github.com/ekkis/telegram-bot-now.git

Получив исходный код, давайте откроем файл rout.js в вашем любимом редакторе (я все еще использую vi, но начинаю разогреваться до VS / Code). Этот файл экспортирует объект, содержащий каждый метод, который определяет поведение вашего бота и является тем, что вам нужно адаптировать для ваших конкретных потребностей.

В связи с этим, потребуется небольшое объяснение

Объяснение

Методы — это одноименные (называемые так же, как) команды, которые они представляют, поэтому, когда пользователь вводит /help в группе, где присутствует бот, он активирует метод, называемый «помощь», условно определяемый так:

help: async function(msg) {
var s = "You can ask me to do any of the following:\n";
s += "/help - this message\n";
return s;
}

Как правило, методы отвечают только за возврат строки, которая будет отображаться пользователю, когда пользователь выполнит данную команду. Возвращаемые строки могут быть оформлены в Markdown, и наблюдательные читатели заметят, что методы получают копию объекта сообщения, сгенерированного сервером Telegram, как определено в официальной документации. Этот объект может быть полезен при выборе поведения, которое мы выбираем.

Например, представьте, что торговый бот поддерживает команду /trade … это может быть сделано в группе, но торговый разговор между пользователем и ботом, возможно, должен быть закрытым. В этом случае мы можем различать, потому что команды, выпущенные в группе, начинаются с косой черты, но в прямых сообщениях они не содержат косой черты. Поэтому такой код может быть реализован:

trade: async function(msg) {
return msg.text.beginsWith('/')
? 'We should speak privately. Please DM me\n'
: 'Sure. Let's get down to business\n'
;
}

Также обратите внимание, что методы должны быть объявлены как асинхронные, так как их будет ожидать вызывающий контекст. Также обратите внимание, что файл rout.js *должен* по крайней мере определять методы start, help и wrong, последний из которых вызывается всякий раз, когда пользователь вводит ошибочную или иным образом неизвестную команду, например:

/helpp

Развертывание бота (деплой)

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

chmod +x mk

и, возможно, вы захотите поместить текущий каталог в ваш путь, чтобы вам не всегда приходилось указывать, где эти объекты находятся. Я хотел бы поставить мой прямо наверху:

export PATH=.:$PATH

Теперь нам нужно сначала сохранить секрет. Вы помните тот API-ключ, который вам дал BotFather, который, как я сказал, вам нужно было надежно хранить? Вам нужно это сейчас:

$ mk secret "735246361:AAFLPovGMjh6lKXENaCyczHyLqwPGH0EDns"

и, сделав это, мы готовы развернуть … барабанная дробь …

$ mk

вот и все. Вы сделали. Удивительно, верно?

Если при развертывании вас интересуют журналы, введите:

$ mk logs

или войдите на Zeit Dashboard и посмотрите на развертывания вашего проекта:

клик любого данного развертывания (обычно требуется только один) покажет вам журналы:

В общем, когда вы вносите изменения в своего бота, чтобы добавить возможности исправлений, вы будете перераспределять его. Каждое изменение требует повторного развертывания. Инструменты mk заботятся об этом, а также привязывают его к вашему боту, но вы также захотите очистить (удалить старые развертывания), что можно сделать на веб-сайте Zeit.

Тестирование вашего бота

Чтобы убедиться, что все работает, просто отправьте своему боту сообщение на Telegram. Вы можете сказать help (помощь) и получить в каталоге список своих сверхспособностей или активировать любую команду, которую вы, возможно, реализовали, чтобы увидеть, насколько хорошо она работает.

Фреймворк

На данный момент в этой статье вообще не говорится о поистине удивительной вещи в этом конкретном решении: о бессерверности подхода. Для тех из вас, кто заботится, вот небольшой бекграунд.

Взгляд на server.js покажет необычайно компактный вид. Все это выполняется примерно за 6 строк кода + немного абстракции. Давайте пройдемся по нему:

const {json, send} = require(‘micro’);

Думайте о Micro как о сверхлегкой структуре веб-служб. Если вы ранее создавали веб-службы в NodeJ, вы, вероятно, использовали express, веб-сервер в пакете, который делает кодирование в Apache похожим на жизнь с Flintstones. Но Micro делает в Express то, что Express не делает в Apache.

const fetch = require(‘node-fetch’);

Это веб-пользовательский агент, HTTP-клиент, как веб-браузер является клиентом

const routes = require(‘./routes’);

И это просто захватывает поведение вашего бота. Теперь начинается интересная часть. Вся работа в основном выполняется лямбда-выражением. Эта функция экспортируется через механизм CommonJS ECMA и получает стандартные объекты запроса (request) и результата (result). Это эквивалентно определению маршрута в Express, за исключением того, что в нем нет остальной части кода, фильтров body parsing, выбора портов, прослушивания, маршрутизатора и так далее.

module.exports = async (req, res) => {
...
}

Все остальное довольно просто. Мы используем парадигму async / await для обработки обещаний (Объект Promise) очень чистым способом:

try {
let msg = (await json(req)).message;
let m = msg.text.match(/\/?([^\s]*)/);
let s = await (routes[m[1]] || routes["wrong"])(msg);
if (typeof s === 'string') msg.text = s;
await post(msg);
res.end("ok");
} catch(err) {
send(res, 500, {error: "Error: " + err});
}

Первая строка устанавливает структуру try/catch. Поймать важно, потому что неперехваченные исключения приводят к падению сервера. Обратите внимание, что в приведенном выше коде метод отправки (send) предоставляется Micro.

Вторая строка ожидает выполнения обещания при преобразовании объекта запроса в формат Json и захватывает объект сообщения, отправленный сервером Telegram. Обратите внимание, что анализатор json также предоставляется Micro

В третьей строке используется регулярное выражение для извлечения команды из пользовательского ввода и используется в 4-й строке, чтобы проверить, был ли определен маршрут для ее обработки. Если нет, вызывается неправильный обработчик. Обратите внимание, что вызов обработчика выполняется в ожидаемом состоянии, так как мы не знаем, является ли работа, выполняемая обработчиком, асинхронной по своей природе.

Если значение, возвращаемое маршрутом, является строкой, мы просто загружаем его в объект сообщения и передаем его в метод post. Мы заканчиваем процесс, будучи хорошими соседями и возвращая код возврата 200, чтобы сервер Telegram знал, что все хорошо.

Вот метод сообщения (post method):

async function post(msg) {
var o = {
chat_id: msg.chat.id, text: msg.text,
reply_to_message_id: msg.message_id,
parse_mode: 'Markdown'
};
    return fetch(bot_url + "/sendMessage", {
method: 'post',
body: JSON.stringify(o),
headers: {'Content-Type': 'application/json'}
});
}

Вы можете видеть лишь тонкую оболочку для HTTP-запроса POST, доставляющего объект Json в форме, ожидаемой хостом Telegram.

Для большего понимания в Telegram API, смотрите их официальные документы.