Mascot Kalorik

Введение

В данной статье мы рассмотрим архитектуру и реализацию Telegram-бота Kalorik, написанного на языке программирования Rust. Этот бот предоставляет пользователям возможность анализировать свой рацион питания, получая автоматический расчёт калорий, макроэлементов и индекса массы тела. Особенностью проекта является использование современного стека на основе tokio, sqlx, teloxide, а также продуманная архитектура с учётом масштабируемости.

Задачи, решаемые ботом

Kalorik реализует следующие функции:

  • Обработка текстовых сообщений с описанием приёма пищи

  • Распознавание изображений и голосовых сообщений

  • Подсчёт калорий, БЖУ, ИМТ

  • Хранение истории и профиля пользователя

  • Настройка целей и отслеживание прогресса

Архитектура проекта

Проект состоит из следующих ключевых модулей:

  • main.rs — точка входа, инициализация окружения и запуск бота

  • telegram/handlers.rs — обработка входящих сообщений Telegram

  • db/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