diff --git a/BotPages.Core/Abstractions/IMessengerAdapter.cs b/BotPages.Core/Abstractions/IMessengerAdapter.cs
index 08e74b3..b1a1b99 100644
--- a/BotPages.Core/Abstractions/IMessengerAdapter.cs
+++ b/BotPages.Core/Abstractions/IMessengerAdapter.cs
@@ -1,4 +1,5 @@
-using BotPages.Core.Messaging;
+using BotPages.Core.Context;
+using BotPages.Core.Messaging;
namespace BotPages.Core.Abstractions;
@@ -40,3 +41,17 @@ public interface IMessengerAdapter
///
Task OnLeaveAsync(PageContext ctx, CancellationToken ct);
}
+
+///
+/// Контракт конфигурации адаптера.
+///
+public interface IMessangerAdapterSetup : IMessengerAdapter
+{
+ ///
+ /// Запуск работы адаптера
+ ///
+ ///
+ ///
+ ///
+ Task StartAdapterAsync(Func onUpdate, CancellationToken ct);
+}
\ No newline at end of file
diff --git a/BotPages.Core/Abstractions/IMessengerAdapterFactory.cs b/BotPages.Core/Abstractions/IMessengerAdapterFactory.cs
index 055a46e..0a1e1da 100644
--- a/BotPages.Core/Abstractions/IMessengerAdapterFactory.cs
+++ b/BotPages.Core/Abstractions/IMessengerAdapterFactory.cs
@@ -4,16 +4,35 @@ namespace BotPages.Core.Abstractions;
///
/// Фабрика адаптеров мессенджеров.
-/// Используется для разрешения конкретного по типу мессенджера.
+/// Используется для разрешения конкретного по типу мессенджера.
///
public interface IMessengerAdapterFactory
{
+ ///
+ /// Список зарегистрированных адаптеров.
+ ///
+ Dictionary Adapters { get; }
+
+ ///
+ /// Зарегистрировать адаптер для указанного типа мессенджера.
+ ///
+ ///
+ /// Тип мессенджера (например, "Telegram", "Slack", "VK").
+ ///
+ ///
+ /// Экземпляр адаптера, реализующий .
+ ///
+ ///
+ /// Текущий экземпляр для цепочки вызовов.
+ ///
+ IMessengerAdapterFactory Register(string messengerType, IMessangerAdapterSetup adapter);
+
///
/// Получить адаптер для указанного мессенджера.
///
///
/// Тип мессенджера (например, "Telegram", "Slack", "VK").
- /// Значение должно совпадать с ..
+ /// Значение должно совпадать с .
///
///
/// Экземпляр , зарегистрированный для данного типа мессенджера.
diff --git a/BotPages.Core/Abstractions/MultiAdapterFactory.cs b/BotPages.Core/Abstractions/MultiAdapterFactory.cs
index 7ad8be3..4be6bfc 100644
--- a/BotPages.Core/Abstractions/MultiAdapterFactory.cs
+++ b/BotPages.Core/Abstractions/MultiAdapterFactory.cs
@@ -6,7 +6,12 @@
///
public sealed class MultiAdapterFactory : IMessengerAdapterFactory
{
- private readonly Dictionary _adapters = new(StringComparer.OrdinalIgnoreCase);
+ private readonly Dictionary _adapters = new(StringComparer.OrdinalIgnoreCase);
+
+ ///
+ /// Список зарегистрированных адаптеров.
+ ///
+ public Dictionary Adapters => _adapters;
///
/// Зарегистрировать адаптер для указанного типа мессенджера.
@@ -20,7 +25,7 @@ public sealed class MultiAdapterFactory : IMessengerAdapterFactory
///
/// Текущий экземпляр для цепочки вызовов.
///
- public MultiAdapterFactory Register(string messengerType, IMessengerAdapter adapter)
+ public IMessengerAdapterFactory Register(string messengerType, IMessangerAdapterSetup adapter)
{
_adapters[messengerType] = adapter;
return this;
diff --git a/BotPages.Core/BotPagesApp.cs b/BotPages.Core/BotPagesApp.cs
index 8618886..4e24099 100644
--- a/BotPages.Core/BotPagesApp.cs
+++ b/BotPages.Core/BotPagesApp.cs
@@ -19,16 +19,30 @@ public sealed class BotPagesApp
private readonly ILogger _logger;
private readonly NavigationService _navigation;
+ ///
+ /// Серсвис логирования.
+ ///
+ public ILogger Logger => _logger;
+
///
/// Создать приложение BotPages.
///
- public BotPagesApp(IMessengerAdapterFactory adapterFactory, IStateStorage state, ILogger logger)
+ public BotPagesApp(IStateStorage state, ILogger logger)
{
- _adapterFactory = adapterFactory;
_state = state;
_logger = logger;
_navigation = new NavigationService(_routes);
+ _adapterFactory = new MultiAdapterFactory();
+ }
+
+ ///
+ /// Добавить адаптер.
+ ///
+ public BotPagesApp AddAdapter(string messengerType, IMessangerAdapterSetup adapter)
+ {
+ _adapterFactory.Register(messengerType, adapter);
+ return this;
}
///
@@ -173,4 +187,17 @@ public sealed class BotPagesApp
///
private Page? ResolveCurrentPage(PageContext ctx)
=> _navigation.ResolveCurrentPage(ctx);
+
+ ///
+ /// Сборка и запуск приложения.
+ ///
+ ///
+ ///
+ public async Task Build(CancellationToken cancellationToken)
+ {
+ foreach (var adapter in _adapterFactory.Adapters)
+ {
+ await adapter.Value.StartAdapterAsync(update => HandleUpdateAsync(update, cancellationToken), cancellationToken);
+ }
+ }
}
\ No newline at end of file
diff --git a/BotPages.Core/Messaging/ButtonAttribute.cs b/BotPages.Core/Messaging/ButtonAttribute.cs
index b287c41..c2e70d3 100644
--- a/BotPages.Core/Messaging/ButtonAttribute.cs
+++ b/BotPages.Core/Messaging/ButtonAttribute.cs
@@ -25,6 +25,9 @@ public class ButtonAttribute : Attribute
public string? Value { get; }
}
+///
+/// Расширение для работы с кнопками.
+///
public static class ButtonExtensions
{
private static readonly Dictionary> _cacheName = new();
diff --git a/BotPages.Core/Storage/InMemoryStateStorage.cs b/BotPages.Core/Storage/InMemoryStateStorage.cs
index dc78040..8874c59 100644
--- a/BotPages.Core/Storage/InMemoryStateStorage.cs
+++ b/BotPages.Core/Storage/InMemoryStateStorage.cs
@@ -31,6 +31,7 @@ public sealed class InMemoryStateStorage : IStateStorage
public Task RemoveAsync(CompositeSessionKey session, string key, CancellationToken ct)
=> Task.FromResult(_store.TryGetValue(session, out var dict) ? dict.TryRemove(key, out _) : true);
+ ///
public Task ClearAsync(CompositeSessionKey session, CancellationToken ct)
=> Task.FromResult(_store.TryRemove(session, out _));
}
\ No newline at end of file
diff --git a/BotPages.Telegram/BotPagesAppExtension.cs b/BotPages.Telegram/BotPagesAppExtension.cs
new file mode 100644
index 0000000..ba54118
--- /dev/null
+++ b/BotPages.Telegram/BotPagesAppExtension.cs
@@ -0,0 +1,22 @@
+using BotPages.Core;
+
+namespace BotPages.Telegram;
+
+///
+/// Расширения для .
+///
+public static class BotPagesAppExtension
+{
+ ///
+ /// Добавление адаптера для телеграмм в
+ ///
+ ///
+ ///
+ ///
+ public static BotPagesApp AddTelegramAdapter(this BotPagesApp app, string token)
+ {
+ var telegram = new TelegramAdapter(app.Logger, token);
+ app.AddAdapter(telegram.MessagerType, telegram);
+ return app;
+ }
+}
diff --git a/BotPages.Telegram/TelegramAdapter.cs b/BotPages.Telegram/TelegramAdapter.cs
index 3e02733..3c3b94e 100644
--- a/BotPages.Telegram/TelegramAdapter.cs
+++ b/BotPages.Telegram/TelegramAdapter.cs
@@ -21,32 +21,44 @@ namespace BotPages.Telegram;
/// Адаптер для Telegram на базе Telegram.Bot.
/// Реализует отправку текста, кнопок, файлов, альбомов и прогресса.
///
-public sealed class TelegramAdapter : IMessengerAdapter
+public sealed class TelegramAdapter : IMessangerAdapterSetup
{
private readonly ILogger _logger;
private TelegramBotClient? _client;
+ private string _token;
+ private string _messagerType;
/// Создать адаптер Telegram.
- public TelegramAdapter(ILogger logger) => _logger = logger;
+ public TelegramAdapter(ILogger logger, string token)
+ {
+ _logger = logger;
+ _token = token;
+ _messagerType = "Telegram: " + Guid.NewGuid().ToString();
+ }
+
+ ///
+ ///Идентификатор мессенджера / адаптера
+ ///
+ public string MessagerType => _messagerType;
///
/// Запустить polling для приема обновлений от Telegram.
///
- public async Task StartPollingAsync(string token, Func onUpdate, CancellationToken ct)
+ public async Task StartAdapterAsync(Func onUpdate, CancellationToken ct)
{
- _client = new TelegramBotClient(token);
+ _client = new TelegramBotClient(_token);
_client.StartReceiving(
updateHandler: async (_, update, ct2) =>
{
- var mapped = TelegramUpdateMapper.Map(update, _client);
+ var mapped = TelegramUpdateMapper.Map(_messagerType, update, _client);
if (mapped is not null)
await onUpdate(mapped);
},
errorHandler: async (_, ex, ct2) =>
{
- _logger.Log(LogLevel.Warn, "Telegram error.", ex);
+ _logger.Log(LogLevel.Warn, $"{_messagerType} error.", ex);
await Task.CompletedTask;
},
@@ -55,7 +67,7 @@ public sealed class TelegramAdapter : IMessengerAdapter
var me = await _client.GetMe();
- _logger.Log(LogLevel.Info, $"Telegram started: @{me.Username}");
+ _logger.Log(LogLevel.Info, $"{_messagerType} started: @{me.Username}");
return;
}
@@ -67,7 +79,7 @@ public sealed class TelegramAdapter : IMessengerAdapter
{
if (_client is null)
{
- _logger.Log(LogLevel.Critical, "Telegram client is not initialized.");
+ _logger.Log(LogLevel.Critical, $"{_messagerType} client is not initialized.");
return;
}
@@ -137,7 +149,7 @@ public sealed class TelegramAdapter : IMessengerAdapter
{
if (_client is null)
{
- _logger.Log(LogLevel.Critical, "Telegram client is not initialized.");
+ _logger.Log(LogLevel.Critical, $"{_messagerType} client is not initialized.");
return;
}
@@ -192,7 +204,7 @@ public sealed class TelegramAdapter : IMessengerAdapter
{
if (_client is null)
{
- _logger.Log(LogLevel.Critical, "Telegram client is not initialized.");
+ _logger.Log(LogLevel.Critical, $"{_messagerType} client is not initialized.");
return null;
}
@@ -216,7 +228,7 @@ public sealed class TelegramAdapter : IMessengerAdapter
{
if (_client is null)
{
- _logger.Log(LogLevel.Critical, "Telegram client is not initialized.");
+ _logger.Log(LogLevel.Critical, $"{_messagerType} client is not initialized.");
return;
}
diff --git a/BotPages.Telegram/TelegramUpdateMapper.cs b/BotPages.Telegram/TelegramUpdateMapper.cs
index 0e73fff..f1dfabb 100644
--- a/BotPages.Telegram/TelegramUpdateMapper.cs
+++ b/BotPages.Telegram/TelegramUpdateMapper.cs
@@ -19,7 +19,7 @@ public static class TelegramUpdateMapper
///
/// Маппинг Telegram Update в UpdateContext BotPages.
///
- public static UpdateContext Map(Update update, TelegramBotClient client)
+ public static UpdateContext Map(string MessagerType, Update update, TelegramBotClient client)
{
var chat = update.Message?.Chat ?? update.CallbackQuery?.Message?.Chat;
var user = update.Message?.From ?? update.CallbackQuery?.From;
@@ -131,7 +131,7 @@ public static class TelegramUpdateMapper
return new UpdateContext
{
- MessengerType = "Telegram",
+ MessengerType = MessagerType,
User = userContext,
Chat = chatContext,
Text = text,
diff --git a/Demo/Models/Request.cs b/Demo/Models/Request.cs
index 7046050..85db492 100644
--- a/Demo/Models/Request.cs
+++ b/Demo/Models/Request.cs
@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Demo.Models
+namespace Demo.Models
{
internal class Request
{
diff --git a/Demo/Pages/DetailsPage.cs b/Demo/Pages/DetailsPage.cs
index 97d6b78..ca7307c 100644
--- a/Demo/Pages/DetailsPage.cs
+++ b/Demo/Pages/DetailsPage.cs
@@ -1,6 +1,5 @@
using BotPages.Core;
using BotPages.Core.Messaging;
-using System.Xml;
namespace Demo.Pages;
@@ -38,12 +37,13 @@ public sealed class DetailsPage : StatefullPage
break;
}
- case "back":
+ case "back":
{
await ctx.Navigation.GoToAsync(ctx, ct);
break;
}
- };
+ }
+ ;
}
public override async Task OnText(PageContext ctx, string text, CancellationToken ct)
diff --git a/Demo/Program.cs b/Demo/Program.cs
index f414227..6863801 100644
--- a/Demo/Program.cs
+++ b/Demo/Program.cs
@@ -1,5 +1,4 @@
using BotPages.Core;
-using BotPages.Core.Abstractions;
using BotPages.Core.Logging;
using BotPages.Core.Middleware;
using BotPages.Core.Storage;
@@ -17,22 +16,15 @@ namespace Demo
var logger = new ConsoleLogger();
var state = new InMemoryStateStorage();
+ using var cts = new CancellationTokenSource();
- var telegram = new TelegramAdapter(logger);
- var factory = new MultiAdapterFactory()
- .Register("Telegram", telegram);
-
- var app = new BotPagesApp(factory, state, logger)
+ var app = new BotPagesApp(state, logger)
.AddDefaultPage()
.MapCommand("/start")
.AddMiddleware(new ErrorHandlingMiddleware(logger))
- .AddMiddleware(new LoggingMiddleware(logger));
-
- using var cts = new CancellationTokenSource();
-
- await telegram.StartPollingAsync(token,
- update => app.HandleUpdateAsync(update, CancellationToken.None),
- cts.Token);
+ .AddMiddleware(new LoggingMiddleware(logger))
+ .AddTelegramAdapter(token)
+ .Build(cts.Token);
Console.ReadKey();
cts.Cancel();