Пишем бота для Telegram на c — Пишем Telegram бота на Ruby для уведомлений в канале

Вступление

Заикнулся в присутствии нового коллеги о своем блоге, первый его вопрос был «что за блог?», а второй «а телеграм бот у тебя есть?». Поймал себя на мысли неужели я стал ретроградом. Когда начался хайп вокруг влогов и youtube каналов я остался верен теплому ламповому формату текстовых статей, так и сейчас, считаю что нет необходимости иметь свой Telegram канал, но задача меня заинтересовала. Вокруг Telegram ботов сейчас много шумихи, не собираюсь писать тут тривиальные вещи как завести своего бота или канал, это сделали за меня, например тут. Не хотелось делать что-то надуманное, только ради «попробовать» и я решил что более менее полезной задачей будет уведомление о новых статьях в этом же блоге и только потом понял, что в принципе, мою реализацию можно использовать для любой RSS ленты c небольшими правками под себя.

  • Вступление
  • Наивная реализация или тупой бот
  • Используем простую БД или чуть более умный бот
  • Прикручиваем РСУБД или умеренно сообразительный бот
  • Заключение

Наивная реализация или тупой бот

Первое что нужно сделать это создать бота и получить API токен.

Недолго думая, я взял самую популярную либу-обертку для ботов Telegram — gem telegram-bot-ruby и наваял следующий код:

Что же тут происходит? Все очень просто, вот здесь:

подключаем библиотеки для работы с RSS и Telegram, а вот здесь:

сетим в переменную с именем token наш API токен, который получили от @BotFather.

Здесь я использую переменные окружения (Environment variables), чтобы это работало, перед запуском скрипта нужно выполнить в командной строке export TELEGRAM_BOT_API_KEY=123456789, где вместо 123456789 нужно вставить собственно токен. Использование переменных окружения один из двенадцати факторов приложения согласно — Adam Wiggins

Затем, с помощью строчки:

сохраняем в объект rss всю RSS ленту моего блога (я точно знаю что у меня там только 10 записей, поэтому не боюсь никаких переполнений или задержек).

После этого мы создаем бот клиента, обходим каждый item внутри rss и отправляем его ссылку в канал Telegram. Таким образом, при каждом запуске скрипта в канал будет отправляться 10 сообщений с одинаковыми ссылками. Я использую бота, хотя для такого же функционала, например, в Slack я бы использовал Incoming Hooks.

Кстати, чтобы бот мог слать сообщения в канал, его нужно добавить в администраторы этого канала.

На этом этапе я понял что нужно хранить состояние постов, т.е. запоминать информацию какие записи уже отправлены в канал, а какие еще нет.

Используем простую БД или чуть более умный бот

На самом деле новая версия бота растянулась на 55 строк кода, и вот он целиком:

Опять же приведу разбор этого кода по кусочкам далее по тексту.

Я использую теже две библиотеки что и раньше rss и telegram, затем подключаю sdbm это встроенная либа Ruby, которая предоставляет простое хранилище типа ключ-значение (key-value), в качестве ключей или значений могут выступать только строки. Далее я подключаю json, чтобы легко кодировать объекты в строки и декодировать обратно. И еще одной библиотекой является logger, который предоставляет простой способ для отладки.

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

Устанавливаем переменную окружения в этот раз мы чуть сложнее. Вначале мы проверяем что ENV пустая и если это так то выбрасываем ошибку через логгер и выходим из программы c использованием exit кода 1, в обратном случае, если переменная не пустая, записываем её значение в переменную, которую мы будем использовать в дальнейшем.

Парсинг RSS ленты происходит таким же образом как и ранее.

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

И после этого мы начинаем первый цикл: обходим все полученные rss итемы, записываем ссылки, заголовок и дату публикации соответственно в переменные key, title и published. Проверяем есть ли в нашей базе ключ с таким же значением как наш и если есть просто выводим в лог текст, что не будем ничего перезаписывать (это нужно только на этапе отладки, но вообще не обязательно), в обратном случае, если в базе нет записи с таким ключом мы генерируем json строку и записываем её в базу. В качестве ключа я выбрал URL, так как они обеспечивают уникальность, всегда написаны в одном регистре и латиницей.

Следующим шагом я создаю объект типа Hash и прохожусь по всем записям которые есть в базе. С помощью hash[k] = JSON.parse(v) я делаю парсинг строки значения и создаю вложенные хеши. Затем проверяю значение поля sended, если там 0 то генерирую текст и отправляю его в канал, после чего перезаписываю объект cо значением 1, чтобы не отправить эту же запись при следующем запуске.

На самом деле это не совсем рабочая версия кода, я почему-то не закоммитил тот момент когда довел работу с SDBM до ума. Можете попробовать сами понять что здесь не так.

Теперь уже я решил попробовать задеплоить мой код на Heroku и выполнить его там. С использоавнием раннера задач все получилось, да только вот Heroku не сохраняет файлы между запусками задачи (да и вообще). Так что мое решение с SDBM является быстрым и простым, но может быть использовано только как selfhosted.

Прикручиваем РСУБД или умеренно сообразительный бот

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

Здесь проделывается точно такая же работа: иницилизируются необходимые компоннеты, синхронизируется rss лента и локальная база данных и отправляются ссылки на записи которые числятся в базе как «неотправленные». Только в качестве базы используется PostgreSQL и все это обернуто в класс и методы. Метод initialize выполняется при вызове метода new на объекте класса DoamTelegramBot, остальные методы нужно вызывать отдельно. И чтобы выполнить всё правильно, я создал каталог bin в который положил файл с именем doam_bot и содержимым:

В котором я указываю что для выполнения скрипта нужно использовать язык Ruby, подключаю созданный мной класс через файл app.rb, создаю объект telegram и передаю в него урл для парсинга и id канала, вызываю методы sync и send. После чего я заливаю код на Heroku и создаю периодическую задачу, например раз в сутки. И если за сутки появились новые записи то в мой канал придет уведомление, уже без моего содействия в автоматическом режиме.

Заключение

Полностью это маленькое приложение можно посмотреть у меня в gitlab (для этого нужно залогиниться в gitlab.com). Лицензия пока не указана, но она MIT, т.е. можете править и использовать в своих целях. Если будет время я хотел бы прикрутить еще несколько функций, например отправку не только в телеграм но и другие сервисы, а также решить проблему первого запуска, когда база наполняется новыми записями с флагом «не отправлено» но неизвестно точно какие записи уже были отправлены в канал. В любом случае готов рассмотреть ваши Merge Requests.

Поделиться:
Нет комментариев

Добавить комментарий

Ваш e-mail не будет опубликован. Все поля обязательны для заполнения.

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.