diff --git a/Telegrator.Hosting/Polling/HostedUpdateReceiver.cs b/Telegrator.Hosting/Polling/HostedUpdateReceiver.cs index bb94ec3..3a5bc52 100644 --- a/Telegrator.Hosting/Polling/HostedUpdateReceiver.cs +++ b/Telegrator.Hosting/Polling/HostedUpdateReceiver.cs @@ -27,7 +27,7 @@ namespace Telegrator.Hosting.Polling { logger.LogInformation("Starting receiving updates via long-polling"); _receiverOptions.AllowedUpdates = botHost.UpdateRouter.HandlersProvider.AllowedTypes.ToArray(); - ReactiveUpdateReceiver updateReceiver = new ReactiveUpdateReceiver(botClient, _receiverOptions); + DefaultUpdateReceiver updateReceiver = new DefaultUpdateReceiver(botClient, _receiverOptions); await updateReceiver.ReceiveAsync(_updateRouter, stoppingToken).ConfigureAwait(false); } } diff --git a/Telegrator/Attributes/DontCollectAttribute.cs b/Telegrator/Annotations/DontCollectAttribute.cs similarity index 91% rename from Telegrator/Attributes/DontCollectAttribute.cs rename to Telegrator/Annotations/DontCollectAttribute.cs index c9766b7..77bd276 100644 --- a/Telegrator/Attributes/DontCollectAttribute.cs +++ b/Telegrator/Annotations/DontCollectAttribute.cs @@ -1,4 +1,4 @@ -namespace Telegrator.Attributes +namespace Telegrator.Annotations { /// /// Attribute that prevents a class from being automatically collected by the handler collection system. diff --git a/Telegrator/Attributes/MightAwaitAttribute.cs b/Telegrator/Annotations/MightAwaitAttribute.cs similarity index 96% rename from Telegrator/Attributes/MightAwaitAttribute.cs rename to Telegrator/Annotations/MightAwaitAttribute.cs index 685550d..0d35886 100644 --- a/Telegrator/Attributes/MightAwaitAttribute.cs +++ b/Telegrator/Annotations/MightAwaitAttribute.cs @@ -1,6 +1,6 @@ using Telegram.Bot.Types.Enums; -namespace Telegrator.Attributes +namespace Telegrator.Annotations { /// /// Attribute that says if this handler can await some of await types, that is not listed by its handler base. diff --git a/Telegrator/Annotations/Targetted/WelcomeAttribute.cs b/Telegrator/Annotations/Targetted/WelcomeAttribute.cs index 590f36c..dece1b4 100644 --- a/Telegrator/Annotations/Targetted/WelcomeAttribute.cs +++ b/Telegrator/Annotations/Targetted/WelcomeAttribute.cs @@ -14,7 +14,10 @@ namespace Telegrator.Annotations.Targetted /// Creates new instance of /// /// - public WelcomeAttribute(bool onlyFirst = false) : base(new MessageChatTypeFilter(ChatType.Private), new CommandAlliasFilter("start"), Filter.If(ctx => !onlyFirst || ctx.Input.Id == 0)) + public WelcomeAttribute(bool onlyFirst = false) : base( + new MessageChatTypeFilter(ChatType.Private), + new CommandAlliasFilter("start"), + Filter.If(ctx => !onlyFirst || ctx.Input.Id == 0)) { } } } diff --git a/Telegrator/Exceptions.cs b/Telegrator/Exceptions.cs index 28cbac6..6bc5ec4 100644 --- a/Telegrator/Exceptions.cs +++ b/Telegrator/Exceptions.cs @@ -36,14 +36,14 @@ namespace Telegrator /// /// The handler info associated with the faulted handler. /// - public readonly DescribedHandlerInfo HandlerInfo; + public readonly DescribedHandlerDescriptor HandlerInfo; /// /// Initializes a new instance of the class. /// /// The handler info. /// The inner exception. - public HandlerFaultedException(DescribedHandlerInfo handlerInfo, Exception inner) + public HandlerFaultedException(DescribedHandlerDescriptor handlerInfo, Exception inner) : base(string.Format("Handler's \"{0}\" execution was faulted", handlerInfo.DisplayString), inner) { HandlerInfo = handlerInfo; diff --git a/Telegrator/Filters/Components/AnonymousTypeFilter.cs b/Telegrator/Filters/Components/AnonymousTypeFilter.cs index 2dddc98..7b194ba 100644 --- a/Telegrator/Filters/Components/AnonymousTypeFilter.cs +++ b/Telegrator/Filters/Components/AnonymousTypeFilter.cs @@ -42,8 +42,7 @@ namespace Telegrator.Filters.Components public static AnonymousTypeFilter Compile(IFilter filter, Func getFilterringTarget) where T : class { return new AnonymousTypeFilter( - filter.GetType().Name, - getFilterringTarget, + filter.GetType().Name, getFilterringTarget, (context, filterringTarget) => CanPassInternal(context, filter, filterringTarget)); } diff --git a/Telegrator/Handlers/Building/AwaiterHandler.cs b/Telegrator/Handlers/Building/AwaiterHandler.cs index f9b3acd..ef21c80 100644 --- a/Telegrator/Handlers/Building/AwaiterHandler.cs +++ b/Telegrator/Handlers/Building/AwaiterHandler.cs @@ -37,7 +37,7 @@ namespace Telegrator.Handlers.Building /// /// The handler information containing the update. /// An empty handler container. - public IHandlerContainer CreateContainer(DescribedHandlerInfo describedHandler) + public IHandlerContainer CreateContainer(DescribedHandlerDescriptor describedHandler) { HandlingUpdate = describedHandler.HandlingUpdate; return new EmptyHandlerContainer(); diff --git a/Telegrator/Handlers/Components/AbstractUpdateHandler.cs b/Telegrator/Handlers/Components/AbstractUpdateHandler.cs index f0b8d2e..075cdcd 100644 --- a/Telegrator/Handlers/Components/AbstractUpdateHandler.cs +++ b/Telegrator/Handlers/Components/AbstractUpdateHandler.cs @@ -62,7 +62,7 @@ namespace Telegrator.Handlers.Components /// /// The handler descriptor info. /// The created handler container. - public virtual IHandlerContainer CreateContainer(DescribedHandlerInfo handlerInfo) + public virtual IHandlerContainer CreateContainer(DescribedHandlerDescriptor handlerInfo) { return new HandlerContainer(handlerInfo); } diff --git a/Telegrator/Handlers/Components/BranchingUpdateHandler.cs b/Telegrator/Handlers/Components/BranchingUpdateHandler.cs index ab8061d..87484d2 100644 --- a/Telegrator/Handlers/Components/BranchingUpdateHandler.cs +++ b/Telegrator/Handlers/Components/BranchingUpdateHandler.cs @@ -106,7 +106,7 @@ namespace Telegrator.Handlers.Components /// The handler information. /// A handler container for this branching handler. /// Thrown when the awaiting provider is not of the expected type. - public override IHandlerContainer CreateContainer(DescribedHandlerInfo handlerInfo) + public override IHandlerContainer CreateContainer(DescribedHandlerDescriptor handlerInfo) { return new HandlerContainer(handlerInfo); } diff --git a/Telegrator/Handlers/Components/IHandlerContainerFactory.cs b/Telegrator/Handlers/Components/IHandlerContainerFactory.cs index 8fc9b1c..c6f91b1 100644 --- a/Telegrator/Handlers/Components/IHandlerContainerFactory.cs +++ b/Telegrator/Handlers/Components/IHandlerContainerFactory.cs @@ -12,8 +12,8 @@ namespace Telegrator.Handlers.Components /// /// Creates a new for the specified awaiting provider and handler info. /// - /// The for the handler. + /// The for the handler. /// A new instance. - public IHandlerContainer CreateContainer(DescribedHandlerInfo handlerInfo); + public IHandlerContainer CreateContainer(DescribedHandlerDescriptor handlerInfo); } } diff --git a/Telegrator/Handlers/Components/IUpdateHandlerBase.cs b/Telegrator/Handlers/Components/IUpdateHandlerBase.cs index a19085f..77f0c73 100644 --- a/Telegrator/Handlers/Components/IUpdateHandlerBase.cs +++ b/Telegrator/Handlers/Components/IUpdateHandlerBase.cs @@ -26,7 +26,7 @@ public interface IUpdateHandlerBase : IDisposable /// /// The cancellation token. /// A representing the asynchronous operation. - Task Execute(DescribedHandlerInfo described, CancellationToken cancellationToken = default); + Task Execute(DescribedHandlerDescriptor described, CancellationToken cancellationToken = default); /// /// Handles failed filters during handler describing. diff --git a/Telegrator/Handlers/Components/UpdateHandlerBase.cs b/Telegrator/Handlers/Components/UpdateHandlerBase.cs index a4acb7c..e672577 100644 --- a/Telegrator/Handlers/Components/UpdateHandlerBase.cs +++ b/Telegrator/Handlers/Components/UpdateHandlerBase.cs @@ -22,13 +22,13 @@ namespace Telegrator.Handlers.Components public HandlerLifetimeToken LifetimeToken { get; } = new HandlerLifetimeToken(); /// - public Result Ok => Result.Ok(); + public static Result Ok => Result.Ok(); /// - public Result Fault => Result.Fault(); + public static Result Fault => Result.Fault(); /// - public Result Next => Result.Next(); + public static Result Next => Result.Next(); /// /// Executes the handler logic and marks the lifetime as ended after execution. @@ -36,7 +36,7 @@ namespace Telegrator.Handlers.Components /// /// The cancellation token. /// A representing the asynchronous operation. - public async Task Execute(DescribedHandlerInfo described, CancellationToken cancellationToken = default) + public async Task Execute(DescribedHandlerDescriptor described, CancellationToken cancellationToken = default) { if (LifetimeToken.IsEnded) throw new Exception(); @@ -125,7 +125,7 @@ namespace Telegrator.Handlers.Components } } - internal IHandlerContainer GetContainer(DescribedHandlerInfo handlerInfo) + internal IHandlerContainer GetContainer(DescribedHandlerDescriptor handlerInfo) { if (this is IHandlerContainerFactory handlerDefainedContainerFactory) return handlerDefainedContainerFactory.CreateContainer(handlerInfo); diff --git a/Telegrator/Handlers/HandlerContainer.cs b/Telegrator/Handlers/HandlerContainer.cs index ffbdd89..5046870 100644 --- a/Telegrator/Handlers/HandlerContainer.cs +++ b/Telegrator/Handlers/HandlerContainer.cs @@ -37,7 +37,7 @@ namespace Telegrator.Handlers /// Initializes new instance of /// /// - public HandlerContainer(DescribedHandlerInfo handlerInfo) + public HandlerContainer(DescribedHandlerDescriptor handlerInfo) { ActualUpdate = handlerInfo.HandlingUpdate.GetActualUpdateObject(); HandlingUpdate = handlerInfo.HandlingUpdate; diff --git a/Telegrator/Handlers/MessageHandler.cs b/Telegrator/Handlers/MessageHandler.cs index 06bdb13..cf823b3 100644 --- a/Telegrator/Handlers/MessageHandler.cs +++ b/Telegrator/Handlers/MessageHandler.cs @@ -197,7 +197,7 @@ namespace Telegrator.Handlers int? directMessageTopicId = null, SuggestedPostParameters? suggestedPostParameters = null, CancellationToken cancellationToken = default) - => await Container.Response( + => await Container.Responce( text, parseMode, replyParameters, replyMarkup, linkPreviewOptions, messageThreadId, entities, diff --git a/Telegrator/MadiatorCore/Descriptors/DescribedHandlerInfo.cs b/Telegrator/MadiatorCore/Descriptors/DescribedHandlerDescriptor.cs similarity index 63% rename from Telegrator/MadiatorCore/Descriptors/DescribedHandlerInfo.cs rename to Telegrator/MadiatorCore/Descriptors/DescribedHandlerDescriptor.cs index 98b917b..5c379c6 100644 --- a/Telegrator/MadiatorCore/Descriptors/DescribedHandlerInfo.cs +++ b/Telegrator/MadiatorCore/Descriptors/DescribedHandlerDescriptor.cs @@ -1,8 +1,6 @@ using Telegram.Bot; -using Telegram.Bot.Polling; using Telegram.Bot.Types; using Telegrator.Filters.Components; -using Telegrator.Handlers; using Telegrator.Handlers.Components; namespace Telegrator.MadiatorCore.Descriptors @@ -10,47 +8,49 @@ namespace Telegrator.MadiatorCore.Descriptors /// /// Contains information about a described handler, including its context, client, and execution logic. /// - public class DescribedHandlerInfo + public class DescribedHandlerDescriptor { + private readonly ManualResetEventSlim ResetEvent = new ManualResetEventSlim(false); + /// /// descriptor from that handler was described from /// - public readonly HandlerDescriptor From; + public HandlerDescriptor From { get; } /// /// The update router associated with this handler. /// - public readonly IUpdateRouter UpdateRouter; + public IUpdateRouter UpdateRouter { get; } /// /// The awaiting provider to fetch new updates inside handler /// - public readonly IAwaitingProvider AwaitingProvider; + public IAwaitingProvider AwaitingProvider { get; } /// /// The Telegram bot client used for this handler. /// - public readonly ITelegramBotClient Client; + public ITelegramBotClient Client { get; } /// /// The handler instance being described. /// - public readonly UpdateHandlerBase HandlerInstance; + public UpdateHandlerBase HandlerInstance { get; } /// /// Extra data associated with the handler execution. /// - public readonly Dictionary ExtraData; + public Dictionary ExtraData { get; } /// /// List of completed filters for this handler. /// - public readonly CompletedFiltersList CompletedFilters; + public CompletedFiltersList CompletedFilters { get; } /// /// The update being handled. /// - public readonly Update HandlingUpdate; + public Update HandlingUpdate { get; } /// /// Lifetime token for the handler instance. @@ -63,7 +63,12 @@ namespace Telegrator.MadiatorCore.Descriptors public string DisplayString { get; set; } /// - /// Initializes a new instance of the class. + /// The final execution result. + /// + public Result? Result { get; private set; } + + /// + /// Initializes a new instance of the class. /// /// descriptor from that handler was described from /// @@ -72,7 +77,14 @@ namespace Telegrator.MadiatorCore.Descriptors /// The handler instance. /// The filter execution context. /// Optional display string. - public DescribedHandlerInfo(HandlerDescriptor fromDescriptor, IUpdateRouter updateRouter, IAwaitingProvider awaitingProvider, ITelegramBotClient client, UpdateHandlerBase handlerInstance, FilterExecutionContext filterContext, string? displayString) + public DescribedHandlerDescriptor( + HandlerDescriptor fromDescriptor, + IUpdateRouter updateRouter, + IAwaitingProvider awaitingProvider, + ITelegramBotClient client, + UpdateHandlerBase handlerInstance, + FilterExecutionContext filterContext, + string? displayString) { From = fromDescriptor; UpdateRouter = updateRouter; @@ -85,6 +97,22 @@ namespace Telegrator.MadiatorCore.Descriptors DisplayString = displayString ?? fromDescriptor.HandlerType.Name; } + public async Task AwaitResult(CancellationToken cancellationToken) + { + await Task.Yield(); + ResetEvent.Reset(); + ResetEvent.Wait(cancellationToken); + } + + public void ReportResult(Result result) + { + if (result != null) + throw new InvalidOperationException("Result already reported"); + + Result = result; + ResetEvent.Set(); + } + /// public override string ToString() => DisplayString ?? From.HandlerType.Name; diff --git a/Telegrator/MadiatorCore/IRouterExceptionHandler.cs b/Telegrator/MadiatorCore/IRouterExceptionHandler.cs index 34f6ca8..f421523 100644 --- a/Telegrator/MadiatorCore/IRouterExceptionHandler.cs +++ b/Telegrator/MadiatorCore/IRouterExceptionHandler.cs @@ -1,6 +1,5 @@ using Telegram.Bot; using Telegram.Bot.Polling; -using Telegrator.Polling; namespace Telegrator.MadiatorCore { diff --git a/Telegrator/MadiatorCore/IUpdateHandlersPool.cs b/Telegrator/MadiatorCore/IUpdateHandlersPool.cs index 82b8817..e5d4d2e 100644 --- a/Telegrator/MadiatorCore/IUpdateHandlersPool.cs +++ b/Telegrator/MadiatorCore/IUpdateHandlersPool.cs @@ -5,14 +5,14 @@ namespace Telegrator.MadiatorCore /// /// Represents a delegate for when a handler is enqueued. /// - /// The for the enqueued handler. - public delegate void HandlerEnqueued(DescribedHandlerInfo args); + /// The for the enqueued handler. + public delegate void HandlerEnqueued(DescribedHandlerDescriptor args); /// /// Represents a delegate for when a handler is executing. /// - /// The for the executing handler. - public delegate void HandlerExecuting(DescribedHandlerInfo args); + /// The for the executing handler. + public delegate void HandlerExecuting(DescribedHandlerDescriptor args); /// /// Provides a pool for managing the execution and queuing of update handlers. @@ -33,20 +33,6 @@ namespace Telegrator.MadiatorCore /// Enqueues a collection of handlers for execution. /// /// The handlers to enqueue. - public Task Enqueue(IEnumerable handlers); - - /* - /// - /// Enqueues a single handler for execution. - /// - /// The handler to enqueue. - public void Enqueue(DescribedHandlerInfo handlerInfo); - - /// - /// Dequeues a handler using its lifetime token. - /// - /// The of the handler to dequeue. - public void Dequeue(HandlerLifetimeToken token); - */ + public Task Enqueue(params IEnumerable handlers); } } diff --git a/Telegrator/Polling/ReactiveUpdateReceiver.cs b/Telegrator/Polling/DefaultUpdateReceiver.cs similarity index 97% rename from Telegrator/Polling/ReactiveUpdateReceiver.cs rename to Telegrator/Polling/DefaultUpdateReceiver.cs index a4d5b68..4edbd8e 100644 --- a/Telegrator/Polling/ReactiveUpdateReceiver.cs +++ b/Telegrator/Polling/DefaultUpdateReceiver.cs @@ -11,7 +11,7 @@ namespace Telegrator.Polling /// /// The Telegram bot client for making API requests. /// Optional receiver options for configuring update polling behavior. - public class ReactiveUpdateReceiver(ITelegramBotClient client, ReceiverOptions? options) : IUpdateReceiver + public class DefaultUpdateReceiver(ITelegramBotClient client, ReceiverOptions? options) : IUpdateReceiver { /// /// Gets the receiver options for configuring update polling behavior. diff --git a/Telegrator/Polling/IUpdateReceiver.cs b/Telegrator/Polling/IUpdateReceiver.cs new file mode 100644 index 0000000..b81f73d --- /dev/null +++ b/Telegrator/Polling/IUpdateReceiver.cs @@ -0,0 +1,21 @@ +using Telegram.Bot.Polling; +using Telegram.Bot.Types; + +namespace Telegrator.Polling; + +/// +/// Requests new s and processes them using provided instance< +/// /summary> +public interface IUpdateReceiver +{ + /// + /// Starts receiving s invoking for each . + /// + /// This method will block if awaited. + /// + /// + /// The used for processing s + /// The with which you can stop receiving + /// A that will be completed when cancellation will be requested through + Task ReceiveAsync(IUpdateHandler updateHandler, CancellationToken cancellationToken = default); +} diff --git a/Telegrator/Polling/UpdateHandlersPool.cs b/Telegrator/Polling/UpdateHandlersPool.cs index 5b3dd49..3e1dd48 100644 --- a/Telegrator/Polling/UpdateHandlersPool.cs +++ b/Telegrator/Polling/UpdateHandlersPool.cs @@ -1,4 +1,6 @@ -using Telegrator.Handlers.Components; +using System.Security.Authentication.ExtendedProtection; +using System.Threading.Channels; +using Telegrator.Handlers.Components; using Telegrator.Logging; using Telegrator.MadiatorCore; using Telegrator.MadiatorCore.Descriptors; @@ -14,12 +16,18 @@ namespace Telegrator.Polling /// /// Synchronization object for thread-safe operations. /// - protected object SyncObj = new object(); + protected readonly object SyncObj = new object(); + + protected readonly Task ChannelReaderTask; + + protected readonly Channel ExecutionChannel; /// /// Semaphore for controlling the number of concurrently executing handlers. /// - protected SemaphoreSlim ExecutingHandlersSemaphore = null!; + protected readonly SemaphoreSlim? ExecutionLimiter; + + protected readonly IUpdateRouter UpdateRouter; /// /// The bot configuration options. @@ -45,72 +53,101 @@ namespace Telegrator.Polling /// /// Initializes a new instance of the class. /// + /// The update handler that claims updates /// The bot configuration options. /// The global cancellation token. - public UpdateHandlersPool(TelegratorOptions options, CancellationToken globalCancellationToken) + public UpdateHandlersPool(IUpdateRouter router, TelegratorOptions options, CancellationToken globalCancellationToken) { + UpdateRouter = router; Options = options; GlobalCancellationToken = globalCancellationToken; + + ExecutionChannel = Channel.CreateUnbounded(new UnboundedChannelOptions() + { + SingleReader = true, + SingleWriter = true, + AllowSynchronousContinuations = false + }); if (options.MaximumParallelWorkingHandlers != null) - { - ExecutingHandlersSemaphore = new SemaphoreSlim(options.MaximumParallelWorkingHandlers.Value); - } + ExecutionLimiter = new SemaphoreSlim(options.MaximumParallelWorkingHandlers.Value); + + GlobalCancellationToken.Register(() => ExecutionChannel.Writer.Complete()); + ChannelReaderTask = ReadChannel(); } /// - public async Task Enqueue(IEnumerable handlers) + public async Task Enqueue(params IEnumerable handlers) { - Result? lastResult = null; - foreach (DescribedHandlerInfo handlerInfo in handlers) + try { - if (lastResult?.NextType != null) + foreach (DescribedHandlerDescriptor handlerInfo in handlers) { - if (lastResult.NextType != handlerInfo.From.HandlerType) - continue; - } + if (handlerInfo.UpdateRouter != UpdateRouter) + throw new InvalidOperationException("Tried to enqueue update handler info from other router."); - if (ExecutingHandlersSemaphore != null) + await ExecutionChannel.Writer.WriteAsync(handlerInfo, GlobalCancellationToken); + } + } + catch (OperationCanceledException) + { + _ = 0xDEADBEEF; + } + } + + private async Task ReadChannel() + { + try + { + await foreach (DescribedHandlerDescriptor handlerInfo in ExecutionChannel.Reader.ReadAllAsync(GlobalCancellationToken)) { - await ExecutingHandlersSemaphore.WaitAsync().ConfigureAwait(false); - } + if (ExecutionLimiter != null) + await ExecutionLimiter.WaitAsync(GlobalCancellationToken); - try + // Как только слот получен, "отстреливаем" задачу в ThreadPool + // и идем на следующий круг цикла за новым обработчиком из канала. + _ = ProcessHandler(handlerInfo); + } + } + catch (ChannelClosedException) + { + // TODO: add logging + } + } + + private async Task ProcessHandler(DescribedHandlerDescriptor handlerInfo) + { + try + { + Alligator.LogDebug("Described handler '{0}' (Update {1})", handlerInfo.DisplayString, handlerInfo.HandlingUpdate.Id); + HandlerExecuting?.Invoke(handlerInfo); + + using UpdateHandlerBase instance = handlerInfo.HandlerInstance; + Task task = instance.Execute(handlerInfo); + HandlerEnqueued?.Invoke(handlerInfo); + + await task.ConfigureAwait(false); + Result lastResult = task.Result; + + handlerInfo.ReportResult(lastResult); + ExecutionLimiter?.Release(1); + + if (lastResult.RouteNext) { - Alligator.LogDebug("Described handler '{0}' (Update {1})", handlerInfo.DisplayString, handlerInfo.HandlingUpdate.Id); - HandlerExecuting?.Invoke(handlerInfo); - - using (UpdateHandlerBase instance = handlerInfo.HandlerInstance) - { - Task task = instance.Execute(handlerInfo); - HandlerEnqueued?.Invoke(handlerInfo); - - await task.ConfigureAwait(false); - lastResult = task.Result; - ExecutingHandlersSemaphore?.Release(1); - } - - if (lastResult.RouteNext) - { - Alligator.LogTrace("Handler '{0}' requested route continuation (Update {1})", handlerInfo.DisplayString, handlerInfo.HandlingUpdate.Id); - } + Alligator.LogTrace("Handler '{0}' requested route continuation (Update {1})", handlerInfo.DisplayString, handlerInfo.HandlingUpdate.Id); } - catch (NotImplementedException) - { - _ = 0xBAD + 0xC0DE; - } - catch (OperationCanceledException) - { - _ = 0xBAD + 0xC0DE; - break; - } - catch (Exception ex) - { - Alligator.LogError("Failed to process handler '{0}' (Update {1})", exception: ex, handlerInfo.DisplayString, handlerInfo.HandlingUpdate.Id); - } - - if (lastResult != null && !lastResult.RouteNext) - break; + } + catch (NotImplementedException) + { + _ = 0xBAD + 0xC0DE; + } + catch (OperationCanceledException) + { + _ = 0xDEADBEEF; + } + catch (Exception ex) + { + Alligator.LogError("Failed to process handler '{0}' (Update {1})", exception: ex, handlerInfo.DisplayString, handlerInfo.HandlingUpdate.Id); } } @@ -122,15 +159,9 @@ namespace Telegrator.Polling if (disposed) return; - if (ExecutingHandlersSemaphore != null) - { - ExecutingHandlersSemaphore.Dispose(); - ExecutingHandlersSemaphore = null!; - } - - if (SyncObj != null) - SyncObj = null!; - + // do not dispose UpdateRouter + ExecutionLimiter?.Dispose(); + GC.SuppressFinalize(this); disposed = true; } diff --git a/Telegrator/Polling/UpdateRouter.cs b/Telegrator/Polling/UpdateRouter.cs index 5091f0e..3c640f4 100644 --- a/Telegrator/Polling/UpdateRouter.cs +++ b/Telegrator/Polling/UpdateRouter.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Runtime.InteropServices; +using System.Text; using Telegram.Bot; using Telegram.Bot.Polling; using Telegram.Bot.Types; @@ -24,6 +25,7 @@ namespace Telegrator.Polling private readonly IAwaitingProvider _awaitingProvider; private readonly IUpdateHandlersPool _HandlersPool; private readonly ITelegramBotInfo _botInfo; + private readonly HandlerDescriptorList _handlingRoutes; /// public IHandlersProvider HandlersProvider => _handlersProvider; @@ -55,8 +57,9 @@ namespace Telegrator.Polling _options = options; _handlersProvider = handlersProvider; _awaitingProvider = awaitingProvider; - _HandlersPool = new UpdateHandlersPool(_options, _options.GlobalCancellationToken); + _HandlersPool = new UpdateHandlersPool(this, _options, _options.GlobalCancellationToken); _botInfo = botInfo; + _handlingRoutes = new HandlerDescriptorList(); } /// @@ -74,6 +77,7 @@ namespace Telegrator.Polling _awaitingProvider = awaitingProvider; _HandlersPool = handlersPool; _botInfo = botInfo; + _handlingRoutes = new HandlerDescriptorList(); } /// @@ -105,19 +109,32 @@ namespace Telegrator.Polling try { - // Getting handlers in update awaiting pool - IEnumerable handlers = GetHandlers(AwaitingProvider, botClient, update, cancellationToken); - if (handlers.Any()) + Result? lastResult = null; + foreach (DescribedHandlerDescriptor handlerInfo in GetHandlers(AwaitingProvider, botClient, update, cancellationToken)) { - // Enqueuing found awiting handlers - await HandlersPool.Enqueue(handlers); - - // Chicking if awaiting handlers has exclusive routing - if (Options.ExclusiveAwaitingHandlerRouting) + if (lastResult?.NextType != null) { - Alligator.LogTrace("Receiving Update ({0}) completed with only awaiting handlers", update.Id); - return; + if (lastResult.NextType != handlerInfo.From.HandlerType) + continue; } + + // Enqueuing found awiting handlers + await HandlersPool.Enqueue(handlerInfo); + await handlerInfo.AwaitResult(cancellationToken).ConfigureAwait(false); + + lastResult = handlerInfo.Result; + if (lastResult == null) + break; // Smth went horribly wrong, better to stop routing + + if (lastResult != null && !lastResult.RouteNext) + break; + } + + // Checking if awaiting handlers has exclusive routing + if (Options.ExclusiveAwaitingHandlerRouting) + { + Alligator.LogTrace("Receiving Update ({0}) completed with only awaiting handlers", update.Id); + return; } // Queuing reagular handlers for execution @@ -144,7 +161,7 @@ namespace Telegrator.Polling /// The incoming Telegram update to process /// /// A collection of described handler information for the update - protected virtual IEnumerable GetHandlers(IHandlersProvider provider, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default) + protected virtual IEnumerable GetHandlers(IHandlersProvider provider, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default) { Alligator.LogTrace("Requested handlers for UpdateType.{0}", update.Type); if (!provider.TryGetDescriptorList(update.Type, out HandlerDescriptorList? descriptors)) @@ -172,13 +189,13 @@ namespace Telegrator.Polling /// The incoming Telegram update to process /// /// A collection of described handler information - protected virtual IEnumerable DescribeDescriptors(IHandlersProvider provider, HandlerDescriptorList descriptors, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default) + protected virtual IEnumerable DescribeDescriptors(IHandlersProvider provider, HandlerDescriptorList descriptors, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default) { Alligator.LogTrace("Describing descriptors of descriptorsList.HandlingType.{0} for Update ({1})", descriptors.HandlingType, update.Id); foreach (HandlerDescriptor descriptor in descriptors.Reverse()) { cancellationToken.ThrowIfCancellationRequested(); - DescribedHandlerInfo? describedHandler = DescribeHandler(provider, descriptor, client, update, out bool breakRouting, cancellationToken); + DescribedHandlerDescriptor? describedHandler = DescribeHandler(provider, descriptor, client, update, out bool breakRouting, cancellationToken); if (breakRouting) yield break; @@ -202,7 +219,7 @@ namespace Telegrator.Polling /// /// /// The described handler info if validation passes; otherwise, null - public virtual DescribedHandlerInfo? DescribeHandler(IHandlersProvider provider, HandlerDescriptor descriptor, ITelegramBotClient client, Update update, out bool breakRouting, CancellationToken cancellationToken = default) + public virtual DescribedHandlerDescriptor? DescribeHandler(IHandlersProvider provider, HandlerDescriptor descriptor, ITelegramBotClient client, Update update, out bool breakRouting, CancellationToken cancellationToken = default) { breakRouting = false; cancellationToken.ThrowIfCancellationRequested(); @@ -231,7 +248,7 @@ namespace Telegrator.Polling } } - return new DescribedHandlerInfo(descriptor, this, AwaitingProvider, client, handlerInstance, filterContext, descriptor.DisplayString); + return new DescribedHandlerDescriptor(descriptor, this, AwaitingProvider, client, handlerInstance, filterContext, descriptor.DisplayString); } /// diff --git a/Telegrator/Providers/HandlersCollection.cs b/Telegrator/Providers/HandlersCollection.cs index 37c4f4b..2c41656 100644 --- a/Telegrator/Providers/HandlersCollection.cs +++ b/Telegrator/Providers/HandlersCollection.cs @@ -1,7 +1,6 @@ using System.Reflection; using Telegram.Bot.Types.Enums; using Telegrator.Annotations; -using Telegrator.Attributes; using Telegrator.Configuration; using Telegrator.MadiatorCore; using Telegrator.MadiatorCore.Descriptors; diff --git a/Telegrator/Providers/HandlersManagerBase.cs b/Telegrator/Providers/HandlersManagerBase.cs index 8f66be4..1883cf1 100644 --- a/Telegrator/Providers/HandlersManagerBase.cs +++ b/Telegrator/Providers/HandlersManagerBase.cs @@ -1,7 +1,6 @@ using System.Reflection; using Telegram.Bot.Types.Enums; using Telegrator.Annotations; -using Telegrator.Attributes; using Telegrator.Configuration; using Telegrator.Handlers.Components; using Telegrator.MadiatorCore; diff --git a/Telegrator/Result.cs b/Telegrator/Result.cs index 6b987a4..b195b43 100644 --- a/Telegrator/Result.cs +++ b/Telegrator/Result.cs @@ -1,9 +1,7 @@ -using Telegram.Bot.Types; -using Telegrator.Aspects; +using Telegrator.Aspects; using Telegrator.Handlers.Components; -using Telegrator.Filters.Components; -using Telegrator.MadiatorCore; using Telegrator.Handlers.Diagnostics; +using Telegrator.MadiatorCore; namespace Telegrator { diff --git a/Telegrator/Telegrator.csproj b/Telegrator/Telegrator.csproj index e542237..0f898c0 100644 --- a/Telegrator/Telegrator.csproj +++ b/Telegrator/Telegrator.csproj @@ -27,7 +27,8 @@ - + + diff --git a/Telegrator/TelegratorClient.cs b/Telegrator/TelegratorClient.cs index 4a08021..ce7057b 100644 --- a/Telegrator/TelegratorClient.cs +++ b/Telegrator/TelegratorClient.cs @@ -97,7 +97,7 @@ namespace Telegrator { try { - await new ReactiveUpdateReceiver(this, receiverOptions) + await new DefaultUpdateReceiver(this, receiverOptions) .ReceiveAsync(UpdateRouter, cancellationToken) .ConfigureAwait(false); }