Как стать автором
Обновить

Как превратить любой скрипт в Telegram-бота

Время на прочтение6 мин
Количество просмотров31K

Если вам нужен простенький Telegram bot, способный выполнять скрипты (написанные на любом языке) и отвечать текстом и картинками, то вам под кат. Там вы найдёте рассказ о бот-движке, который делает то, что вам надо.


Telegram bot


Краткий список возможностей движка:


  • Движок может обслуживать сразу несколько ботов
  • Бот выполняет скрипты (написанные на любом языке)
  • Сообщение попадает на вход скрипту в виде аргументов и переменных окружения
  • Вывод скрипта может быть текстом, форматированным текстом или изображением (распознаётся автоматически)
  • Движок гарантирует поочерёдный запуск скриптов (при написании скриптов можно не думать про локи и конкурентный доступ к ресурсам)
  • Бота можно дёргать по HTTP, чтобы отправлять сообщен не в ответ на запрос, а "асинхронно" (например по cron-у)

Движок максимально неприхотлив: ему не нужны базы данных, публичные IP-шники, SSL-сертификаты… Можно просто запустить на лаптопе, сидя за НАТом с наглухо закрытыми портами. В общем, начать экспериментировать вы можете прямо не сейчас, не отрываясь от чтения.


Сейчас я покажу, как это всё запустить и оживить.


Сборка


Вам понадобится язык Go. Чтобы его поставить, не нужны даже root-права. Но, для простоты, далее, я буду предполагать, что он у вас стоит в системе.


Скачиваем и собираем проект:


cd tmp
git clone https://github.com/michurin/cnbot.git
cd cnbot
go build ./cmd/...
./cnbot

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


Начинаем разговор


Первым делом, вам надо зарегистрировать бота и получить для него токен. Это совсем не сложно: инструкция на сайте Telegram.


Создаём минимальный кофигурационный файл (config.yaml):


bots:
  firstBot:
    token: "22222222:AAAAAAAAAAAAAA"
    script: "/usr/bin/true"

Тут должен быть правильный токен и любой исполняемый файл в качестве скрипта (рекомендую выбить что-нибудь побезопасней, чем /bin/rm). Проверяем настройки (-i):


./cnbot -i -c config.yaml

Если токен правильный, вы получите отчёт о состоянии бота.


Запускаем бота (без -i)


./cnbot -c config.yaml

Пытаемся добавить его в Telegram-клиенте. В логах бота видим ошибку


user 500050880 is not allowed

Это ваш user_id (у вас он будет другой), добавляем его в конфиг


bots:
  firstBot:
    token: "22222222:AAAAAAAAAAAAAA"
    script: "/bin/echo"
    allowed_users: [500050880]

Обратите внимание, я прописал echo в параметр script. Это быстрый (хоть и кривоватый) способ сделать echo-бота. Вы уже можете поговорить с ним. Попробуйте сказать hi, Hi!, -n hi.


Из подобного разговора сразу видно как легко получить уязвимость (-n было интерпретировано как параметр echo). Так же видно как формируются аргументы скрипта: сообщение приводится к нижнему регистру; допустимыми символами считаются буквы, цифры, минус, точка и подчёркивание; все недопустимые символы считаются разделителями.


Полное сообщение тоже доступно. Давайте заменим /bin/echo на простой скрипт и посмотрим переменные окружения:


#!/bin/sh
env

Если сказать этому боту Hello! Он покажет переменные окружения


BOT_TEXT=Hello!
BOT_FROM_FIRSTNAME=Alexey
BOT_NAME=firstBot
BOT_CHAT=500050880
BOT_FROM=500050880

Видно, что доступно оригинальное сообщение, имя и ID пользователя, который отправил сообщение, название бота (согласно конфигу; напомню, что движок может обслуживать сразу несколько ботов).


Если бот получает контакт или пересылку сообщения, то к переменным окружения добавляется информация о контакте или авторе оригинального сообщения. Это удобно, когда вы хотите добавить в white list нового пользователя. Чтобы узнать его ID — просто перешлите его контакт или любое его сообщение боту. См. пример в demo.sh.


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


Скрипт можно чуть усовершенствовать


#!/bin/sh
echo '%!PRE'
env | sort

Теперь вывод будет преформатированный. Можно использовать и markdown, см. всё тот же demo.sh.


Чтобы ответить картинкой, её достаточно просто вывалить на stdout:


#!/bin/sh
curl -qfs https://golang.org/lib/godoc/images/footer-gopher.jpg

Если скрипт не выдаст ничего, то бот отправит сообщение "empty", чтобы бот действительно ничего не ответил, скрипт должен ответить одним единственным символом "точка".


Все примеры можно найти в demo-скрипте, а я бы чуть подробнее остановился бы на асинхронной нотификации.


