-
released this
2026-06-06 08:18:37 +03:00 | 27 commits to main since this release🎯 Discord-визард создания игр
Платформо-нейтральный wizard (тот же движок, что в Telegram-боте с 3.8.0) теперь работает и в Discord-боте.
/newsession-wizardоткрывает пошаговый сценарий с кнопками, выпадающими меню и модальными окнами, через которые мастер (GM) собирает одиночную игру или пул слотов. Single-source-of-truth стейт-машинаGameCreationWizardживёт вGmRelay.Sharedи используется обеими платформами.Что нового для пользователей
/newsession-wizardоткрывает Discord-wizard: тип → одиночная или пул- Внутри: native Discord components — buttons, StringSelectMenu, modals
- Тот же flow, что в Telegram: 11 шагов для single, 7 для pool (общие system/duration на уровне пула)
- Owner / co-GM / admin проверка прав через
DiscordPermissionChecker+group_managersв БД - Предпросмотр перед созданием
- Кнопка «Другое… ✏️» на шагах System / Duration / PoolSystemDuration открывает модалку для свободного ввода
- PickClub-шаг использует реальный SQL к
club_membershipsс фильтром по роли Owner/CoGm (раньше возвращал пустой список) - 3-retry finalize с retry/cancel кнопками при ошибке БД
Архитектура
Shared (платформо-нейтральное ядро)
GameCreationWizard— state machine без I/O, читает/пишетWizardPayloadиз JSONBIWizardMessenger— контракт мессенджера (edit/send/answer/getOwnerClubs). Обе платформы реализуютWizardInteraction— record взаимодействия (OwnerId,Text,CallbackPayload,PhotoFileId,PhotoUrl,InteractionId)WizardAction/WizardKeyboard— модель кнопок и рядовWizardPayloadJsonContext— AOT JSON source-genWizardStepViewBuilder— pure-функцииpayload → (text, actions)(вместо Telegram-специфичногоWizardStepрендерера)WizardDraft— добавлено полеPlatform; миграция V032 добавилаplatform TEXT NOT NULL DEFAULT 'Telegram'- Telegram-мессенджер (
ITelegramWizardMessenger) адаптирован под новый контракт, поведение не поменялось
Discord-адаптер
DiscordWizardCommand—/newsession-wizardslash-команда с owner/co-GM проверкойDiscordWizardStep— рендер 15 шагов в NetCord embed + buttons/StringSelectMenu/modalsDiscordWizardMessenger— реализацияIWizardMessengerчерез NetCord REST (edit с fallback на re-send при 401/403/404)DiscordWizardSubmitter— 3-retry finalize: sharedCreateSessionHandler→ edit embed «✅ Создано» или retry/cancel кнопкиDiscordWizardContextStore— in-memory кэш(guildId, channelId, messageId, threadId?)поdraft.Idдля восстановления после 15-мин interaction tokenDiscordWizardInteractionModule— 3 NetCordComponentInteractionModule<TContext>(button / StringMenu / modal) + sharedWizardInteractionDispatcherDiscordPermissionLookup— DB-хелперgroup_managersдля slash-команды
Custom-id wire format (канонический, единый для renderer'а и dispatcher'а):
wizard:btn:choice:<step>:<value>— choice кнопкиwizard:btn:<action>:1— control (back/cancel/skip/create/resume)wizard:btn:modal:<step>— открыть модалкуwizard:select:<step>— StringSelectMenuwizard:modal:<step>— модальный submit
Тесты
- 66 wizard-юнит-тестов — state-machine переходы, pool-slot lifecycle, валидация, рендер, cancel/back
- 12 source-level structural smoke-тестов на Discord interaction module (NetCord registration, dispatcher shape, customId format)
- 2 новых parser-roundtrip теста (
Renderer_And_Dispatcher_Agree_On_Wire_Format,ControlButtons_Are_Parsed_As_Control_Not_Choice) — реальные behavioural тесты, не string-grep - 8 handler/router тестов — submit-валидация, missing fields, cleanup-tick, router delegation, stale-command reset
- Полный прогон: 583 passed, 2 skipped, 0 failed
dotnet format --verify-no-changescleandotnet list package --vulnerable --include-transitivecleandotnet build— 0 warnings- AOT-safe: zero
System.Reflection, zerodynamic, zeroActivator.CreateInstanceРІ Discord Рё Shared РєРѕРґРµ SecretRedactor.RedactConnectionStringРЅР° startup log
Безопасность
- Все SQL-запросы параметризованы (
@GuildId,@Platform,@ExternalUserId,@OwnerId) - DM-инвокации
/newsession-wizardотклоняются (null-check наGuildId) Context.User as GuildInteractionUser— null-check перед использованием- Internal
ex.Messageот Postgres НЕ утекает в Discord channel (C-3 фикс) — пользователь видит generic error, полный exception остался в server-side log
Рзвестные ограничения
- Free-text модалы (
SystemFreeText,DurationFreeText,PoolSystemDurationFreeText) маппятся на канонические шаги (System,Duration,PoolSystemDuration) хаком в диспетчере. Работает, но чистое решение — добавить отдельные free-text шаги вWizardStepNames resume:continue— re-render текущего шага, не настоящий resume (однако wizard всё равно сохраняет state в БД, так что переживает перезапуск бота)- Один активный draft per owner per платформа (by design state machine'а)
DiscordProjectStructureTests.Version_ShouldBeSynchronizedForDiscordFeatureRelease— broken test из 3.7.1, синхронизирован в этом релизе под 3.9.0
Что вышло из скоупа
- Шаг с загрузкой фото-обложки в Discord wizard (image upload) — пока URL-only (как в Telegram-варианте)
- Slack / Teams адаптеры — общий контракт
IWizardMessengerготов, но платформенные реализации не в скоупе - Backlog: cache для
LoadManagerUserIdsAsync(I-1), exhaustiveness-проверка для switch (I-4), глобальная константаWizardEmbedColor(Nit)
Closes #112.
Downloads