* Renamed DescribeHandlerInfo to DescribedHandlerDescriptor
* Added AwaitResult and ReportResult methods to DescribedHandlerDescriptor * Moved DontCollectAttribute and MightAwaitAttribute to Annotations namespace * Rewrote UpdateRouter and UpdateHandlersPool * Fixed Synchronous handlers routing bug * Code cleanup
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace Telegrator.Attributes
|
||||
namespace Telegrator.Annotations
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute that prevents a class from being automatically collected by the handler collection system.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using Telegram.Bot.Types.Enums;
|
||||
|
||||
namespace Telegrator.Attributes
|
||||
namespace Telegrator.Annotations
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute that says if this handler can await some of await types, that is not listed by its handler base.
|
||||
@@ -14,7 +14,10 @@ namespace Telegrator.Annotations.Targetted
|
||||
/// Creates new instance of <see cref="WelcomeAttribute"/>
|
||||
/// </summary>
|
||||
/// <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))
|
||||
public WelcomeAttribute(bool onlyFirst = false) : base(
|
||||
new MessageChatTypeFilter(ChatType.Private),
|
||||
new CommandAlliasFilter("start"),
|
||||
Filter<Message>.If(ctx => !onlyFirst || ctx.Input.Id == 0))
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,14 +36,14 @@ namespace Telegrator
|
||||
/// <summary>
|
||||
/// The handler info associated with the faulted handler.
|
||||
/// </summary>
|
||||
public readonly DescribedHandlerInfo HandlerInfo;
|
||||
public readonly DescribedHandlerDescriptor HandlerInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HandlerFaultedException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="handlerInfo">The handler info.</param>
|
||||
/// <param name="inner">The inner exception.</param>
|
||||
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;
|
||||
|
||||
@@ -42,8 +42,7 @@ namespace Telegrator.Filters.Components
|
||||
public static AnonymousTypeFilter Compile<T>(IFilter<T> filter, Func<Update, T?> getFilterringTarget) where T : class
|
||||
{
|
||||
return new AnonymousTypeFilter(
|
||||
filter.GetType().Name,
|
||||
getFilterringTarget,
|
||||
filter.GetType().Name, getFilterringTarget,
|
||||
(context, filterringTarget) => CanPassInternal(context, filter, filterringTarget));
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Telegrator.Handlers.Building
|
||||
/// </summary>
|
||||
/// <param name="describedHandler">The handler information containing the update.</param>
|
||||
/// <returns>An empty handler container.</returns>
|
||||
public IHandlerContainer CreateContainer(DescribedHandlerInfo describedHandler)
|
||||
public IHandlerContainer CreateContainer(DescribedHandlerDescriptor describedHandler)
|
||||
{
|
||||
HandlingUpdate = describedHandler.HandlingUpdate;
|
||||
return new EmptyHandlerContainer();
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Telegrator.Handlers.Components
|
||||
/// </summary>
|
||||
/// <param name="handlerInfo">The handler descriptor info.</param>
|
||||
/// <returns>The created handler container.</returns>
|
||||
public virtual IHandlerContainer CreateContainer(DescribedHandlerInfo handlerInfo)
|
||||
public virtual IHandlerContainer CreateContainer(DescribedHandlerDescriptor handlerInfo)
|
||||
{
|
||||
return new HandlerContainer<TUpdate>(handlerInfo);
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Telegrator.Handlers.Components
|
||||
/// <param name="handlerInfo">The handler information.</param>
|
||||
/// <returns>A handler container for this branching handler.</returns>
|
||||
/// <exception cref="Exception">Thrown when the awaiting provider is not of the expected type.</exception>
|
||||
public override IHandlerContainer CreateContainer(DescribedHandlerInfo handlerInfo)
|
||||
public override IHandlerContainer CreateContainer(DescribedHandlerDescriptor handlerInfo)
|
||||
{
|
||||
return new HandlerContainer<TUpdate>(handlerInfo);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace Telegrator.Handlers.Components
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IHandlerContainer"/> for the specified awaiting provider and handler info.
|
||||
/// </summary>
|
||||
/// <param name="handlerInfo">The <see cref="DescribedHandlerInfo"/> for the handler.</param>
|
||||
/// <param name="handlerInfo">The <see cref="DescribedHandlerDescriptor"/> for the handler.</param>
|
||||
/// <returns>A new <see cref="IHandlerContainer"/> instance.</returns>
|
||||
public IHandlerContainer CreateContainer(DescribedHandlerInfo handlerInfo);
|
||||
public IHandlerContainer CreateContainer(DescribedHandlerDescriptor handlerInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public interface IUpdateHandlerBase : IDisposable
|
||||
/// <param name="described"></param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task<Result> Execute(DescribedHandlerInfo described, CancellationToken cancellationToken = default);
|
||||
Task<Result> Execute(DescribedHandlerDescriptor described, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Handles failed filters during handler describing.
|
||||
|
||||
@@ -22,13 +22,13 @@ namespace Telegrator.Handlers.Components
|
||||
public HandlerLifetimeToken LifetimeToken { get; } = new HandlerLifetimeToken();
|
||||
|
||||
/// <inheritdoc cref="Result.Ok"/>
|
||||
public Result Ok => Result.Ok();
|
||||
public static Result Ok => Result.Ok();
|
||||
|
||||
/// <inheritdoc cref="Result.Fault"/>
|
||||
public Result Fault => Result.Fault();
|
||||
public static Result Fault => Result.Fault();
|
||||
|
||||
/// <inheritdoc cref="Result.Next"/>
|
||||
public Result Next => Result.Next();
|
||||
public static Result Next => Result.Next();
|
||||
|
||||
/// <summary>
|
||||
/// Executes the handler logic and marks the lifetime as ended after execution.
|
||||
@@ -36,7 +36,7 @@ namespace Telegrator.Handlers.Components
|
||||
/// <param name="described"></param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
public async Task<Result> Execute(DescribedHandlerInfo described, CancellationToken cancellationToken = default)
|
||||
public async Task<Result> 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);
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Telegrator.Handlers
|
||||
/// Initializes new instance of <see cref="HandlerContainer{TUpdate}"/>
|
||||
/// </summary>
|
||||
/// <param name="handlerInfo"></param>
|
||||
public HandlerContainer(DescribedHandlerInfo handlerInfo)
|
||||
public HandlerContainer(DescribedHandlerDescriptor handlerInfo)
|
||||
{
|
||||
ActualUpdate = handlerInfo.HandlingUpdate.GetActualUpdateObject<TUpdate>();
|
||||
HandlingUpdate = handlerInfo.HandlingUpdate;
|
||||
|
||||
@@ -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,
|
||||
|
||||
+41
-13
@@ -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
|
||||
/// <summary>
|
||||
/// Contains information about a described handler, including its context, client, and execution logic.
|
||||
/// </summary>
|
||||
public class DescribedHandlerInfo
|
||||
public class DescribedHandlerDescriptor
|
||||
{
|
||||
private readonly ManualResetEventSlim ResetEvent = new ManualResetEventSlim(false);
|
||||
|
||||
/// <summary>
|
||||
/// descriptor from that handler was described from
|
||||
/// </summary>
|
||||
public readonly HandlerDescriptor From;
|
||||
public HandlerDescriptor From { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The update router associated with this handler.
|
||||
/// </summary>
|
||||
public readonly IUpdateRouter UpdateRouter;
|
||||
public IUpdateRouter UpdateRouter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The awaiting provider to fetch new updates inside handler
|
||||
/// </summary>
|
||||
public readonly IAwaitingProvider AwaitingProvider;
|
||||
public IAwaitingProvider AwaitingProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Telegram bot client used for this handler.
|
||||
/// </summary>
|
||||
public readonly ITelegramBotClient Client;
|
||||
public ITelegramBotClient Client { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The handler instance being described.
|
||||
/// </summary>
|
||||
public readonly UpdateHandlerBase HandlerInstance;
|
||||
public UpdateHandlerBase HandlerInstance { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Extra data associated with the handler execution.
|
||||
/// </summary>
|
||||
public readonly Dictionary<string, object> ExtraData;
|
||||
public Dictionary<string, object> ExtraData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// List of completed filters for this handler.
|
||||
/// </summary>
|
||||
public readonly CompletedFiltersList CompletedFilters;
|
||||
public CompletedFiltersList CompletedFilters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The update being handled.
|
||||
/// </summary>
|
||||
public readonly Update HandlingUpdate;
|
||||
public Update HandlingUpdate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Lifetime token for the handler instance.
|
||||
@@ -63,7 +63,12 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
public string DisplayString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DescribedHandlerInfo"/> class.
|
||||
/// The final execution result.
|
||||
/// </summary>
|
||||
public Result? Result { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DescribedHandlerDescriptor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="fromDescriptor">descriptor from that handler was described from</param>
|
||||
/// <param name="awaitingProvider"></param>
|
||||
@@ -72,7 +77,14 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
/// <param name="handlerInstance">The handler instance.</param>
|
||||
/// <param name="filterContext">The filter execution context.</param>
|
||||
/// <param name="displayString">Optional display string.</param>
|
||||
public DescribedHandlerInfo(HandlerDescriptor fromDescriptor, IUpdateRouter updateRouter, IAwaitingProvider awaitingProvider, ITelegramBotClient client, UpdateHandlerBase handlerInstance, FilterExecutionContext<Update> filterContext, string? displayString)
|
||||
public DescribedHandlerDescriptor(
|
||||
HandlerDescriptor fromDescriptor,
|
||||
IUpdateRouter updateRouter,
|
||||
IAwaitingProvider awaitingProvider,
|
||||
ITelegramBotClient client,
|
||||
UpdateHandlerBase handlerInstance,
|
||||
FilterExecutionContext<Update> 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();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
=> DisplayString ?? From.HandlerType.Name;
|
||||
@@ -1,6 +1,5 @@
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Polling;
|
||||
using Telegrator.Polling;
|
||||
|
||||
namespace Telegrator.MadiatorCore
|
||||
{
|
||||
|
||||
@@ -5,14 +5,14 @@ namespace Telegrator.MadiatorCore
|
||||
/// <summary>
|
||||
/// Represents a delegate for when a handler is enqueued.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="DescribedHandlerInfo"/> for the enqueued handler.</param>
|
||||
public delegate void HandlerEnqueued(DescribedHandlerInfo args);
|
||||
/// <param name="args">The <see cref="DescribedHandlerDescriptor"/> for the enqueued handler.</param>
|
||||
public delegate void HandlerEnqueued(DescribedHandlerDescriptor args);
|
||||
|
||||
/// <summary>
|
||||
/// Represents a delegate for when a handler is executing.
|
||||
/// </summary>
|
||||
/// <param name="args">The <see cref="DescribedHandlerInfo"/> for the executing handler.</param>
|
||||
public delegate void HandlerExecuting(DescribedHandlerInfo args);
|
||||
/// <param name="args">The <see cref="DescribedHandlerDescriptor"/> for the executing handler.</param>
|
||||
public delegate void HandlerExecuting(DescribedHandlerDescriptor args);
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="handlers">The handlers to enqueue.</param>
|
||||
public Task Enqueue(IEnumerable<DescribedHandlerInfo> handlers);
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Enqueues a single handler for execution.
|
||||
/// </summary>
|
||||
/// <param name="handlerInfo">The handler to enqueue.</param>
|
||||
public void Enqueue(DescribedHandlerInfo handlerInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Dequeues a handler using its lifetime token.
|
||||
/// </summary>
|
||||
/// <param name="token">The <see cref="HandlerLifetimeToken"/> of the handler to dequeue.</param>
|
||||
public void Dequeue(HandlerLifetimeToken token);
|
||||
*/
|
||||
public Task Enqueue(params IEnumerable<DescribedHandlerDescriptor> handlers);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ namespace Telegrator.Polling
|
||||
/// </summary>
|
||||
/// <param name="client">The Telegram bot client for making API requests.</param>
|
||||
/// <param name="options">Optional receiver options for configuring update polling behavior.</param>
|
||||
public class ReactiveUpdateReceiver(ITelegramBotClient client, ReceiverOptions? options) : IUpdateReceiver
|
||||
public class DefaultUpdateReceiver(ITelegramBotClient client, ReceiverOptions? options) : IUpdateReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the receiver options for configuring update polling behavior.
|
||||
@@ -0,0 +1,21 @@
|
||||
using Telegram.Bot.Polling;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace Telegrator.Polling;
|
||||
|
||||
/// <summary>
|
||||
/// Requests new <see cref="Update"/>s and processes them using provided <see cref="IUpdateHandler"/> instance<
|
||||
/// /summary>
|
||||
public interface IUpdateReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts receiving <see cref="Update"/>s invoking <see cref="IUpdateHandler.HandleUpdateAsync"/> for each <see cref="Update"/>.
|
||||
/// <para>
|
||||
/// This method will block if awaited.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="updateHandler">The <see cref="IUpdateHandler"/> used for processing <see cref="Update"/>s</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> with which you can stop receiving</param>
|
||||
/// <returns>A <see cref="Task"/> that will be completed when cancellation will be requested through <paramref name="cancellationToken"/></returns>
|
||||
Task ReceiveAsync(IUpdateHandler updateHandler, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Synchronization object for thread-safe operations.
|
||||
/// </summary>
|
||||
protected object SyncObj = new object();
|
||||
protected readonly object SyncObj = new object();
|
||||
|
||||
protected readonly Task ChannelReaderTask;
|
||||
|
||||
protected readonly Channel<DescribedHandlerDescriptor> ExecutionChannel;
|
||||
|
||||
/// <summary>
|
||||
/// Semaphore for controlling the number of concurrently executing handlers.
|
||||
/// </summary>
|
||||
protected SemaphoreSlim ExecutingHandlersSemaphore = null!;
|
||||
protected readonly SemaphoreSlim? ExecutionLimiter;
|
||||
|
||||
protected readonly IUpdateRouter UpdateRouter;
|
||||
|
||||
/// <summary>
|
||||
/// The bot configuration options.
|
||||
@@ -45,50 +53,84 @@ namespace Telegrator.Polling
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateHandlersPool"/> class.
|
||||
/// </summary>
|
||||
/// <param name="router">The update handler that claims updates</param>
|
||||
/// <param name="options">The bot configuration options.</param>
|
||||
/// <param name="globalCancellationToken">The global cancellation token.</param>
|
||||
public UpdateHandlersPool(TelegratorOptions options, CancellationToken globalCancellationToken)
|
||||
public UpdateHandlersPool(IUpdateRouter router, TelegratorOptions options, CancellationToken globalCancellationToken)
|
||||
{
|
||||
UpdateRouter = router;
|
||||
Options = options;
|
||||
GlobalCancellationToken = globalCancellationToken;
|
||||
|
||||
if (options.MaximumParallelWorkingHandlers != null)
|
||||
ExecutionChannel = Channel.CreateUnbounded<DescribedHandlerDescriptor>(new UnboundedChannelOptions()
|
||||
{
|
||||
ExecutingHandlersSemaphore = new SemaphoreSlim(options.MaximumParallelWorkingHandlers.Value);
|
||||
}
|
||||
SingleReader = true,
|
||||
SingleWriter = true,
|
||||
AllowSynchronousContinuations = false
|
||||
});
|
||||
|
||||
if (options.MaximumParallelWorkingHandlers != null)
|
||||
ExecutionLimiter = new SemaphoreSlim(options.MaximumParallelWorkingHandlers.Value);
|
||||
|
||||
GlobalCancellationToken.Register(() => ExecutionChannel.Writer.Complete());
|
||||
ChannelReaderTask = ReadChannel();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task Enqueue(IEnumerable<DescribedHandlerInfo> handlers)
|
||||
public async Task Enqueue(params IEnumerable<DescribedHandlerDescriptor> 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.");
|
||||
|
||||
await ExecutionChannel.Writer.WriteAsync(handlerInfo, GlobalCancellationToken);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_ = 0xDEADBEEF;
|
||||
}
|
||||
}
|
||||
|
||||
if (ExecutingHandlersSemaphore != null)
|
||||
private async Task ReadChannel()
|
||||
{
|
||||
await ExecutingHandlersSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await foreach (DescribedHandlerDescriptor handlerInfo in ExecutionChannel.Reader.ReadAllAsync(GlobalCancellationToken))
|
||||
{
|
||||
if (ExecutionLimiter != null)
|
||||
await ExecutionLimiter.WaitAsync(GlobalCancellationToken);
|
||||
|
||||
// Как только слот получен, "отстреливаем" задачу в 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)
|
||||
{
|
||||
using UpdateHandlerBase instance = handlerInfo.HandlerInstance;
|
||||
Task<Result> task = instance.Execute(handlerInfo);
|
||||
HandlerEnqueued?.Invoke(handlerInfo);
|
||||
|
||||
await task.ConfigureAwait(false);
|
||||
lastResult = task.Result;
|
||||
ExecutingHandlersSemaphore?.Release(1);
|
||||
}
|
||||
Result lastResult = task.Result;
|
||||
|
||||
handlerInfo.ReportResult(lastResult);
|
||||
ExecutionLimiter?.Release(1);
|
||||
|
||||
if (lastResult.RouteNext)
|
||||
{
|
||||
@@ -101,17 +143,12 @@ namespace Telegrator.Polling
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_ = 0xBAD + 0xC0DE;
|
||||
break;
|
||||
_ = 0xDEADBEEF;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -122,14 +159,8 @@ 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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <inheritdoc/>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -74,6 +77,7 @@ namespace Telegrator.Polling
|
||||
_awaitingProvider = awaitingProvider;
|
||||
_HandlersPool = handlersPool;
|
||||
_botInfo = botInfo;
|
||||
_handlingRoutes = new HandlerDescriptorList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -105,20 +109,33 @@ namespace Telegrator.Polling
|
||||
|
||||
try
|
||||
{
|
||||
// Getting handlers in update awaiting pool
|
||||
IEnumerable<DescribedHandlerInfo> 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);
|
||||
if (lastResult?.NextType != null)
|
||||
{
|
||||
if (lastResult.NextType != handlerInfo.From.HandlerType)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Chicking if awaiting handlers has exclusive routing
|
||||
// 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
|
||||
await HandlersPool.Enqueue(GetHandlers(HandlersProvider, botClient, update, cancellationToken));
|
||||
@@ -144,7 +161,7 @@ namespace Telegrator.Polling
|
||||
/// <param name="update">The incoming Telegram update to process</param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>A collection of described handler information for the update</returns>
|
||||
protected virtual IEnumerable<DescribedHandlerInfo> GetHandlers(IHandlersProvider provider, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
||||
protected virtual IEnumerable<DescribedHandlerDescriptor> 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
|
||||
/// <param name="update">The incoming Telegram update to process</param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>A collection of described handler information</returns>
|
||||
protected virtual IEnumerable<DescribedHandlerInfo> DescribeDescriptors(IHandlersProvider provider, HandlerDescriptorList descriptors, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
||||
protected virtual IEnumerable<DescribedHandlerDescriptor> 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
|
||||
/// <param name="breakRouting"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>The described handler info if validation passes; otherwise, null</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Telegram.Bot" Version="22.6.2" />
|
||||
<PackageReference Include="System.Threading.Channels" Version="10.0.3" />
|
||||
<PackageReference Include="Telegram.Bot" Version="22.9.5.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace Telegrator
|
||||
{
|
||||
try
|
||||
{
|
||||
await new ReactiveUpdateReceiver(this, receiverOptions)
|
||||
await new DefaultUpdateReceiver(this, receiverOptions)
|
||||
.ReceiveAsync(UpdateRouter, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user