Стек: teloxide + sqlx + axum + tokio-cron-scheduler. Вся логика перенесена: /start, /help, /settings, выбор частоты, cron-рассылка цитат, admin API. Совместимость с существующей БД сохранена (camelCase колонки). Старый TypeScript-код удалён. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.6 KiB
4.6 KiB
Mental Health Bot
Telegram-бот на Rust, который отправляет пользователям мотивирующие цитаты с настраиваемой периодичностью.
Стек технологий
- Язык: Rust (edition 2021)
- Async runtime: tokio
- Telegram: teloxide 0.13
- База данных: PostgreSQL через sqlx 0.8
- HTTP (admin API): axum 0.8
- Планировщик: tokio-cron-scheduler 0.13
Структура проекта
mental_health/
├── Cargo.toml
├── .env # BOT_TOKEN, DATABASE_URL, ADMIN_PASSWORD, PORT
├── migrations/
│ └── 20260216000000_create_users.sql
├── quotes.json # ~200 мотивирующих цитат на русском языке
└── src/
├── main.rs # Точка входа: pool + bot + axum + scheduler
├── config.rs # Загрузка env vars в struct Config
├── app_state.rs # Shared state: PgPool, Bot, Config, QuotesStore
├── db.rs # SQL-запросы (User struct + CRUD функции)
├── quotes.rs # Загрузка quotes.json, случайная цитата
├── bot.rs # Teloxide: /start, /help, /settings, callback frequency_N
├── scheduler.rs # Cron каждый час — рассылка цитат
└── admin.rs # Axum: POST /admin/send-message
Архитектура runtime
tokio::main
├── tokio::spawn(axum) → HTTP-сервер на 0.0.0.0:PORT
├── tokio::spawn(scheduler) → cron "0 0 * * * *"
└── dispatcher.dispatch() → teloxide long-polling (блокирует main)
Все три системы разделяют один tokio runtime, один PgPool и один Bot (через Arc<AppState>).
Переменные окружения
| Переменная | Описание |
|---|---|
BOT_TOKEN |
Токен Telegram-бота |
DATABASE_URL |
Строка подключения к PostgreSQL |
ADMIN_PASSWORD |
Пароль для админского API |
PORT |
Порт HTTP-сервера (по умолчанию 3000) |
Ключевая логика
- Регистрация: при
/startбот создаёт/обновляет пользователя в БД черезINSERT ON CONFLICT - Настройки: пользователь выбирает частоту получения цитат (1, 3, 5, 7, 9, 12 часов) через inline-кнопки
- Рассылка: cron каждый час — для каждого пользователя проверяется, прошло ли достаточно времени с последней отправки (с допуском 3 мин), и отправляется случайная цитата
- Админка: REST endpoint
POST /admin/send-messageс JSON body{ userId, password, message }(camelCase)
Команды
cargo build # Сборка
cargo run # Запуск (dev)
cargo build --release # Релизная сборка
cargo clippy # Линтер
cargo fmt # Форматирование
cargo test # Тесты
cargo sqlx prepare # Подготовка офлайн-запросов для CI
Стиль кода
cargo fmt— стандартный rustfmtcargo clippy— без предупреждений- Комментарии в коде — на русском языке
Особенности
- Имена колонок в БД — camelCase в кавычках (
"telegramId","lastQuoteSentAt") для совместимости с Prisma-схемой - SQL-запросы используют AS-алиасы для маппинга в snake_case поля Rust-структуры
- Цитаты загружаются один раз при старте в
QuotesStore(в отличие от TypeScript-версии, где файл читался при каждом запросе) - Постоянная кнопка "⚙️ Настройки" отображается через
KeyboardMarkup::resize_keyboard() - Миграция использует
IF NOT EXISTS— безопасна для существующей таблицы