[Мессенджеры, JavaScript, API, Социальные сети и сообщества] Botsman: новая платформа для разработки Telegram-ботов

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
30-Дек-2020 13:32

Сегодня, в предпоследний день уходящего года, хочу рассказать о созданном мной сервисе, помогающем быстро проектировать, отлаживать и следить за работоспособностью ботов в мессенджере Телеграм. Надеюсь, он окажется удобным инструментом. Под катом — довольно подробный рассказ о том, как этот сервис зародился, какие технологии я для него выбрал и обзор того, что он сейчас умеет.Для тех же, кому уже захотелось ознакомиться с Botsman (но не очень хочется много читать) — вот ссылка, милости прошу: https://bots.mn/. Главное, о чём стоит помнить — платформа только-только запустилась, и (пока что) не стоит переносить на неё что-то серьёзное и масштабное.Предыстория: путь к созданию БотсманаПять лет назад в Telegram появилась возможность создавать ботов — автоматизированные аккаунты, которыми можно управлять с помощью своих скриптов. Меня это сразу заинтересовало: я люблю делать маленькие, но полезные утилитки.Правда, в отличие от десктопных приложений, браузерных расширений и простых веб-сервисов, с ботами есть некоторая сложность в их разработке: в отличие от традиционных веб-сервисов, их труднее отлаживать. Даже если настроить логирование, бывает трудно понять, что именно происходит с ботом и всё ли работает так, как нужно.Поэтому почти сразу пришла в голову идея сделать такую платформу, которая возьмёт весь мониторинг на себя: будет логировать и входящие обновления, и то, как бот на них реагирует (и какие возникают при этом ошибки). Заодно можно и графики удобные строить.Но, к сожалению, дальше придумывания красивого названия — Botsman — и покупки короткого домена bots.mn дело поначалу не зашло. Проект оказался непривычно большим для того, чтобы осилить его в одиночку, и за пять лет я то брался за него, то снова откладывал на потом.И только к концу злополучного 2020-го я нашёл в себе силы довести его до рабочего состояния, которое можно показать широкой публике. Специально поставил цель зарелизиться до Нового года: чтобы добавить этому году плюс одно хорошее событие :)Итак, что же сейчас предлагает данная платформа?Проксирование запросовВо-первых, мой сервис умеет работать как прокси: вы подключаете бота к Botsman, он ловит все приходящие от Telegram события, логирует, и передаёт вашему серверу. Это удобно, если у вас уже написан и где-то крутится бот: ваш код может даже не подозревать про эту прослойку.
Выбор способа обработки запросов ботомУ этой фичи есть очевидный недостаток: небольшое увеличение времени отклика (поскольку в цепочке «Telegramваш сервер» появляется дополнительное звено).Но этой осенью Telegram сделал крутую вещь: они выложили в открытый доступ код сервера-посредника Bot API. По своей сути это такое приложение, которое внутри общается с Телеграмом как клиент по их протоколу MTProto, а снаружи у него торчит уже простое и понятное Bot API. И когда вы обращаетесь к публичному Bot API по HTTPS — запрос на самом деле идёт к инстансу такого сервера, а теперь стало можно поднять его самому. И конечно же, внутри Ботсмана я так и сделал (и это новшество оказалось ещё одним мотиватором закончить проект).Таким образом, вашего бота можно настроить так, что цепочка не станет длиннее: вместо
«Telegramсервер Bot APIваш сервер» будет
«TelegramBotsmanваш сервер».Правда, тут уже потребуются правки в коде вашего бота: исходящие запросы придётся делать не на api.telegram.org, а на api.bots.mn/telegram. Зато Botsman сможет логировать и их тоже!Собственно, поговорим о логировании:«Живая» лента обновленийПосле настройки бота в Botsman, можно сразу открыть страницу «Events», отправить что-то в Телеграме своему боту, и увидеть, как это сообщение появилось на экране в реальном времени. Если у вас настроен прокси — вы увидите и результат перенаправления запроса вашему серверу. Если ваш сервер шлёт запросы через проксирующий эндпоинт api.bots.mn/telegram — они тоже туда попадут.
Так в интерфейсе Botsman выглядит лог всех происходящих с ботом событийС технической стороны, конечно, тут всё просто: я использую веб-сокеты чтобы проталкивать все обновления на клиент, если у вас открыта соответствующая вкладка (если открытых страниц нет, лишней нагрузки это создавать не должно). Возможно, если бы я начинал этот проект сейчас, я бы попробовал использовать Server-sent events вместо веб-сокетов, но вряд ли есть какой-то повод переходить на них.Кроме бесконечного лога, куда все события сыпятся вперемешку, Botsman ещё умеет показывать интерфейс, похожий на обычный список диалогов в самом мессенджере — то, как их «видит» ваш бот. При желании можно даже никак не обрабатывать запросы, а просто вручную открывать диалоги и отвечать от имени бота (этакий режим техподдержки — возможно, впоследствии я добавлю поддержку управления ботами с нескольких аккаунтов с разными ролями, если это действительно будет актуально).Слежение за показателями ботаНу и конечно же, статистика и графики, куда без них. Честно говоря, аналитик из меня так себе, поэтому сейчас Botsman показывает только довольно базовые метрики — общее число апдейтов, число чатов, число пользователей, дневную и месячную аудиторию (DAU и MAU). Графики — по числу апдейтов на каждый день/час, и по среднему времени обработки запросов. Было бы, конечно, интересно смотреть на всякую демографию, но в Telegram в этом плане мало информации о пользователях.
Графики в разделе «Stats». Как видно, через одного из моих ботов уже прошло почти 20 млн апдейтов.Для подсчёта статистики я с самого начала выбрал старый добрый Redis, а вот на клиентской стороне решил рисовать графики с помощью библиотеки, которую до этого сам же написал на конкурс того же Телеграма. Не пропадать же добру :)СкриптингНо вернёмся к тому, как вообще «оживить» вашего бота. Просто проксировать запросы к другому серверу скучно (да и от дополнительного «звена» всё-таки лучше избавиться). Мне хотелось дать возможность прямо внутри сервиса описать логику работы бота. Знаю, что существует множество конструкторов ботов для этого (хотя я и старался всё делать без оглядки на них), но мне хотелось возможности писать полноценный код прямо в браузере.При этом мне не очень хотелось прибегать к компилируемым в нативный код языкам: это производительно, но тогда пришлось бы строить сложную систему, оборачивая код каждого бота в контейнер и держать их все непрерывно запущенными.Поэтому я выбрал JavaScript: моя изначальная идея была взять встроенную в Node песочницу, немного доработать (как это сделано в библиотеках Sandcastle или vm2), чтобы сделать её безопаснее, и выполнять код ботов в ней.Но в процессе очередного «подхода» к допиливанию проекта пришло осознание, что даже с доработками такой подход неидеален: код в таком случае выполнялся бы все равно в одном общем потоке и при росте нагрузке неизбежно привёл бы к тормозам. Да и риск проблем с изолированностью исполняемого кода всё-таки сохраняется.В итоге я обратил внимание на библиотеку isolated-vm: она тоже реализует песочницу в JS, но делает это другим, более безопасным (и, что важно, многопоточным) образом. По сути это обёртка над присутствующим в V8 механизмом «изолятов» — независимых контекстов, которые ничего не знают друг про друга. Эта же библиотека, кстати, используется в игре Screeps, где игрокам тоже нужно писать своих ботов.Что особенно хорошо для ботов: состояние одного такого изолята можно держать в памяти как кусок данных (не расходуя процессорное время), и только когда боту понадобится обработать прилетевший апдейт от Телеграма — «оживлять» это состояние, вызывая в нём функцию-обработчик. И конечно можно ограничить как потребляемую память, так и процессорное время (с этим, помнится, в нодовской песочнице были некоторые сложности).Скриптинг: внутреннее API, обработчики событийДальше одним из камней преткновения было дать удобное внутреннее API для написания ботов. Конечно, можно было сказать «вашему коду будет доступна переменная update и метод callMethod, а дальше делайте, что хотите». Но раз уж я проектирую всю песочницу, нужно идти до конца.В частности, мне хотелось реализовать механизм «состояний»: часто боты рассчитаны на пошаговый диалог, и это надо учесть, как-то запоминать контекст, даже если пользователь бросил диалог и вернулся к нему на следующий день. В итоге я решил, что для каждого чата, пользователя или сообщения я позволю хранить объект с произвольными полями. А у чатов (как личных, так и групповых) в придачу есть понятие «пути», route — оно позволяет раскидать обработчики событий по тому, на каком шаге находится пользователь.Само добавление обработчиков делается довольно просто:
on(ctx => {
  ctx.log('Some update received: ', update);
});
Это самый универсальный обработчик: он вызывается при любом событии (напомню, кроме события «пришло сообщение» Telegram присылает и ряд других оповещений).Перед функцией-коллбэком можно указать один конкретный тип события: тогда обработчик будет вызываться исключительно для него.
on('message', ctx => {
  ctx.message.reply('Hi!');
});
А что будет, если объявить два обработчика и они оба подходят для текущего апдейта? Botsman вызовет только первый из них — но можно передать управление следующему, если вернуть false (ну или промис, резолвящийся в false — разумеется, всё делалось с расчётом на асинхронный код).Ещё есть удобные способы обработывать только текстовые сообщения с помощью on.text (их можно заодно ещё и фильтровать по регэкспу), только команды — с помощью on.command, инлайн-запросы — on.inline, и коллбэк-запросы (нажатия на кнопки под отправленными сообщениями) — on.callback. О них можно почитать в документации.В примерах выше видно, что обработчику передаётся единственный параметр — экземпляр класса EventContext, и через него делаются все вызовы. Это позволяет Ботсману связывать, например, вызовы API или возникающие ошибки с конкретным апдейтом, который к ним привёл.Ну а как разграничить обработчики для разных состояний (путей) чата? Для этого предназначена глобальная функция state:
state('step1', (on, state) => {
  // Этот обработчик вызовется для любого сообщения,
  // если наш чат в состоянии 'step1' - и переведёт его
  // в состояние 'step2'
  on.text(ctx => {
    ctx.route.to('step2');
  });
});
state('step2', (on, state) => {
  // А этот обработчик вызывается, если наш чат в
  // состоянии 'step2' и возвращает его в 'step1'
  on.text(ctx => {
    ctx.route.to('step1');
  });
});
Обратите внимание: функция state немедленно вызывает переданный ей коллбэк с двумя аргументами, которые заменяют собой глобальные функции on и state. Добавленный с помощью локальной функции on обработчик будет вызываться только в указанном состоянии, а с помощью локальной функции state можно создавать «вложенные» состояния (хотя их можно создать и вручную, просто записывая путь, разделённый слэшами: 'step1/substep1/branchA'). Пока что, впрочем, иерархическая структура состояний особых преимуществ по сравнению с линейной не имеет (но может помочь их логически упорядочить).Скриптинг: форматируем сообщения с помощью tagged template literalsОтдельно поделюсь одной, казалось бы, незначительной, но весьма радующей лично меня деталью. Если вы уже пробовали использовать Telegram API, то возможно сталкивались со сложностями при отправке текста с форматированием — особенно когда в него нужно подставить пользовательские данные. Telegram умеет принимать и HTML, и Markdown, но и в том, и в другом случае подставляемые данные нужно обрабатывать, эскейпить управляющие символы, что не очень удобно.К счастью, не так давно при отправке сообщения (и в других методах, где можно отправлять форматированный текст) появилась возможность просто указать отдельно, какие участки в нём нужно отформатировать. Добавляем к этому tagged templates из ES6 и получаем вот что:
await ctx.call('sendMessage', {
  chat_id: 12345,
  ...fmt`Hello ${fmt.bold(foo)}! You can combine ${fmt.italic(bar).bold()} styles together.
Links are supported ${fmt.text_link(linkLabel, linkUrl)}.`,
});
Выглядит немного непривычно, зато а) не нужно ничего эскейпить, б) невозможно сломать вёрстку, потеряв какой-нибудь HTML-тэг или символ разметки Markdown. Если у вас валидный JS — будет и валидная вёрстка. Под капотом запись fmt`something` возвращает объект с двумя полями — text и entities — поэтому его нужно распаковывать с помощью ... (spread syntax). Ну или его можно передать напрямую в короткие методы типа ctx.message.reply(fmt`something`) или ctx.chat.say(fmt`something`).Мне кажется, что у tagged template literals вообще не очень много уместных применений в реальном мире, но тут у меня получилось найти одно из них :)Скриптинг: код по расписанию и запросы к внешним APIДолжен сделать важную оговорку: так как код выполняется в изолированных контекстах, у скриптов нет ни доступа к API самой Node, ни возможности импортировать внешние модули. Однако я реализовал метод fetch (по аналогии с одноимённым браузерным API) — он позволяет делать не слишком тяжёлые запросы к внешним серверам. Кроме того, доступна глобальная функция cron — с помощью неё можно запланировать регулярное выполнение повторяющихся действий:
cron('0 0 * * FRI', ctx => {
  ctx.log('This function should execute each Friday at midnight');
});
Первый параметр тут — либо привычный формат crontab, либо интервал в секундах (правда, и то, и другое с разрешением не чаще раза в пять минут). Я думаю, что возможности выполнять код по расписанию и делать запросы к внешним API позволят реализовать множество практических задач. И не исключено, что в будущем список возможностей (например, импорт каких-то проверенных модулей) будет расширяться.Скриптинг: веб-интерфейсПисать код прямо в браузере — не самое комфортное занятие, но я стремлюсь сделать его хотя бы сравнимым с разработкой в IDE. Сейчас в качестве редактора я использую CodeMirror (в первую очередь из-за его относительно небольшого веса), в дальнейшем, вероятно, добавлю возможность переключиться на Monaco — он ощутимо тяжелее, зато должен быть шустрее и функциональнее (именно он используется внутри VS Code).
Так сейчас выглядит редактор кода в BotsmanПока что в Botsman нет разбивки кода на отдельные «файлы» — в этом тоже может быть неудобство, если вы собираетесь писать сложного бота. Зато код сохраняется автоматически, и есть возможность хранить все его предыдущие версии — это ещё не Git, но всё же.И ещё одна небольшая, но довольно ценная возможность: проверка кода перед «деплоем» с помощью тестов. При этом Botsman по нажатию одной кнопки сразу покажет подробный лог выполнения (см. скриншот выше): время инициализации, обработки запроса, отладочный вывод и возникшие ошибки (код с синтаксическими ошибками задеплоить не выйдет в принципе).Песочница для запросов к TelegramНу и последняя небольшая, но довольно полезная фича Botsman, о которой хотелось бы рассказать — панель для формирования запросов к Telegram. Нередко социальные сети предоставляют собственный инструмент для этого, но у Телеграма его, к сожалению, нет. Поэтому я сделал страницу, где можно выбрать нужный метод API, заполнить его параметры и наглядно увидеть результат вызова. Заодно можно сразу скопировать эквивалентный код, делающий этот запрос.
Панель вызова методов Telegram APIБудущие планыКак было сказано ранее, Botsman находится в самом начале своего пути. Возможно, его даже настигнет Хабраэффект (надеюсь, что нет!). Возможно, ему станет тяжко, если созданные на нём боты наберут популярность — не исключено, что тогда придётся вводить платные возможности. Поскольку занимаюсь им я сейчас в одиночку, сложно сказать, что с ним будет.В очень примерных планах сейчас такие фичи:
  • Визуальный конструктор в дополнение к скриптингу
  • Глобальное key-value хранилище + создание собственных хранилищ
  • Поддержка других платформ, кроме Telegram
  • Доступный снаружи эндпоинт для вызова кода бота
  • Управление ботом с нескольких аккаунтов
  • Навигация по коду, разбивка на модули, поддержка сторонних модулей
  • Более гибкое тестирование кода, автоматические тесты
  • Больше статистики и графиков
  • Оповещения (если с ботом что-то не так)
  • Улучшение вида чатов
  • Улучшение работы с файлами (скачивание, загрузка), в том числе в песочнице
В любом случае, надеюсь, что из этого сервиса выйдет что-то хорошее! Буду рад услышать ваши мысли, пожелания и соображения в комментариях.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_messendzhery (Мессенджеры), #_javascript, #_api, #_sotsialnye_seti_i_soobschestva (Социальные сети и сообщества), #_telegram, #_telegram (Телеграм), #_boty (боты), #_chatboty (чат-боты), #_bot, #_telegram_bot, #_telegram_bots, #_bot_api, #_telegram_api, #_botsman, #_messendzhery (
Мессенджеры
)
, #_javascript, #_api, #_sotsialnye_seti_i_soobschestva (
Социальные сети и сообщества
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 20:15
Часовой пояс: UTC + 5