Бот говорит сам


В боте можно включить HTTP сервер добавлением одной строки bind_address в конфиг:


bots:
  firstBot:
    token: "22222222:AAAAAAAAAAAAAA"
    script: "/bin/echo"
    allowed_users: [500050880]
    bind_address: ":9091"

Теперь вы можете отправить асинхронное сообщение:


echo "ok" | curl -qfsX POST --data-binary @- "http://:9091/500050880"

То есть, пользователь получит его не в ответ на своё сообщение, а просто как нотификацию. Тело сообщения обрабатывается по тем же правилам, что и output скрипта. То есть, вы можете отправлять форматированный текст и картинки.


Можно использовать и multipart/form-data:


curl -qfsX POST -F to=500050880 -F msg=ok "http://:9091"

Думаю, нет смысла обсасывать каждую деталь работы бота. У вас уже есть достаточно информации, чтобы понять, нужно оно вам или нет. Полный спектр возможностей можно посмотреть, поговорив с demo-сриптом. В конфиге можно указать таймауты для http-клиента и выполнения скрипта. Все опции есть в readme проекта, хотя, думаю, большинству будет комфортно жить и с дефолтами.


Любые вопросы, пожелания, предложения — приветствуются.


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


Если вы всё ещё читаете...


..., то могу рассказать, как я дошел до жизни такой.


Мы как-то делали очень глубокий редевелопмент системы. Фактически, мы запускали абсолютно новый проект сразу под огромной нагрузкой. Конечно, где могли, мы подстелили соломки. Но у нас не было ресурсов, что бы поддерживать на лету сразу два проекта такого масштаба. То есть мы не имели возможности быстрого переключения на старую версию.


В день старта я уходил домой очень поздно, но мне хотелось держать руку на пульсе постоянно. Мне не подходил сценарий: найти местечко в метро, достать ноут, поднять VPN, посмотреть логи… Хотелось иметь возможность глянуть основные вещи с телефона и, возможно, что-то подтюнить, сбегая по эскалатору.


Я гуглонул, что на это тему знает Интернет, и оказалось, что Telegram предоставляет бесплатное и великолепное API для ботов. Я написал бота-уродца в несколько строчек на bash+curl+jq, который умел выполнять буквально три команды, и поехал домой.


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


Тем временем, я начал использовать таких ботов во многих бытовых делах. У меня есть бот для учёта тренировок и отслеживания прогресса (графики), есть бот для управления домашним микротиком… В конце концов, у меня накопился очень чёткий список фичей, которыми должен обладать бот. А так же, список тупиковых идей, которые выглядят хорошо, но на деле мало полезны.


В 2018 я начал учить Go и, просто для эксперимента, заимплементил движок ботов на Go. Это был мой первый код на новом языке и он был ужасен :-) Но, глобально, Go оказался очень удобной штукой для подобных вещей. И вот, поднабравшись опыта в Go, я решал вернуться к этому проекту и переписать его на Go, но уже "правильно" (ну или близко к тому).


Так и появилось это поделье. Почему репа называется cnbot, я так и не смог вспомнить по прошествии лет.


Куда я планирую всё это развивать?


Я подумываю о расширении функциональности, но очень осторожно. Очень не хотелось бы переусложнять. Если вам нужно какое-то специфичное решение, — просто напишите своего бота. А этот движок я хотел бы оставить максимально простым.


Но я бы хотел развивать движок в сторону встраиваемости: выделить из него какую-то простую часть, которую можно было бы подключить библиотекой к любой Go-программе. Условно, если у вас уже есть микросервис для… для чего годно… хоть для рендеринга 3D-сцен, — вы можете в одну-две строчки встроить в него чат бота для оперативной диагностики/мониторинга/управления… Вот это, мне кажется, было бы полезно. На самом деле, вы уже сейчас можете так сделать. Просто это не очень удобно.


upd через месяц: заметка не потеряла актуальность, но произошла куча доработок. Основное:


  • добавлены inline-клавиатуры
  • добавлена возможность менять сообщение с клавиатурой, чтобы делать меню как в @BotFather
  • добавлены алерты: модальные и простые вверху окна
  • добавлена обработка редактирования сообщений пользователем
  • добавлена обработка гео-позиций
  • выкинута наркоманская магия с точкой, вместо неё сделана обычная управляющая строчка %!SILENT
  • возможность добавлять подписи к картинкам
  • добавлено много новых переменных окружения
  • запустить демо-скрипт локально стало очень просто: не надо ничего конфигурировать/редактировать
  • я запустил демо-бота @cnbot_demobot. Можно познакомиться с основными возможностями, ничего не устанавливая.
Теги:
Хабы:
Всего голосов 12: ↑11 и ↓1+15
Комментарии10

Публикации

Истории

Работа

Ближайшие события

25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань