feat(wizard): add ITelegramWizardMessenger (edit/send/answer/club-list)

This commit is contained in:
2026-06-04 08:28:30 +03:00
parent cff4e48b57
commit 96a4807002
2 changed files with 86 additions and 0 deletions
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace GmRelay.Bot.Features.Sessions.CreateSession.Wizard;
public sealed record WizardClubOption(Guid ClubId, string Name);
public interface ITelegramWizardMessenger
{
Task<long> EditMessageTextAsync(long chatId, int? messageThreadId, long messageId, string text, Telegram.Bot.Types.ReplyMarkups.InlineKeyboardMarkup keyboard, CancellationToken ct);
Task<long> SendGroupMessageAsync(long chatId, int? messageThreadId, string text, Telegram.Bot.Types.ReplyMarkups.InlineKeyboardMarkup keyboard, CancellationToken ct);
Task AnswerCallbackAsync(string callbackId, string? text, CancellationToken ct);
Task<IReadOnlyList<WizardClubOption>> GetGmClubsAsync(long ownerTelegramId, CancellationToken ct);
}
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Dapper;
using Npgsql;
using Telegram.Bot;
using Telegram.Bot.Types.ReplyMarkups;
namespace GmRelay.Bot.Features.Sessions.CreateSession.Wizard;
public sealed class TelegramWizardMessenger(
ITelegramBotClient bot,
NpgsqlDataSource dataSource) : ITelegramWizardMessenger
{
public async Task<long> EditMessageTextAsync(
long chatId, int? messageThreadId, long messageId, string text,
InlineKeyboardMarkup keyboard, CancellationToken ct)
{
var msg = await bot.EditMessageText(
chatId: chatId,
messageId: (int)messageId,
text: text,
replyMarkup: keyboard,
cancellationToken: ct);
return msg.MessageId;
}
public async Task<long> SendGroupMessageAsync(
long chatId, int? messageThreadId, string text,
InlineKeyboardMarkup keyboard, CancellationToken ct)
{
var msg = await bot.SendMessage(
chatId: chatId,
text: text,
messageThreadId: messageThreadId,
replyMarkup: keyboard,
cancellationToken: ct);
return msg.MessageId;
}
public async Task AnswerCallbackAsync(string callbackId, string? text, CancellationToken ct)
{
await bot.AnswerCallbackQuery(callbackId, text: text, cancellationToken: ct);
}
public async Task<IReadOnlyList<WizardClubOption>> GetGmClubsAsync(long ownerTelegramId, CancellationToken ct)
{
// Adjusted from the plan: this codebase models "clubs" as game_groups
// (V001 created game_groups; V026 added public_slug; no `clubs` table exists,
// and game_groups has no `club_id` FK). The picker therefore returns the
// game_groups the owner manages as a GM (via group_managers), matching
// the WizardClubOption contract (UUID id, name) used downstream.
const string sql = """
SELECT g.id AS ClubId,
g.name AS Name
FROM game_groups g
JOIN group_managers gm ON gm.group_id = g.id
JOIN players p ON p.id = gm.player_id
WHERE p.platform = 'Telegram'
AND p.external_user_id = @ExternalId
GROUP BY g.id, g.name
ORDER BY g.name
""";
await using var connection = await dataSource.OpenConnectionAsync(ct);
var rows = await connection.QueryAsync<WizardClubOption>(
new CommandDefinition(sql, new { ExternalId = ownerTelegramId.ToString() }, cancellationToken: ct));
return rows.AsList();
}
}