From 4124e8c554b1fd8c74a74e894adce6e0a7c18c34 Mon Sep 17 00:00:00 2001 From: FrigaT Date: Wed, 24 Dec 2025 16:30:43 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D1=80=D1=8B=D0=B5=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Context/PageContextAdapterExtensions.cs | 62 ------------------- BotPages.Core/Messaging/MessageBuilder.cs | 30 +++++++-- BotPages.Telegram/MessageBuilderExtensions.cs | 26 +------- BotPages.Telegram/TelegramAdapter.cs | 59 ++---------------- BotPages.Telegram/TelegramAlbumBuilder.cs | 23 ++++++- Demo/Pages/FileSendPage.cs | 6 +- Demo/Program.cs | 10 ++- 7 files changed, 66 insertions(+), 150 deletions(-) diff --git a/BotPages.Core/Context/PageContextAdapterExtensions.cs b/BotPages.Core/Context/PageContextAdapterExtensions.cs index 58b2909..c316fb0 100644 --- a/BotPages.Core/Context/PageContextAdapterExtensions.cs +++ b/BotPages.Core/Context/PageContextAdapterExtensions.cs @@ -14,66 +14,4 @@ public static class PageContextAdapterExtensions /// public static Task SendAsync(this PageContext ctx, SendRequest request, CancellationToken ct = default) => ctx.Adapter.SendAsync(request, ct); - - /// - /// Удобная оболочка: отправить текстовое сообщение. - /// - public static Task SendTextAsync(this PageContext ctx, - string text, - MessageFormat format = MessageFormat.Plain, - IEnumerable>? inline = null, - IEnumerable>? reply = null, - string? messageId = null, - object? adapterOptions = null, - CancellationToken ct = default) - { - var bag = adapterOptions switch - { - null => null, - AdapterOptionsBag b => b, - _ => throw new ArgumentException("adapterOptions must be an AdapterOptionsBag or null. Use MessageBuilder extensions to set adapter options.", nameof(adapterOptions)) - }; - - return ctx.SendAsync(new SendRequest - { - ChatId = ctx.Update.Chat.Id, - Text = text, - TextFormat = format, - Inline = inline, - Reply = reply, - MessageId = messageId, - AdapterOptions = bag - }, ct); - } - - /// - /// Удобная оболочка: отправить файл. - /// - public static Task SendFileAsync(this PageContext ctx, - FileDescriptor file, - string? caption = null, - MessageFormat? captionFormat = null, - IEnumerable>? inline = null, - IEnumerable>? reply = null, - object? adapterOptions = null, - CancellationToken ct = default) - { - var bag = adapterOptions switch - { - null => null, - AdapterOptionsBag b => b, - _ => throw new ArgumentException("adapterOptions must be an AdapterOptionsBag or null. Use MessageBuilder extensions to set adapter options.", nameof(adapterOptions)) - }; - - return ctx.SendAsync(new SendRequest - { - ChatId = ctx.Update.Chat.Id, - File = file, - Caption = caption, - CaptionFormat = captionFormat, - Inline = inline, - Reply = reply, - AdapterOptions = bag - }, ct); - } } \ No newline at end of file diff --git a/BotPages.Core/Messaging/MessageBuilder.cs b/BotPages.Core/Messaging/MessageBuilder.cs index 5853daf..546b03d 100644 --- a/BotPages.Core/Messaging/MessageBuilder.cs +++ b/BotPages.Core/Messaging/MessageBuilder.cs @@ -25,10 +25,10 @@ public sealed class MessageBuilder /// /// Установить опции для конкретного адаптера. Ключ адаптера определяется адаптером (напр., "telegram"). /// - public MessageBuilder WithAdapterOption(string adapterKey, T options) + public MessageBuilder WithAdapterOption(string adapterType, T options) { if (_adapterOptions is null) _adapterOptions = new AdapterOptionsBag(); - _adapterOptions.Set(adapterKey, options); + _adapterOptions.Set(adapterType, options); return this; } @@ -141,13 +141,35 @@ public sealed class MessageBuilder // Текст if (!string.IsNullOrWhiteSpace(_text)) { - messageId = await _ctx.SendTextAsync(_text, _format, _inline, reply, _editMessageId, _adapterOptions, ct); + var req = new SendRequest + { + ChatId = _ctx.Update.Chat.Id, + Text = _text, + TextFormat = _format, + Inline = _inline, + Reply = reply, + MessageId = _editMessageId, + AdapterOptions = _adapterOptions + }; + + messageId = await _ctx.SendAsync(req, ct); } // Файлы foreach (var (file, caption, captionFormat) in _files) { - var res = await _ctx.SendFileAsync(file, caption, captionFormat, _inline, reply, _adapterOptions, ct); + var req = new SendRequest + { + ChatId = _ctx.Update.Chat.Id, + File = file, + Caption = caption, + CaptionFormat = captionFormat, + Inline = _inline, + Reply = reply, + AdapterOptions = _adapterOptions + }; + + var res = await _ctx.SendAsync(req, ct); // сохранить первый возвращённый id сообщения if (messageId is null && res is not null) messageId = res; } diff --git a/BotPages.Telegram/MessageBuilderExtensions.cs b/BotPages.Telegram/MessageBuilderExtensions.cs index ccd4209..0df0629 100644 --- a/BotPages.Telegram/MessageBuilderExtensions.cs +++ b/BotPages.Telegram/MessageBuilderExtensions.cs @@ -1,4 +1,3 @@ -using BotPages.Core.Abstractions; using BotPages.Core.Messaging; namespace BotPages.Telegram; @@ -15,29 +14,6 @@ public static class MessageBuilderExtensions /// public static MessageBuilder WithTelegramOptions(this MessageBuilder builder, TelegramOptions options) { - // Ensure bag exists - var bag = (builder as object) switch - { - MessageBuilder mb => GetAdapterBag(mb) ?? CreateAndSetAdapterBag(mb), - _ => null - }; - - bag?.Set("telegram", options); - return builder; - } - - // Reflection helpers to access private adapterOptions field on MessageBuilder - private static AdapterOptionsBag? GetAdapterBag(MessageBuilder builder) - { - var fi = typeof(MessageBuilder).GetField("_adapterOptions", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - return fi?.GetValue(builder) as AdapterOptionsBag; - } - - private static AdapterOptionsBag CreateAndSetAdapterBag(MessageBuilder builder) - { - var fi = typeof(MessageBuilder).GetField("_adapterOptions", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - var bag = new AdapterOptionsBag(); - fi?.SetValue(builder, bag); - return bag; + return builder.WithAdapterOption(TelegramAdapter.AdapterType, options); } } diff --git a/BotPages.Telegram/TelegramAdapter.cs b/BotPages.Telegram/TelegramAdapter.cs index 5ad0e5c..63ee7c6 100644 --- a/BotPages.Telegram/TelegramAdapter.cs +++ b/BotPages.Telegram/TelegramAdapter.cs @@ -2,7 +2,6 @@ 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; @@ -23,6 +22,8 @@ namespace BotPages.Telegram; /// public sealed class TelegramAdapter : IMessengerAdapterSetup { + internal static readonly string AdapterType = typeof(TelegramAdapter).FullName; + private readonly ILogger _logger; private TelegramBotClient? _client; private string _token; @@ -104,9 +105,9 @@ public sealed class TelegramAdapter : IMessengerAdapterSetup return null; } - // determine adapter-specific options (TelegramOptions) from bag or use adapter default + // Определите параметры адаптера (TelegramOptions) из папки или используйте параметры адаптера по умолчанию. TelegramOptions telegramOptions = _options; - if (req.AdapterOptions is AdapterOptionsBag bag && bag.TryGet("telegram", out TelegramOptions? opt) && opt is not null) + if (req.AdapterOptions is AdapterOptionsBag bag && bag.TryGet(AdapterType, out TelegramOptions? opt) && opt is not null) { telegramOptions = opt; } @@ -137,7 +138,7 @@ public sealed class TelegramAdapter : IMessengerAdapterSetup } } - // If there is a file — send file + // Если есть файл - отправить файл if (req.File is not null) { var file = req.File; @@ -193,7 +194,7 @@ public sealed class TelegramAdapter : IMessengerAdapterSetup return sentMessage?.MessageId.ToString(); } - // Otherwise treat as text + // Иначе - отправить текст if (!string.IsNullOrWhiteSpace(req.Text)) { var format = req.TextFormat ?? MessageFormat.Plain; @@ -254,54 +255,6 @@ public sealed class TelegramAdapter : IMessengerAdapterSetup return null; } - /// - public Task SendTextAsync(string chatId, string text, - MessageFormat format = MessageFormat.Plain, - IEnumerable>? inline = null, - IEnumerable>? reply = null, - string? messageId = null, - object? adapterOptions = null, - CancellationToken ct = default) - { - var req = new SendRequest - { - ChatId = chatId, - Text = text, - TextFormat = format, - Inline = inline, - Reply = reply, - MessageId = messageId, - AdapterOptions = adapterOptions as AdapterOptionsBag - }; - - return SendAsync(req, ct); - } - - /// - public Task SendFileAsync(string chatId, - FileDescriptor file, - string? caption = null, - MessageFormat? captionFormat = null, - IEnumerable>? inline = null, - IEnumerable>? reply = null, - object? adapterOptions = null, - CancellationToken ct = default - ) - { - var req = new SendRequest - { - ChatId = chatId, - File = file, - Caption = caption, - CaptionFormat = captionFormat, - Inline = inline, - Reply = reply, - AdapterOptions = adapterOptions as AdapterOptionsBag - }; - - return SendAsync(req, ct); - } - /// public IAlbumBuilder CreateAlbumBuilder(PageContext ctx) => new TelegramAlbumBuilder(this, ctx, _logger, _client); diff --git a/BotPages.Telegram/TelegramAlbumBuilder.cs b/BotPages.Telegram/TelegramAlbumBuilder.cs index 3d2192b..3f6a980 100644 --- a/BotPages.Telegram/TelegramAlbumBuilder.cs +++ b/BotPages.Telegram/TelegramAlbumBuilder.cs @@ -53,7 +53,17 @@ public sealed class TelegramAlbumBuilder : IAlbumBuilder { _logger.Log(LogLevel.Warn, "Albums not supported. Degraded to sequential sends."); foreach (var (file, caption, captionFormat) in _items) - await _adapter.SendFileAsync(_ctx.Update.Chat.Id, file, caption, captionFormat, ct: ct); + { + var req = new SendRequest + { + ChatId = _ctx.Update.Chat.Id, + File = file, + Caption = caption, + CaptionFormat = captionFormat + }; + + await _ctx.SendAsync(req, ct); + } return; } @@ -95,7 +105,16 @@ public sealed class TelegramAlbumBuilder : IAlbumBuilder { // Telegram не поддерживает document в альбомах — деградация _logger.Log(LogLevel.Warn, $"Document '{file.Kind}' in album not supported. Sending document separately."); - await _adapter.SendFileAsync(_ctx.Update.Chat.Id, file, caption, captionFormat, ct: ct); + + var req = new SendRequest + { + ChatId = _ctx.Update.Chat.Id, + File = file, + Caption = caption, + CaptionFormat = captionFormat + }; + + await _ctx.SendAsync(req, ct); } } diff --git a/Demo/Pages/FileSendPage.cs b/Demo/Pages/FileSendPage.cs index 0c76c4a..879b122 100644 --- a/Demo/Pages/FileSendPage.cs +++ b/Demo/Pages/FileSendPage.cs @@ -8,7 +8,7 @@ namespace Demo.Pages; [Route("FileSend")] public sealed class FileSendPage : SingletonPage { - public override Task OnEnter(PageContext ctx, CancellationToken ct) + public override async Task OnEnter(PageContext ctx, CancellationToken ct) { var content = "Hello from BotPages! This file is generated on the fly."; var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(content)); @@ -23,9 +23,11 @@ public sealed class FileSendPage : SingletonPage GetStreamAsync = _ => Task.FromResult(stream) }; - return new MessageBuilder(ctx) + await new MessageBuilder(ctx) .Text("Вот пример отправки нового файла 📎", MessageFormat.Markdown) .File(demoFile, "Демонстрационный файл") .SendAsync(ct); + + await ctx.GoToHomeAsync(ct); } } diff --git a/Demo/Program.cs b/Demo/Program.cs index 355fa7d..fb95aae 100644 --- a/Demo/Program.cs +++ b/Demo/Program.cs @@ -26,7 +26,13 @@ namespace Demo { if (args is null || !args.TryGetValue("page", out var pageName)) { - await ctx.SendTextAsync("Не указана страница для открытия.", ct: ct); + var req = new BotPages.Core.Abstractions.SendRequest + { + ChatId = ctx.Update.Chat.Id, + Text = "Не указана страница для открытия." + }; + + await ctx.SendAsync(req, ct: ct); return; } @@ -37,7 +43,7 @@ namespace Demo var app = new BotPagesApp(state, logger) .AddDefaultPage() .MapCommand("/start", true, "Главная") - .MapCommand("/open {page}", openHandler, true, "открыть станицу /open {page}") + .MapCommand("/open {page}", openHandler, true, "открыть страницу /open {page}") .MapCommand(DetailsPage.Command, DetailsPage.CommandHandler, true, DetailsPage.CommandDescription) .AutoMapRoute() .AddMiddleware(new ErrorHandlingMiddleware(logger))