From 0089926fb07085b0813b1c939b08678e052c26b9 Mon Sep 17 00:00:00 2001 From: FrigaT Date: Wed, 24 Dec 2025 17:09:15 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A7=D0=B8=D1=81=D1=82=D0=BA=D0=B0=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Abstractions/IMessengerAdapter.cs | 1 - BotPages.Core/Abstractions/SendRequest.cs | 1 - .../Context/PageContextAdapterExtensions.cs | 1 - BotPages.Telegram/TelegramAdapter.cs | 252 +++++++++--------- 4 files changed, 125 insertions(+), 130 deletions(-) diff --git a/BotPages.Core/Abstractions/IMessengerAdapter.cs b/BotPages.Core/Abstractions/IMessengerAdapter.cs index 6bc9f72..491832e 100644 --- a/BotPages.Core/Abstractions/IMessengerAdapter.cs +++ b/BotPages.Core/Abstractions/IMessengerAdapter.cs @@ -1,5 +1,4 @@ using BotPages.Core.Context; -using BotPages.Core.Messaging; namespace BotPages.Core.Abstractions; diff --git a/BotPages.Core/Abstractions/SendRequest.cs b/BotPages.Core/Abstractions/SendRequest.cs index 08e2376..d009c97 100644 --- a/BotPages.Core/Abstractions/SendRequest.cs +++ b/BotPages.Core/Abstractions/SendRequest.cs @@ -4,7 +4,6 @@ namespace BotPages.Core.Abstractions; /// /// /, . -/// Core . /// public sealed class SendRequest { diff --git a/BotPages.Core/Context/PageContextAdapterExtensions.cs b/BotPages.Core/Context/PageContextAdapterExtensions.cs index c316fb0..d1426bb 100644 --- a/BotPages.Core/Context/PageContextAdapterExtensions.cs +++ b/BotPages.Core/Context/PageContextAdapterExtensions.cs @@ -1,5 +1,4 @@ using BotPages.Core.Abstractions; -using BotPages.Core.Messaging; namespace BotPages.Core; diff --git a/BotPages.Telegram/TelegramAdapter.cs b/BotPages.Telegram/TelegramAdapter.cs index 63ee7c6..2119af0 100644 --- a/BotPages.Telegram/TelegramAdapter.cs +++ b/BotPages.Telegram/TelegramAdapter.cs @@ -2,6 +2,7 @@ using BotPages.Core.Abstractions; using BotPages.Core.Context; using BotPages.Core.Logging; +using BotPages.Core.Messaging; using System; using System.Collections.Generic; using System.IO; @@ -114,147 +115,144 @@ public sealed class TelegramAdapter : IMessengerAdapterSetup var disableNotification = !telegramOptions.NotifyOnSend; // Build markup - InlineKeyboardMarkup? inlineMarkup = null; - ReplyMarkup? markup = null; + var inlineMarkup = BuildInlineMarkup(req.Inline); + ReplyMarkup? markup = BuildReplyMarkup(req.Reply); - if (req.Inline is not null && req.Inline.Any()) - { - inlineMarkup = new InlineKeyboardMarkup( - req.Inline.Select(row => row.Select(b => new InlineKeyboardButton(b.Label, b.Value)).ToArray()).ToArray() - ); - } - else if (req.Reply is not null) - { - if (req.Reply.Any()) - { - markup = new ReplyKeyboardMarkup(req.Reply.Select(row => row.Select(b => new KeyboardButton(b.Label)).ToArray()).ToArray()) - { - ResizeKeyboard = true - }; - } - else - { - markup = new ReplyKeyboardRemove(); - } - } - - // Если есть файл - отправить файл + // Файлы: сейчас поддерживается один файл через SendRequest.File. + // При необходимости для нескольких файлов следует использовать альбомы (CreateAlbumBuilder) if (req.File is not null) { - var file = req.File; - - // Получаем поток, если он задан - Stream? stream = null; - if (file.GetStreamAsync is not null) - { - stream = await file.GetStreamAsync(ct); - if (stream is not null) stream.Position = 0; - } - - InputFile inputFile; - - if (stream is not null && stream != Stream.Null) - { - inputFile = new InputFileStream(stream, file.Name); - } - else if (file.Id.StartsWith("http://") || file.Id.StartsWith("https://")) - { - inputFile = new InputFileUrl(file.Id); - } - else - { - inputFile = new InputFileId(file.Id); - } - - var parseMode = ParseMode.None; - switch (req.CaptionFormat) - { - case MessageFormat.Html: - parseMode = ParseMode.Html; - break; - case MessageFormat.Markdown: - parseMode = ParseMode.MarkdownV2; - break; - case MessageFormat.Plain: - case null: - parseMode = ParseMode.None; - break; - } - if (inlineMarkup is not null) markup = inlineMarkup; - Message? sentMessage = req.File.Kind switch - { - FileKind.Photo => await _client.SendPhoto(long.Parse(req.ChatId), inputFile, req.Caption ?? "", parseMode, replyMarkup: markup, disableNotification: disableNotification, cancellationToken: ct), - FileKind.Video => await _client.SendVideo(long.Parse(req.ChatId), inputFile, caption: req.Caption ?? "", parseMode, replyMarkup: markup, disableNotification: disableNotification, cancellationToken: ct), - FileKind.Audio => await _client.SendAudio(long.Parse(req.ChatId), inputFile, req.Caption ?? "", parseMode, replyMarkup: markup, disableNotification: disableNotification, cancellationToken: ct), - _ => await _client.SendDocument(long.Parse(req.ChatId), inputFile, req.Caption ?? "", parseMode, replyMarkup: markup, disableNotification: disableNotification, cancellationToken: ct), - }; - - return sentMessage?.MessageId.ToString(); + var sent = await SendFileAsync(req.File, req.ChatId, markup, disableNotification, req.Caption, req.CaptionFormat, ct); + return sent?.MessageId.ToString(); } - // Иначе - отправить текст + // Текст if (!string.IsNullOrWhiteSpace(req.Text)) { - var format = req.TextFormat ?? MessageFormat.Plain; - var parseMode = ParseMode.None; - - switch (format) - { - case MessageFormat.Html: - parseMode = ParseMode.Html; - break; - case MessageFormat.Markdown: - parseMode = ParseMode.MarkdownV2; - break; - case MessageFormat.Plain: - default: - parseMode = ParseMode.None; - break; - } - - // Длина сообщения - var text = req.Text!; - if (text.Length > Capabilities.MaxMessageLength) - { - _logger.Log(LogLevel.Warn, $"Message too long ({text.Length}). Truncated to {Capabilities.MaxMessageLength}."); - text = text.Substring(0, Capabilities.MaxMessageLength); - } - - if (!string.IsNullOrWhiteSpace(req.MessageId)) - { - await _client.EditMessageText( - messageId: int.Parse(req.MessageId), - chatId: long.Parse(req.ChatId), - text: text, - parseMode: parseMode, - replyMarkup: inlineMarkup, - cancellationToken: ct - ); - - return req.MessageId; - } - else - { - if (inlineMarkup is not null) markup = inlineMarkup; - - var message = await _client.SendMessage( - chatId: long.Parse(req.ChatId), - text: text, - parseMode: parseMode, - replyMarkup: markup, - disableNotification: disableNotification, - cancellationToken: ct - ); - - return message.MessageId.ToString(); - } + return await SendTextAsync(req, inlineMarkup, markup, disableNotification, ct); } return null; } + // --- Helpers --- + + private InlineKeyboardMarkup? BuildInlineMarkup(IEnumerable>? inline) + { + if (inline is null || !inline.Any()) return null; + return new InlineKeyboardMarkup( + inline.Select(row => row.Select(b => new InlineKeyboardButton(b.Label, b.Value)).ToArray()).ToArray() + ); + } + + private ReplyMarkup? BuildReplyMarkup(IEnumerable>? reply) + { + if (reply is null) return null; + + if (reply.Any()) + { + return new ReplyKeyboardMarkup(reply.Select(row => row.Select(b => new KeyboardButton(b.Label)).ToArray()).ToArray()) + { + ResizeKeyboard = true + }; + } + else + { + return new ReplyKeyboardRemove(); + } + } + + private static ParseMode GetParseMode(MessageFormat? format) + { + return format switch + { + MessageFormat.Html => ParseMode.Html, + MessageFormat.Markdown => ParseMode.MarkdownV2, + _ => ParseMode.None, + }; + } + + private async Task CreateInputFileAsync(FileDescriptor file, CancellationToken ct) + { + Stream? stream = null; + if (file.GetStreamAsync is not null) + { + stream = await file.GetStreamAsync(ct); + if (stream is not null) stream.Position = 0; + } + + if (stream is not null && stream != Stream.Null) + { + return new InputFileStream(stream, file.Name); + } + else if (file.Id.StartsWith("http://") || file.Id.StartsWith("https://")) + { + return new InputFileUrl(file.Id); + } + else + { + return new InputFileId(file.Id); + } + } + + private async Task SendFileAsync(FileDescriptor file, string chatId, ReplyMarkup? markup, bool disableNotification, string? caption, MessageFormat? captionFormat, CancellationToken ct) + { + var inputFile = await CreateInputFileAsync(file, ct); + var parseMode = GetParseMode(captionFormat); + + return file.Kind switch + { + FileKind.Photo => await _client.SendPhoto(long.Parse(chatId), inputFile, caption ?? "", parseMode, replyMarkup: markup, disableNotification: disableNotification, cancellationToken: ct), + FileKind.Video => await _client.SendVideo(long.Parse(chatId), inputFile, caption: caption ?? "", parseMode, replyMarkup: markup, disableNotification: disableNotification, cancellationToken: ct), + FileKind.Audio => await _client.SendAudio(long.Parse(chatId), inputFile, caption ?? "", parseMode, replyMarkup: markup, disableNotification: disableNotification, cancellationToken: ct), + _ => await _client.SendDocument(long.Parse(chatId), inputFile, caption ?? "", parseMode, replyMarkup: markup, disableNotification: disableNotification, cancellationToken: ct), + }; + } + + private async Task SendTextAsync(SendRequest req, InlineKeyboardMarkup? inlineMarkup, ReplyMarkup? markup, bool disableNotification, CancellationToken ct) + { + var format = req.TextFormat ?? MessageFormat.Plain; + var parseMode = GetParseMode(format); + + var text = req.Text!; + if (text.Length > Capabilities.MaxMessageLength) + { + _logger.Log(LogLevel.Warn, $"Message too long ({text.Length}). Truncated to {Capabilities.MaxMessageLength}."); + text = text.Substring(0, Capabilities.MaxMessageLength); + } + + if (!string.IsNullOrWhiteSpace(req.MessageId)) + { + await _client.EditMessageText( + messageId: int.Parse(req.MessageId), + chatId: long.Parse(req.ChatId), + text: text, + parseMode: parseMode, + replyMarkup: inlineMarkup, + cancellationToken: ct + ); + + return req.MessageId; + } + else + { + if (inlineMarkup is not null) markup = inlineMarkup; + + var message = await _client.SendMessage( + chatId: long.Parse(req.ChatId), + text: text, + parseMode: parseMode, + replyMarkup: markup, + disableNotification: disableNotification, + cancellationToken: ct + ); + + return message.MessageId.ToString(); + } + } + /// public IAlbumBuilder CreateAlbumBuilder(PageContext ctx) => new TelegramAlbumBuilder(this, ctx, _logger, _client);