From 5dc071c7509364f190001673f8a597e3919001c4 Mon Sep 17 00:00:00 2001 From: FrigaT Date: Fri, 6 Feb 2026 07:45:19 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20?= =?UTF-8?q?=D0=BD=D0=B5=D1=81=D0=BA=D0=BE=D0=BB=D1=8C=D0=BA=D0=B8=D0=BC?= =?UTF-8?q?=D0=B8=20=D0=B0=D0=B4=D0=B0=D0=BF=D1=82=D0=B5=D1=80=D0=B0=D0=BC?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Abstractions/CompositeSessionKey.cs | 2 +- .../Abstractions/IMessengerAdapter.cs | 16 +++ .../Abstractions/MessengerAdapterBase.cs | 102 ------------------ .../Abstractions/MultiAdapterFactory.cs | 22 +--- BotPages.Core/BotPagesApp.cs | 2 +- BotPages.Core/Context/PageContext.cs | 4 +- BotPages.Core/Context/UpdateContext.cs | 7 +- BotPages.Core/Middleware/LoggingMiddleware.cs | 2 +- BotPages.Telegram/TelegramAdapter.cs | 84 +++++++++------ BotPages.Telegram/TelegramUpdateMapper.cs | 3 +- 10 files changed, 79 insertions(+), 165 deletions(-) delete mode 100644 BotPages.Core/Abstractions/MessengerAdapterBase.cs diff --git a/BotPages.Core/Abstractions/CompositeSessionKey.cs b/BotPages.Core/Abstractions/CompositeSessionKey.cs index 9809b31..69c4ca6 100644 --- a/BotPages.Core/Abstractions/CompositeSessionKey.cs +++ b/BotPages.Core/Abstractions/CompositeSessionKey.cs @@ -13,7 +13,7 @@ public readonly record struct CompositeSessionKey(string AdapterId, string ChatI public static CompositeSessionKey FromUpdate(UpdateContext update) { return new CompositeSessionKey( - update.AdapterId, + update.Adapter.Id, update.Chat.Id, update.User.Id); } diff --git a/BotPages.Core/Abstractions/IMessengerAdapter.cs b/BotPages.Core/Abstractions/IMessengerAdapter.cs index e92f31b..3b46212 100644 --- a/BotPages.Core/Abstractions/IMessengerAdapter.cs +++ b/BotPages.Core/Abstractions/IMessengerAdapter.cs @@ -9,6 +9,16 @@ namespace BotPages.Core.Abstractions; /// public interface IMessengerAdapter { + /// + /// Уникальный идентификатор адаптера. + /// + string Id { get; } + + /// + /// Тип адаптера (Telegram, VK, WhatsApp и т.д.). + /// + string Type { get; } + /// /// Доступные возможности мессенджера. /// @@ -87,10 +97,16 @@ public interface IMessengerAdapter /// public interface IMessengerAdapterSetup : IMessengerAdapter { + /// + /// Внутренний метод для установки ID адаптера. + /// + void SetAdapterId(string adapterId); + /// /// Запуск работы адаптера /// /// + /// /// /// Task StartAdapterAsync(Func onUpdate, List commands, CancellationToken ct); diff --git a/BotPages.Core/Abstractions/MessengerAdapterBase.cs b/BotPages.Core/Abstractions/MessengerAdapterBase.cs deleted file mode 100644 index 2240527..0000000 --- a/BotPages.Core/Abstractions/MessengerAdapterBase.cs +++ /dev/null @@ -1,102 +0,0 @@ -using BotPages.Core.Context; -using BotPages.Core.Messaging; - -namespace BotPages.Core.Abstractions; - -/// -/// Базовый класс для адаптеров мессенджеров. -/// -public abstract class MessengerAdapterBase : IMessengerAdapterSetup -{ - /// - /// Уникальный идентификатор адаптера. - /// - public string AdapterId { get; internal set; } = string.Empty; - - /// - /// Тип адаптера (Telegram, VK, WhatsApp и т.д.). - /// - public abstract string AdapterType { get; } - - /// - /// Название адаптера для отображения. - /// - public string DisplayName { get; set; } = string.Empty; - - /// - /// Доступные возможности мессенджера. - /// - public abstract Capabilities Capabilities { get; } - - /// - /// Универсальный метод отправки с использованием общего описания запроса. - /// - public abstract Task SendAsync(SendRequest request, CancellationToken ct = default); - - /// - /// Универсальный метод удаления сообщения. - /// - public abstract Task DeleteAsync(string chatId, string messageId, CancellationToken ct = default); - - /// - /// Удалить несколько сообщений за раз. - /// - public abstract Task DeleteMultipleAsync(string chatId, IEnumerable messageIds, CancellationToken ct = default); - - /// - /// Редактировать только текст сообщения. - /// - public abstract Task EditTextAsync(string chatId, string messageId, string text, - MessageFormat? format = null, CancellationToken ct = default); - - /// - /// Редактировать только клавиатуру сообщения. - /// - public abstract Task EditButtonsAsync(string chatId, string messageId, - IEnumerable>? inlineButtons = null, - CancellationToken ct = default); - - /// - /// Закрепить сообщение в чате. - /// - public abstract Task PinMessageAsync(string chatId, string messageId, bool disableNotification = false, - CancellationToken ct = default); - - /// - /// Открепить сообщение в чате. - /// - public abstract Task UnpinMessageAsync(string chatId, string messageId, CancellationToken ct = default); - - /// - /// Получить информацию о сообщении. - /// - public abstract Task GetMessageInfoAsync(string chatId, string messageId, CancellationToken ct = default); - - /// - /// Переслать сообщение. - /// - public abstract Task ForwardMessageAsync(string fromChatId, string messageId, string toChatId, - bool disableNotification = false, CancellationToken ct = default); - - /// - /// Копировать сообщение с возможностью редактирования. - /// - public abstract Task CopyMessageAsync(string fromChatId, string messageId, string toChatId, - string? caption = null, MessageFormat? captionFormat = null, - bool disableNotification = false, CancellationToken ct = default); - - /// - /// Создать билдер альбома для отправки медиагруппы. - /// - public abstract IAlbumBuilder CreateAlbumBuilder(PageContext ctx); - - /// - /// Вызывается при выходе со страницы. - /// - public abstract Task OnLeaveAsync(PageContext ctx, CancellationToken ct); - - /// - /// Запуск работы адаптера. - /// - public abstract Task StartAdapterAsync(Func onUpdate, List commands, CancellationToken ct); -} \ No newline at end of file diff --git a/BotPages.Core/Abstractions/MultiAdapterFactory.cs b/BotPages.Core/Abstractions/MultiAdapterFactory.cs index ec3ac10..cd4cfca 100644 --- a/BotPages.Core/Abstractions/MultiAdapterFactory.cs +++ b/BotPages.Core/Abstractions/MultiAdapterFactory.cs @@ -26,16 +26,13 @@ public sealed class MultiAdapterFactory : IMessengerAdapterFactory throw new ArgumentException($"Adapter with ID '{adapterId}' is already registered", nameof(adapterId)); // Устанавливаем идентификатор в адаптер - if (adapter is MessengerAdapterBase adapterBase) - { - adapterBase.AdapterId = adapterId; - } + adapter.SetAdapterId(adapterId); _allAdapters.Add(adapter); _adaptersById[adapterId] = adapter; // Группируем по типу - var adapterType = GetAdapterType(adapter); + var adapterType = adapter.Type; if (!_adaptersByType.TryGetValue(adapterType, out var typeList)) { typeList = new List(); @@ -106,7 +103,7 @@ public sealed class MultiAdapterFactory : IMessengerAdapterFactory _allAdapters.Remove(adapter); _adaptersById.Remove(adapterId); - var adapterType = GetAdapterType(adapter); + var adapterType = adapter.Type; if (_adaptersByType.TryGetValue(adapterType, out var typeList)) { typeList.Remove(adapter); @@ -122,20 +119,9 @@ public sealed class MultiAdapterFactory : IMessengerAdapterFactory return false; } - private static string GetAdapterType(IMessengerAdapter adapter) - { - if (adapter is MessengerAdapterBase adapterBase) - { - return adapterBase.AdapterType; - } - - // Для обратной совместимости - return adapter.GetType().Name.Replace("Adapter", ""); - } - private static string GenerateAdapterId(IMessengerAdapter adapter) { - var adapterType = GetAdapterType(adapter); + var adapterType = adapter.Type; var guid = Guid.NewGuid().ToString("N").Substring(0, 8); return $"{adapterType}_{guid}".ToLowerInvariant(); } diff --git a/BotPages.Core/BotPagesApp.cs b/BotPages.Core/BotPagesApp.cs index 069bf3b..ce3bcc9 100644 --- a/BotPages.Core/BotPagesApp.cs +++ b/BotPages.Core/BotPagesApp.cs @@ -215,7 +215,7 @@ public sealed class BotPagesApp SessionKey = sessionKey, StateStorage = _state, Navigation = _navigation, - Adapter = _adapterFactory.Resolve(update.AdapterId), + Adapter = _adapterFactory.Resolve(update.Adapter.Id), AdapterFactory = _adapterFactory, }; diff --git a/BotPages.Core/Context/PageContext.cs b/BotPages.Core/Context/PageContext.cs index f00ad13..0ccf417 100644 --- a/BotPages.Core/Context/PageContext.cs +++ b/BotPages.Core/Context/PageContext.cs @@ -52,12 +52,12 @@ public sealed class PageContext /// /// Получить текущий тип адаптера. /// - public string CurrentAdapterType => Update.AdapterType; + public string CurrentAdapterType => Update.Adapter.Type; /// /// Получить текущий ID адаптера. /// - public string CurrentAdapterId => Update.AdapterId; + public string CurrentAdapterId => Update.Adapter.Id; /// /// Получить все адаптеры. diff --git a/BotPages.Core/Context/UpdateContext.cs b/BotPages.Core/Context/UpdateContext.cs index a8f9014..8da32bf 100644 --- a/BotPages.Core/Context/UpdateContext.cs +++ b/BotPages.Core/Context/UpdateContext.cs @@ -8,11 +8,8 @@ namespace BotPages.Core.Context; /// public sealed class UpdateContext { - /// Идентификатор адаптера, от которого пришло обновление. - public required string AdapterId { get; init; } - - /// Тип адаптера (определяется адаптером). - public required string AdapterType { get; init; } + /// Адаптер, от которого пришло обновление. + public required IMessengerAdapter Adapter { get; init; } /// /// Данные пользователя, от которого пришло обновление. diff --git a/BotPages.Core/Middleware/LoggingMiddleware.cs b/BotPages.Core/Middleware/LoggingMiddleware.cs index a01cb03..0722cf8 100644 --- a/BotPages.Core/Middleware/LoggingMiddleware.cs +++ b/BotPages.Core/Middleware/LoggingMiddleware.cs @@ -17,7 +17,7 @@ public sealed class LoggingMiddleware : IPageMiddleware public async Task InvokeAsync(PageContext ctx, Func next, CancellationToken ct) { // Логируем базовую информацию - _logger.Log(LogLevel.Info, $"Update from {ctx.Update.AdapterId} | Chat: {ctx.Update.Chat.Id} | User: {ctx.Update.User.Id}"); + _logger.Log(LogLevel.Info, $"Update from {ctx.Update.Adapter.Id} | Chat: {ctx.Update.Chat.Id} | User: {ctx.Update.User.Id}"); // Логируем текст, кнопки, файлы if (ctx.Update.Text is not null) diff --git a/BotPages.Telegram/TelegramAdapter.cs b/BotPages.Telegram/TelegramAdapter.cs index ea5ece3..833d7fa 100644 --- a/BotPages.Telegram/TelegramAdapter.cs +++ b/BotPages.Telegram/TelegramAdapter.cs @@ -20,7 +20,7 @@ namespace BotPages.Telegram; /// Адаптер для Telegram на базе Telegram.Bot. /// Реализует отправку текста, кнопок, файлов, альбомов и прогресса. /// -public sealed class TelegramAdapter : MessengerAdapterBase +public sealed class TelegramAdapter : IMessengerAdapterSetup { private readonly ILogger _logger; private TelegramBotClient? _client; @@ -43,27 +43,33 @@ public sealed class TelegramAdapter : MessengerAdapterBase _token = token; _options = options ?? new TelegramOptions(); - // Устанавливаем имя для отображения - DisplayName = $"Telegram Bot"; + Id = Guid.NewGuid().ToString(); + DisplayName = $"Telegram {Id}"; } - /// Тип адаптера. - public override string AdapterType => "Telegram"; + /// + public string Type => "Telegram"; + + /// + public string Id { get; set; } /// - /// Идентификатор мессенджера / адаптера + /// Получение отображаемого имени бота (например, "Telegram: @mybot"). Доступно после запуска адаптера. /// - public string MessengerType => "Telegram"; + public string DisplayName { get; private set; } + + /// + public void SetAdapterId(string adapterId) => Id = adapterId; /// /// Доступные возможности адаптера. /// - public override Capabilities Capabilities => _capabilities; + public Capabilities Capabilities => _capabilities; /// /// Запустить polling для приема обновлений от Telegram. /// - public override async Task StartAdapterAsync(Func onUpdate, List commands, CancellationToken ct) + public async Task StartAdapterAsync(Func onUpdate, List commands, CancellationToken ct) { _client = new TelegramBotClient(_token); @@ -81,7 +87,7 @@ public sealed class TelegramAdapter : MessengerAdapterBase errorHandler: async (_, ex, ct2) => { - _logger.Log(LogLevel.Warn, $"{AdapterType} ({AdapterId}) error.", ex); + _logger.Log(LogLevel.Warn, $"{Type} ({Id}) error.", ex); await Task.CompletedTask; }, @@ -92,7 +98,7 @@ public sealed class TelegramAdapter : MessengerAdapterBase var me = await _client.GetMe(); DisplayName = $"Telegram: @{me.Username}"; - _logger.Log(LogLevel.Info, $"{AdapterType} ({AdapterId}) started: {DisplayName}"); + _logger.Log(LogLevel.Info, $"{Type} ({Id}) started: {DisplayName}"); return; } @@ -101,17 +107,17 @@ public sealed class TelegramAdapter : MessengerAdapterBase /// Универсальный внутренний метод отправки — определяет, нужно ли отправлять текст или файл по параметрам. /// Возвращает id сообщения (или null). /// - public override async Task SendAsync(SendRequest req, CancellationToken ct) + public async Task SendAsync(SendRequest req, CancellationToken ct) { if (_client is null) { - _logger.Log(LogLevel.Critical, $"{MessengerType} client is not initialized."); + _logger.Log(LogLevel.Critical, $"{Type} client is not initialized."); return null; } // Определите параметры адаптера (TelegramOptions) из папки или используйте параметры адаптера по умолчанию. TelegramOptions telegramOptions = _options; - if (req.AdapterOptions is AdapterOptionsBag bag && bag.TryGet(AdapterType, out TelegramOptions? opt) && opt is not null) + if (req.AdapterOptions is AdapterOptionsBag bag && bag.TryGet(Type, out TelegramOptions? opt) && opt is not null) { telegramOptions = opt; } @@ -150,11 +156,11 @@ public sealed class TelegramAdapter : MessengerAdapterBase } /// - public override Task DeleteAsync(string chatId, string messageId, CancellationToken ct = default) + public Task DeleteAsync(string chatId, string messageId, CancellationToken ct = default) { if (_client is null) { - _logger.Log(LogLevel.Critical, $"{MessengerType} client is not initialized."); + _logger.Log(LogLevel.Critical, $"{Type} client is not initialized."); return Task.CompletedTask; } @@ -162,11 +168,11 @@ public sealed class TelegramAdapter : MessengerAdapterBase } /// - public override async Task DeleteMultipleAsync(string chatId, IEnumerable messageIds, CancellationToken ct = default) + public async Task DeleteMultipleAsync(string chatId, IEnumerable messageIds, CancellationToken ct = default) { if (_client is null) { - _logger.Log(LogLevel.Critical, $"{MessengerType} client is not initialized."); + _logger.Log(LogLevel.Critical, $"{Type} client is not initialized."); return false; } @@ -187,12 +193,12 @@ public sealed class TelegramAdapter : MessengerAdapterBase } /// - public override async Task EditTextAsync(string chatId, string messageId, string text, + public async Task EditTextAsync(string chatId, string messageId, string text, MessageFormat? format = null, CancellationToken ct = default) { if (_client is null) { - _logger.Log(LogLevel.Critical, $"{MessengerType} client is not initialized."); + _logger.Log(LogLevel.Critical, $"{Type} client is not initialized."); return null; } @@ -216,12 +222,12 @@ public sealed class TelegramAdapter : MessengerAdapterBase } /// - public override async Task EditButtonsAsync(string chatId, string messageId, + public async Task EditButtonsAsync(string chatId, string messageId, IEnumerable>? inlineButtons = null, CancellationToken ct = default) { if (_client is null) { - _logger.Log(LogLevel.Critical, $"{MessengerType} client is not initialized."); + _logger.Log(LogLevel.Critical, $"{Type} client is not initialized."); return null; } @@ -244,12 +250,12 @@ public sealed class TelegramAdapter : MessengerAdapterBase } /// - public override async Task PinMessageAsync(string chatId, string messageId, bool disableNotification = false, + public async Task PinMessageAsync(string chatId, string messageId, bool disableNotification = false, CancellationToken ct = default) { if (_client is null) { - _logger.Log(LogLevel.Critical, $"{MessengerType} client is not initialized."); + _logger.Log(LogLevel.Critical, $"{Type} client is not initialized."); return false; } @@ -271,11 +277,11 @@ public sealed class TelegramAdapter : MessengerAdapterBase } /// - public override async Task UnpinMessageAsync(string chatId, string messageId, CancellationToken ct = default) + public async Task UnpinMessageAsync(string chatId, string messageId, CancellationToken ct = default) { if (_client is null) { - _logger.Log(LogLevel.Critical, $"{MessengerType} client is not initialized."); + _logger.Log(LogLevel.Critical, $"{Type} client is not initialized."); return false; } @@ -296,18 +302,18 @@ public sealed class TelegramAdapter : MessengerAdapterBase } /// - public override async Task GetMessageInfoAsync(string chatId, string messageId, CancellationToken ct = default) + public async Task GetMessageInfoAsync(string chatId, string messageId, CancellationToken ct = default) { throw new NotImplementedException(); } /// - public override async Task ForwardMessageAsync(string fromChatId, string messageId, string toChatId, + public async Task ForwardMessageAsync(string fromChatId, string messageId, string toChatId, bool disableNotification = false, CancellationToken ct = default) { if (_client is null) { - _logger.Log(LogLevel.Critical, $"{MessengerType} client is not initialized."); + _logger.Log(LogLevel.Critical, $"{Type} client is not initialized."); return null; } @@ -331,13 +337,13 @@ public sealed class TelegramAdapter : MessengerAdapterBase } /// - public override async Task CopyMessageAsync(string fromChatId, string messageId, string toChatId, + public async Task CopyMessageAsync(string fromChatId, string messageId, string toChatId, string? caption = null, MessageFormat? captionFormat = null, bool disableNotification = false, CancellationToken ct = default) { if (_client is null) { - _logger.Log(LogLevel.Critical, $"{MessengerType} client is not initialized."); + _logger.Log(LogLevel.Critical, $"{Type} client is not initialized."); return null; } @@ -427,6 +433,12 @@ public sealed class TelegramAdapter : MessengerAdapterBase bool disableNotification, string? caption, MessageFormat? captionFormat, int? replyToMessageId, bool protectContent, CancellationToken ct) { + if (_client is null) + { + _logger.Log(LogLevel.Critical, $"{Type} client is not initialized."); + return null; + } + var inputFile = await CreateInputFileAsync(file, ct); var parseMode = GetParseMode(captionFormat); @@ -482,6 +494,12 @@ public sealed class TelegramAdapter : MessengerAdapterBase ReplyMarkup? markup, bool disableNotification, int? replyToMessageId, bool disableWebPagePreview, bool protectContent, CancellationToken ct) { + if (_client is null) + { + _logger.Log(LogLevel.Critical, $"{Type} client is not initialized."); + return null; + } + var format = req.TextFormat ?? MessageFormat.Plain; var parseMode = GetParseMode(format); @@ -533,8 +551,8 @@ public sealed class TelegramAdapter : MessengerAdapterBase } /// - public override IAlbumBuilder CreateAlbumBuilder(PageContext ctx) => new TelegramAlbumBuilder(this, ctx, _logger, _client); + public IAlbumBuilder CreateAlbumBuilder(PageContext ctx) => new TelegramAlbumBuilder(this, ctx, _logger, _client); /// - public override Task OnLeaveAsync(PageContext ctx, CancellationToken ct) => Task.CompletedTask; + public Task OnLeaveAsync(PageContext ctx, CancellationToken ct) => Task.CompletedTask; } \ No newline at end of file diff --git a/BotPages.Telegram/TelegramUpdateMapper.cs b/BotPages.Telegram/TelegramUpdateMapper.cs index c17ae65..49bb787 100644 --- a/BotPages.Telegram/TelegramUpdateMapper.cs +++ b/BotPages.Telegram/TelegramUpdateMapper.cs @@ -182,8 +182,7 @@ public static class TelegramUpdateMapper return new UpdateContext { - AdapterId = adapter.AdapterId, - AdapterType = adapter.AdapterType, + Adapter = adapter, User = userContext, Chat = chatContext, Text = text,