* Added Result class to communicate with router from handler
* Removed "ExecuteOnlyFirstFoundHanlder" in sake of testing new Result pattern based routing system * Removed obsolete option property "DescendDescriptorIndex" * Changed router logic * Changed handlers pool logic
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
// This file is used by Code Analysis to maintain SuppressMessage
|
||||
// attributes that are applied to this project.
|
||||
// Project-level suppressions either have no target or are given
|
||||
// a specific target and scoped to a namespace, type, member, etc.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("Style", "IDE0090")]
|
||||
@@ -0,0 +1,8 @@
|
||||
// This file is used by Code Analysis to maintain SuppressMessage
|
||||
// attributes that are applied to this project.
|
||||
// Project-level suppressions either have no target or are given
|
||||
// a specific target and scoped to a namespace, type, member, etc.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("Style", "IDE0090")]
|
||||
@@ -1,6 +1,5 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
@@ -22,7 +21,7 @@ namespace Telegrator.Hosting.Web.Polling
|
||||
private readonly ITelegramBotWebHost _botHost;
|
||||
private readonly ITelegramBotClient _botClient;
|
||||
private readonly IUpdateRouter _updateRouter;
|
||||
private readonly TelegramBotWebOptions _options;
|
||||
private readonly TelegratorWebOptions _options;
|
||||
|
||||
/// <summary>
|
||||
/// Initiallizes new instance of <see cref="HostedUpdateWebhooker"/>
|
||||
@@ -32,7 +31,7 @@ namespace Telegrator.Hosting.Web.Polling
|
||||
/// <param name="updateRouter"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public HostedUpdateWebhooker(ITelegramBotWebHost botHost, ITelegramBotClient botClient, IUpdateRouter updateRouter, IOptions<TelegramBotWebOptions> options)
|
||||
public HostedUpdateWebhooker(ITelegramBotWebHost botHost, ITelegramBotClient botClient, IUpdateRouter updateRouter, IOptions<TelegratorWebOptions> options)
|
||||
{
|
||||
if (string.IsNullOrEmpty(options.Value.WebhookUri))
|
||||
throw new ArgumentNullException(nameof(options), "Option \"WebhookUrl\" must be set to subscribe for update recieving");
|
||||
|
||||
@@ -5,8 +5,10 @@ using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Text;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegrator.Configuration;
|
||||
using Telegrator.Hosting.Components;
|
||||
using Telegrator.Hosting.Providers;
|
||||
using Telegrator.Hosting.Web.Components;
|
||||
@@ -22,7 +24,7 @@ namespace Telegrator.Hosting.Web
|
||||
{
|
||||
private readonly WebApplication _innerApp;
|
||||
private readonly IUpdateRouter _updateRouter;
|
||||
private readonly ILogger<TelegramBotHost> _logger;
|
||||
private readonly ILogger<TelegramBotWebHost> _logger;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
@@ -43,7 +45,7 @@ namespace Telegrator.Hosting.Web
|
||||
/// <summary>
|
||||
/// This application's logger
|
||||
/// </summary>
|
||||
public ILogger<TelegramBotHost> Logger => _logger;
|
||||
public ILogger<TelegramBotWebHost> Logger => _logger;
|
||||
|
||||
// Private interface fields
|
||||
IServiceProvider IEndpointRouteBuilder.ServiceProvider => Services;
|
||||
@@ -53,12 +55,20 @@ namespace Telegrator.Hosting.Web
|
||||
|
||||
internal TelegramBotWebHost(WebApplicationBuilder webApplicationBuilder, HostHandlersCollection handlers)
|
||||
{
|
||||
// Registering this host in services for easy access
|
||||
RegisterHostServices(webApplicationBuilder, handlers);
|
||||
|
||||
// Building proxy application
|
||||
_innerApp = webApplicationBuilder.Build();
|
||||
|
||||
_updateRouter = Services.GetRequiredService<IUpdateRouter>();
|
||||
_logger = Services.GetRequiredService<ILogger<TelegramBotHost>>();
|
||||
// Initializing bot info, as it requires to make a request via tg bot
|
||||
Services.GetRequiredService<ITelegramBotInfo>();
|
||||
|
||||
// Reruesting services for this host
|
||||
_updateRouter = Services.GetRequiredService<IUpdateRouter>();
|
||||
_logger = Services.GetRequiredService<ILogger<TelegramBotWebHost>>();
|
||||
|
||||
// Logging registering handlers in DEBUG purposes
|
||||
LogHandlers(handlers);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,10 +43,6 @@ namespace Telegrator.Hosting.Web
|
||||
_innerBuilder = webApplicationBuilder;
|
||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||
_handlers = new HostHandlersCollection(Services, _settings);
|
||||
|
||||
Services.AddSingleton<IOptions<TelegramBotWebOptions>>(Options.Create(settings));
|
||||
Services.Configure<TelegratorOptions>(Configuration.GetSection(nameof(TelegratorOptions)));
|
||||
Services.Configure<TelegramBotClientOptions>(Configuration.GetSection(nameof(TelegramBotClientOptions)), new TelegramBotClientOptionsProxy());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -67,6 +63,14 @@ namespace Telegrator.Hosting.Web
|
||||
}
|
||||
}
|
||||
|
||||
if (!_settings.DisableAutoConfigure)
|
||||
{
|
||||
Services.Configure<TelegratorWebOptions>(Configuration.GetSection(nameof(TelegratorWebOptions)));
|
||||
Services.Configure<TelegratorOptions>(Configuration.GetSection(nameof(TelegratorOptions)));
|
||||
Services.Configure<TelegramBotClientOptions>(Configuration.GetSection(nameof(TelegramBotClientOptions)), new TelegramBotClientOptionsProxy());
|
||||
}
|
||||
|
||||
Services.AddSingleton<IOptions<TelegratorOptions>>(Options.Create(_settings));
|
||||
return new TelegramBotWebHost(_innerBuilder, _handlers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,18 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Telegrator.Hosting.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration options for Telegram bot behavior and execution settings.
|
||||
/// Controls various aspects of bot operation including concurrency, routing, webhook receiving, and execution policies.
|
||||
/// Options for configuring the behavior for TelegramBotWebHost.
|
||||
/// </summary>
|
||||
public class TelegramBotWebOptions : TelegratorOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets HTTPS URL to send updates to. Use an empty string to remove webhook integration
|
||||
/// Disables automatic configuration for all of required <see cref="IOptions{TOptions}"/> instances
|
||||
/// </summary>
|
||||
[StringSyntax(StringSyntaxAttribute.Uri)]
|
||||
public required string WebhookUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A secret token to be sent in a header “X-Telegram-Bot-Api-Secret-Token” in every webhook request, 1-256 characters.
|
||||
/// Only characters A-Z, a-z, 0-9, _ and - are allowed.
|
||||
/// The header is useful to ensure that the request comes from a webhook set by you.
|
||||
/// </summary>
|
||||
public string? SecretToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40.
|
||||
/// Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput.
|
||||
/// </summary>
|
||||
public int MaxConnections { get; set; } = 40;
|
||||
|
||||
/// <summary>
|
||||
/// Pass true to drop all pending updates
|
||||
/// </summary>
|
||||
public bool DropPendingUpdates { get; set; }
|
||||
public bool DisableAutoConfigure { get; set; }
|
||||
|
||||
/// <inheritdoc cref="WebApplicationOptions.Args"/>
|
||||
public string[]? Args { get; init; }
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Telegrator.Hosting.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration options for Telegram bot behavior and execution settings.
|
||||
/// Controls various aspects of bot operation including concurrency, routing, webhook receiving, and execution policies.
|
||||
/// </summary>
|
||||
public class TelegratorWebOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets HTTPS URL to send updates to. Use an empty string to remove webhook integration
|
||||
/// </summary>
|
||||
[StringSyntax(StringSyntaxAttribute.Uri)]
|
||||
public required string WebhookUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A secret token to be sent in a header “X-Telegram-Bot-Api-Secret-Token” in every webhook request, 1-256 characters.
|
||||
/// Only characters A-Z, a-z, 0-9, _ and - are allowed.
|
||||
/// The header is useful to ensure that the request comes from a webhook set by you.
|
||||
/// </summary>
|
||||
public string? SecretToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40.
|
||||
/// Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput.
|
||||
/// </summary>
|
||||
public int MaxConnections { get; set; } = 40;
|
||||
|
||||
/// <summary>
|
||||
/// Pass true to drop all pending updates
|
||||
/// </summary>
|
||||
public bool DropPendingUpdates { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Telegrator.MadiatorCore.Descriptors;
|
||||
using Telegrator.Polling;
|
||||
|
||||
namespace Telegrator.Hosting.Polling
|
||||
@@ -9,12 +8,5 @@ namespace Telegrator.Hosting.Polling
|
||||
public class HostUpdateHandlersPool(IOptions<TelegratorOptions> options, ILogger<HostUpdateHandlersPool> logger) : UpdateHandlersPool(options.Value, options.Value.GlobalCancellationToken)
|
||||
{
|
||||
private readonly ILogger<HostUpdateHandlersPool> _logger = logger;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteHandlerWrapper(DescribedHandlerInfo enqueuedHandler)
|
||||
{
|
||||
//_logger.LogInformation("Handler \"{0}\" has entered execution pool", enqueuedHandler.DisplayString);
|
||||
await base.ExecuteHandlerWrapper(enqueuedHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Polling;
|
||||
using Telegrator.Hosting;
|
||||
@@ -46,6 +47,7 @@ namespace Telegrator.Hosting
|
||||
{
|
||||
_innerBuilder = hostApplicationBuilder;
|
||||
_settings = settings ?? new TelegramBotHostBuilderSettings();
|
||||
|
||||
_handlers = new HostHandlersCollection(Services, _settings);
|
||||
|
||||
_innerBuilder.Logging.ClearProviders();
|
||||
@@ -71,11 +73,11 @@ namespace Telegrator.Hosting
|
||||
|
||||
if (!_settings.DisableAutoConfigure)
|
||||
{
|
||||
Services.Configure<TelegratorOptions>(Configuration.GetSection(nameof(TelegratorOptions)));
|
||||
Services.Configure<ReceiverOptions>(Configuration.GetSection(nameof(ReceiverOptions)));
|
||||
Services.Configure<TelegramBotClientOptions>(Configuration.GetSection(nameof(TelegramBotClientOptions)), new TelegramBotClientOptionsProxy());
|
||||
}
|
||||
|
||||
Services.AddSingleton<IOptions<TelegratorOptions>>(Options.Create(_settings));
|
||||
return new TelegramBotHost(_innerBuilder, _handlers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ namespace Telegrator.Tests
|
||||
{
|
||||
public bool WasExecuted { get; private set; }
|
||||
|
||||
public override Task Execute(IAbstractHandlerContainer<Message> container, CancellationToken cancellationToken)
|
||||
public override Task<Result> Execute(IAbstractHandlerContainer<Message> container, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
WasExecuted = true;
|
||||
return Task.CompletedTask;
|
||||
return Task.FromResult(Result.Ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,18 @@
|
||||
/// </summary>
|
||||
public interface ITelegratorOptions
|
||||
{
|
||||
/*
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether only the first found handler should be executed for each update.
|
||||
/// </summary>
|
||||
public bool ExecuteOnlyFirstFoundHanlder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to descend the indexr of handler's index on register. ('false' by default)
|
||||
/// </summary>
|
||||
public bool DescendDescriptorIndex { get; set; }
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of parallel working handlers. Null means no limit.
|
||||
/// </summary>
|
||||
@@ -21,19 +28,14 @@
|
||||
/// </summary>
|
||||
public bool ExclusiveAwaitingHandlerRouting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the global cancellation token for all bot operations.
|
||||
/// </summary>
|
||||
public CancellationToken GlobalCancellationToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to descend the indexr of handler's index on register. ('false' by default)
|
||||
/// </summary>
|
||||
public bool DescendDescriptorIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to exclude intersecting command aliases.
|
||||
/// </summary>
|
||||
public bool ExceptIntersectingCommandAliases { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the global cancellation token for all bot operations.
|
||||
/// </summary>
|
||||
public CancellationToken GlobalCancellationToken { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,10 +51,10 @@ namespace Telegrator.Handlers.Building
|
||||
/// <param name="container">The handler container (unused).</param>
|
||||
/// <param name="cancellation">The cancellation token (unused).</param>
|
||||
/// <returns>A completed task.</returns>
|
||||
protected override Task ExecuteInternal(IHandlerContainer container, CancellationToken cancellation)
|
||||
protected override Task<Result> ExecuteInternal(IHandlerContainer container, CancellationToken cancellation)
|
||||
{
|
||||
ResetEvent.Set();
|
||||
return Task.CompletedTask;
|
||||
return Task.FromResult(Result.Ok());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Telegrator.Handlers.Building
|
||||
/// <param name="container">The handler container with execution context.</param>
|
||||
/// <param name="cancellation">The cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous execution.</returns>
|
||||
public override Task Execute(IAbstractHandlerContainer<TUpdate> container, CancellationToken cancellation)
|
||||
public override Task<Result> Execute(IAbstractHandlerContainer<TUpdate> container, CancellationToken cancellation)
|
||||
=> HandlerAction.Invoke(container, cancellation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Telegrator.Handlers.Building
|
||||
/// <param name="container">The handler container with execution context.</param>
|
||||
/// <param name="cancellation">The cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous execution.</returns>
|
||||
public delegate Task AbstractHandlerAction<TUpdate>(IAbstractHandlerContainer<TUpdate> container, CancellationToken cancellation) where TUpdate : class;
|
||||
public delegate Task<Result> AbstractHandlerAction<TUpdate>(IAbstractHandlerContainer<TUpdate> container, CancellationToken cancellation) where TUpdate : class;
|
||||
|
||||
/// <summary>
|
||||
/// Builder class for creating regular handlers that can process updates.
|
||||
|
||||
@@ -74,10 +74,10 @@ namespace Telegrator.Handlers.Components
|
||||
/// <param name="container">The handler container.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
protected override sealed async Task ExecuteInternal(IHandlerContainer container, CancellationToken cancellationToken)
|
||||
protected override sealed async Task<Result> ExecuteInternal(IHandlerContainer container, CancellationToken cancellationToken)
|
||||
{
|
||||
Container = (IAbstractHandlerContainer<TUpdate>)container;
|
||||
await Execute(Container, cancellationToken);
|
||||
return await Execute(Container, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -86,6 +86,6 @@ namespace Telegrator.Handlers.Components
|
||||
/// <param name="container">The handler container.</param>
|
||||
/// <param name="cancellation">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public abstract Task Execute(IAbstractHandlerContainer<TUpdate> container, CancellationToken cancellation);
|
||||
public abstract Task<Result> Execute(IAbstractHandlerContainer<TUpdate> container, CancellationToken cancellation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,13 +118,13 @@ namespace Telegrator.Handlers.Components
|
||||
/// <param name="container">The handler container.</param>
|
||||
/// <param name="cancellation">The cancellation token.</param>
|
||||
/// <exception cref="Exception">Thrown when no branch method is set.</exception>
|
||||
public override async Task Execute(IAbstractHandlerContainer<TUpdate> container, CancellationToken cancellation)
|
||||
public override async Task<Result> Execute(IAbstractHandlerContainer<TUpdate> container, CancellationToken cancellation)
|
||||
{
|
||||
if (branchMethodInfo is null)
|
||||
throw new Exception();
|
||||
|
||||
Cancellation = cancellation;
|
||||
await BranchExecuteWrapper(container, branchMethodInfo);
|
||||
return await BranchExecuteWrapper(container, branchMethodInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -132,21 +132,20 @@ namespace Telegrator.Handlers.Components
|
||||
/// </summary>
|
||||
/// <param name="container">The handler container.</param>
|
||||
/// <param name="methodInfo">The method to execute.</param>
|
||||
protected virtual async Task BranchExecuteWrapper(IAbstractHandlerContainer<TUpdate> container, MethodInfo methodInfo)
|
||||
protected virtual async Task<Result> BranchExecuteWrapper(IAbstractHandlerContainer<TUpdate> container, MethodInfo methodInfo)
|
||||
{
|
||||
if (methodInfo.ReturnType == typeof(void))
|
||||
{
|
||||
methodInfo.Invoke(this, []);
|
||||
return;
|
||||
return Result.Ok();
|
||||
}
|
||||
else
|
||||
{
|
||||
object branchReturn = methodInfo.Invoke(this, []);
|
||||
if (branchReturn == null)
|
||||
return;
|
||||
if (branchReturn is not Task<Result> branchTask)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
if (branchReturn is Task branchTask)
|
||||
await branchTask;
|
||||
return await branchTask;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,17 @@ namespace Telegrator.Handlers.Components
|
||||
/// <param name="container">The <see cref="IHandlerContainer"/> for the update.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
public async Task Execute(IHandlerContainer container, CancellationToken cancellationToken = default)
|
||||
public async Task<Result> Execute(IHandlerContainer container, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await ExecuteInternal(container, cancellationToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await ExecuteInternal(container, cancellationToken);
|
||||
LifetimeToken.LifetimeEnded();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the handler logic for the given container and cancellation token.
|
||||
@@ -36,6 +42,6 @@ namespace Telegrator.Handlers.Components
|
||||
/// <param name="container">The <see cref="IHandlerContainer"/> for the update.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
protected abstract Task ExecuteInternal(IHandlerContainer container, CancellationToken cancellationToken);
|
||||
protected abstract Task<Result> ExecuteInternal(IHandlerContainer container, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace Telegrator.Handlers
|
||||
{
|
||||
public sealed class Result
|
||||
{
|
||||
public bool Positive { get; }
|
||||
|
||||
public bool RouteNext { get; }
|
||||
|
||||
public Type? NextType { get; }
|
||||
|
||||
internal Result(bool positive, bool routeNext, Type? nextType)
|
||||
{
|
||||
Positive = positive;
|
||||
RouteNext = routeNext;
|
||||
NextType = nextType;
|
||||
}
|
||||
|
||||
public static Result Ok()
|
||||
=> new Result(true, false, null);
|
||||
|
||||
public static Result Fault()
|
||||
=> new Result(false, false, null);
|
||||
|
||||
public static Result Next()
|
||||
=> new Result(true, true, null);
|
||||
|
||||
public static Result Next<T>()
|
||||
=> new Result(true, true, typeof(T));
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
{
|
||||
private readonly MethodInfo Method = method;
|
||||
|
||||
public override async Task Execute(IAbstractHandlerContainer<TUpdate> container, CancellationToken cancellation)
|
||||
public override async Task<Result> Execute(IAbstractHandlerContainer<TUpdate> container, CancellationToken cancellation)
|
||||
{
|
||||
if (Method is null)
|
||||
throw new Exception();
|
||||
@@ -44,16 +44,15 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
if (Method.ReturnType == typeof(void))
|
||||
{
|
||||
Method.Invoke(this, [container, cancellation]);
|
||||
return;
|
||||
return Result.Ok();
|
||||
}
|
||||
else
|
||||
{
|
||||
object branchReturn = Method.Invoke(this, [container, cancellation]);
|
||||
if (branchReturn == null)
|
||||
return;
|
||||
if (branchReturn is not Task<Result> branchTask)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
if (branchReturn is Task branchTask)
|
||||
await branchTask;
|
||||
return await branchTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Telegram.Bot.Polling;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.Handlers;
|
||||
using Telegrator.Handlers.Components;
|
||||
|
||||
namespace Telegrator.MadiatorCore.Descriptors
|
||||
@@ -81,36 +82,43 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
/// <exception cref="Exception">Thrown if the handler lifetime has ended or the handler is not a container factory.</exception>
|
||||
public async Task Execute(CancellationToken cancellationToken)
|
||||
public async Task<Result> Execute(CancellationToken cancellationToken)
|
||||
{
|
||||
if (HandlerLifetime.IsEnded)
|
||||
throw new Exception();
|
||||
|
||||
IHandlerContainerFactory? containerFactory = HandlerInstance is IHandlerContainerFactory handlerDefainedContainerFactory
|
||||
? handlerDefainedContainerFactory
|
||||
: UpdateRouter.DefaultContainerFactory is not null
|
||||
? UpdateRouter.DefaultContainerFactory
|
||||
: throw new Exception();
|
||||
|
||||
try
|
||||
{
|
||||
HandlerContainer = containerFactory.CreateContainer(UpdateRouter.AwaitingProvider, this);
|
||||
await HandlerInstance.Execute(HandlerContainer, cancellationToken);
|
||||
HandlerContainer = GetContainer(UpdateRouter.AwaitingProvider, this);
|
||||
return await HandlerInstance.Execute(HandlerContainer, cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Cancelled
|
||||
_ = 0xBAD + 0xC0DE;
|
||||
return;
|
||||
return Result.Ok();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
await UpdateRouter
|
||||
.HandleErrorAsync(Client, exception, HandleErrorSource.HandleUpdateError, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return Result.Fault();
|
||||
}
|
||||
}
|
||||
|
||||
private IHandlerContainer GetContainer(IAwaitingProvider awaitingProvider, DescribedHandlerInfo handlerInfo)
|
||||
{
|
||||
if (HandlerInstance is IHandlerContainerFactory handlerDefainedContainerFactory)
|
||||
return handlerDefainedContainerFactory.CreateContainer(awaitingProvider, handlerInfo);
|
||||
|
||||
if (UpdateRouter.DefaultContainerFactory is not null)
|
||||
return UpdateRouter.DefaultContainerFactory.CreateContainer(awaitingProvider, handlerInfo);
|
||||
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
=> DisplayString ?? HandlerInstance.GetType().Name;
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace Telegrator.MadiatorCore
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="DescribedHandlerInfo"/> for the enqueued handler.</param>
|
||||
public delegate void HandlerEnqueued(DescribedHandlerInfo args);
|
||||
|
||||
/// <summary>
|
||||
/// Represents a delegate for when a handler is executing.
|
||||
/// </summary>
|
||||
@@ -24,7 +25,7 @@ namespace Telegrator.MadiatorCore
|
||||
public event HandlerEnqueued? HandlerEnqueued;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a handler is executing.
|
||||
/// Occurs when a handler is entering execution.
|
||||
/// </summary>
|
||||
public event HandlerExecuting? HandlerExecuting;
|
||||
|
||||
@@ -32,8 +33,9 @@ namespace Telegrator.MadiatorCore
|
||||
/// Enqueues a collection of handlers for execution.
|
||||
/// </summary>
|
||||
/// <param name="handlers">The handlers to enqueue.</param>
|
||||
public void Enqueue(IEnumerable<DescribedHandlerInfo> handlers);
|
||||
public Task Enqueue(IEnumerable<DescribedHandlerInfo> handlers);
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Enqueues a single handler for execution.
|
||||
/// </summary>
|
||||
@@ -45,5 +47,6 @@ namespace Telegrator.MadiatorCore
|
||||
/// </summary>
|
||||
/// <param name="token">The <see cref="HandlerLifetimeToken"/> of the handler to dequeue.</param>
|
||||
public void Dequeue(HandlerLifetimeToken token);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Telegrator.Polling
|
||||
{
|
||||
public class LimitedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IDisposable
|
||||
{
|
||||
private readonly int? _maximum;
|
||||
private readonly SemaphoreSlim _semaphore = null!;
|
||||
private readonly ConcurrentDictionary<TKey, TValue> _dict = [];
|
||||
|
||||
public LimitedDictionary(int? maximum)
|
||||
{
|
||||
_maximum = maximum;
|
||||
if (maximum != null)
|
||||
{
|
||||
int value = maximum.Value;
|
||||
_semaphore = new SemaphoreSlim(value, value);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> Add(TKey key, TValue value, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_semaphore != null)
|
||||
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return _dict.TryAdd(key, value);
|
||||
}
|
||||
|
||||
public bool Remove(TKey key, out TValue result)
|
||||
{
|
||||
if (_dict.TryRemove(key, out result))
|
||||
{
|
||||
_semaphore?.Release(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _dict.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _dict.GetEnumerator();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
_semaphore.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Telegrator.Polling
|
||||
{
|
||||
public class LimitedQueue<T>
|
||||
{
|
||||
private readonly int? _maximum;
|
||||
private readonly ConcurrentQueue<T> _queue = [];
|
||||
private readonly SemaphoreSlim _semaphore = null!;
|
||||
|
||||
public LimitedQueue(int? maximum)
|
||||
{
|
||||
_maximum = maximum;
|
||||
if (maximum != null)
|
||||
{
|
||||
int value = maximum.Value;
|
||||
_semaphore = new SemaphoreSlim(value, value);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Enqueue(T item, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_maximum.HasValue)
|
||||
await _semaphore.WaitAsync(cancellationToken);
|
||||
|
||||
_queue.Enqueue(item);
|
||||
}
|
||||
|
||||
public bool Dequeue(out T result)
|
||||
{
|
||||
if (_queue.TryDequeue(out result))
|
||||
{
|
||||
if (_maximum.HasValue)
|
||||
_semaphore?.Release(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Telegrator.Handlers;
|
||||
using Telegrator.MadiatorCore;
|
||||
using Telegrator.MadiatorCore.Descriptors;
|
||||
|
||||
@@ -15,6 +16,7 @@ namespace Telegrator.Polling
|
||||
/// </summary>
|
||||
protected object SyncObj = new object();
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Event that signals when awaiting handlers are queued.
|
||||
/// </summary>
|
||||
@@ -34,6 +36,13 @@ namespace Telegrator.Polling
|
||||
/// Dictionary for tracking currently executing handlers.
|
||||
/// </summary>
|
||||
protected readonly ConcurrentDictionary<HandlerLifetimeToken, Task> ExecutingHandlersPool = [];
|
||||
*/
|
||||
|
||||
//protected readonly ConcurrentDictionary<Type, LimitedQueue<DescribedHandlerInfo>> AwaitingHandlersQueue;
|
||||
|
||||
//protected readonly LimitedDictionary<HandlerLifetimeToken, Task> ExecutingHandlersPool;
|
||||
|
||||
protected SemaphoreSlim ExecutingHandlersSemaphore = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The bot configuration options.
|
||||
@@ -65,26 +74,62 @@ namespace Telegrator.Polling
|
||||
{
|
||||
Options = options;
|
||||
GlobalCancellationToken = globalCancellationToken;
|
||||
//AwaitingHandlersQueue = new ConcurrentDictionary<Type, LimitedQueue<DescribedHandlerInfo>>();
|
||||
//ExecutingHandlersPool = new LimitedDictionary<HandlerLifetimeToken, Task>(options.MaximumParallelWorkingHandlers);
|
||||
|
||||
if (options.MaximumParallelWorkingHandlers != null)
|
||||
{
|
||||
ExecutingHandlersSemaphore = new SemaphoreSlim(options.MaximumParallelWorkingHandlers ?? 0);
|
||||
AwaitingHandlersQueuedEvent = new ManualResetEventSlim(false);
|
||||
ExecutingHandlersSemaphore = new SemaphoreSlim(options.MaximumParallelWorkingHandlers.Value);
|
||||
//AwaitingHandlersQueuedEvent = new ManualResetEventSlim(false);
|
||||
}
|
||||
|
||||
/*
|
||||
if (Options.MaximumParallelWorkingHandlers != null)
|
||||
HandlersCheckpoint();
|
||||
*/
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Enqueue(IEnumerable<DescribedHandlerInfo> handlers)
|
||||
public async Task Enqueue(IEnumerable<DescribedHandlerInfo> handlers)
|
||||
{
|
||||
handlers.ForEach(Enqueue);
|
||||
//handlers.ForEach(Enqueue);
|
||||
|
||||
Result? lastResult = null;
|
||||
foreach (DescribedHandlerInfo handlerInfo in handlers)
|
||||
{
|
||||
if (lastResult?.NextType != null)
|
||||
{
|
||||
if (lastResult.NextType != handlerInfo.HandlerInstance.GetType())
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ExecutingHandlersSemaphore != null)
|
||||
{
|
||||
await ExecutingHandlersSemaphore.WaitAsync();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
HandlerExecuting?.Invoke(handlerInfo);
|
||||
lastResult = await handlerInfo.Execute(GlobalCancellationToken);
|
||||
ExecutingHandlersSemaphore?.Release(1);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!lastResult.RouteNext)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// <inheritdoc/>
|
||||
public void Enqueue(DescribedHandlerInfo handlerInfo)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
if (Options.MaximumParallelWorkingHandlers == null)
|
||||
{
|
||||
Task.Run(async () => await ExecuteHandlerWrapper(handlerInfo));
|
||||
@@ -111,7 +156,9 @@ namespace Telegrator.Polling
|
||||
ExecutingHandlersSemaphore.Release(1);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Main checkpoint method that manages handler execution in a loop.
|
||||
/// Continuously processes queued handlers while respecting concurrency limits.
|
||||
@@ -206,6 +253,7 @@ namespace Telegrator.Polling
|
||||
return AwaitingHandlersQueue.TryDequeue(out enqueuedHandler);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of the handlers pool and releases all resources.
|
||||
@@ -221,11 +269,13 @@ namespace Telegrator.Polling
|
||||
ExecutingHandlersSemaphore = null!;
|
||||
}
|
||||
|
||||
/*
|
||||
if (AwaitingHandlersQueuedEvent != null)
|
||||
{
|
||||
AwaitingHandlersQueuedEvent.Dispose();
|
||||
AwaitingHandlersQueuedEvent = null!;
|
||||
}
|
||||
*/
|
||||
|
||||
if (SyncObj != null)
|
||||
SyncObj = null!;
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace Telegrator.Polling
|
||||
/// <param name="update">The update to handle.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous update handling operation.</returns>
|
||||
public virtual Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
|
||||
public virtual async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
|
||||
{
|
||||
// Logging
|
||||
Alligator.RouterWriteLine("Received Update ({0}) of type \"{1}\"", update.Id, update.Type);
|
||||
@@ -109,31 +109,28 @@ namespace Telegrator.Polling
|
||||
if (handlers.Any())
|
||||
{
|
||||
// Enqueuing found awiting handlers
|
||||
HandlersPool.Enqueue(handlers);
|
||||
await HandlersPool.Enqueue(handlers);
|
||||
|
||||
// Chicking if awaiting handlers has exclusive routing
|
||||
if (Options.ExclusiveAwaitingHandlerRouting)
|
||||
{
|
||||
Alligator.RouterWriteLine("Receiving Update ({0}) completed with only awaiting handlers", update.Id);
|
||||
return Task.CompletedTask;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Queuing reagular handlers for execution
|
||||
HandlersPool.Enqueue(GetHandlers(HandlersProvider, this, botClient, update, cancellationToken));
|
||||
await HandlersPool.Enqueue(GetHandlers(HandlersProvider, this, botClient, update, cancellationToken));
|
||||
Alligator.RouterWriteLine("Receiving Update ({0}) finished", update.Id);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Alligator.RouterWriteLine("Receiving Update ({0}) cancelled", update.Id);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Alligator.RouterWriteLine("Receiving Update ({0}) finished with exception {1}", update.Id, ex.Message);
|
||||
ExceptionHandler?.HandleException(botClient, ex, HandleErrorSource.PollingError, cancellationToken);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,10 +159,11 @@ namespace Telegrator.Polling
|
||||
return [];
|
||||
}
|
||||
|
||||
IEnumerable<DescribedHandlerInfo> described = DescribeDescriptors(provider, descriptors, updateRouter, client, update, cancellationToken);
|
||||
Alligator.RouterWriteLine("Described total of {0} handlers for Update ({1}) from {2} provider", described.Count(), update.Id, provider.GetType().Name);
|
||||
Alligator.RouterWriteLine("Described handlers : {0}", string.Join(", ", described));
|
||||
return described;
|
||||
//IEnumerable<DescribedHandlerInfo> described = DescribeDescriptors(provider, descriptors, updateRouter, client, update, cancellationToken);
|
||||
//Alligator.RouterWriteLine("Described total of {0} handlers for Update ({1}) from {2} provider", described.Count(), update.Id, provider.GetType().Name);
|
||||
//Alligator.RouterWriteLine("Described handlers : {0}", string.Join(", ", described));
|
||||
|
||||
return DescribeDescriptors(provider, descriptors, updateRouter, client, update, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -192,8 +190,11 @@ namespace Telegrator.Polling
|
||||
continue;
|
||||
|
||||
yield return describedHandler;
|
||||
|
||||
/*
|
||||
if (Options.ExecuteOnlyFirstFoundHanlder)
|
||||
break;
|
||||
*/
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -8,23 +8,24 @@ namespace Telegrator
|
||||
/// </summary>
|
||||
public class TelegratorOptions : ITelegratorOptions
|
||||
{
|
||||
/*
|
||||
/// <inheritdoc/>
|
||||
public bool ExecuteOnlyFirstFoundHanlder { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool DescendDescriptorIndex { get; set; } = true;
|
||||
*/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int? MaximumParallelWorkingHandlers { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool ExclusiveAwaitingHandlerRouting { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public CancellationToken GlobalCancellationToken { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool DescendDescriptorIndex { get; set; } = true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool ExceptIntersectingCommandAliases { get; set; } = true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public CancellationToken GlobalCancellationToken { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user