* Renamed "concurrency" parameters in handlers attributes to more relevant and intuitive "importance" parameter
* Added debug logging helper that use default Debug tracer to trace filter, providers, routers and pool execution * Added debug logging for failing during handlers resolving, filters * Added new "CreateEmptyBuilder" methods for TelegramBotHost class * Addede ability to name handlers using "DisplayNameAttribute". This name is writed to descriptor's "DisplayString" property * Fixed missing summaries inside Hosting library
This commit is contained in:
@@ -6,11 +6,12 @@ using Telegrator.Polling;
|
|||||||
|
|
||||||
namespace Telegrator.Hosting.Polling
|
namespace Telegrator.Hosting.Polling
|
||||||
{
|
{
|
||||||
public class HostUpdateHandlersPool(IOptions<TelegramBotOptions> options, ILogger<HostUpdateHandlersPool> logger)
|
/// <inheritdoc/>
|
||||||
: UpdateHandlersPool(options.Value, options.Value.GlobalCancellationToken)
|
public class HostUpdateHandlersPool(IOptions<TelegramBotOptions> options, ILogger<HostUpdateHandlersPool> logger) : UpdateHandlersPool(options.Value, options.Value.GlobalCancellationToken)
|
||||||
{
|
{
|
||||||
private readonly ILogger<HostUpdateHandlersPool> _logger = logger;
|
private readonly ILogger<HostUpdateHandlersPool> _logger = logger;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
protected override async Task ExecuteHandlerWrapper(DescribedHandlerInfo enqueuedHandler)
|
protected override async Task ExecuteHandlerWrapper(DescribedHandlerInfo enqueuedHandler)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Handler \"{0}\" has entered execution pool", enqueuedHandler.DisplayString);
|
_logger.LogInformation("Handler \"{0}\" has entered execution pool", enqueuedHandler.DisplayString);
|
||||||
|
|||||||
@@ -10,23 +10,38 @@ using Telegrator.Polling;
|
|||||||
|
|
||||||
namespace Telegrator.Hosting.Polling
|
namespace Telegrator.Hosting.Polling
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
public class HostUpdateRouter : UpdateRouter
|
public class HostUpdateRouter : UpdateRouter
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="ILogger"/> of this router
|
||||||
|
/// </summary>
|
||||||
protected readonly ILogger<HostUpdateRouter> Logger;
|
protected readonly ILogger<HostUpdateRouter> Logger;
|
||||||
|
|
||||||
public HostUpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, IOptions<TelegramBotOptions> options, IUpdateHandlersPool handlersPool, ILogger<HostUpdateRouter> logger)
|
// Ehat a mess :/
|
||||||
: base(handlersProvider, awaitingProvider, options.Value, handlersPool)
|
/// <inheritdoc/>
|
||||||
|
public HostUpdateRouter(
|
||||||
|
IHandlersProvider handlersProvider,
|
||||||
|
IAwaitingProvider awaitingProvider,
|
||||||
|
IOptions<TelegramBotOptions> options,
|
||||||
|
IUpdateHandlersPool handlersPool,
|
||||||
|
ILogger<HostUpdateRouter> logger) : base(handlersProvider, awaitingProvider, options.Value, handlersPool)
|
||||||
{
|
{
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
ExceptionHandler = new HostExceptionHandler(logger);
|
ExceptionHandler = new HostExceptionHandler(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
|
public override Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Received update of type \"{type}\"", update.Type);
|
Logger.LogInformation("Received update of type \"{type}\"", update.Type);
|
||||||
return base.HandleUpdateAsync(botClient, update, cancellationToken);
|
return base.HandleUpdateAsync(botClient, update, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default exception handler of this router
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger"></param>
|
||||||
private class HostExceptionHandler(ILogger<HostUpdateRouter> logger) : IRouterExceptionHandler
|
private class HostExceptionHandler(ILogger<HostUpdateRouter> logger) : IRouterExceptionHandler
|
||||||
{
|
{
|
||||||
public void HandleException(ITelegramBotClient botClient, Exception exception, HandleErrorSource source, CancellationToken cancellationToken)
|
public void HandleException(ITelegramBotClient botClient, Exception exception, HandleErrorSource source, CancellationToken cancellationToken)
|
||||||
@@ -34,7 +49,7 @@ namespace Telegrator.Hosting.Polling
|
|||||||
if (exception is HandlerFaultedException handlerFaultedException)
|
if (exception is HandlerFaultedException handlerFaultedException)
|
||||||
{
|
{
|
||||||
logger.LogError("\"{handler}\" handler's execution was faulted :\n{exception}",
|
logger.LogError("\"{handler}\" handler's execution was faulted :\n{exception}",
|
||||||
handlerFaultedException.HandlerInfo.DisplayString,
|
handlerFaultedException.HandlerInfo.ToString(),
|
||||||
handlerFaultedException.InnerException?.ToString() ?? "No inner exception");
|
handlerFaultedException.InnerException?.ToString() ?? "No inner exception");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,17 +9,26 @@ using Telegrator.Polling;
|
|||||||
|
|
||||||
namespace Telegrator.Hosting.Polling
|
namespace Telegrator.Hosting.Polling
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Service for receiving updates for Hosted telegram bots
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="botHost"></param>
|
||||||
|
/// <param name="botClient"></param>
|
||||||
|
/// <param name="updateRouter"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <param name="logger"></param>
|
||||||
public class HostedUpdateReceiver(ITelegramBotHost botHost, ITelegramBotClient botClient, IUpdateRouter updateRouter, IOptions<ReceiverOptions> options, ILogger<HostedUpdateReceiver> logger) : BackgroundService
|
public class HostedUpdateReceiver(ITelegramBotHost botHost, ITelegramBotClient botClient, IUpdateRouter updateRouter, IOptions<ReceiverOptions> options, ILogger<HostedUpdateReceiver> logger) : BackgroundService
|
||||||
{
|
{
|
||||||
private readonly ReceiverOptions ReceiverOptions = options.Value;
|
private readonly ReceiverOptions _receiverOptions = options.Value;
|
||||||
private readonly IUpdateRouter UpdateRouter = updateRouter;
|
private readonly IUpdateRouter _updateRouter = updateRouter;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
logger.LogInformation("Starting receiving updates via long-polling");
|
logger.LogInformation("Starting receiving updates via long-polling");
|
||||||
ReceiverOptions.AllowedUpdates = botHost.UpdateRouter.HandlersProvider.AllowedTypes.ToArray();
|
_receiverOptions.AllowedUpdates = botHost.UpdateRouter.HandlersProvider.AllowedTypes.ToArray();
|
||||||
ReactiveUpdateReceiver updateReceiver = new ReactiveUpdateReceiver(botClient, ReceiverOptions);
|
ReactiveUpdateReceiver updateReceiver = new ReactiveUpdateReceiver(botClient, _receiverOptions);
|
||||||
await updateReceiver.ReceiveAsync(UpdateRouter, stoppingToken).ConfigureAwait(false);
|
await updateReceiver.ReceiveAsync(_updateRouter, stoppingToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ using Telegrator.Providers;
|
|||||||
|
|
||||||
namespace Telegrator.Hosting.Providers
|
namespace Telegrator.Hosting.Providers
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
public class HostAwaitingProvider(IOptions<TelegramBotOptions> options, ITelegramBotInfo botInfo, ILogger<HostAwaitingProvider> logger) : AwaitingProvider(options.Value, botInfo)
|
public class HostAwaitingProvider(IOptions<TelegramBotOptions> options, ITelegramBotInfo botInfo, ILogger<HostAwaitingProvider> logger) : AwaitingProvider(options.Value, botInfo)
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
public override IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
public override IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
IEnumerable<DescribedHandlerInfo> handlers = base.GetHandlers(updateRouter, client, update, cancellationToken).ToArray();
|
IEnumerable<DescribedHandlerInfo> handlers = base.GetHandlers(updateRouter, client, update, cancellationToken).ToArray();
|
||||||
|
|||||||
@@ -8,28 +8,35 @@ using Telegrator.Providers;
|
|||||||
|
|
||||||
namespace Telegrator.Hosting.Providers
|
namespace Telegrator.Hosting.Providers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Pre host building task
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
public delegate void PreBuildingRoutine(TelegramBotHostBuilder builder);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public class HostHandlersCollection(IServiceCollection hostServiceColletion, IHandlersCollectingOptions options) : HandlersCollection(options)
|
public class HostHandlersCollection(IServiceCollection hostServiceColletion, IHandlersCollectingOptions options) : HandlersCollection(options)
|
||||||
{
|
{
|
||||||
private readonly IServiceCollection Services = hostServiceColletion;
|
private readonly IServiceCollection Services = hostServiceColletion;
|
||||||
public readonly List<Action<TelegramBotHostBuilder>> PreBuilderRoutines = [];
|
|
||||||
|
/// <inheritdoc/>
|
||||||
protected override bool MustHaveParameterlessCtor => false;
|
protected override bool MustHaveParameterlessCtor => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of tasks that should be completed right before building the bot
|
||||||
|
/// </summary>
|
||||||
|
public readonly List<PreBuildingRoutine> PreBuilderRoutines = [];
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override IHandlersCollection AddHandler(Type handlerType)
|
public override IHandlersCollection AddHandler(Type handlerType)
|
||||||
{
|
{
|
||||||
//
|
if (handlerType.IsPreBuildingRoutine(out MethodInfo? routineMethod))
|
||||||
if (handlerType.GetInterface(nameof(IPreBuildingRoutine)) != null)
|
PreBuilderRoutines.Add(routineMethod.CreateDelegate<PreBuildingRoutine>(null));
|
||||||
{
|
|
||||||
MethodInfo? methodInfo = handlerType.GetMethod(nameof(IPreBuildingRoutine.PreBuildingRoutine), BindingFlags.Static | BindingFlags.Public);
|
|
||||||
if (methodInfo != null)
|
|
||||||
{
|
|
||||||
Action<TelegramBotHostBuilder> routineDelegate = methodInfo.CreateDelegate<Action<TelegramBotHostBuilder>>(null);
|
|
||||||
PreBuilderRoutines.Add(routineDelegate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.AddHandler(handlerType);
|
return base.AddHandler(handlerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override IHandlersCollection AddDescriptor(HandlerDescriptor descriptor)
|
public override IHandlersCollection AddDescriptor(HandlerDescriptor descriptor)
|
||||||
{
|
{
|
||||||
switch (descriptor.Type)
|
switch (descriptor.Type)
|
||||||
|
|||||||
@@ -11,18 +11,25 @@ using Telegrator.Providers;
|
|||||||
|
|
||||||
namespace Telegrator.Hosting.Providers
|
namespace Telegrator.Hosting.Providers
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
public class HostHandlersProvider : HandlersProvider
|
public class HostHandlersProvider : HandlersProvider
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider Services;
|
private readonly IServiceProvider Services;
|
||||||
private readonly ILogger<HostHandlersProvider> Logger;
|
private readonly ILogger<HostHandlersProvider> Logger;
|
||||||
|
|
||||||
public HostHandlersProvider(IHandlersCollection handlers, IOptions<TelegramBotOptions> options, ITelegramBotInfo botInfo, IServiceProvider serviceProvider, ILogger<HostHandlersProvider> logger)
|
/// <inheritdoc/>
|
||||||
: base(handlers, options.Value, botInfo)
|
public HostHandlersProvider(
|
||||||
|
IHandlersCollection handlers,
|
||||||
|
IOptions<TelegramBotOptions> options,
|
||||||
|
ITelegramBotInfo botInfo,
|
||||||
|
IServiceProvider serviceProvider,
|
||||||
|
ILogger<HostHandlersProvider> logger) : base(handlers, options.Value, botInfo)
|
||||||
{
|
{
|
||||||
Services = serviceProvider;
|
Services = serviceProvider;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
public override IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
IEnumerable<DescribedHandlerInfo> handlers = base.GetHandlers(updateRouter, client, update, cancellationToken).ToArray();
|
IEnumerable<DescribedHandlerInfo> handlers = base.GetHandlers(updateRouter, client, update, cancellationToken).ToArray();
|
||||||
@@ -30,6 +37,7 @@ namespace Telegrator.Hosting.Providers
|
|||||||
return handlers;
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override UpdateHandlerBase GetHandlerInstance(HandlerDescriptor descriptor, CancellationToken cancellationToken = default)
|
public override UpdateHandlerBase GetHandlerInstance(HandlerDescriptor descriptor, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ using Telegrator.MadiatorCore.Descriptors;
|
|||||||
|
|
||||||
namespace Telegrator.Hosting
|
namespace Telegrator.Hosting
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a hosted telegram bot
|
||||||
|
/// </summary>
|
||||||
public class TelegramBotHost : ITelegramBotHost
|
public class TelegramBotHost : ITelegramBotHost
|
||||||
{
|
{
|
||||||
private readonly IHost _innerHost;
|
private readonly IHost _innerHost;
|
||||||
@@ -32,7 +35,8 @@ namespace Telegrator.Hosting
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TelegramBotHost"/> class.
|
/// Initializes a new instance of the <see cref="TelegramBotHost"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serviceProvider">The service provider.</param>
|
/// <param name="hostApplicationBuilder">The service provider.</param>
|
||||||
|
/// <param name="handlers"></param>
|
||||||
internal TelegramBotHost(HostApplicationBuilder hostApplicationBuilder, HostHandlersCollection handlers)
|
internal TelegramBotHost(HostApplicationBuilder hostApplicationBuilder, HostHandlersCollection handlers)
|
||||||
{
|
{
|
||||||
RegisterHostServices(hostApplicationBuilder, handlers);
|
RegisterHostServices(hostApplicationBuilder, handlers);
|
||||||
@@ -44,22 +48,52 @@ namespace Telegrator.Hosting
|
|||||||
LogHandlers(handlers);
|
LogHandlers(handlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates new <see cref="TelegramBotHostBuilder"/> with default configuration, services and long-polling update receiving scheme
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public static TelegramBotHostBuilder CreateBuilder()
|
public static TelegramBotHostBuilder CreateBuilder()
|
||||||
{
|
{
|
||||||
TelegramBotHostBuilder builder = new TelegramBotHostBuilder(null);
|
HostApplicationBuilder innerBuilder = new HostApplicationBuilder(settings: null);
|
||||||
|
TelegramBotHostBuilder builder = new TelegramBotHostBuilder(innerBuilder, null);
|
||||||
builder.Services.AddTelegramBotHostDefaults();
|
builder.Services.AddTelegramBotHostDefaults();
|
||||||
builder.Services.AddTelegramReceiver();
|
builder.Services.AddTelegramReceiver();
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates new <see cref="TelegramBotHostBuilder"/> with default services and long-polling update receiving scheme
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public static TelegramBotHostBuilder CreateBuilder(TelegramBotHostBuilderSettings? settings)
|
public static TelegramBotHostBuilder CreateBuilder(TelegramBotHostBuilderSettings? settings)
|
||||||
{
|
{
|
||||||
TelegramBotHostBuilder builder = new TelegramBotHostBuilder(settings);
|
HostApplicationBuilder innerBuilder = new HostApplicationBuilder(settings?.ToApplicationBuilderSettings());
|
||||||
|
TelegramBotHostBuilder builder = new TelegramBotHostBuilder(innerBuilder, settings);
|
||||||
builder.Services.AddTelegramBotHostDefaults();
|
builder.Services.AddTelegramBotHostDefaults();
|
||||||
builder.Services.AddTelegramReceiver();
|
builder.Services.AddTelegramReceiver();
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates new EMPTY <see cref="TelegramBotHostBuilder"/> WITHOUT any services or update receiving schemes
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static TelegramBotHostBuilder CreateEmptyBuilder()
|
||||||
|
{
|
||||||
|
HostApplicationBuilder innerBuilder = Host.CreateEmptyApplicationBuilder(null);
|
||||||
|
return new TelegramBotHostBuilder(innerBuilder, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates new EMPTY <see cref="TelegramBotHostBuilder"/> WITHOUT any services or update receiving schemes
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static TelegramBotHostBuilder CreateEmptyBuilder(TelegramBotHostBuilderSettings? settings)
|
||||||
|
{
|
||||||
|
HostApplicationBuilder innerBuilder = Host.CreateEmptyApplicationBuilder(null);
|
||||||
|
return new TelegramBotHostBuilder(innerBuilder, settings);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task StartAsync(CancellationToken cancellationToken = default)
|
public async Task StartAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ using Telegrator.MadiatorCore;
|
|||||||
#pragma warning disable IDE0001
|
#pragma warning disable IDE0001
|
||||||
namespace Telegrator.Hosting
|
namespace Telegrator.Hosting
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a hosted telegram bots and services builder that helps manage configuration, logging, lifetime, and more.
|
||||||
|
/// </summary>
|
||||||
public class TelegramBotHostBuilder : ITelegramBotHostBuilder
|
public class TelegramBotHostBuilder : ITelegramBotHostBuilder
|
||||||
{
|
{
|
||||||
private readonly HostApplicationBuilder _innerBuilder;
|
private readonly HostApplicationBuilder _innerBuilder;
|
||||||
@@ -38,10 +41,12 @@ namespace Telegrator.Hosting
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TelegramBotHostBuilder"/> class.
|
/// Initializes a new instance of the <see cref="TelegramBotHostBuilder"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal TelegramBotHostBuilder(TelegramBotHostBuilderSettings? settings = null)
|
/// <param name="hostApplicationBuilder"></param>
|
||||||
|
/// <param name="settings"></param>
|
||||||
|
internal TelegramBotHostBuilder(HostApplicationBuilder hostApplicationBuilder, TelegramBotHostBuilderSettings? settings = null)
|
||||||
{
|
{
|
||||||
|
_innerBuilder = hostApplicationBuilder;
|
||||||
_settings = settings ?? new TelegramBotHostBuilderSettings();
|
_settings = settings ?? new TelegramBotHostBuilderSettings();
|
||||||
_innerBuilder = new HostApplicationBuilder(settings?.ToApplicationBuilderSettings());
|
|
||||||
_handlers = new HostHandlersCollection(Services, _settings);
|
_handlers = new HostHandlersCollection(Services, _settings);
|
||||||
|
|
||||||
Services.Configure<TelegramBotOptions>(Configuration.GetSection(nameof(TelegramBotOptions)));
|
Services.Configure<TelegramBotOptions>(Configuration.GetSection(nameof(TelegramBotOptions)));
|
||||||
@@ -55,7 +60,7 @@ namespace Telegrator.Hosting
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public TelegramBotHost Build()
|
public TelegramBotHost Build()
|
||||||
{
|
{
|
||||||
foreach (var preBuildRoutine in _handlers.PreBuilderRoutines)
|
foreach (PreBuildingRoutine preBuildRoutine in _handlers.PreBuilderRoutines)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using Telegrator.Configuration;
|
|||||||
namespace Telegrator.Hosting
|
namespace Telegrator.Hosting
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Settings os hosted Telegram bot
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TelegramBotHostBuilderSettings() : IHandlersCollectingOptions
|
public class TelegramBotHostBuilderSettings() : IHandlersCollectingOptions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
||||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
|
<Version>1.0.1</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using Telegram.Bot;
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator;
|
|
||||||
using Telegrator.Configuration;
|
using Telegrator.Configuration;
|
||||||
using Telegrator.Hosting.Components;
|
using Telegrator.Hosting.Components;
|
||||||
using Telegrator.Hosting.Configuration;
|
using Telegrator.Hosting.Configuration;
|
||||||
@@ -14,14 +16,31 @@ using Telegrator.MadiatorCore;
|
|||||||
|
|
||||||
namespace Telegrator.Hosting
|
namespace Telegrator.Hosting
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains extensions for <see cref="IServiceCollection"/>
|
||||||
|
/// Provides method to configure <see cref="ITelegramBotHost"/>
|
||||||
|
/// </summary>
|
||||||
public static class ServicesCollectionExtensions
|
public static class ServicesCollectionExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a configuration instance that strongly-typed <typeparamref name="TOptions"/> will bind against using <see cref="ConfigureOptionsProxy{TOptions}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TOptions"></typeparam>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="configuration"></param>
|
||||||
|
/// <param name="optionsProxy"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration configuration, ConfigureOptionsProxy<TOptions> optionsProxy) where TOptions : class
|
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration configuration, ConfigureOptionsProxy<TOptions> optionsProxy) where TOptions : class
|
||||||
{
|
{
|
||||||
optionsProxy.Configure(services, configuration);
|
optionsProxy.Configure(services, configuration);
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers <see cref="TelegramBotHost"/> default services
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static IServiceCollection AddTelegramBotHostDefaults(this IServiceCollection services)
|
public static IServiceCollection AddTelegramBotHostDefaults(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddLogging(builder => builder.AddConsole());
|
services.AddLogging(builder => builder.AddConsole());
|
||||||
@@ -34,6 +53,11 @@ namespace Telegrator.Hosting
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers <see cref="ITelegramBotClient"/> service with <see cref="HostedUpdateReceiver"/> to receive updates using long polling
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static IServiceCollection AddTelegramReceiver(this IServiceCollection services)
|
public static IServiceCollection AddTelegramReceiver(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddHttpClient<ITelegramBotClient>("tgreceiver").RemoveAllLoggers().AddTypedClient(TypedTelegramBotClientFactory);
|
services.AddHttpClient<ITelegramBotClient>("tgreceiver").RemoveAllLoggers().AddTypedClient(TypedTelegramBotClientFactory);
|
||||||
@@ -41,12 +65,26 @@ namespace Telegrator.Hosting
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="ITelegramBotClient"/> factory method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpClient"></param>
|
||||||
|
/// <param name="provider"></param>
|
||||||
|
/// <returns></returns>
|
||||||
private static ITelegramBotClient TypedTelegramBotClientFactory(HttpClient httpClient, IServiceProvider provider)
|
private static ITelegramBotClient TypedTelegramBotClientFactory(HttpClient httpClient, IServiceProvider provider)
|
||||||
=> new TelegramBotClient(provider.GetRequiredService<IOptions<TelegramBotClientOptions>>().Value, httpClient);
|
=> new TelegramBotClient(provider.GetRequiredService<IOptions<TelegramBotClientOptions>>().Value, httpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides useful methods to adjust <see cref="ITelegramBotHost"/>
|
||||||
|
/// </summary>
|
||||||
public static class TelegramBotHostExtensions
|
public static class TelegramBotHostExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures bots available commands depending on what handlers was registered
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="botHost"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static ITelegramBotHost SetBotCommands(this ITelegramBotHost botHost)
|
public static ITelegramBotHost SetBotCommands(this ITelegramBotHost botHost)
|
||||||
{
|
{
|
||||||
ITelegramBotClient client = botHost.Services.GetRequiredService<ITelegramBotClient>();
|
ITelegramBotClient client = botHost.Services.GetRequiredService<ITelegramBotClient>();
|
||||||
@@ -55,4 +93,26 @@ namespace Telegrator.Hosting
|
|||||||
return botHost;
|
return botHost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for reflection and type inspection.
|
||||||
|
/// </summary>
|
||||||
|
public static class ReflectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a type implements the <see cref="IPreBuildingRoutine"/> interface.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handlerType">The type to check.</param>
|
||||||
|
/// <param name="routineMethod"></param>
|
||||||
|
/// <returns>True if the type implements IPreBuildingRoutine; otherwise, false.</returns>
|
||||||
|
public static bool IsPreBuildingRoutine(this Type handlerType, [NotNullWhen(true)] out MethodInfo? routineMethod)
|
||||||
|
{
|
||||||
|
routineMethod = null;
|
||||||
|
if (handlerType.GetInterface(nameof(IPreBuildingRoutine)) == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
routineMethod = handlerType.GetMethod(nameof(IPreBuildingRoutine.PreBuildingRoutine), BindingFlags.Static | BindingFlags.Public);
|
||||||
|
return routineMethod != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using Telegram.Bot.Types;
|
||||||
|
using Telegram.Bot.Types.Enums;
|
||||||
|
using Telegrator.Attributes;
|
||||||
|
using Telegrator.Filters;
|
||||||
|
using Telegrator.Filters.Components;
|
||||||
|
|
||||||
|
namespace Telegrator.Annotations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Abstract base attribute for filtering callback-based updates.
|
||||||
|
/// Supports various message types including regular messages, edited messages, channel posts, and business messages.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filters">The filters to apply to messages</param>
|
||||||
|
public abstract class CallbackQueryAttribute(params IFilter<CallbackQuery>[] filters) : UpdateFilterAttribute<CallbackQuery>(filters)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the allowed update types that this filter can process.
|
||||||
|
/// </summary>
|
||||||
|
public override UpdateType[] AllowedTypes => [UpdateType.CallbackQuery];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts the message from various types of updates.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="update">The Telegram update</param>
|
||||||
|
/// <returns>The message from the update, or null if not present</returns>
|
||||||
|
public override CallbackQuery? GetFilterringTarget(Update update)
|
||||||
|
=> update.CallbackQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attribute for filtering <see cref="CallbackQuery"/>'s data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
public class CallbackDataAttribute(string data)
|
||||||
|
: CallbackQueryAttribute(new CallbackDataFilter(data))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attribute to check if <see cref="CallbackQuery"/> belongs to a specific message by its ID
|
||||||
|
/// </summary>
|
||||||
|
public class CallbackInlineIdAttribute(string inlineMessageId)
|
||||||
|
: CallbackQueryAttribute(new CallbackInlineIdFilter(inlineMessageId))
|
||||||
|
{ }
|
||||||
|
}
|
||||||
@@ -47,7 +47,7 @@ namespace Telegrator.Annotations
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="alliases">The command aliases to match against.</param>
|
/// <param name="alliases">The command aliases to match against.</param>
|
||||||
public CommandAlliasAttribute(params string[] alliases)
|
public CommandAlliasAttribute(params string[] alliases)
|
||||||
: base(new CommandAlliasFilter(alliases)) => Alliases = alliases;
|
: base(new CommandAlliasFilter(alliases)) => Alliases = alliases.Select(c => c.TrimStart('/')).ToArray();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the filtering target (Message) from the update.
|
/// Gets the filtering target (Message) from the update.
|
||||||
|
|||||||
@@ -10,9 +10,18 @@ namespace Telegrator.Annotations
|
|||||||
{ }
|
{ }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attribute for filtering messages in reply chain.
|
/// Attribute for checking message's reply chain.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MessageRepliedAttribute(int replyDepth = 1)
|
public class HasReplyAttribute(int replyDepth = 1)
|
||||||
: MessageFilterAttribute(new MessageRepliedFilter(replyDepth))
|
: MessageFilterAttribute(new MessageHasReplyFilter(replyDepth))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper filter class for filters that operate on replied messages.
|
||||||
|
/// Provides functionality to traverse reply chains and access replied message content.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="replyDepth"></param>
|
||||||
|
public class FromReplyChainAttribute(int replyDepth = 1)
|
||||||
|
: MessageFilterAttribute(new FromReplyChainFilter(replyDepth))
|
||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types;
|
||||||
|
using Telegram.Bot.Types.Enums;
|
||||||
using Telegrator.Filters;
|
using Telegrator.Filters;
|
||||||
|
|
||||||
namespace Telegrator.Annotations
|
namespace Telegrator.Annotations
|
||||||
@@ -12,7 +13,8 @@ namespace Telegrator.Annotations
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates new instance of <see cref="WelcomeAttribute"/>
|
/// Creates new instance of <see cref="WelcomeAttribute"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WelcomeAttribute() : base(new MessageChatTypeFilter(ChatType.Private), new CommandAlliasFilter("start"))
|
/// <param name="onlyFirst"></param>
|
||||||
|
public WelcomeAttribute(bool onlyFirst = false) : base(new MessageChatTypeFilter(ChatType.Private), new CommandAlliasFilter("start"), Filter<Message>.If(ctx => !onlyFirst || ctx.Input.Id == 0))
|
||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ namespace Telegrator.Attributes.Components
|
|||||||
public UpdateType Type { get; private set; }
|
public UpdateType Type { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets concurrency of this <see cref="UpdateHandlerBase"/> in same <see cref="UpdateType"/> pool
|
/// Gets or sets importance of this <see cref="UpdateHandlerBase"/> in same <see cref="UpdateType"/> pool
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Concurrency { get; set; }
|
public int Importance { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets priority of this <see cref="UpdateHandlerBase"/> in same type handlers pool
|
/// Gets or sets priority of this <see cref="UpdateHandlerBase"/> in same type handlers pool
|
||||||
@@ -40,11 +40,11 @@ namespace Telegrator.Attributes.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="expectingHandlerType"></param>
|
/// <param name="expectingHandlerType"></param>
|
||||||
/// <param name="updateType"></param>
|
/// <param name="updateType"></param>
|
||||||
/// <param name="concurrency"></param>
|
/// <param name="importance"></param>
|
||||||
/// <exception cref="ArgumentNullException"></exception>
|
/// <exception cref="ArgumentNullException"></exception>
|
||||||
/// <exception cref="ArgumentException"></exception>
|
/// <exception cref="ArgumentException"></exception>
|
||||||
/// <exception cref="Exception"></exception>
|
/// <exception cref="Exception"></exception>
|
||||||
protected internal UpdateHandlerAttributeBase(Type[] expectingHandlerType, UpdateType updateType, int concurrency = 0)
|
protected internal UpdateHandlerAttributeBase(Type[] expectingHandlerType, UpdateType updateType, int importance = 0)
|
||||||
{
|
{
|
||||||
if (expectingHandlerType == null)
|
if (expectingHandlerType == null)
|
||||||
throw new ArgumentNullException(nameof(expectingHandlerType));
|
throw new ArgumentNullException(nameof(expectingHandlerType));
|
||||||
@@ -57,11 +57,11 @@ namespace Telegrator.Attributes.Components
|
|||||||
|
|
||||||
ExpectingHandlerType = expectingHandlerType;
|
ExpectingHandlerType = expectingHandlerType;
|
||||||
Type = updateType;
|
Type = updateType;
|
||||||
Concurrency = concurrency;
|
Importance = importance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an <see cref="DescriptorIndexer"/> of this <see cref="UpdateHandlerAttributeBase"/> from <see cref="Concurrency"/> and <see cref="Priority"/>
|
/// Gets an <see cref="DescriptorIndexer"/> of this <see cref="UpdateHandlerAttributeBase"/> from <see cref="Importance"/> and <see cref="Priority"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public DescriptorIndexer GetIndexer()
|
public DescriptorIndexer GetIndexer()
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ namespace Telegrator.Attributes
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Abstract base attribute for marking update handler classes.
|
/// Abstract base attribute for marking update handler classes.
|
||||||
/// Provides a type-safe way to associate handler types with specific update types and concurrency settings.
|
/// Provides a type-safe way to associate handler types with specific update types and importance settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of the update handler that this attribute is applied to.</typeparam>
|
/// <typeparam name="T">The type of the update handler that this attribute is applied to.</typeparam>
|
||||||
/// <param name="updateType">The type of update that this handler can process.</param>
|
/// <param name="updateType">The type of update that this handler can process.</param>
|
||||||
/// <param name="concurrency">The concurrency level for this handler (default: 0 for unlimited).</param>
|
/// <param name="importance">The importance level for this handler (default: 0 for unlimited).</param>
|
||||||
public abstract class UpdateHandlerAttribute<T>(UpdateType updateType, int concurrency = 0)
|
public abstract class UpdateHandlerAttribute<T>(UpdateType updateType, int importance = 0)
|
||||||
: UpdateHandlerAttributeBase([typeof(T)], updateType, concurrency) where T : UpdateHandlerBase
|
: UpdateHandlerAttributeBase([typeof(T)], updateType, importance) where T : UpdateHandlerBase
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using Telegram.Bot.Types;
|
||||||
|
using Telegrator.Filters.Components;
|
||||||
|
|
||||||
|
namespace Telegrator.Filters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Filter thet checks <see cref="CallbackQuery"/>'s data
|
||||||
|
/// </summary>
|
||||||
|
public class CallbackDataFilter : Filter<CallbackQuery>
|
||||||
|
{
|
||||||
|
private readonly string _data;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize new instance of <see cref="CallbackDataFilter"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
public CallbackDataFilter(string data)
|
||||||
|
{
|
||||||
|
_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CanPass(FilterExecutionContext<CallbackQuery> context)
|
||||||
|
{
|
||||||
|
return context.Input.Data == _data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Filter that checks if <see cref="CallbackQuery"/> belongs to a specific message
|
||||||
|
/// </summary>
|
||||||
|
public class CallbackInlineIdFilter : Filter<CallbackQuery>
|
||||||
|
{
|
||||||
|
private readonly string _inlineMessageId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize new instance of <see cref="CallbackInlineIdFilter"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="inlineMessageId"></param>
|
||||||
|
public CallbackInlineIdFilter(string inlineMessageId)
|
||||||
|
{
|
||||||
|
_inlineMessageId = inlineMessageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CanPass(FilterExecutionContext<CallbackQuery> context)
|
||||||
|
{
|
||||||
|
return context.Input.InlineMessageId == _inlineMessageId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,7 +45,12 @@ namespace Telegrator.Filters.Components
|
|||||||
foreach (IFilter<T> filter in filters)
|
foreach (IFilter<T> filter in filters)
|
||||||
{
|
{
|
||||||
if (!filter.CanPass(context))
|
if (!filter.CanPass(context))
|
||||||
|
{
|
||||||
|
if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter)
|
||||||
|
LeveledDebug.FilterWriteLine("(E) {0} filter of {1} didnt pass!", filter.GetType().Name, context.Data["handler_name"]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
context.CompletedFilters.Add(filter);
|
context.CompletedFilters.Add(filter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,12 @@ namespace Telegrator.Filters.Components
|
|||||||
{
|
{
|
||||||
FilterExecutionContext<T> context = updateContext.CreateChild((T)filterringTarget);
|
FilterExecutionContext<T> context = updateContext.CreateChild((T)filterringTarget);
|
||||||
if (!filter.CanPass(context))
|
if (!filter.CanPass(context))
|
||||||
|
{
|
||||||
|
if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter)
|
||||||
|
LeveledDebug.FilterWriteLine("(E) {0} filter of {1} didnt pass!", filter.GetType().Name, context.Data["handler_name"]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
context.CompletedFilters.Add(filter);
|
context.CompletedFilters.Add(filter);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -37,7 +37,12 @@
|
|||||||
foreach (IFilter<T> filter in Filters)
|
foreach (IFilter<T> filter in Filters)
|
||||||
{
|
{
|
||||||
if (!filter.CanPass(context))
|
if (!filter.CanPass(context))
|
||||||
|
{
|
||||||
|
if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter)
|
||||||
|
LeveledDebug.FilterWriteLine("(E) {0} filter of {1} didnt pass!", filter.GetType().Name, context.Data["handler_name"]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
context.CompletedFilters.Add(filter);
|
context.CompletedFilters.Add(filter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System.Reflection;
|
using Telegrator.Filters.Components;
|
||||||
using Telegrator;
|
|
||||||
using Telegrator.Filters.Components;
|
|
||||||
|
|
||||||
namespace Telegrator.Filters
|
namespace Telegrator.Filters
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace Telegrator.Filters
|
|||||||
/// <param name="mention">The username to check for in the mention.</param>
|
/// <param name="mention">The username to check for in the mention.</param>
|
||||||
public MentionedFilter(string mention)
|
public MentionedFilter(string mention)
|
||||||
{
|
{
|
||||||
Mention = mention;
|
Mention = mention.TrimStart('@');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Telegrator.Filters
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool CanPass(FilterExecutionContext<Message> context)
|
public override bool CanPass(FilterExecutionContext<Message> context)
|
||||||
{
|
{
|
||||||
MessageRepliedFilter? repliedFilter = context.CompletedFilters.Get<MessageRepliedFilter>().SingleOrDefault();
|
FromReplyChainFilter? repliedFilter = context.CompletedFilters.Get<FromReplyChainFilter>().SingleOrDefault();
|
||||||
Target = repliedFilter?.Reply ?? context.Input;
|
Target = repliedFilter?.Reply ?? context.Input;
|
||||||
return CanPassNext(context);
|
return CanPassNext(context);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,51 @@ using Telegrator.Filters.Components;
|
|||||||
namespace Telegrator.Filters
|
namespace Telegrator.Filters
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Abstract base class for filters that operate on replied messages.
|
/// Filter that checks if message has appropriate reply chain.
|
||||||
/// Provides functionality to traverse reply chains and access replied message content.
|
/// DOES NOT SHiFT MESSAGE FILTERS TARGET
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
|
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
|
||||||
public class MessageRepliedFilter(int replyDepth = 1) : Filter<Message>
|
public class MessageHasReplyFilter(int replyDepth = 1) : Filter<Message>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the replied message at the specified depth in the reply chain.
|
||||||
|
/// </summary>
|
||||||
|
public Message Reply { get; private set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the depth of reply chain traversal.
|
||||||
|
/// </summary>
|
||||||
|
public int ReplyDepth { get; private set; } = replyDepth;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the message can pass through the filter by first validating
|
||||||
|
/// the reply chain and then applying specific filter logic.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The filter execution context containing the message.</param>
|
||||||
|
/// <returns>True if the message passes both reply validation and specific filter criteria; otherwise, false.</returns>
|
||||||
|
public override bool CanPass(FilterExecutionContext<Message> context)
|
||||||
|
{
|
||||||
|
Message reply = context.Input;
|
||||||
|
for (int i = ReplyDepth; i > 0; i--)
|
||||||
|
{
|
||||||
|
if (reply.ReplyToMessage is not { Id: > 0 } replyMessage)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
reply = replyMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reply = reply;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper filter class for filters that operate on replied messages.
|
||||||
|
/// Provides functionality to traverse reply chains and access replied message content
|
||||||
|
/// and shifts any next message filter to filter the content of found reply.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="replyDepth"></param>
|
||||||
|
public class FromReplyChainFilter(int replyDepth = 1) : Filter<Message>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the replied message at the specified depth in the reply chain.
|
/// Gets the replied message at the specified depth in the reply chain.
|
||||||
@@ -44,7 +84,7 @@ namespace Telegrator.Filters
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Filter that checks if the replied message was sent by the bot itself.
|
/// Filter that checks if the replied message was sent by the bot itself.
|
||||||
/// <para>( ! ): REQUIRES <see cref="MessageRepliedFilter"/> before</para>
|
/// <para>( ! ): REQUIRES <see cref="MessageHasReplyFilter"/> before</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MeRepliedFilter : Filter<Message>
|
public class MeRepliedFilter : Filter<Message>
|
||||||
{
|
{
|
||||||
@@ -55,7 +95,7 @@ namespace Telegrator.Filters
|
|||||||
/// <returns>True if the replied message was sent by the bot; otherwise, false.</returns>
|
/// <returns>True if the replied message was sent by the bot; otherwise, false.</returns>
|
||||||
public override bool CanPass(FilterExecutionContext<Message> context)
|
public override bool CanPass(FilterExecutionContext<Message> context)
|
||||||
{
|
{
|
||||||
MessageRepliedFilter repliedFilter = context.CompletedFilters.Get<MessageRepliedFilter>(0);
|
MessageHasReplyFilter repliedFilter = context.CompletedFilters.Get<MessageHasReplyFilter>(0);
|
||||||
return context.BotInfo.User == repliedFilter.Reply.From;
|
return context.BotInfo.User == repliedFilter.Reply.From;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace Telegrator.Filters
|
|||||||
if (!base.CanPass(context))
|
if (!base.CanPass(context))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Message = context.Update.Message!;
|
Message = context.Input!;
|
||||||
if (Message is not { Id: > 0 })
|
if (Message is not { Id: > 0 })
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ namespace Telegrator.Handlers
|
|||||||
/// Attribute that marks a handler to process any type of update.
|
/// Attribute that marks a handler to process any type of update.
|
||||||
/// This handler will be triggered for all incoming updates regardless of their type.
|
/// This handler will be triggered for all incoming updates regardless of their type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="concurrency">The maximum number of concurrent executions allowed (default: -1 for unlimited).</param>
|
/// <param name="importance"></param>
|
||||||
public class AnyUpdateHandlerAttribute(int concurrency = -1) : UpdateHandlerAttribute<AnyUpdateHandler>(UpdateType.Unknown, concurrency)
|
public class AnyUpdateHandlerAttribute(int importance = -1) : UpdateHandlerAttribute<AnyUpdateHandler>(UpdateType.Unknown, importance)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Always returns true, allowing any update to pass through this filter.
|
/// Always returns true, allowing any update to pass through this filter.
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ namespace Telegrator.Handlers.Building.Components
|
|||||||
/// <returns>The builder instance.</returns>
|
/// <returns>The builder instance.</returns>
|
||||||
public void SetConcurreny(int concurrency)
|
public void SetConcurreny(int concurrency)
|
||||||
{
|
{
|
||||||
Indexer = Indexer.UpdateConcurrency(concurrency);
|
Indexer = Indexer.UpdateImportance(concurrency);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ namespace Telegrator.Handlers
|
|||||||
/// Attribute that marks a handler to process callback query updates.
|
/// Attribute that marks a handler to process callback query updates.
|
||||||
/// This handler will be triggered when users interact with inline keyboards or other callback mechanisms.
|
/// This handler will be triggered when users interact with inline keyboards or other callback mechanisms.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="concurrency">The maximum number of concurrent executions allowed (default: 0 for unlimited).</param>
|
/// <param name="importance"></param>
|
||||||
public sealed class CallbackQueryHandlerAttribute(int concurrency = 0) : UpdateHandlerAttribute<CallbackQueryHandler>(UpdateType.CallbackQuery, concurrency)
|
public sealed class CallbackQueryHandlerAttribute(int importance = 0) : UpdateHandlerAttribute<CallbackQueryHandler>(UpdateType.CallbackQuery, importance)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Always returns true, allowing any callback query update to pass through this filter.
|
/// Always returns true, allowing any callback query update to pass through this filter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The filter execution context (unused).</param>
|
/// <param name="context">The filter execution context (unused).</param>
|
||||||
/// <returns>Always returns true to allow any callback query update.</returns>
|
/// <returns>Always returns true to allow any callback query update.</returns>
|
||||||
public override bool CanPass(FilterExecutionContext<Update> context) => true;
|
public override bool CanPass(FilterExecutionContext<Update> context) => context.Input is { CallbackQuery: { } };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ namespace Telegrator.Handlers
|
|||||||
/// Attribute that marks a handler to process command messages.
|
/// Attribute that marks a handler to process command messages.
|
||||||
/// This handler will be triggered when users send bot commands (messages starting with '/').
|
/// This handler will be triggered when users send bot commands (messages starting with '/').
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="concurrency">The maximum number of concurrent executions allowed (default: 1).</param>
|
public class CommandHandlerAttribute(int importance = 1) : UpdateHandlerAttribute<CommandHandler>(UpdateType.Message, importance)
|
||||||
public class CommandHandlerAttribute(int concurrency = 1) : UpdateHandlerAttribute<CommandHandler>(UpdateType.Message, concurrency)
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the command that was extracted from the message (without the '/' prefix and bot username).
|
/// Gets the command that was extracted from the message (without the '/' prefix and bot username).
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ namespace Telegrator.Handlers
|
|||||||
/// Attribute that marks a handler to process message updates.
|
/// Attribute that marks a handler to process message updates.
|
||||||
/// This handler will be triggered when users send messages in chats.
|
/// This handler will be triggered when users send messages in chats.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="concurrency">The maximum number of concurrent executions allowed (default: 0 for unlimited).</param>
|
public class MessageHandlerAttribute(int importance = 0) : UpdateHandlerAttribute<MessageHandler>(UpdateType.Message, importance)
|
||||||
public class MessageHandlerAttribute(int concurrency = 0) : UpdateHandlerAttribute<MessageHandler>(UpdateType.Message, concurrency)
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the update contains a valid message.
|
/// Checks if the update contains a valid message.
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Telegrator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Telegrator's Debug logger helper
|
||||||
|
/// </summary>
|
||||||
|
public static class LeveledDebug
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Writes debug message if Indent level has Router flag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
public static void RouterWriteLine(string message)
|
||||||
|
{
|
||||||
|
if (Debug.IndentLevel.HasFlag(DebugLevel.Router))
|
||||||
|
Debug.WriteLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes debug message if Indent level has Router flag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
public static void RouterWriteLine(string message, params object[] args)
|
||||||
|
{
|
||||||
|
if (Debug.IndentLevel.HasFlag(DebugLevel.Router))
|
||||||
|
Debug.WriteLine(message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes debug message if Indent level has Providers flag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
public static void ProviderWriteLine(string message)
|
||||||
|
{
|
||||||
|
if (Debug.IndentLevel.HasFlag(DebugLevel.Providers))
|
||||||
|
Debug.WriteLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes debug message if Indent level has Providers flag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
public static void ProviderWriteLine(string message, params object[] args)
|
||||||
|
{
|
||||||
|
if (Debug.IndentLevel.HasFlag(DebugLevel.Providers))
|
||||||
|
Debug.WriteLine(message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes debug message if Indent level has Filters flag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
public static void FilterWriteLine(string message)
|
||||||
|
{
|
||||||
|
if (Debug.IndentLevel.HasFlag(DebugLevel.Filters))
|
||||||
|
Debug.WriteLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes debug message if Indent level has Filters flag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
public static void FilterWriteLine(string message, params object[] args)
|
||||||
|
{
|
||||||
|
if (Debug.IndentLevel.HasFlag(DebugLevel.Filters))
|
||||||
|
Debug.WriteLine(message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes debug message if Indent level has Pool flag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
public static void PoolWriteLine(string message)
|
||||||
|
{
|
||||||
|
if (Debug.IndentLevel.HasFlag(DebugLevel.HandlersPool))
|
||||||
|
Debug.WriteLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes debug message if Indent level has Pool flag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
public static void PoolWriteLine(string message, params object[] args)
|
||||||
|
{
|
||||||
|
if (Debug.IndentLevel.HasFlag(DebugLevel.HandlersPool))
|
||||||
|
Debug.WriteLine(message, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Levels of debug writing
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum DebugLevel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Write debug messages from filters execution
|
||||||
|
/// </summary>
|
||||||
|
Filters = 0x1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write debug messages from handlers providers execution
|
||||||
|
/// </summary>
|
||||||
|
Providers = 0x2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write debug messages from update router's execution
|
||||||
|
/// </summary>
|
||||||
|
Router = 0x4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write debug messages from handlers pool execution
|
||||||
|
/// </summary>
|
||||||
|
HandlersPool = 0x8
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ using Telegram.Bot.Polling;
|
|||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator.Filters.Components;
|
using Telegrator.Filters.Components;
|
||||||
using Telegrator.Handlers.Components;
|
using Telegrator.Handlers.Components;
|
||||||
using Telegrator.MadiatorCore;
|
|
||||||
|
|
||||||
namespace Telegrator.MadiatorCore.Descriptors
|
namespace Telegrator.MadiatorCore.Descriptors
|
||||||
{
|
{
|
||||||
@@ -111,5 +110,9 @@ namespace Telegrator.MadiatorCore.Descriptors
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString()
|
||||||
|
=> DisplayString ?? HandlerInstance.GetType().Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,16 +46,24 @@ namespace Telegrator.MadiatorCore.Descriptors
|
|||||||
if (UpdateValidator != null)
|
if (UpdateValidator != null)
|
||||||
{
|
{
|
||||||
if (!UpdateValidator.CanPass(filterContext))
|
if (!UpdateValidator.CanPass(filterContext))
|
||||||
|
{
|
||||||
|
LeveledDebug.FilterWriteLine("(E) UpdateValidator filter of {0} for Update ({2}) didnt pass!", filterContext.Data["handler_name"]);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//LeveledDebug.FilterWriteLine("UpdateValidator of {0} for Update ({2}) passed", filterContext.Data["handler_name"]);
|
||||||
filterContext.CompletedFilters.Add(UpdateValidator);
|
filterContext.CompletedFilters.Add(UpdateValidator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StateKeeperValidator != null)
|
if (StateKeeperValidator != null)
|
||||||
{
|
{
|
||||||
if (!StateKeeperValidator.CanPass(filterContext))
|
if (!StateKeeperValidator.CanPass(filterContext))
|
||||||
|
{
|
||||||
|
LeveledDebug.FilterWriteLine("(E) StateKeeperValidator filter of {0} for Update ({2}) didnt pass!", filterContext.Data["handler_name"]);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//LeveledDebug.FilterWriteLine("StateKeeperValidator of {0} for Update ({2}) passed", filterContext.Data["handler_name"]);
|
||||||
filterContext.CompletedFilters.Add(StateKeeperValidator);
|
filterContext.CompletedFilters.Add(StateKeeperValidator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +72,14 @@ namespace Telegrator.MadiatorCore.Descriptors
|
|||||||
foreach (IFilter<Update> filter in UpdateFilters)
|
foreach (IFilter<Update> filter in UpdateFilters)
|
||||||
{
|
{
|
||||||
if (!filter.CanPass(filterContext))
|
if (!filter.CanPass(filterContext))
|
||||||
return false;
|
{
|
||||||
|
if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter)
|
||||||
|
LeveledDebug.FilterWriteLine("(E) {0} filter of {1} didnt pass!", filter.GetType().Name, filterContext.Data["handler_name"]);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//LeveledDebug.FilterWriteLine("{0} filter of {1} for Update ({2}) passed", filter.GetType().Name, filterContext.Data["handler_name"]);
|
||||||
filterContext.CompletedFilters.Add(filter);
|
filterContext.CompletedFilters.Add(filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
namespace Telegrator.MadiatorCore.Descriptors
|
namespace Telegrator.MadiatorCore.Descriptors
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an indexer for handler descriptors, containing concurrency and priority information.
|
/// Represents an indexer for handler descriptors, containing importance and priority information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly struct DescriptorIndexer(int routerIndex, int concurrency, int priority) : IComparable<DescriptorIndexer>
|
public readonly struct DescriptorIndexer(int routerIndex, int importance, int priority) : IComparable<DescriptorIndexer>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Index of this descriptor when it was added to router
|
/// Index of this descriptor when it was added to router
|
||||||
@@ -15,7 +15,7 @@ namespace Telegrator.MadiatorCore.Descriptors
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Of this handlert type
|
/// Of this handlert type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int Importance = concurrency;
|
public readonly int Importance = importance;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The priority of the handler.
|
/// The priority of the handler.
|
||||||
@@ -28,7 +28,7 @@ namespace Telegrator.MadiatorCore.Descriptors
|
|||||||
/// <param name="routerIndex"></param>
|
/// <param name="routerIndex"></param>
|
||||||
/// <param name="pollingHandler">The handler attribute.</param>
|
/// <param name="pollingHandler">The handler attribute.</param>
|
||||||
public DescriptorIndexer(int routerIndex, UpdateHandlerAttributeBase pollingHandler)
|
public DescriptorIndexer(int routerIndex, UpdateHandlerAttributeBase pollingHandler)
|
||||||
: this(routerIndex, pollingHandler.Concurrency, pollingHandler.Priority) { }
|
: this(routerIndex, pollingHandler.Importance, pollingHandler.Priority) { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a new <see cref="DescriptorIndexer"/> with updated priority.
|
/// Returns a new <see cref="DescriptorIndexer"/> with updated priority.
|
||||||
@@ -39,12 +39,12 @@ namespace Telegrator.MadiatorCore.Descriptors
|
|||||||
=> new DescriptorIndexer(RouterIndex, Importance, priority);
|
=> new DescriptorIndexer(RouterIndex, Importance, priority);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a new <see cref="DescriptorIndexer"/> with updated concurrency.
|
/// Returns a new <see cref="DescriptorIndexer"/> with updated importance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="concurrency">The new concurrency value.</param>
|
/// <param name="importance">The new importance value.</param>
|
||||||
/// <returns>A new <see cref="DescriptorIndexer"/> instance.</returns>
|
/// <returns>A new <see cref="DescriptorIndexer"/> instance.</returns>
|
||||||
public DescriptorIndexer UpdateConcurrency(int concurrency)
|
public DescriptorIndexer UpdateImportance(int importance)
|
||||||
=> new DescriptorIndexer(RouterIndex, concurrency, Priority);
|
=> new DescriptorIndexer(RouterIndex, importance, Priority);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a new <see cref="DescriptorIndexer"/> with updated RouterIndex.
|
/// Returns a new <see cref="DescriptorIndexer"/> with updated RouterIndex.
|
||||||
@@ -79,7 +79,7 @@ namespace Telegrator.MadiatorCore.Descriptors
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a string representation of the indexer.
|
/// Returns a string representation of the indexer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A string in the format (C:concurrency, P:priority).</returns>
|
/// <returns>A string in the format (C:importance, P:priority).</returns>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return string.Format("(I:{0}, C:{1}, P:{2})", RouterIndex, Importance, Priority);
|
return string.Format("(I:{0}, C:{1}, P:{2})", RouterIndex, Importance, Priority);
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ namespace Telegrator.MadiatorCore.Descriptors
|
|||||||
UpdateType = handlerAttribute.Type;
|
UpdateType = handlerAttribute.Type;
|
||||||
Indexer = handlerAttribute.GetIndexer();
|
Indexer = handlerAttribute.GetIndexer();
|
||||||
Filters = new DescriptorFiltersSet(handlerAttribute, stateKeeperAttribute, filters);
|
Filters = new DescriptorFiltersSet(handlerAttribute, stateKeeperAttribute, filters);
|
||||||
|
DisplayString = HandlerInspector.GetDisplayName(handlerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -402,5 +403,9 @@ namespace Telegrator.MadiatorCore.Descriptors
|
|||||||
ServiceKey = serviceKey ?? throw new ArgumentNullException(nameof(serviceKey));
|
ServiceKey = serviceKey ?? throw new ArgumentNullException(nameof(serviceKey));
|
||||||
InstanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
|
InstanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString()
|
||||||
|
=> DisplayString ?? HandlerType.Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
using Telegrator;
|
|
||||||
using Telegrator.Configuration;
|
using Telegrator.Configuration;
|
||||||
using Telegrator.MadiatorCore;
|
|
||||||
|
|
||||||
namespace Telegrator.MadiatorCore.Descriptors
|
namespace Telegrator.MadiatorCore.Descriptors
|
||||||
{
|
{
|
||||||
@@ -29,6 +27,11 @@ namespace Telegrator.MadiatorCore.Descriptors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public UpdateType HandlingType => _handlingType;
|
public UpdateType HandlingType => _handlingType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets count of registered handlers in list
|
||||||
|
/// </summary>
|
||||||
|
public int Count => _innerCollection.Count;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="HandlerDescriptor"/> at the specified index.
|
/// Gets or sets the <see cref="HandlerDescriptor"/> at the specified index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Reflection;
|
using System.ComponentModel;
|
||||||
|
using System.Reflection;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
using Telegrator.Attributes.Components;
|
using Telegrator.Attributes.Components;
|
||||||
@@ -11,6 +12,16 @@ namespace Telegrator.MadiatorCore.Descriptors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class HandlerInspector
|
public static class HandlerInspector
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets handler's display name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handlerType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string? GetDisplayName(MemberInfo handlerType)
|
||||||
|
{
|
||||||
|
return handlerType.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the handler attribute from the specified member info.
|
/// Gets the handler attribute from the specified member info.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Telegram.Bot;
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Polling;
|
using Telegram.Bot.Polling;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator.Polling;
|
using Telegram.Bot.Types.Enums;
|
||||||
using Telegrator.Configuration;
|
using Telegrator.Configuration;
|
||||||
using Telegrator.Handlers.Components;
|
using Telegrator.Handlers.Components;
|
||||||
using Telegrator.MadiatorCore;
|
using Telegrator.MadiatorCore;
|
||||||
@@ -92,6 +94,7 @@ namespace Telegrator.Polling
|
|||||||
/// <returns>A task representing the asynchronous error handling operation.</returns>
|
/// <returns>A task representing the asynchronous error handling operation.</returns>
|
||||||
public virtual Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, HandleErrorSource source, CancellationToken cancellationToken)
|
public virtual Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, HandleErrorSource source, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
LeveledDebug.RouterWriteLine("Handling exception {0}", exception.GetType().Name);
|
||||||
ExceptionHandler?.HandleException(botClient, exception, source, cancellationToken);
|
ExceptionHandler?.HandleException(botClient, exception, source, cancellationToken);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@@ -105,21 +108,60 @@ namespace Telegrator.Polling
|
|||||||
/// <returns>A task representing the asynchronous update handling operation.</returns>
|
/// <returns>A task representing the asynchronous update handling operation.</returns>
|
||||||
public virtual Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
|
public virtual Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
// Logging
|
||||||
|
LeveledDebug.RouterWriteLine("Received Update ({0}) of type \"{1}\"", update.Id, update.Type);
|
||||||
|
LogUpdate(update);
|
||||||
|
|
||||||
// Queuing handlers for execution
|
// Queuing handlers for execution
|
||||||
foreach (DescribedHandlerInfo handler in GetHandlers(botClient, update, cancellationToken))
|
foreach (DescribedHandlerInfo handler in GetHandlers(botClient, update, cancellationToken))
|
||||||
HandlersPool.Enqueue(handler);
|
HandlersPool.Enqueue(handler);
|
||||||
|
|
||||||
|
LeveledDebug.RouterWriteLine("Receiving Update ({0}) finished", update.Id);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<DescribedHandlerInfo> GetHandlers(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
|
private IEnumerable<DescribedHandlerInfo> GetHandlers(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Getting handlers in update awaiting pool
|
try
|
||||||
IEnumerable<DescribedHandlerInfo> handlers = AwaitingProvider.GetHandlers(this, botClient, update, cancellationToken);
|
{
|
||||||
if (handlers.Any() && Options.ExclusiveAwaitingHandlerRouting)
|
// Getting handlers in update awaiting pool
|
||||||
return handlers;
|
IEnumerable<DescribedHandlerInfo> handlers = AwaitingProvider.GetHandlers(this, botClient, update, cancellationToken);
|
||||||
|
if (handlers.Any() && Options.ExclusiveAwaitingHandlerRouting)
|
||||||
|
return handlers;
|
||||||
|
|
||||||
return handlers.Concat(HandlersProvider.GetHandlers(this, botClient, update, cancellationToken));
|
return handlers.Concat(HandlersProvider.GetHandlers(this, botClient, update, cancellationToken));
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
_ = 0xBAD + 0xC0DE;
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ExceptionHandler?.HandleException(botClient, ex, HandleErrorSource.PollingError, cancellationToken);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LogUpdate(Update update)
|
||||||
|
{
|
||||||
|
switch (update.Type)
|
||||||
|
{
|
||||||
|
case UpdateType.Message:
|
||||||
|
{
|
||||||
|
Message msg = update.Message ?? throw new NullReferenceException();
|
||||||
|
StringBuilder sb = new StringBuilder("Update.Message");
|
||||||
|
|
||||||
|
if (msg.From != null)
|
||||||
|
sb.AppendFormat(" from {0} ({1})", msg.From.Username, msg.From.Id);
|
||||||
|
|
||||||
|
if (msg.Text != null)
|
||||||
|
sb.AppendFormat("'{0}'", msg.Text);
|
||||||
|
|
||||||
|
LeveledDebug.RouterWriteLine(sb.ToString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Telegram.Bot;
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
@@ -79,16 +80,23 @@ namespace Telegrator.Providers
|
|||||||
/// <returns>A collection of described handler information for the update</returns>
|
/// <returns>A collection of described handler information for the update</returns>
|
||||||
public virtual IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
public virtual IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
LeveledDebug.ProviderWriteLine("Requested handlers for UpdateType.{0}", update.Type);
|
||||||
if (!HandlersDictionary.TryGetValue(update.Type, out HandlerDescriptorList? descriptors))
|
if (!HandlersDictionary.TryGetValue(update.Type, out HandlerDescriptorList? descriptors))
|
||||||
{
|
{
|
||||||
if (!HandlersDictionary.TryGetValue(UpdateType.Unknown, out descriptors))
|
LeveledDebug.ProviderWriteLine("No registered, providing Any");
|
||||||
return [];
|
HandlersDictionary.TryGetValue(UpdateType.Unknown, out descriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptors == null || !descriptors.Any())
|
if (descriptors == null || descriptors.Count == 0)
|
||||||
|
{
|
||||||
|
LeveledDebug.ProviderWriteLine("No handlers provided");
|
||||||
return [];
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return DescribeDescriptors(descriptors, updateRouter, client, update, cancellationToken);
|
IEnumerable<DescribedHandlerInfo> described = DescribeDescriptors(descriptors, updateRouter, client, update, cancellationToken);
|
||||||
|
LeveledDebug.ProviderWriteLine("Described total of {0} handlers for Update ({1})", described.Count(), update.Id);
|
||||||
|
LeveledDebug.ProviderWriteLine("Described handlers : {0}", string.Join(", ", described));
|
||||||
|
return described;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -103,16 +111,24 @@ namespace Telegrator.Providers
|
|||||||
/// <returns>A collection of described handler information</returns>
|
/// <returns>A collection of described handler information</returns>
|
||||||
public virtual IEnumerable<DescribedHandlerInfo> DescribeDescriptors(HandlerDescriptorList descriptors, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
public virtual IEnumerable<DescribedHandlerInfo> DescribeDescriptors(HandlerDescriptorList descriptors, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
foreach (HandlerDescriptor descriptor in descriptors.Reverse())
|
try
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
LeveledDebug.ProviderWriteLine("Describing descriptors of descriptorsList.HandlingType.{0} for Update ({1})", descriptors.HandlingType, update.Id);
|
||||||
DescribedHandlerInfo? describedHandler = DescribeHandler(descriptor, updateRouter, client, update, cancellationToken);
|
foreach (HandlerDescriptor descriptor in descriptors.Reverse())
|
||||||
if (describedHandler == null)
|
{
|
||||||
continue;
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
DescribedHandlerInfo? describedHandler = DescribeHandler(descriptor, updateRouter, client, update, cancellationToken);
|
||||||
|
if (describedHandler == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
yield return describedHandler;
|
yield return describedHandler;
|
||||||
if (Options.ExecuteOnlyFirstFoundHanlder)
|
if (Options.ExecuteOnlyFirstFoundHanlder)
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
LeveledDebug.ProviderWriteLine("Describing for Update ({0}) finished", update.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +145,12 @@ namespace Telegrator.Providers
|
|||||||
public virtual DescribedHandlerInfo? DescribeHandler(HandlerDescriptor descriptor, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
public virtual DescribedHandlerInfo? DescribeHandler(HandlerDescriptor descriptor, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(BotInfo, update, update);
|
Dictionary<string, object> data = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{ "handler_name", descriptor.ToString() }
|
||||||
|
};
|
||||||
|
|
||||||
|
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(BotInfo, update, update, data, []);
|
||||||
if (!descriptor.Filters.Validate(filterContext))
|
if (!descriptor.Filters.Validate(filterContext))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
||||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
|
<Version>1.0.1</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
using Telegram.Bot.Types.Payments;
|
using Telegram.Bot.Types.Payments;
|
||||||
@@ -902,4 +903,28 @@ namespace Telegrator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains extension method for number types
|
||||||
|
/// </summary>
|
||||||
|
public static class NumbersExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Check if int value has int flag using bit compare
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="flag"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool HasFlag(this int value, int flag)
|
||||||
|
=> (value & flag) == flag;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if int value has enum flag using bit compare
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="flag"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool HasFlag<T>(this int value, T flag) where T : Enum
|
||||||
|
=> value.HasFlag(Convert.ToInt32(flag));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user