Telegram бот на Rust, два, три и Raspberry Pi 2

Опубликовано 2023-03-03 12:11:17

Идея

Написать telegram бота-помощника на Rust, который будет работать на Raspberry Pi. Идея была давно. До этого изучал Rust и решил применить его здесь.

Существуют разные сервисы, вроде перевода раскладки с одного языка на другой, конвертера unix timestamp в форматированную дату, base64 кодирования, jwt, json validator/prettifier, bin2hex и так далее. Почему бы не переместить эти функции в телеграм бота и пользоваться из одного приложения вместо посещения нескольких ресурсов. Реализовать вроде не сложно. При этом давно валяется без дела Raspberry Pi 2, купленная у коллеги за символическую сумму.

Подготовка и выбор инструментов

Вижу три фронта работ: работа с железом, написание программы и интеграция.

Начнём с железяки aka малинка aka Raspberry PI 2 Model B. Вначале нужно было установить на неё ОС. Делается это с помощью записи образа на карту памяти. Этот процесс описан тут. Выбрал Debian. Подключил малинку к монитору, настроил ssh. После того как ssh настроен, можно убрать плату подальше со стола (поближе к роутеру).

Рисунок 1 - Raspberry Pi 2

Рисунок 1 - Raspberry Pi 2

Далее переместился к своему прекрасному, мощному, удивительному и неповторимому компьютеру, где я столько раз проливал чай и кофе, теперь можно подключиться по ssh, и творить дела удалённо. Удалил ненужные пакеты и отключил службы, которые не планирую использовать. Хотел удалить python, но подумал и не стал. Не удаляй python - не совершай ошибку. Python используют другие утилиты и удаление python сделает систему не пригодной для работы. С железякой закончили.

Дальше надо написать простейшее приложение (hello world) на рабочей машине, скомпилировать под платформу arm, выгрузить на Raspberry Pi и запустить. Добавлю подробностей. Рабочий компьютер это x86 архитектура, а Raspberry Pi это arm. Если коротко

Рисунок 2 - Нельзя просто так скомпилировать программу на одной архитектуре, а исполнить на другой

Рисунок 2 - Нельзя просто так скомпилировать программу на одной архитектуре, а исполнить на другой

Умные люди придумали кросс-компиляторы. Это программы, которые собирают программу под целевую (target) платформу. Помучавшись со сборкой libc и openssl, нашёл проект cross - zero setup cross compilation and cross testing, который решил все проблемы.

cross build 
  --release  
  --target arm-unknown-linux-musleabihf

так выглядит команда на сборку под Raspberry Pi. Под капотом у cross работает docker. Указываете target и cross выкачивает нужный образ и собирает проект. Изящно, не правда ли?

Есть ряд настроек, которые позволяют уменьшить размер бинарника.

[profile.release]
strip = true
opt-level = "z"
lto = true
codegen-units = 1

теперь можно быстрее выгружать бинарник по ssh. Деплоить Rust приложение - одно удовольствие. Бинарник достаточно перенести на платформу и запустить. Также добавил .env файл, чтобы задать переменные окружения. В systemd файле конфигурации /etc/systemd/system/bezzabot.service указал .env файл. Из него будут загружены пeременные окружения, необходимые для работы приложения.

[Service]
ExecStart=/srv/bezzabot/bezzabot
ExecReload=bash -c "/srv/bezzabot/bezzabot"
User=radio
EnvironmentFile=/srv/bezzabot/.env
Restart=always
RestartSec=2

Считаем, что вопрос со сборкой и deploy приложения временно решен. Перейдём к регистрации бота, домена, сертификатам. Это была самая ненавистная часть, но, как оказалось, зря я её ненавидел. Выбрал webhooks вместо long-polling как способ коммуникации бота. В таком случае telegram требует https со всеми вытекающими. Я являюсь счастливым обладателем роутера keenetic zyxel и в dashboard обнаружил пункт меню Domain name. Оказалось Keenetic предоставляет доменное имя 4-го уровня. Вместе с сертификатом. Кайф. Зарегистрировал доменное имя и пробросил порты до raspberry. Ещё одна проблема решена малой кровью.

Как регистрировать бота в телеграм рассказывать не буду, а оставлю ссылку.

Бот зарегистрирован, webhook настроен, https работает. Переходим к созданию бота. Сэкономить время можно на реализации клиента для API. Несмотря на то, что для текущей задачи не нужна большая функциональность я бы не хотел ограничивать себя в будущем. Первой ссылкой по теме telegram bot rust выпадает teloxide. Пример с командами это почти все, что мне нужно.

Определяете набор команд. Определяете реакцию на команды. Теперь к деталям. По-умолчанию teloxide работает в long-polling режиме, и поэтому надо (мне надо) перенастроить на webhook-mode. Смотрите пример здесь. А дальше пишем логику. Единственное, что хочу отметить это парсинг аргументов. В моем случае команды содержат обязательные и необязательные параметры. Пользователь может задать лишние параметры, перепутать, ввести ерунду. Я использую поле parse_with макроса BotCommand, в которой указываю собственную функцию для разбора аргументов.

#[derive(BotCommands, Debug, Clone)]
#[command(
  rename_rule = "lowercase", 
  description = "Доступные команды:"
)]
pub enum BotCommand {
  #[command(
    description = "Отображает этот текст")
  ]
  Help,

  #[command(
    parse_with = skb_parser,
    description = "йцукен -> qwerty"
  )]
  Skb(
    String, 
    Layout, 
    FromLanguage, 
    ToLanguage
  ),
  ...
}

Доступны 3 команды, не считая help:

В планах добавить ещё. Кстати, если есть идеи команд - буду рад рассмотреть и реализовать. В завершении отмечу, что создать dev-окружение для бота не составило труда. Я создал второго бота, ещё один домен и пробросил порт до рабочей машины.

В качестве заключения хочется сказать, что создание простого telegram бота на Raspberry Pi это совсем не сложно и даже весело.

Ссылки

Bezzabot - Helper for developers - github

Bezzabot - Telegram

Teloxide - library to build Telegram bots on Rust

How Do I Create a Bot?

Botfather

Asynchronous Programming in Rust

How to Set Up a Raspberry Pi for the First Time

Minimizing Rust Binary Size

Telegram Bot API

Cross - zero setup cross compilation

Rust

Характеристики Raspberry Pi