Переписал бота с NestJS/TypeScript на Rust

Стек: teloxide + sqlx + axum + tokio-cron-scheduler.
Вся логика перенесена: /start, /help, /settings, выбор частоты,
cron-рассылка цитат, admin API. Совместимость с существующей БД
сохранена (camelCase колонки). Старый TypeScript-код удалён.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikhail Kilin
2026-02-16 01:55:20 +03:00
parent 0269e62f16
commit b885fd39b9
43 changed files with 4085 additions and 10826 deletions

85
CLAUDE.md Normal file
View File

@@ -0,0 +1,85 @@
# 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)
## Команды
```bash
cargo build # Сборка
cargo run # Запуск (dev)
cargo build --release # Релизная сборка
cargo clippy # Линтер
cargo fmt # Форматирование
cargo test # Тесты
cargo sqlx prepare # Подготовка офлайн-запросов для CI
```
## Стиль кода
- `cargo fmt` — стандартный rustfmt
- `cargo clippy` — без предупреждений
- Комментарии в коде — на русском языке
## Особенности
- Имена колонок в БД — camelCase в кавычках (`"telegramId"`, `"lastQuoteSentAt"`) для совместимости с Prisma-схемой
- SQL-запросы используют AS-алиасы для маппинга в snake_case поля Rust-структуры
- Цитаты загружаются один раз при старте в `QuotesStore` (в отличие от TypeScript-версии, где файл читался при каждом запросе)
- Постоянная кнопка "⚙️ Настройки" отображается через `KeyboardMarkup::resize_keyboard()`
- Миграция использует `IF NOT EXISTS` — безопасна для существующей таблицы