Вступление
Заикнулся в присутствии нового коллеги о своем блоге, первый его вопрос был “что за блог?”, а второй “а телеграм канал у тебя есть?”. Поймал себя на мысли неужели я стал ретроградом. Когда начался хайп вокруг влогов и 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.
#Ruby #Telegram #Heroku #TechAndDev