refactor(wizard): move core to Shared, add IWizardMessenger contract (issue #112)
Moves the game-creation wizard state machine, view builder, and platform-neutral contracts (callback data, step names, storage exception, club option, step limits) from GmRelay.Bot to GmRelay.Shared. Telegram continues to work through a new TelegramWizardMessenger implementing IWizardMessenger and a WizardInteractionMapper that converts Update → WizardInteraction. Wires the new platform column on wizard_drafts (V032 migration) and switches chat/owner/thread/message ids to TEXT so the same table can hold Discord snowflakes later. - GameCreationWizard: now in Shared, takes IWizardMessenger + IWizardDraftRepository, dispatches on WizardInteraction. - New IWizardMessenger contract with Edit/Send/Answer/GetOwnerClubs (returns string ids so Telegram longs and Discord snowflakes both fit). - New WizardStepViewBuilder in Shared returns (text, IReadOnlyList<WizardAction>); TelegramWizardMessenger renders actions into InlineKeyboardMarkup via a new Bot-side ToInlineKeyboard helper. - New WizardInteractionMapper in Bot (5-case test) converts Telegram Update to WizardInteraction. - WizardDraft gains a Platform column; ChatId/MessageThreadId/OwnerId/ DraftMessageId switched to string. V032 migrates existing rows and rebuilds the owner lookup index on (platform, owner_id). - All existing wizard / create-session tests updated to the new contract (HandleInteractionAsync + WizardInteraction). Wizard callback-data format preserved. - dotnet build clean, dotnet format --verify-no-changes clean, all 101 wizard tests pass.
This commit is contained in:
+11
-10
@@ -8,14 +8,14 @@ namespace GmRelay.Shared.Features.Sessions.CreateSession.Wizard;
|
||||
|
||||
public sealed class WizardDraftRepository(NpgsqlDataSource dataSource) : IWizardDraftRepository
|
||||
{
|
||||
public async Task<WizardDraft?> GetActiveAsync(
|
||||
long chatId, int? messageThreadId, long ownerTelegramId, CancellationToken ct)
|
||||
public async Task<WizardDraft?> GetActiveAsync(string platform, string ownerId, CancellationToken ct)
|
||||
{
|
||||
const string sql = """
|
||||
SELECT id AS Id,
|
||||
chat_id AS ChatId,
|
||||
message_thread_id AS MessageThreadId,
|
||||
owner_telegram_id AS OwnerTelegramId,
|
||||
owner_id AS OwnerId,
|
||||
platform AS Platform,
|
||||
step AS Step,
|
||||
payload::text AS PayloadJson,
|
||||
draft_message_id AS DraftMessageId,
|
||||
@@ -23,17 +23,18 @@ public sealed class WizardDraftRepository(NpgsqlDataSource dataSource) : IWizard
|
||||
updated_at AS UpdatedAt,
|
||||
expires_at AS ExpiresAt
|
||||
FROM wizard_drafts
|
||||
WHERE chat_id = @ChatId
|
||||
AND (message_thread_id = @ThreadId OR (@ThreadId IS NULL AND message_thread_id IS NULL))
|
||||
AND owner_telegram_id = @OwnerId
|
||||
WHERE platform = @Platform
|
||||
AND owner_id = @OwnerId
|
||||
AND expires_at > NOW()
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 1
|
||||
""";
|
||||
|
||||
await using var connection = await dataSource.OpenConnectionAsync(ct);
|
||||
return await connection.QuerySingleOrDefaultAsync<WizardDraft>(
|
||||
new CommandDefinition(sql,
|
||||
new { ChatId = chatId, ThreadId = messageThreadId, OwnerId = ownerTelegramId },
|
||||
new CommandDefinition(
|
||||
sql,
|
||||
new { Platform = platform, OwnerId = ownerId },
|
||||
cancellationToken: ct));
|
||||
}
|
||||
|
||||
@@ -41,9 +42,9 @@ public sealed class WizardDraftRepository(NpgsqlDataSource dataSource) : IWizard
|
||||
{
|
||||
const string sql = """
|
||||
INSERT INTO wizard_drafts
|
||||
(id, chat_id, message_thread_id, owner_telegram_id, step, payload, draft_message_id, created_at, updated_at, expires_at)
|
||||
(id, chat_id, message_thread_id, owner_id, platform, step, payload, draft_message_id, created_at, updated_at, expires_at)
|
||||
VALUES
|
||||
(@Id, @ChatId, @MessageThreadId, @OwnerTelegramId, @Step, @PayloadJson::jsonb, @DraftMessageId, @CreatedAt, @UpdatedAt, @ExpiresAt)
|
||||
(@Id, @ChatId, @MessageThreadId, @OwnerId, @Platform, @Step, @PayloadJson::jsonb, @DraftMessageId, @CreatedAt, @UpdatedAt, @ExpiresAt)
|
||||
ON CONFLICT (id) DO UPDATE
|
||||
SET step = EXCLUDED.step,
|
||||
payload = EXCLUDED.payload,
|
||||
|
||||
Reference in New Issue
Block a user