diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 3d76fc9..ee8ad8a 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -6,7 +6,7 @@ on: - main env: - VERSION: 1.9.6 + VERSION: 1.9.7 jobs: # ЧАСТЬ 1: Собираем образы и кладем в Gitea (чтобы делиться с ребятами) diff --git a/.gitignore b/.gitignore index 4aa1493..e51a547 100644 Binary files a/.gitignore and b/.gitignore differ diff --git a/Directory.Build.props b/Directory.Build.props index 1223b97..1a2464b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.9.6 + 1.9.7 net10.0 preview enable diff --git a/compose.yaml b/compose.yaml index d815769..b04fc08 100644 --- a/compose.yaml +++ b/compose.yaml @@ -17,7 +17,7 @@ services: retries: 10 bot: - image: git.codeanddice.ru/toutsu/gmrelay-bot:1.9.6 + image: git.codeanddice.ru/toutsu/gmrelay-bot:1.9.7 restart: always depends_on: db: @@ -30,7 +30,7 @@ services: - gmrelay web: - image: git.codeanddice.ru/toutsu/gmrelay-web:1.9.6 + image: git.codeanddice.ru/toutsu/gmrelay-web:1.9.7 restart: always depends_on: db: diff --git a/src/GmRelay.Bot/Features/Sessions/CreateSession/CancelSessionHandler.cs b/src/GmRelay.Bot/Features/Sessions/CreateSession/CancelSessionHandler.cs index 55464c3..71057cb 100644 --- a/src/GmRelay.Bot/Features/Sessions/CreateSession/CancelSessionHandler.cs +++ b/src/GmRelay.Bot/Features/Sessions/CreateSession/CancelSessionHandler.cs @@ -106,13 +106,13 @@ public sealed class CancelSessionHandler( try { - await bot.EditMessageText( + await BatchMessageEditor.EditBatchMessageAsync( + bot, chatId: command.ChatId, messageId: session.BatchMessageId ?? command.MessageId, text: renderResult.Text, - parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, - replyMarkup: renderResult.Markup, - cancellationToken: ct); + replyMarkup: renderResult.Markup, + ct); await bot.AnswerCallbackQuery(command.CallbackQueryId, "Сессия отменена!", cancellationToken: ct); diff --git a/src/GmRelay.Bot/Features/Sessions/CreateSession/CreateSessionHandler.cs b/src/GmRelay.Bot/Features/Sessions/CreateSession/CreateSessionHandler.cs index 610a9a7..853da9b 100644 --- a/src/GmRelay.Bot/Features/Sessions/CreateSession/CreateSessionHandler.cs +++ b/src/GmRelay.Bot/Features/Sessions/CreateSession/CreateSessionHandler.cs @@ -185,31 +185,63 @@ public sealed class CreateSessionHandler( var renderResult = SessionBatchRenderer.Render(title, sessions, Array.Empty()); - if (imageReference is not null) + Message batchMessage; + + if (imageReference is not null && renderResult.Text.Length <= 1024) { + // Картинка + расписание умещаются в одном Telegram-фото с подписью try { - await botClient.SendPhoto( + batchMessage = await botClient.SendPhoto( chatId: chatId, messageThreadId: messageThreadId, photo: InputFile.FromString(imageReference), - caption: $"🎲 {System.Net.WebUtility.HtmlEncode(title)}", + caption: renderResult.Text, parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, + replyMarkup: renderResult.Markup, cancellationToken: cancellationToken); } catch (Exception ex) { - logger.LogWarning(ex, "Не удалось отправить картинку для батча {BatchId}", batchId); + logger.LogWarning(ex, "Не удалось отправить картинку для батча {BatchId}, отправляем текстом", batchId); + batchMessage = await botClient.SendMessage( + chatId: chatId, + messageThreadId: messageThreadId, + text: renderResult.Text, + parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, + replyMarkup: renderResult.Markup, + cancellationToken: cancellationToken); } } + else + { + // Текст слишком длинный для caption — fallback на два сообщения + if (imageReference is not null) + { + try + { + await botClient.SendPhoto( + chatId: chatId, + messageThreadId: messageThreadId, + photo: InputFile.FromString(imageReference), + caption: $"🎲 {System.Net.WebUtility.HtmlEncode(title)}", + parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, + cancellationToken: cancellationToken); + } + catch (Exception ex) + { + logger.LogWarning(ex, "Не удалось отправить картинку для батча {BatchId}", batchId); + } + } - var batchMessage = await botClient.SendMessage( - chatId: chatId, - messageThreadId: messageThreadId, - text: renderResult.Text, - parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, - replyMarkup: renderResult.Markup, - cancellationToken: cancellationToken); + batchMessage = await botClient.SendMessage( + chatId: chatId, + messageThreadId: messageThreadId, + text: renderResult.Text, + parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, + replyMarkup: renderResult.Markup, + cancellationToken: cancellationToken); + } await connection.ExecuteAsync( "UPDATE sessions SET batch_message_id = @MsgId WHERE batch_id = @BatchId", diff --git a/src/GmRelay.Bot/Features/Sessions/CreateSession/JoinSessionHandler.cs b/src/GmRelay.Bot/Features/Sessions/CreateSession/JoinSessionHandler.cs index cadfba1..a7aec54 100644 --- a/src/GmRelay.Bot/Features/Sessions/CreateSession/JoinSessionHandler.cs +++ b/src/GmRelay.Bot/Features/Sessions/CreateSession/JoinSessionHandler.cs @@ -138,13 +138,13 @@ public sealed class JoinSessionHandler( // 4. Перерисовываем сообщение var renderResult = SessionBatchRenderer.Render(batchInfo.Title, batchSessions.ToList(), batchParticipants.ToList()); - await bot.EditMessageText( + await BatchMessageEditor.EditBatchMessageAsync( + bot, chatId: command.ChatId, messageId: command.MessageId, text: renderResult.Text, - parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, replyMarkup: renderResult.Markup, - cancellationToken: ct); + ct); var callbackText = registrationStatus == ParticipantRegistrationStatus.Waitlisted ? "Основной состав заполнен. Вы добавлены в лист ожидания." diff --git a/src/GmRelay.Bot/Features/Sessions/CreateSession/LeaveSessionHandler.cs b/src/GmRelay.Bot/Features/Sessions/CreateSession/LeaveSessionHandler.cs index d96ade6..9ba1415 100644 --- a/src/GmRelay.Bot/Features/Sessions/CreateSession/LeaveSessionHandler.cs +++ b/src/GmRelay.Bot/Features/Sessions/CreateSession/LeaveSessionHandler.cs @@ -184,13 +184,13 @@ public sealed class LeaveSessionHandler( var renderResult = SessionBatchRenderer.Render(session.Title, batchSessions, batchParticipants); - await bot.EditMessageText( + await BatchMessageEditor.EditBatchMessageAsync( + bot, chatId: command.ChatId, messageId: command.MessageId, text: renderResult.Text, - parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, replyMarkup: renderResult.Markup, - cancellationToken: ct); + ct); var callbackText = participant.RegistrationStatus == ParticipantRegistrationStatus.Waitlisted ? "Вы удалены из листа ожидания." diff --git a/src/GmRelay.Bot/Features/Sessions/CreateSession/PromoteWaitlistedPlayerHandler.cs b/src/GmRelay.Bot/Features/Sessions/CreateSession/PromoteWaitlistedPlayerHandler.cs index 3266d35..7d0c650 100644 --- a/src/GmRelay.Bot/Features/Sessions/CreateSession/PromoteWaitlistedPlayerHandler.cs +++ b/src/GmRelay.Bot/Features/Sessions/CreateSession/PromoteWaitlistedPlayerHandler.cs @@ -164,13 +164,13 @@ public sealed class PromoteWaitlistedPlayerHandler( var renderResult = SessionBatchRenderer.Render(session.Title, batchSessions, batchParticipants); - await bot.EditMessageText( + await BatchMessageEditor.EditBatchMessageAsync( + bot, chatId: command.ChatId, messageId: session.BatchMessageId ?? command.MessageId, text: renderResult.Text, - parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, replyMarkup: renderResult.Markup, - cancellationToken: ct); + ct); await bot.AnswerCallbackQuery(command.CallbackQueryId, $"{promoted.DisplayName} переведен(а) в основной состав.", cancellationToken: ct); } diff --git a/src/GmRelay.Bot/Features/Sessions/RescheduleSession/HandleRescheduleTimeInputHandler.cs b/src/GmRelay.Bot/Features/Sessions/RescheduleSession/HandleRescheduleTimeInputHandler.cs index 590d786..078c8cc 100644 --- a/src/GmRelay.Bot/Features/Sessions/RescheduleSession/HandleRescheduleTimeInputHandler.cs +++ b/src/GmRelay.Bot/Features/Sessions/RescheduleSession/HandleRescheduleTimeInputHandler.cs @@ -378,13 +378,13 @@ public sealed class HandleRescheduleTimeInputHandler( var renderResult = SessionBatchRenderer.Render( proposal.Title, batchSessions, batchParticipants); - await bot.EditMessageText( + await BatchMessageEditor.EditBatchMessageAsync( + bot, chatId: proposal.TelegramChatId, messageId: proposal.BatchMessageId.Value, text: renderResult.Text, - parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, replyMarkup: renderResult.Markup, - cancellationToken: ct); + ct); } else { diff --git a/src/GmRelay.Bot/Features/Sessions/RescheduleSession/RescheduleVotingDeadlineService.cs b/src/GmRelay.Bot/Features/Sessions/RescheduleSession/RescheduleVotingDeadlineService.cs index ce63e95..a678649 100644 --- a/src/GmRelay.Bot/Features/Sessions/RescheduleSession/RescheduleVotingDeadlineService.cs +++ b/src/GmRelay.Bot/Features/Sessions/RescheduleSession/RescheduleVotingDeadlineService.cs @@ -306,13 +306,13 @@ public sealed class RescheduleVotingDeadlineService( { var renderResult = SessionBatchRenderer.Render(proposal.Title, batchSessions, batchParticipants); - await bot.EditMessageText( + await BatchMessageEditor.EditBatchMessageAsync( + bot, chatId: proposal.TelegramChatId, messageId: proposal.BatchMessageId.Value, text: renderResult.Text, - parseMode: ParseMode.Html, replyMarkup: renderResult.Markup, - cancellationToken: ct); + ct); } else { diff --git a/src/GmRelay.Shared/Rendering/BatchMessageEditor.cs b/src/GmRelay.Shared/Rendering/BatchMessageEditor.cs new file mode 100644 index 0000000..194b119 --- /dev/null +++ b/src/GmRelay.Shared/Rendering/BatchMessageEditor.cs @@ -0,0 +1,51 @@ +using Telegram.Bot; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.ReplyMarkups; + +namespace GmRelay.Shared.Rendering; + +/// +/// 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. +/// +public static class BatchMessageEditor +{ + /// + /// Edits a batch message, automatically detecting whether it is a text or photo message. + /// Tries EditMessageText first; on failure falls back to EditMessageCaption. + /// + 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); + } + } +} diff --git a/src/GmRelay.Web/Services/SessionService.cs b/src/GmRelay.Web/Services/SessionService.cs index bcea77f..b60ed5a 100644 --- a/src/GmRelay.Web/Services/SessionService.cs +++ b/src/GmRelay.Web/Services/SessionService.cs @@ -1048,11 +1048,11 @@ public sealed class SessionService( var renderResult = SessionBatchRenderer.Render(title, sessions, participants); - await bot.EditMessageText( + await BatchMessageEditor.EditBatchMessageAsync( + bot, chatId: chatId, messageId: messageId, text: renderResult.Text, - parseMode: Telegram.Bot.Types.Enums.ParseMode.Html, replyMarkup: renderResult.Markup); } catch (Exception ex)