Appearance
Бот MAX
Обзор
Бот работает внутри процесса бэкенда параллельно с Fastify REST API. Использует библиотеку @maxhub/max-bot-api для взаимодействия с мессенджером MAX через long polling. Бот отвечает на команды пользователей и отправляет уведомления о мероприятиях по расписанию.
Структура
back/src/bot/
├── index.ts # Инициализация бота, регистрация команд, запуск
├── utils.ts # Утилиты: форматирование дат в МСК
├── handlers/
│ ├── index.ts # Регистрация всех обработчиков
│ ├── start.ts # /start и bot_started — приветствие
│ └── events.ts # /events — список мероприятий пользователя
└── scheduler/
└── notifications.ts # Cron-планировщик уведомленийЗапуск
Бот запускается в back/src/index.ts после инициализации базы данных и загрузки модулей API:
typescript
await initializeDatabase();
await loadModules();
const { startBot } = await import('./bot/index.js');
await startBot();
await server.listen({ ... });startBot() выполняет:
- Создаёт экземпляр
Botс токеном изBOT_TOKEN - Устанавливает подсказки команд через
setMyCommands - Регистрирует обработчики команд
- Запускает cron-планировщик уведомлений
- Запускает long polling через
bot.start()
Бот и Fastify работают асинхронно в одном Node.js процессе на одном event loop.
Команды
/start
Отправляется при первом запуске бота или по команде. Приветственное сообщение с описанием приложения и списком доступных команд.
Также обрабатывается событие bot_started — когда пользователь впервые открывает диалог с ботом.
/events
Показывает список предстоящих мероприятий пользователя.
Логика:
- Получает
user_idиз контекста MAX (идентификатор пользователя в мессенджере) - Ищет пользователя в БД по
xamId - Если не найден — сообщает что нужно зарегистрироваться через приложение
- Загружает регистрации пользователя с populate событий и их типов
- Фильтрует только будущие мероприятия
- Форматирует список с эмодзи-статусами: ✅ посещено, ⏳ ожидается
Пример ответа:
Ваши предстоящие мероприятия (2):
1. ⏳ Научная конференция
📅 15.05.2026 в 10:00
📍 Главный корпус, ауд. 301
🏷 Конференция, Наука
2. ⏳ Спортивный день
📅 20.05.2026 в 09:00
📍 Стадион СКФУ
🏷 СпортПланировщик уведомлений
Реализован на node-cron. Два cron-задания:
Утреннее уведомление — 0 4 * * * (7:00 МСК)
Каждый день в 7:00 по московскому времени (4:00 UTC):
- Находит все мероприятия, запланированные на сегодня
- Для каждого мероприятия получает зарегистрированных пользователей
- Фильтрует тех, у кого
notifications = trueиxamIdне null - Отправляет каждому сообщение в личный чат с ботом
Пример:
🌅 Сегодня состоится мероприятие!
Научная конференция
⏰ Начало в 10:00
📍 Главный корпус, ауд. 301Напоминание за 30 минут — * * * * * (каждую минуту)
Каждую минуту проверяет, есть ли мероприятия начинающиеся через 29-31 минуту (окно в 2 минуты для надёжности):
- Запрашивает мероприятия в этом временном окне
- Отправляет напоминание зарегистрированным участникам
Пример:
⏰ Мероприятие начнётся через 30 минут!
Научная конференция
📍 Главный корпус, ауд. 301Уведомление об обновлении мероприятия
Не привязано к cron — вызывается синхронно при обновлении мероприятия через API (POST /webapp/v1/events/update). Отправляет всем зарегистрированным участникам актуальные данные мероприятия.
Пример:
📝 Мероприятие обновлено!
Научная конференция
📅 16.05.2026 в 11:00
📍 Корпус 2, ауд. 205
👥 Вместимость: 150
🏷 Конференция, Наука
Описание мероприятия...Часовой пояс
Все даты в БД хранятся в UTC (Docker-контейнер по умолчанию в UTC). Для отображения пользователям используется конвертация в МСК (UTC+3) через утилиты в bot/utils.ts:
toMsk(date)— добавляет 3 часа к UTCformatDateRu(date)— форматДД.ММ.ГГГГ в ЧЧ:ММв МСКformatTimeRu(date)— форматЧЧ:ММв МСК
Обработка ошибок
bot.catch()— перехватывает все ошибки бота и логирует их без завершения процесса- Каждая отправка уведомления обёрнута в try/catch — если не удалось отправить одному пользователю, остальные всё равно получат
- Cron-задания оборачивают вызовы в
.catch()для логирования ошибок планировщика
Переменные окружения
| Переменная | Описание |
|---|---|
BOT_TOKEN | Токен бота MAX (из настроек Master Bot) |
WEBAPP_URL | URL фронтенда (используется в конфиге, не в боте напрямую) |