f796b7d1e4
Production regression in 3.9.0: Telegram bot silently dropped every update. WizardDraftRepository.GetActiveAsync was called on every Telegram update (via UpdateRouter -> TryGetWizardContext) and threw System.PlatformNotSupportedException in NativeAOT, because Dapper.AOT 1.0.48 only generates interceptors for the (sql, object?) extension overloads and NOT for the (CommandDefinition) overload. The runtime then fell back to Dapper.SqlMapper.CreateParamInfoGenerator, which uses Reflection.Emit and fails on AOT. TelegramBotService swallowed the exception, so /newsession appeared to start but no button press reached the wizard and no session was created. Two related changes in WizardDraft: 1. Switched WizardDraftRepository.* from 'new CommandDefinition(sql, params, cancellationToken: ct)' to the direct 'connection.Query*(sql, params)' overload, matching the working pattern in JoinSessionHandler. Dapper.AOT now generates CommandFactory30<WizardDraft> + RowFactory17<WizardDraft> + QuerySingleOrDefaultAsync37<WizardDraft> for all four methods. 2. WizardDraft.CreatedAt/UpdatedAt/ExpiresAt are now DateTime (UTC) instead of DateTimeOffset. AOT RowFactory calls reader.GetDateTime() directly and does not perform DateTime -> DateTimeOffset conversion; the previous type raised InvalidCastException on the very first wizard_drafts query. All 588/590 tests pass (2 pre-existing skipped, +5 new AOT regression tests in WizardDraftRepositoryAotShapeTests). dotnet format clean. Bumps: 3.9.0 -> 3.9.1. Note: GetOwnerClubsAsync (Telegram/Discord), DiscordPermissionLookup, and DiscordWizardInteractionModule.GetOwnerClubsAsync still use CommandDefinition and will hit the same Reflection.Emit AOT failure when the user reaches the PickClub visibility step. Follow-up in 3.9.2.
80 lines
9.1 KiB
Markdown
80 lines
9.1 KiB
Markdown
## 🐞 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 format` clean, `dotnet build` 0 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` — добавлено поле `Platform`
|
||
- `Migrations/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 через `DiscordPermissionLookup`
|
||
- `Features/Sessions/Wizard/DiscordWizardStep.cs` — рендер 15 шагов в NetCord embed + buttons/StringSelectMenu/modals
|
||
- `Features/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 token
|
||
- `Features/Sessions/Wizard/DiscordWizardInteractionModule.cs` — inbound handlers: 3 NetCord `ComponentInteractionModule<TContext>` (button/StringMenu/Modal) + `WizardInteractionDispatcher`
|
||
- `Features/Sessions/Wizard/DiscordPermissionLookup.cs` — DB-хелпер для `group_managers`
|
||
- `Program.cs` — DI-регистрации + 3 `AddComponentInteractions<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
|