refactor(#22): разделить SessionBatchRenderer на neutral view и Telegram renderer
- SessionBatchViewBuilder в Shared собирает нейтральную view model - TelegramSessionBatchRenderer в Bot/Web рендерит HTML + InlineKeyboardMarkup - DiscordSessionBatchRenderer заглушка подготовлена - BatchMessageEditor перенесён из Shared в Bot/Web - Удалён SessionBatchRenderer, убран Telegram.Bot из Shared.csproj - Обновлены все вызовы (7 handler-ов + Web SessionService + smoke tests) - Новые тесты на builder и Telegram renderer
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
|
||||
namespace GmRelay.Bot.Infrastructure.Telegram;
|
||||
|
||||
/// <summary>
|
||||
/// Handles editing batch messages that may be either text or photo messages.
|
||||
/// When the batch was created with SendPhoto (image + caption), we need
|
||||
/// EditMessageCaption instead of EditMessageText.
|
||||
/// </summary>
|
||||
public static class BatchMessageEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Edits a batch message, automatically detecting whether it is a text or photo message.
|
||||
/// Tries EditMessageText first; on failure falls back to EditMessageCaption.
|
||||
/// </summary>
|
||||
public static async Task EditBatchMessageAsync(
|
||||
ITelegramBotClient bot,
|
||||
long chatId,
|
||||
int messageId,
|
||||
string text,
|
||||
InlineKeyboardMarkup? replyMarkup,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await bot.EditMessageText(
|
||||
chatId: chatId,
|
||||
messageId: messageId,
|
||||
text: text,
|
||||
parseMode: ParseMode.Html,
|
||||
replyMarkup: replyMarkup,
|
||||
cancellationToken: ct);
|
||||
}
|
||||
catch (Telegram.Bot.Exceptions.ApiRequestException ex)
|
||||
when (ex.Message.Contains("there is no text in the message", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// The batch message is a photo — use EditMessageCaption instead.
|
||||
// Caption is limited to 1024 chars; if text exceeds that, truncate gracefully.
|
||||
var caption = text.Length <= 1024 ? text : text[..1021] + "...";
|
||||
await bot.EditMessageCaption(
|
||||
chatId: chatId,
|
||||
messageId: messageId,
|
||||
caption: caption,
|
||||
parseMode: ParseMode.Html,
|
||||
replyMarkup: replyMarkup,
|
||||
cancellationToken: ct);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// NOTE: duplicated in GmRelay.Web/Services/TelegramSessionBatchRenderer.cs
|
||||
using GmRelay.Shared.Rendering;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
|
||||
namespace GmRelay.Bot.Infrastructure.Telegram;
|
||||
|
||||
public static class TelegramSessionBatchRenderer
|
||||
{
|
||||
public static (string Text, InlineKeyboardMarkup Markup) Render(SessionBatchViewModel view)
|
||||
{
|
||||
var messageText = $"🎲 <b>Новые игры:</b> {System.Net.WebUtility.HtmlEncode(view.Title)}\n\n" +
|
||||
$"<b>Расписание:</b>\n\n";
|
||||
|
||||
var buttons = new List<InlineKeyboardButton[]>();
|
||||
|
||||
foreach (var session in view.Sessions)
|
||||
{
|
||||
messageText += $"📅 <b>{session.ScheduledAt.FormatMoscow()}</b>\n";
|
||||
messageText += session.MaxPlayers.HasValue
|
||||
? $"👥 Места: {session.ActivePlayerCount}/{session.MaxPlayers.Value}\n"
|
||||
: $"👥 Игроки ({session.ActivePlayerCount}):\n";
|
||||
|
||||
if (session.ActivePlayers.Count > 0)
|
||||
{
|
||||
messageText += string.Join("\n", session.ActivePlayers.Select(p =>
|
||||
$" 👤 {(p.TelegramUsername != null ? "@" + p.TelegramUsername : p.DisplayName)}")) + "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
messageText += " <i>Пока никто не записался</i>\n";
|
||||
}
|
||||
|
||||
if (session.WaitlistedPlayers.Count > 0)
|
||||
{
|
||||
messageText += $"⏳ Лист ожидания ({session.WaitlistedPlayers.Count}):\n";
|
||||
messageText += string.Join("\n", session.WaitlistedPlayers.Select(p =>
|
||||
$" ⏱ {(p.TelegramUsername != null ? "@" + p.TelegramUsername : p.DisplayName)}")) + "\n";
|
||||
}
|
||||
|
||||
if (GmRelay.Shared.Domain.SessionStatus.IsCancelled(session.Status))
|
||||
{
|
||||
messageText += "❌ <i>Сессия отменена</i>\n\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
messageText += "\n";
|
||||
var actionRow = session.AvailableActions
|
||||
.Select(a => InlineKeyboardButton.WithCallbackData(a.Label, $"{a.ActionKey}:{a.SessionId}"))
|
||||
.ToArray();
|
||||
if (actionRow.Length > 0)
|
||||
buttons.Add(actionRow);
|
||||
}
|
||||
}
|
||||
|
||||
return (messageText, new InlineKeyboardMarkup(buttons));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user