Add Discord event announcement functionality to backend
This commit introduces a new Discord event announcer to the backend, allowing for event announcements via Discord. It includes the addition of new environment variables for Discord configuration in `.env.example` and `compose.yaml`. The `main.go` file has been updated to initialize the announcer, and a new `discord` package has been created, containing the announcer logic and tests. Additionally, the service layer has been modified to support bracket draft management, enhancing the overall event workflow. Integration tests have been updated to ensure proper functionality of the new features.
This commit is contained in:
@@ -2,10 +2,11 @@
|
||||
|
||||
## Текущее состояние
|
||||
|
||||
Собран полный вертикальный production-пайплайн скрима: закрытие регистрации → versioned балансировка → ручная правка и подтверждение составов 1/2/2 → капитаны → атомарный старт Bo3 или турнира → жеребьёвка → баны карт → баны героев → результаты и завершение. Состояние события, roster draft и серии сохраняется в PostgreSQL с optimistic locking, аудитом и глобальной SSE-синхронизацией без ручного обновления страницы.
|
||||
Собран полный вертикальный production-пайплайн скрима: закрытие регистрации → versioned балансировка → ручная правка и подтверждение составов 1/2/2 → капитаны → drag-and-drop редактор произвольного графа матчей → атомарный старт готовых Bo3 → жеребьёвка → баны карт → баны героев → результаты и динамическое разрешение Winner/Loser-переходов. Состояние события, roster draft, bracket draft и серии сохраняется в PostgreSQL с optimistic locking, аудитом и глобальной SSE-синхронизацией.
|
||||
|
||||
Admin UI содержит пошаговый workflow и roster editor с same-role swap, резервом и запуском. Live, spectator и bracket используют реальные API-команды и realtime refresh; участник турнира автоматически открывает собственный матч, остальные попадают в spectator mode, а актуальная сетка и соседние параллельные матчи доступны прямо над серией. Demo fixtures остались только локальным showcase.
|
||||
Добавлена роль Moderator: она имеет все операционные права Admin, но управление списком модераторов доступно только Admin. До состояния Live workflow можно откатить на один этап назад.
|
||||
Добавлен опциональный Discord-анонс нового микса: после успешного создания Event backend через Bot REST API публикует локализованный embed с `@everyone` и кнопкой регистрации. Ошибка Discord логируется, но не отменяет создание события; без bot token и channel ID интеграция отключена.
|
||||
|
||||
## Подтверждённые требования
|
||||
|
||||
@@ -31,6 +32,7 @@ Admin UI содержит пошаговый workflow и roster editor с same-r
|
||||
- PostgreSQL в том же Compose-стеке с persistent volume;
|
||||
- мобильный и десктопный интерфейс.
|
||||
- карточки отменённых событий отображаются красными, завершённых — зелёными, активных — розовыми с меткой LIVE.
|
||||
- новый микс может автоматически анонсироваться в настроенном Discord-канале со ссылкой на регистрацию.
|
||||
|
||||
## Исходный регламент
|
||||
|
||||
@@ -61,7 +63,7 @@ Admin UI содержит пошаговый workflow и roster editor с same-r
|
||||
- балансировщик автоматически назначает роли и собирает команды 1/2/2;
|
||||
- предпочтения ролей, союзников и avoid-списки учитываются как мягкие штрафы после требований баланса;
|
||||
- участники сверх полного состава остаются в резерве;
|
||||
- первая турнирная сетка — single elimination;
|
||||
- сетка редактируется как последовательный граф Team / Winner / Loser; последний матч определяет общего победителя;
|
||||
- игрок отмечается на микс через календарь;
|
||||
- Admin может изменить RSVP за игрока, а действие сохраняет автора;
|
||||
- капитан назначается отдельно для каждой команды и должен входить в её состав;
|
||||
@@ -77,6 +79,6 @@ Admin UI содержит пошаговый workflow и roster editor с same-r
|
||||
|
||||
## Ближайший следующий шаг
|
||||
|
||||
Проверить миграцию `006_event_workflow.sql` и полный сценарий на staging с реальной PostgreSQL (`TEST_DATABASE_URL`), затем провести первый пробный микс через production UI.
|
||||
Проверить миграцию `006_event_workflow.sql` и полный сценарий на staging с реальной PostgreSQL (`TEST_DATABASE_URL`), проверить тестовый Discord-анонс с production bot token/channel ID, затем провести первый пробный микс через production UI.
|
||||
|
||||
Публичная вкладка FAQ описывает регистрацию, балансировку, полномочия капитанов, выбор карт, баны героев и подсчёт Bo3.
|
||||
|
||||
@@ -88,7 +88,7 @@ Application-слой управляет единым versioned workflow собы
|
||||
|
||||
Ручное редактирование работает с серверным roster draft. Backend разрешает обмен только между одинаковыми ролевыми слотами и замену слота игроком из резерва, после чего пересчитывает средние рейтинги и метрики. Подтверждение требует полного состава 1/2/2, уникальных игроков и капитана внутри каждой команды.
|
||||
|
||||
`start-scrim` атомарно блокирует составы, создаёт одну или несколько Bo3-серий и первый draft state. Для двух команд создаётся одиночная серия; для четырёх и более подтверждённых команд создаётся single-elimination турнир. Tournament read-model гидратируется свежими версиями всех сохранённых серий, поэтому параллельные матчи одного раунда независимо обновляют счёт и фазу в общей сетке. Умный event-level live-вход направляет участника в его матч, капитана/staff — к доступным командам, а остальных — в spectator mode; над серией остаются компактная сетка и навигация между активными матчами раунда.
|
||||
После подтверждения составов workflow переходит в versioned `BracketDraft`. Staff собирает DAG матчей из источников Team / Winner / Loser; ссылки разрешены только на предыдущие колонки, а в последней колонке должен быть один финальный матч. `start-scrim` материализует все готовые Bo3, а завершение серии разрешает зависимости и атомарно создаёт следующие матчи. Поэтому одна колонка может содержать параллельные серии, а три команды могут играть последовательную ротацию через проигравшего. Tournament read-model гидратируется свежими версиями Series. Умный event-level live-вход направляет участника в его матч, капитана/staff — к доступным командам, а остальных — в spectator mode.
|
||||
|
||||
### Драфт как конечный автомат
|
||||
|
||||
@@ -112,6 +112,12 @@ Application-слой управляет единым versioned workflow собы
|
||||
|
||||
Один глобальный authenticated SSE-канал подписывается на wildcard topic и адресно инвалидирует TanStack Query-кэш для событий, RSVP, составов, серий, турниров и игроков. Поэтому новые регистрации, участники, жеребьёвка, баны, результаты и сетка появляются без перезагрузки. WebSocket не требуется: команды остаются обычными HTTP mutations, а серверные изменения передаются клиентам однонаправленно через SSE. Nginx отключает buffering и держит соединение открытым.
|
||||
|
||||
### Discord-уведомления
|
||||
|
||||
После успешного создания события application-слой вызывает consumer-owned `EventAnnouncer` port. Discord REST adapter публикует в настроенный текстовый канал локализованный RU/EN embed с `@everyone`, временем начала и ссылкой на `/events/{eventId}`. Внешний вызов имеет короткий timeout и выполняется только после сохранения события: ошибка Discord логируется, но не откатывает созданный микс и не превращает успешную команду в HTTP 500.
|
||||
|
||||
Интеграция опциональна и отключена, если bot token и channel ID не заданы. Bot token доступен только API-контейнеру; OAuth client secret по-прежнему используется исключительно для входа. Для анонсов не нужны Gateway intents или постоянно открытое Gateway-соединение.
|
||||
|
||||
### Капитаны
|
||||
|
||||
Капитан — назначение внутри конкретной команды, а не глобальная роль аккаунта. Admin может назначить или заменить капитана только участником этой команды. Только текущий капитан выполняет командные действия драфта; Admin имеет аварийное право выполнить действие с обязательной записью в аудит.
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
|
||||
Отметка игрока в календаре со статусом Going, Maybe или NotGoing. Игрок меняет собственный статус, а Admin может изменить его за игрока. Регистрация хранит автора и время последнего изменения, чтобы интерфейс явно показывал административное действие. В момент закрытия регистрации Admin подтверждает список участников; только Going-игроки по умолчанию попадают в балансировку.
|
||||
|
||||
### Discord-анонс события
|
||||
|
||||
Созданный микс проецируется во внешний Discord-канал как информационный анонс со ссылкой на страницу Event. Анонс не является источником состояния регистрации: RSVP, доступность события и все переходы workflow по-прежнему определяет backend Mixmaker. Недоступность Discord не отменяет валидное создание Event.
|
||||
|
||||
### Жизненный цикл события
|
||||
|
||||
Событие проходит серверно контролируемые состояния:
|
||||
@@ -29,9 +33,10 @@
|
||||
3. `Balancing` — создаются варианты команд и резерв;
|
||||
4. `RostersDraft` — Admin выбирает вариант, меняет игроков одной роли местами и заменяет их резервными;
|
||||
5. `RostersConfirmed` — составы 1/2/2 и капитаны проверены и заблокированы;
|
||||
6. `Live` — создана серия, разрешены только действия текущего шага жеребьёвки, банов или результата;
|
||||
7. `Completed` — серия или турнир завершены;
|
||||
8. `Cancelled` — Admin отменил событие; оно остаётся в календаре, но игровые действия заблокированы.
|
||||
6. `BracketDraft` — staff задаёт порядок Bo3 и источники Team / Winner / Loser для слотов;
|
||||
7. `Live` — созданы готовые серии, разрешены только действия текущего шага жеребьёвки, банов или результата;
|
||||
8. `Completed` — выполнены все запланированные матчи;
|
||||
9. `Cancelled` — Admin отменил событие; оно остаётся в календаре, но игровые действия заблокированы.
|
||||
|
||||
Переходы выполняются отдельными командами API с проверкой ожидаемой версии. До запуска скрима staff-пользователь может вернуться на один этап назад: черновик удаляется при возврате из `RostersDraft`, а состав снова разблокируется при возврате из `RostersConfirmed`. Возврат из `Live` и `Completed` запрещён. Нельзя начать скрим без минимум двух полных команд, валидного состава 1/2/2 и капитана в каждой команде. После `RostersConfirmed` обычная перестановка запрещена. Аварийная замена во время `Live` доступна staff-пользователю, должна сохранять ролевой состав и записывается в аудит. Полное удаление события является отдельной staff-командой и каскадно удаляет его RSVP, составы, roster draft, серии, турнир и event-scoped аудит.
|
||||
|
||||
@@ -73,7 +78,7 @@
|
||||
|
||||
### Tournament
|
||||
|
||||
Формат и состояние турнирной сетки, участники, раунды, пары и продвижение победителей.
|
||||
Versioned граф матчей, в котором каждый слот ссылается на конкретную Team либо Winner/Loser матча из предыдущей колонки. Готовые зависимости материализуются в отдельные Bo3-серии автоматически; несколько готовых матчей могут идти параллельно. Последняя колонка содержит один матч, победитель которого становится общим победителем. Для трёх команд стартовый шаблон проводит `A–B`, затем `Loser(M1)–C`, затем финал победителей первых двух матчей.
|
||||
|
||||
## Правила текущего регламента
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
- [x] дать Admin обменивать игроков одинаковой роли и заменять их резервными;
|
||||
- [x] пересчитывать показатели после каждой ручной правки;
|
||||
- [x] подтвердить составы 1/2/2 и капитанов;
|
||||
- [x] запускать одиночную Bo3-серию для двух команд или single-elimination турнир для нескольких;
|
||||
- [x] редактировать произвольный граф Bo3 через Team / Winner / Loser и запускать все готовые пары;
|
||||
- [x] связать жеребьёвку, map draft, hero draft и результаты карт единым versioned FSM;
|
||||
- [x] блокировать обычное изменение составов после старта;
|
||||
- [x] разрешить аудируемую аварийную замену Admin;
|
||||
@@ -69,8 +69,8 @@
|
||||
|
||||
## Этап 4 — турнир
|
||||
|
||||
- single-elimination сетка;
|
||||
- автоматическое создание следующих пар;
|
||||
- versioned drag-and-drop редактор произвольной сетки;
|
||||
- автоматическое создание следующих пар по Winner/Loser-зависимостям;
|
||||
- экран трансляции/зрителя;
|
||||
- история результатов игрового вечера.
|
||||
|
||||
@@ -79,7 +79,9 @@
|
||||
- пресеты регламентов;
|
||||
- статистика игроков и команд;
|
||||
- повторная жеребьёвка с защитой от одинаковых составов;
|
||||
- Discord-уведомления;
|
||||
- [x] Discord-анонс нового микса с `@everyone` и ссылкой на регистрацию;
|
||||
- обновление анонса, напоминания, публикация составов и результатов;
|
||||
- синхронизация временных Discord-ролей и командных каналов;
|
||||
- импорт данных и резервное копирование.
|
||||
|
||||
## Идеи после MVP
|
||||
|
||||
Reference in New Issue
Block a user