The 3.9.1 hotfix only repaired WizardDraftRepository, the most common
Dapper call in the wizard. The same AOT-unsafe CommandDefinition pattern
remained in 4 other places that the user hit immediately after the
deploy: the 'Choose visibility' wizard step triggers GetOwnerClubsAsync
when the user picks 'Публичная в витрине клуба' or 'Только для членов
клуба'. The wizard swallowed PlatformNotSupportedException, the
callback ack replied with '⚠️ Ошибка', and the next step never rendered.
Privacy 'didn't stick' from the user's perspective.
Two changes to fix the Discord side as well:
1. Switched GetOwnerClubsAsync / LoadClubsAsync / LoadManagerUserIdsAsync
to the direct (sql, params) overload across TelegramWizardMessenger,
DiscordWizardMessenger, DiscordWizardInteractionModule, and
DiscordPermissionLookup — same pattern as the 3.9.1 fix.
2. Added Dapper.AOT module attribute ([module: Dapper.DapperAot]) and
InterceptorsPreviewNamespaces to the DiscordBot project. The
DiscordBot assembly was previously skipped by the AOT source
generator, so even the direct-overload fix wouldn't have produced
interceptors for the Discord-specific Dapper call sites. With this
addition, the generator emits 3 DiscordBot-specific interceptors
(DiscordWizardMessenger, DiscordWizardInteractionModule,
DiscordPermissionLookup) and the AssemblyLoad ships with the right
GmRelay.DiscordBot.generated.cs.
Also expanded the AOT shape regression tests to cover all 4
CommandDefinition sites + added a 'containingClass' parameter to
ExtractMethodBody to disambiguate the duplicated LoadClubsAsync names
in DiscordWizardInteractionModule.
Bumps: 3.9.1 -> 3.9.2.
12 KiB
🐞 Patch 3.9.2 — Hotfix: club-picker молча падал на шаге «Видимость» (3.9.1 неполный)
В 3.9.1 был починен только WizardDraftRepository (самый частый путь). Тот же баг с (CommandDefinition)-оверлоадом Dapper остался в 4 клуб-пикерах / permission-локапах — Wizard доходил до шага «Видимость», и при выборе «Публичная в витрине клуба» / «Только для членов клуба» PersistAndRenderAsync дёргал _messenger.GetOwnerClubsAsync → PlatformNotSupportedException → GameCreationWizard глотал исключение → кнопка ack отправлялась с тостом «⚠️ Ошибка», но нового шага пользователь не видел. Privacy «не цеплялась».
🩹 Что починено
src/GmRelay.Bot/Features/Sessions/CreateSession/Wizard/TelegramWizardMessenger.cs::GetOwnerClubsAsync—new CommandDefinition(...)→ прямойQueryAsync<WizardClubOption>(sql, params).src/GmRelay.DiscordBot/Features/Sessions/Wizard/DiscordWizardMessenger.cs::GetOwnerClubsAsync— то же.src/GmRelay.DiscordBot/Features/Sessions/Wizard/DiscordWizardInteractionModule.cs::WizardClubLookup.LoadClubsAsync— то же.src/GmRelay.DiscordBot/Features/Sessions/Wizard/DiscordPermissionLookup.cs::LoadManagerUserIdsAsync— то же.src/GmRelay.DiscordBot/GmRelay.DiscordBot.csproj— добавлен<InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Dapper.AOT</InterceptorsPreviewNamespaces>(раньше был только в Shared и Bot). Без этогоDapper.AOT-генератор не сканировал DiscordBot, иnew CommandDefinition-вызовы в DiscordBot падали бы в рантайме даже после фикса сигнатур.src/GmRelay.DiscordBot/Program.cs— добавлен[module: Dapper.DapperAot](раньше только в Bot и Shared).Directory.Build.props/compose.yaml/.gitea/workflows/deploy.yml/NavMenu.razor— бамп 3.9.1 → 3.9.2.tests/.../WizardDraftRepositoryAotShapeTests.cs— расширеныClubPickerAndPermissionLookups_ShouldNotUseCommandDefinitionна 4 inline-cases + опциональныйcontainingClassдля дизамбигуации одинаковых имён методов в DiscordWizardInteractionModule.
⚠️ Известные ограничения
- Web-проект не под NativeAOT (Blazor Server), там
Dapper.AOTне подключён и используется обычный Dapper; регрессия его не касается.
🧪 Тесты
- 592/594 passed (2 pre-existing skipped),
dotnet formatclean,dotnet build0 warnings/errors, AOT-генератор эмитит интерсепторы для всех 4 клуб-пикеров +WizardDraftRepository(всего 5 файлов: 4 в Bot/DiscordBot/DiscordBot + 1 в Shared).
🐞 Patch 3.9.1 — Hotfix: Telegram-визард мёртв после 3.9.0
Регрессия в WizardDraftRepository (NativeAOT). В Telegram не реагировали кнопки и не создавались игры, потому что Dapper.AOT 1.0.48 не генерирует интерсепторы для оверлоада (CommandDefinition) — рантайм падал в CreateParamInfoGenerator → PlatformNotSupportedException на каждом апдейте, TelegramBotService глотал исключение и апдейт терялся.
🩹 Что починено
src/GmRelay.Shared/Features/Sessions/CreateSession/Wizard/WizardDraftRepository.cs— все 4 метода переписаны сnew CommandDefinition(sql, params, cancellationToken: ct)на прямой оверлоадconnection.QuerySingleOrDefaultAsync<WizardDraft>(sql, params)(паттернJoinSessionHandler). Dapper.AOT генерирует интерсепторы только для прямого оверлоада.src/GmRelay.Shared/Features/Sessions/CreateSession/Wizard/WizardDraft.cs—CreatedAt/UpdatedAt/ExpiresAtпереведены сDateTimeOffsetнаDateTime(UTC). AOT RowFactory вызываетreader.GetDateTime()напрямую и не делаетDateTime → DateTimeOffsetконверсию.src/GmRelay.Bot/Features/Sessions/CreateSession/CreateSessionHandler.cs,src/GmRelay.DiscordBot/Features/Sessions/Wizard/DiscordWizardCommand.cs—DateTimeOffset.UtcNow→DateTime.UtcNowв новых драфтах.Directory.Build.props/compose.yaml/.gitea/workflows/deploy.yml/NavMenu.razor— бамп 3.9.0 → 3.9.1.tests/GmRelay.Bot.Tests/Features/Sessions/CreateSession/Wizard/WizardDraftRepositoryAotShapeTests.cs— 5 source-grep регрессионных тестов: ни один методWizardDraftRepositoryне должен использоватьnew CommandDefinition, и три timestamp-свойстваWizardDraftдолжны бытьDateTime(неDateTimeOffset).
⚠️ Известные ограничения
- В
TelegramWizardMessenger.GetOwnerClubsAsync,DiscordWizardMessenger.GetOwnerClubsAsync,DiscordPermissionLookup.LoadManagerUserIdsAsync,DiscordWizardInteractionModule.GetOwnerClubsAsyncостаётсяnew CommandDefinition. Эти вызовы падают на AOT так же, как падалWizardDraftRepositoryв 3.9.0. Пользователь натыкается на это только когда выбирает «видимость = клуб/мемберы» и доходит до шага выбора клуба. Будет исправлено в 3.9.2 вместе с переводомDiscordWizardInteractionModuleна прямые Dapper-оверлоады.
🧪 Тесты
- 588/590 passed (2 pre-existing skipped),
dotnet formatclean,dotnet build0 warnings/errors, AOT-генератор эмитит 4 интерсептора +RowFactory17<WizardDraft>+CommandFactory30<WizardDraft>.
🎯 Minor 3.9.0 — Discord-визард создания игры/пула (issue #112)
Пошаговый сценарий создания одиночной игры или пула игр в Discord-чате, по аналогии с Telegram-визардом из 3.8.0. Платформо-нейтральная стейт-машина GameCreationWizard и контракт IWizardMessenger перенесены в GmRelay.Shared, чтобы обе платформы (Telegram/Discord) использовали один и тот же движок визарда.
🧩 Что вошло в релиз
Платформо-нейтральный рефакторинг (GmRelay.Shared)
Features/Sessions/CreateSession/Wizard/GameCreationWizard.cs— стейт-машина визарда (один источник правды для обеих платформ)Features/Sessions/CreateSession/Wizard/IWizardMessenger.cs— контракт мессенджера (edit/send/answer/getOwnerClubs)Features/Sessions/CreateSession/Wizard/WizardInteraction.cs— запись взаимодействия (OwnerId, Text, CallbackPayload, PhotoFileId, PhotoUrl, InteractionId)Features/Sessions/CreateSession/Wizard/WizardAction.cs,WizardKeyboard.cs,WizardStepLimits.cs— модель кнопок и лимитовFeatures/Sessions/CreateSession/Wizard/WizardDraft.cs— добавлено полеPlatformMigrations/V032__add_wizard_drafts_platform.sql—ALTER TABLE wizard_drafts ADD COLUMN platform TEXT NOT NULL DEFAULT 'Telegram'
Discord-адаптер (GmRelay.DiscordBot)
Features/Sessions/Wizard/DiscordWizardCommand.cs— slash-команда/newsession-wizardс проверкой owner/co-GM черезDiscordPermissionLookupFeatures/Sessions/Wizard/DiscordWizardStep.cs— рендер 15 шагов в NetCord embed + buttons/StringSelectMenu/modalsFeatures/Sessions/Wizard/DiscordWizardMessenger.cs— реализацияIWizardMessengerчерез NetCord REST (edit с fallback на re-send при 401/403/404)Features/Sessions/Wizard/DiscordWizardSubmitter.cs— финализация с 3-retry цикломFeatures/Sessions/Wizard/DiscordWizardContextStore.cs— in-memory кэш контекста (guild/channel/messageId) для 15-минутного interaction tokenFeatures/Sessions/Wizard/DiscordWizardInteractionModule.cs— inbound handlers: 3 NetCordComponentInteractionModule<TContext>(button/StringMenu/Modal) +WizardInteractionDispatcherFeatures/Sessions/Wizard/DiscordPermissionLookup.cs— DB-хелпер дляgroup_managersProgram.cs— DI-регистрации + 3AddComponentInteractions<TInteraction, TContext>
Тесты
tests/GmRelay.Bot.Tests/Features/Sessions/CreateSession/Wizard/*— обновлены под новый контрактtests/GmRelay.Bot.Tests/Discord/DiscordWizardInteractionModuleSourceTests.cs— 12 source-level smoke-тестов на структуру interaction module
🗺 Что это даёт
- Мастера (GM) могут пошагово создавать игры и пулы слотов прямо в Discord через slash-команду, кнопки, выпадающие меню и модальные окна.
- UX адаптирован под Discord (нативные components), а не скопирован из Telegram.
- Общая стейт-машина и валидация: Telegram и Discord визарды развиваются синхронно, баги фиксятся в одном месте.
- PickClub-шаг использует реальный SQL-запрос к
club_membershipsс фильтром по роли Owner/CoGm.
📦 Версия и деплой
- Версия обновлена до 3.9.0 (
NavMenu.razor,.gitea/workflows/deploy.yml) - Docker-образы будут тегированы
3.9.0при пуше вmain - Миграция V032 применяется автоматически на старте Bot
🛠 Patch 2.4.0 — Discord /newsession и /listsessions
Реализованы slash-команды Discord для создания сессий и просмотра расписания без Web Dashboard.
🧩 Что вошло в релиз
- src/GmRelay.DiscordBot/Features/Sessions/DiscordNewSessionCommand.cs — slash-команда /newsession с параметрами (title, time, seats, link)
- src/GmRelay.DiscordBot/Features/Sessions/DiscordNewSessionHandler.cs — handler создания batch + session в БД
- src/GmRelay.DiscordBot/Features/Sessions/DiscordListSessionsCommand.cs — slash-команда /listsessions
- src/GmRelay.DiscordBot/Features/Sessions/DiscordListSessionsHandler.cs — handler запроса активных сессий с embed-рендерингом
- src/GmRelay.DiscordBot/Infrastructure/Discord/DiscordPermissionChecker.cs — проверка прав через Discord permissions bitflag (Administrator = 0x8)
- src/GmRelay.DiscordBot/Infrastructure/Discord/DiscordPlatformMessenger.cs — реализация IPlatformMessenger для Discord через NetCord REST
- src/GmRelay.DiscordBot/Program.cs — регистрация DI: handlers, permission checker, messenger
- ests/GmRelay.Bot.Tests/Discord/ — 20+ TDD-тестов на парсинг, права, структуру, DI, рендеринг
- Синхронизированы версии: Directory.Build.props, NavMenu.razor, compose.yaml, deploy.yml → 2.4.0
🗺 Что это даёт
- Мастера (GM) могут создавать сессии прямо из Discord, не заходя в Web.
- Участники сервера видят расписание через /listsessions.
- Единая PostgreSQL модель для Telegram и Discord — никакого дублирования данных.
📦 Версия и деплой
- версия обновлена до 2.4.0
- Docker-образы используют тег 2.4.0