Kalorik: Telegram-бот на Rust для анализа питания Часть 1 (Preview)
Введение
В данной статье мы рассмотрим архитектуру и реализацию Telegram-бота Kalorik, написанного на языке программирования Rust. Этот бот предоставляет пользователям возможность анализировать свой рацион питания, получая автоматический расчёт калорий, макроэлементов и индекса массы тела. Особенностью проекта является использование современного стека на основе tokio
, sqlx
, teloxide
, а также продуманная архитектура с учётом масштабируемости.
Задачи, решаемые ботом
Kalorik реализует следующие функции:
Обработка текстовых сообщений с описанием приёма пищи
Распознавание изображений и голосовых сообщений
Подсчёт калорий, БЖУ, ИМТ
Хранение истории и профиля пользователя
Настройка целей и отслеживание прогресса
Архитектура проекта
Проект состоит из следующих ключевых модулей:
main.rs
— точка входа, инициализация окружения и запуск ботаtelegram/
handlers.rs
— обработка входящих сообщений Telegramdb/
queries.rs
— доступ к базе данных (PostgreSQL черезsqlx
)db/
models.rs
— структура таблиц и моделейservices/
nutrition.rs
— логика анализа продуктов и подсчёта нутриентов
Проект построен с использованием OnceLock
для глобального пула соединений с базой данных и асинхронного исполнения через tokio
.
Пример: Регистрация пользователя
pub async fn register_user(chat_id: i64) -> Result<(), sqlx::Error> {
let Some(pool) = DB_POOL.get() else {
return Err(sqlx::Error::PoolTimedOut);
};
sqlx::query!(
r#"
INSERT INTO users (chat_id, created_at)
VALUES ($1, $2)
ON CONFLICT (chat_id) DO NOTHING
"#,
chat_id,
Utc::now()
)
.execute(pool)
.await?;
Ok(())
}
Здесь реализуется вставка пользователя в таблицу, если он отсутствует. Используется UPSERT-подход, обеспечивающий идемпотентность.
Работа с Telegram
match &msg.kind {
MessageKind::Common(msg) => match &msg.media_kind {
MediaKind::Text { text, .. } => {
if text == "/start" {
register_user(msg.chat.id).await?;
bot.send_message(msg.chat.id, "Введите описание приёма пищи").await?;
} else {
let result = analyze_food_description(text).await;
bot.send_message(msg.chat.id, result).await?;
}
}
MediaKind::Photo { photo, .. } => {
// Обработка фото через модель
}
MediaKind::Voice { voice, .. } => {
// Обработка голосовых сообщений
}
_ => {}
}
_ => {}
}
Здесь представлен разбор варианта обработки текста и мультимедиа. Используется teloxide
, который предоставляет удобный API для работы с Telegram Bot API.
Хранение и миграции
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL not set");
let pool = PgPoolOptions::new().connect(&db_url).await?;
sqlx::migrate!("./migrations").run(&pool).await?;
Проект использует sqlx
с автоматическим применением миграций. Миграции хранятся в отдельной папке и обеспечивают прозрачность в изменениях схемы.
Развёртывание
Бот может быть запущен как systemd-сервис или Docker-контейнер. Пример systemd unit-файла:
[Unit]
Description=Kalorik Telegram Bot
After=network.target
[Service]
ExecStart=/usr/local/bin/kalorik
WorkingDirectory=/var/www/kalorik
Restart=always
Environment=DATABASE_URL=postgres://...
[Install]
WantedBy=multi-user.target
Также возможно подключение GitHub Actions для CI/CD и обновления контейнера при пуше в main
ветку.
Заключение
Kalorik демонстрирует, как на Rust можно создать безопасного и надёжного Telegram-бота с использованием производительных и типобезопасных библиотек. Благодаря sqlx
, tokio
и teloxide
, разработка получилась эффективной и лаконичной. Проект легко масштабируется и адаптируется под другие задачи, связанные с пользовательскими данными или обработкой сообщений.
Проект открыт для расширений: можно добавить мини-приложение, авторизацию через Telegram Web App, отчёты по питанию, интеграцию с OpenAI или Hugging Face для анализа описаний еды.
Попробовать бота @kalorikbot
Репозиторий проект, где находится весь код https://github.com/digkill/Kalorik