Telegram бот на Rust: почему это стоит попробовать?
Rust — это не просто очередной язык системного программирования. Он предлагает невероятную скорость, безопасность памяти без сборщика мусора и потрясающую конкурентность. В мире, где каждый миллисекунд задержки может стоить пользователя, Telegram бот на Rust становится козырем в рукаве разработчика. Вы получаете производительность, сравнимую с C++, но с гораздо более приятным синтаксисом и строгой системой типов, которая отлавливает ошибки на этапе компиляции.
В этом гайде мы пройдем путь от установки инструментов до публикации работающего бота. Вы узнаете, как обрабатывать команды, работать с состояниями и даже развернуть бота на сервере. Если вы уже знакомы с основами Rust, но никогда не писали ботов — добро пожаловать. Если вы новичок в Rust — не пугайтесь, мы будем двигаться шаг за шагом.
Что понадобится для старта
Прежде чем погрузиться в код, убедитесь, что у вас есть:
- Rust и Cargo — установите через
rustup(официальный сайт rustup.rs). Проверьте версию:rustc --version - Токен Telegram бота — получите его у @BotFather в Telegram. Просто отправьте
/newbotи следуйте инструкциям - Базовое понимание async/await — в Rust это делается через
tokioилиasync-std
Мы будем использовать библиотеку teloxide — это, пожалуй, самый зрелый и документированный фреймворк для создания Telegram ботов на Rust. Она построена на tokio и предоставляет удобный API для всех видов взаимодействий.
Шаг 1: Создание проекта и установка зависимостей
Откройте терминал и выполните:
cargo new my_telegram_bot
cd my_telegram_bot
Теперь отредактируйте файл Cargo.toml, добавив зависимости:
[package]
name = "my_telegram_bot"
version = "0.1.0"
edition = "2021"
[dependencies]
teloxide = { version = "0.12", features = ["macros"] }
tokio = { version = "1", features = ["full"] }
log = "0.4"
env_logger = "0.10"
Объяснение: teloxide — наш главный инструмент, tokio — асинхронный рантайм, log и env_logger — для удобного логирования.
Шаг 2: Первый запуск — бот-эхо
Напишем простейшего бота, который отвечает тем же текстом, что ему отправили. Замените содержимое src/main.rs на:
use teloxide::prelude::*;
#[tokio::main]
async fn main() {
env_logger::init();
log::info!("Запускаем бота-эхо...");
let bot = Bot::from_env();
teloxide::repl(bot, |bot: Bot, msg: Message| async move {
if let Some(text) = msg.text() {
bot.send_message(msg.chat.id, text).await?;
}
Ok(())
})
.await;
}
Обратите внимание: Bot::from_env() ищет переменную окружения TELOXIDE_TOKEN. Создайте файл .env в корне проекта или экспортируйте токен вручную:
export TELOXIDE_TOKEN="ваш_токен_бота"
Запустите бота: cargo run. Отправьте ему любое сообщение в Telegram — он должен ответить. Поздравляю, ваш первый Telegram бот на Rust работает!
Шаг 3: Обработка команд и фильтрация сообщений
Простое эхо — это скучно. Давайте добавим команды /start и /help. Используем макросы teloxide для создания диалоговой машины (dialogue).
use teloxide::prelude::*;
use teloxide::utils::command::BotCommand;
#[derive(BotCommand)]
#[command(rename = "lowercase", description = "Доступные команды:")]
enum Command {
#[command(description = "Начать общение")]
Start,
#[command(description = "Показать справку")]
Help,
}
async fn handle_command(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> {
match cmd {
Command::Start => {
bot.send_message(msg.chat.id, "Привет! Я бот на Rust. Чем могу помочь?").await?;
}
Command::Help => {
bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?;
}
}
Ok(())
}
#[tokio::main]
async fn main() {
env_logger::init();
log::info!("Запускаем бота с командами...");
let bot = Bot::from_env();
Command::repl(bot, handle_command).await;
}
Теперь бот реагирует только на команды, начинающиеся с /. Это базовая, но уже полезная функциональность для любого Telegram бота на Rust.
Шаг 4: Работа с состояниями (диалоги)
Настоящие боты часто требуют поддержки многошаговых диалогов. Например, бот-опросник: сначала спрашивает имя, потом возраст. В teloxide это реализуется через перечисления с состояниями.
Добавим в Cargo.toml зависимость dptree (она уже включена в teloxide, но для явности укажем):
dptree = "0.2"
Теперь напишем бота, который запоминает имя пользователя:
use teloxide::prelude::*;
use teloxide::types::Message;
#[derive(Clone, Default)]
enum State {
#[default]
Start,
WaitingForName,
Done,
}
async fn handle_message(bot: Bot, msg: Message, state: State) -> TransitionOut<State> {
match state {
State::Start => {
bot.send_message(msg.chat.id, "Как тебя зовут?").await?;
next(State::WaitingForName)
}
State::WaitingForName => {
let name = msg.text().unwrap_or("безымянный");
bot.send_message(msg.chat.id, format!("Приятно познакомиться, {}!", name)).await?;
next(State::Done)
}
State::Done => {
bot.send_message(msg.chat.id, "Мы уже познакомились. Напиши /start чтобы начать заново.").await?;
end()
}
}
}
#[tokio::main]
async fn main() {
env_logger::init();
let bot = Bot::from_env();
Dispatcher::builder(bot, dptree::entry().endpoint(handle_message))
.dependencies(dptree::deps![State::default()])
.build()
.dispatch()
.await;
}
Этот код использует TransitionOut для переключения между состояниями. Обратите внимание: состояние хранится в памяти, поэтому при перезапуске бота оно сбрасывается. Для продакшена нужно подключа