Table of Contents
Архитектура
GM-Relay состоит из нескольких .NET-проектов и общей PostgreSQL БД.
Проекты
src/GmRelay.AppHost— Aspire AppHost. Поднимает PostgreSQL с PgAdmin и запускает bot/web проекты.src/GmRelay.Bot— Worker Service с Telegram long polling, миграциями, планировщиком и вертикальными срезами бота.src/GmRelay.Web— Blazor Server UI для ГМа.src/GmRelay.Shared— общие доменные значения и rendering-код Telegram-сообщений.src/GmRelay.ServiceDefaults— общие Aspire defaults: OpenTelemetry, service discovery, resilience, health checks.
Архитектурное решение
В проекте принят подход Vertical Slice Architecture. Логика бота лежит в src/GmRelay.Bot/Features/*, где каждый сценарий содержит свой handler и локальные DTO.
Причины такого выбора зафиксированы в docs/adr/0001-use-vertical-slice-native-aot-and-aspire.md:
- сценариев немного, отдельный Clean Architecture слой был бы лишним;
- зависимости Npgsql и Telegram.Bot стабильны;
- Native AOT требует явной регистрации зависимостей и аккуратного SQL-маппинга;
- BackgroundService + PeriodicTimer проще и надёжнее Quartz.NET для периодических проверок расписания и дедлайнов голосования.
Поток Telegram updates
TelegramBotService получает updates через long polling и передаёт их в UpdateRouter.
UpdateRouter явно маршрутизирует:
/start,/help,/newsession,/listsessions,/exportcalendar;- callback
join_session:<sessionId>; - callback
leave_session:<sessionId>; - callback
cancel_session:<sessionId>; - callback
delete_session:<sessionId>; - callback
reschedule_session:<sessionId>; - callback
reschedule_vote:<optionId>; - callback
rsvp:<confirm|decline>:<sessionId>; - обычные текстовые сообщения ГМа как ввод 2-3 вариантов времени и дедлайна для активного переноса.
Маршрутизация не использует reflection или assembly scanning.
Планировщик
SessionSchedulerService — stateless background service. Он запускается сразу при старте, затем раз в минуту:
- Ищет сессии
Planned, у которыхscheduled_at - 24h <= now(). - Вызывает
SendConfirmationHandlerи переводит сессию вConfirmationSent. - Ищет сессии
ConfirmedилиConfirmationSent, у которыхscheduled_at - 1h <= now()иone_hour_reminder_processed_at IS NULL. - Вызывает
SendOneHourReminderHandler, который учитываетsessions.notification_mode. - Ищет сессии
Confirmed, у которыхscheduled_at - 5min <= now()иlink_message_id IS NULL. - Вызывает
SendJoinLinkHandler.
Состояние хранится в PostgreSQL, поэтому после рестарта сервис продолжает работу без in-memory очередей.
RescheduleVotingDeadlineService — отдельный stateless background service. Он раз в минуту ищет reschedule_proposals в статусе Voting, у которых voting_deadline_at <= now(), выбирает вариант с наибольшим числом голосов, применяет перенос или отклоняет голосование при ничьей/нуле голосов, обновляет Telegram-сообщения и сбрасывает RSVP при успешном переносе.
Web UI
Blazor Server-приложение использует cookie auth и Telegram Login Widget.
Основные сервисы:
TelegramAuthServiceвалидирует HMAC-SHA256 подпись Telegram Login Widget иauth_date.SessionServiceчитает и обновляет группы/сессии через Dapper, включая bulk-операции поbatch_id.SessionServiceсохраняет режим уведомлений batch и дублирует Web-уведомления в ЛС, если включёнGroupAndDirect.SessionServiceчитаетgroup_managers, проверяет ролиOwner/CoGmи сохраняет назначения co-GM.BatchSchedulePlannerзадаёт детерминированные правила fixed-interval переноса и clone-смещения batch.AuthorizedSessionServiceпроверяет, что текущий Telegram ID является owner или co-GM группы; owner-only операции делегирования проходят через отдельную проверку роли.
Основные страницы:
/login— вход через Telegram./— список групп, где пользователь owner или co-GM./group/{GroupId}— управляющие группы, будущие сессии и batch bulk-операции./session/edit/{SessionId}— редактирование названия, времени и ссылки.
Общий домен
GmRelay.Shared содержит:
SessionStatus:Planned,ConfirmationSent,Confirmed,Cancelled.RsvpStatus:Pending,Confirmed,Declined.SessionNotificationMode:GroupAndDirect,GroupOnly.GroupManagerRole:Owner,CoGm.MoscowTime: парсинг и форматирование МСК (UTC+3).SessionBatchRenderer: единый renderer для Telegram-сообщения пачки сессий.
Диаграммы
C4-диаграммы хранятся в docs/c4-system-context.md. Там описаны system context, container view и component view для GmRelay.Bot.