* Little renaming in StateKeeping namespace

* Changed "FilterAnnotation{T}" to work withou type specific sub-classes
* Changed interfaces "IUpdateRouter" and "IHandlersProvider" to match their names more correctly. Before filter validating was on providers, now its router works AS ITS SHOULD BE
This commit is contained in:
2025-07-27 14:19:40 +04:00
parent b86699a65e
commit b01d576564
18 changed files with 265 additions and 306 deletions
@@ -103,10 +103,13 @@ namespace Telegrator.Generators
private static void ParseClassDeclaration(StringBuilder sourceBuilder, ClassDeclarationSyntax classDeclaration, Dictionary<string, string> targeters) private static void ParseClassDeclaration(StringBuilder sourceBuilder, ClassDeclarationSyntax classDeclaration, Dictionary<string, string> targeters)
{ {
string className = classDeclaration.Identifier.ToString();
if (className == "FilterAnnotation")
return;
IEnumerable<MethodDeclarationSyntax> methods = classDeclaration.Members.OfType<MethodDeclarationSyntax>(); IEnumerable<MethodDeclarationSyntax> methods = classDeclaration.Members.OfType<MethodDeclarationSyntax>();
MethodDeclarationSyntax? targeterMethod = methods.FirstOrDefault(method => method.Identifier.ToString() == "GetFilterringTarget"); MethodDeclarationSyntax? targeterMethod = methods.FirstOrDefault(method => method.Identifier.ToString() == "GetFilterringTarget");
string className = classDeclaration.Identifier.ToString();
string filterName = className.Replace("Attribute", string.Empty); string filterName = className.Replace("Attribute", string.Empty);
string classTargetterMethodName = filterName + "_GetFilterringTarget"; string classTargetterMethodName = filterName + "_GetFilterringTarget";
@@ -3,7 +3,6 @@ using Microsoft.Extensions.Options;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Polling; using Telegram.Bot.Polling;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Telegrator;
using Telegrator.Configuration; using Telegrator.Configuration;
using Telegrator.MadiatorCore; using Telegrator.MadiatorCore;
using Telegrator.Polling; using Telegrator.Polling;
@@ -24,7 +23,8 @@ namespace Telegrator.Hosting.Polling
IAwaitingProvider awaitingProvider, IAwaitingProvider awaitingProvider,
IOptions<TelegramBotOptions> options, IOptions<TelegramBotOptions> options,
IUpdateHandlersPool handlersPool, IUpdateHandlersPool handlersPool,
ILogger<HostUpdateRouter> logger) : base(handlersProvider, awaitingProvider, options.Value, handlersPool) ITelegramBotInfo botInfo,
ILogger<HostUpdateRouter> logger) : base(handlersProvider, awaitingProvider, options.Value, handlersPool, botInfo)
{ {
Logger = logger; Logger = logger;
ExceptionHandler = new DefaultRouterExceptionHandler(HandleException); ExceptionHandler = new DefaultRouterExceptionHandler(HandleException);
@@ -1,17 +1,16 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegrator.Configuration; using Telegrator.Configuration;
using Telegrator.MadiatorCore;
using Telegrator.MadiatorCore.Descriptors;
using Telegrator.Providers; using Telegrator.Providers;
namespace Telegrator.Hosting.Providers namespace Telegrator.Hosting.Providers
{ {
/// <inheritdoc/> /// <inheritdoc/>
public class HostAwaitingProvider(IOptions<TelegramBotOptions> options, ITelegramBotInfo botInfo, ILogger<HostAwaitingProvider> logger) : AwaitingProvider(options.Value, botInfo) public class HostAwaitingProvider(IOptions<TelegramBotOptions> options, ILogger<HostAwaitingProvider> logger) : AwaitingProvider(options.Value)
{ {
private readonly ILogger<HostAwaitingProvider> _logger = logger;
/*
/// <inheritdoc/> /// <inheritdoc/>
public override IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default) public override IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
{ {
@@ -19,5 +18,6 @@ namespace Telegrator.Hosting.Providers
logger.LogInformation("Described awaiting handlers : {handlers}", string.Join(", ", handlers.Select(hndlr => hndlr.HandlerInstance.GetType().Name))); logger.LogInformation("Described awaiting handlers : {handlers}", string.Join(", ", handlers.Select(hndlr => hndlr.HandlerInstance.GetType().Name)));
return handlers; return handlers;
} }
*/
} }
} }
@@ -1,8 +1,6 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegrator.Configuration; using Telegrator.Configuration;
using Telegrator.Handlers.Components; using Telegrator.Handlers.Components;
using Telegrator.MadiatorCore; using Telegrator.MadiatorCore;
@@ -21,14 +19,14 @@ namespace Telegrator.Hosting.Providers
public HostHandlersProvider( public HostHandlersProvider(
IHandlersCollection handlers, IHandlersCollection handlers,
IOptions<TelegramBotOptions> options, IOptions<TelegramBotOptions> options,
ITelegramBotInfo botInfo,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
ILogger<HostHandlersProvider> logger) : base(handlers, options.Value, botInfo) ILogger<HostHandlersProvider> logger) : base(handlers, options.Value)
{ {
Services = serviceProvider; Services = serviceProvider;
Logger = logger; Logger = logger;
} }
/*
/// <inheritdoc/> /// <inheritdoc/>
public override IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default) public override IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
{ {
@@ -36,18 +34,20 @@ namespace Telegrator.Hosting.Providers
Logger.LogInformation("Described handlers : {handlers}", string.Join(", ", handlers.Select(hndlr => hndlr.DisplayString ?? hndlr.HandlerInstance.GetType().Name))); Logger.LogInformation("Described handlers : {handlers}", string.Join(", ", handlers.Select(hndlr => hndlr.DisplayString ?? hndlr.HandlerInstance.GetType().Name)));
return handlers; return handlers;
} }
*/
/// <inheritdoc/> /// <inheritdoc/>
public override UpdateHandlerBase GetHandlerInstance(HandlerDescriptor descriptor, CancellationToken cancellationToken = default) public override UpdateHandlerBase GetHandlerInstance(HandlerDescriptor descriptor, CancellationToken cancellationToken = default)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
IServiceScope scope = Services.CreateScope(); IServiceScope scope = Services.CreateScope();
object handlerInstance = descriptor.ServiceKey == null object handlerInstance = descriptor.ServiceKey == null
? scope.ServiceProvider.GetRequiredService(descriptor.HandlerType) ? scope.ServiceProvider.GetRequiredService(descriptor.HandlerType)
: scope.ServiceProvider.GetRequiredKeyedService(descriptor.HandlerType, descriptor.ServiceKey); : scope.ServiceProvider.GetRequiredKeyedService(descriptor.HandlerType, descriptor.ServiceKey);
if (handlerInstance is not UpdateHandlerBase updateHandler) if (handlerInstance is not UpdateHandlerBase updateHandler)
throw new InvalidOperationException(); throw new InvalidOperationException("Failed to resolve " + descriptor.HandlerType + " as UpdateHandlerBase");
updateHandler.LifetimeToken.OnLifetimeEnded += _ => scope.Dispose(); updateHandler.LifetimeToken.OnLifetimeEnded += _ => scope.Dispose();
return updateHandler; return updateHandler;
+1 -1
View File
@@ -15,7 +15,7 @@
<EnableNETAnalyzers>True</EnableNETAnalyzers> <EnableNETAnalyzers>True</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild> <EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<Version>1.0.2</Version> <Version>1.0.3</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -39,39 +39,5 @@ namespace Telegrator.Annotations.StateKeeping
/// <param name="myState">The string state to associate</param> /// <param name="myState">The string state to associate</param>
public StringStateAttribute(string myState) public StringStateAttribute(string myState)
: base(myState, new SenderIdResolver()) { } : base(myState, new SenderIdResolver()) { }
/// <summary>
/// Initializes the attribute with a specific state, a custom key resolver, and a set of possible states.
/// </summary>
/// <param name="myState">The string state to associate</param>
/// <param name="keyResolver">The key resolver for state keeping</param>
/// <param name="states">The set of possible string states</param>
public StringStateAttribute(string myState, IStateKeyResolver<long> keyResolver, params string[] states)
: base(new StringStateKeeper(states), myState, keyResolver) { }
/// <summary>
/// Initializes the attribute with a special state, a custom key resolver, and a set of possible states.
/// </summary>
/// <param name="specialState">The special state to associate</param>
/// <param name="keyResolver">The key resolver for state keeping</param>
/// <param name="states">The set of possible string states</param>
public StringStateAttribute(SpecialState specialState, IStateKeyResolver<long> keyResolver, params string[] states)
: base(new StringStateKeeper(states), specialState, keyResolver) { }
/// <summary>
/// Initializes the attribute with a specific state, the default sender ID resolver, and a set of possible states.
/// </summary>
/// <param name="myState">The string state to associate</param>
/// <param name="states">The set of possible string states</param>
public StringStateAttribute(string myState, params string[] states)
: base(new StringStateKeeper(states), myState, new SenderIdResolver()) { }
/// <summary>
/// Initializes the attribute with a special state, the default sender ID resolver, and a set of possible states.
/// </summary>
/// <param name="specialState">The special state to associate</param>
/// <param name="states">The set of possible string states</param>
public StringStateAttribute(SpecialState specialState, params string[] states)
: base(new StringStateKeeper(states), specialState, new SenderIdResolver()) { }
} }
} }
+8 -21
View File
@@ -12,7 +12,10 @@ namespace Telegrator.Attributes
public abstract class FilterAnnotation<T> : UpdateFilterAttribute<T>, IFilter<T> where T : class public abstract class FilterAnnotation<T> : UpdateFilterAttribute<T>, IFilter<T> where T : class
{ {
/// <inheritdoc/> /// <inheritdoc/>
public bool IsCollectible => false; public virtual bool IsCollectible { get; } = false;
/// <inheritdoc/>
public override UpdateType[] AllowedTypes { get; } = typeof(T).GetAllowedUpdateTypes();
/// <summary> /// <summary>
/// Initializes new instance of <see cref="FilterAnnotation{T}"/> /// Initializes new instance of <see cref="FilterAnnotation{T}"/>
@@ -23,27 +26,11 @@ namespace Telegrator.Attributes
AnonymousFilter = AnonymousTypeFilter.Compile(UpdateFilter, GetFilterringTarget); AnonymousFilter = AnonymousTypeFilter.Compile(UpdateFilter, GetFilterringTarget);
} }
/// <inheritdoc/>
public override T? GetFilterringTarget(Update update)
=> update.GetActualUpdateObject<T>();
/// <inheritdoc/> /// <inheritdoc/>
public abstract bool CanPass(FilterExecutionContext<T> context); public abstract bool CanPass(FilterExecutionContext<T> context);
} }
/// <inheritdoc/>
public abstract class MessageFilterAnnotation() : FilterAnnotation<Message>()
{
/// <inheritdoc/>
public override UpdateType[] AllowedTypes => [UpdateType.Message, UpdateType.EditedMessage, UpdateType.ChannelPost, UpdateType.EditedChannelPost, UpdateType.BusinessMessage, UpdateType.EditedBusinessMessage];
/// <inheritdoc/>
public override Message? GetFilterringTarget(Update update) => update.Message;
}
/// <inheritdoc/>
public abstract class CallbackQueryFilterAnnotation() : FilterAnnotation<CallbackQuery>()
{
/// <inheritdoc/>
public override UpdateType[] AllowedTypes => [UpdateType.CallbackQuery];
/// <inheritdoc/>
public override CallbackQuery? GetFilterringTarget(Update update) => update.CallbackQuery;
}
} }
+17 -7
View File
@@ -15,10 +15,20 @@ namespace Telegrator.Attributes
/// <typeparam name="TKeeper">The type of the state keeper implementation.</typeparam> /// <typeparam name="TKeeper">The type of the state keeper implementation.</typeparam>
public abstract class StateKeeperAttribute<TKey, TState, TKeeper> : StateKeeperAttributeBase where TKey : notnull where TState : notnull where TKeeper : StateKeeperBase<TKey, TState>, new() public abstract class StateKeeperAttribute<TKey, TState, TKeeper> : StateKeeperAttributeBase where TKey : notnull where TState : notnull where TKeeper : StateKeeperBase<TKey, TState>, new()
{ {
/*
private static readonly TKeeper _shared = new TKeeper();
private static readonly Dictionary<TKey, TKeeper> _keyed = [];
*/
/// <summary> /// <summary>
/// Gets or sets the singleton instance of the state keeper for this attribute type. /// Gets or sets the singleton instance of the state keeper for this attribute type.
/// </summary> /// </summary>
public static TKeeper StateKeeper { get; internal set; } = null!; public static TKeeper Shared { get; } = new TKeeper();
/// <summary>
/// Gets the default state value of this statekeeper.
/// </summary>
public static TState DefaultState => Shared.DefaultState;
/// <summary> /// <summary>
/// Gets the state value associated with this attribute instance. /// Gets the state value associated with this attribute instance.
@@ -37,8 +47,7 @@ namespace Telegrator.Attributes
/// <param name="keyResolver">The key resolver for state keeping</param> /// <param name="keyResolver">The key resolver for state keeping</param>
protected StateKeeperAttribute(TState myState, IStateKeyResolver<TKey> keyResolver) : base(typeof(TKeeper)) protected StateKeeperAttribute(TState myState, IStateKeyResolver<TKey> keyResolver) : base(typeof(TKeeper))
{ {
StateKeeper ??= new TKeeper(); Shared.KeyResolver = keyResolver;
StateKeeper.KeyResolver = keyResolver;
MyState = myState; MyState = myState;
SpecialState = SpecialState.None; SpecialState = SpecialState.None;
} }
@@ -50,12 +59,12 @@ namespace Telegrator.Attributes
/// <param name="keyResolver">The key resolver for state keeping</param> /// <param name="keyResolver">The key resolver for state keeping</param>
protected StateKeeperAttribute(SpecialState specialState, IStateKeyResolver<TKey> keyResolver) : base(typeof(TKeeper)) protected StateKeeperAttribute(SpecialState specialState, IStateKeyResolver<TKey> keyResolver) : base(typeof(TKeeper))
{ {
StateKeeper ??= new TKeeper(); Shared.KeyResolver = keyResolver;
StateKeeper.KeyResolver = keyResolver; MyState = Shared.DefaultState;
MyState = StateKeeper.DefaultState;
SpecialState = specialState; SpecialState = specialState;
} }
/*
/// <summary> /// <summary>
/// Initializes the attribute with a custom state keeper, a specific state, and a custom key resolver. /// Initializes the attribute with a custom state keeper, a specific state, and a custom key resolver.
/// </summary> /// </summary>
@@ -83,6 +92,7 @@ namespace Telegrator.Attributes
MyState = StateKeeper.DefaultState; MyState = StateKeeper.DefaultState;
SpecialState = specialState; SpecialState = specialState;
} }
*/
/// <summary> /// <summary>
/// Determines whether the current update context passes the state filter. /// Determines whether the current update context passes the state filter.
@@ -94,7 +104,7 @@ namespace Telegrator.Attributes
if (SpecialState == SpecialState.AnyState) if (SpecialState == SpecialState.AnyState)
return true; return true;
if (!StateKeeper.TryGetState(context.Input, out TState? state)) if (!Shared.TryGetState(context.Input, out TState? state))
return SpecialState == SpecialState.NoState; return SpecialState == SpecialState.NoState;
if (state == null) if (state == null)
+9 -40
View File
@@ -1,6 +1,4 @@
using Telegram.Bot; using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegrator.Handlers.Components; using Telegrator.Handlers.Components;
using Telegrator.MadiatorCore.Descriptors; using Telegrator.MadiatorCore.Descriptors;
@@ -17,51 +15,22 @@ namespace Telegrator.MadiatorCore
public IEnumerable<UpdateType> AllowedTypes { get; } public IEnumerable<UpdateType> AllowedTypes { get; }
/// <summary> /// <summary>
/// Gets the handlers for the specified update and context. ///
/// </summary> /// </summary>
/// <param name="updateRouter">The update router.</param> /// <param name="updateType"></param>
/// <param name="client">The Telegram bot client.</param> /// <param name="list"></param>
/// <param name="update">The update to handle.</param> /// <returns></returns>
/// <param name="cancellationToken"></param> public bool TryGetDescriptorList(UpdateType updateType, out HandlerDescriptorList? list);
/// <returns>An enumerable of described handler info.</returns>
public IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Describes all handler descriptors in the list for the given context. /// Instantiates a handler for the given descriptor, using the appropriate creation strategy based on descriptor type.
/// </summary> /// Supports singleton, implicit, keyed, and general descriptor types with different instantiation patterns.
/// <param name="descriptors">The handler descriptor list.</param>
/// <param name="updateRouter">The update router.</param>
/// <param name="client">The Telegram bot client.</param>
/// <param name="update">The update to handle.</param>
/// <param name="cancellationToken"></param>
/// <returns>An enumerable of described handler info.</returns>
public IEnumerable<DescribedHandlerInfo> DescribeDescriptors(HandlerDescriptorList descriptors, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default);
/// <summary>
/// Describes a single handler descriptor for the given context.
/// </summary>
/// <param name="descriptor">The handler descriptor.</param>
/// <param name="updateRouter">The update router.</param>
/// <param name="client">The Telegram bot client.</param>
/// <param name="update">The update to handle.</param>
/// <param name="cancellationToken"></param>
/// <returns>The described handler info, or null if not applicable.</returns>
public DescribedHandlerInfo? DescribeHandler(HandlerDescriptor descriptor, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default);
/// <summary>
/// Gets an instance of the handler for the specified descriptor.
/// </summary> /// </summary>
/// <param name="descriptor">The handler descriptor.</param> /// <param name="descriptor">The handler descriptor.</param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns>The handler instance.</returns> /// <returns>An instance of <see cref="UpdateHandlerBase"/> for the descriptor</returns>
public UpdateHandlerBase GetHandlerInstance(HandlerDescriptor descriptor, CancellationToken cancellationToken = default); public UpdateHandlerBase GetHandlerInstance(HandlerDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Gets the list of bot commands supported by the provider.
/// </summary>
/// <returns>An enumerable of bot commands.</returns>
public IEnumerable<BotCommand> GetBotCommands(CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Determines whether the provider contains any handlers. /// Determines whether the provider contains any handlers.
/// </summary> /// </summary>
+128 -37
View File
@@ -1,10 +1,10 @@
using System; using System.Text;
using System.Text;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Polling; using Telegram.Bot.Polling;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
using Telegrator.Configuration; using Telegrator.Configuration;
using Telegrator.Filters.Components;
using Telegrator.Handlers.Components; using Telegrator.Handlers.Components;
using Telegrator.MadiatorCore; using Telegrator.MadiatorCore;
using Telegrator.MadiatorCore.Descriptors; using Telegrator.MadiatorCore.Descriptors;
@@ -17,25 +17,11 @@ namespace Telegrator.Polling
/// </summary> /// </summary>
public class UpdateRouter : IUpdateRouter public class UpdateRouter : IUpdateRouter
{ {
/// <summary>
/// The bot configuration options.
/// </summary>
private readonly TelegramBotOptions _options; private readonly TelegramBotOptions _options;
/// <summary>
/// The provider for regular handlers.
/// </summary>
private readonly IHandlersProvider _handlersProvider; private readonly IHandlersProvider _handlersProvider;
/// <summary>
/// The provider for awaiting handlers.
/// </summary>
private readonly IAwaitingProvider _awaitingProvider; private readonly IAwaitingProvider _awaitingProvider;
/// <summary>
/// The pool for managing handler execution.
/// </summary>
private readonly IUpdateHandlersPool _HandlersPool; private readonly IUpdateHandlersPool _HandlersPool;
private readonly ITelegramBotInfo _botInfo;
/// <inheritdoc/> /// <inheritdoc/>
public IHandlersProvider HandlersProvider => _handlersProvider; public IHandlersProvider HandlersProvider => _handlersProvider;
@@ -61,12 +47,14 @@ namespace Telegrator.Polling
/// <param name="handlersProvider">The provider for regular handlers.</param> /// <param name="handlersProvider">The provider for regular handlers.</param>
/// <param name="awaitingProvider">The provider for awaiting handlers.</param> /// <param name="awaitingProvider">The provider for awaiting handlers.</param>
/// <param name="options">The bot configuration options.</param> /// <param name="options">The bot configuration options.</param>
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, TelegramBotOptions options) /// <param name="botInfo"></param>
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, TelegramBotOptions options, ITelegramBotInfo botInfo)
{ {
_options = options; _options = options;
_handlersProvider = handlersProvider; _handlersProvider = handlersProvider;
_awaitingProvider = awaitingProvider; _awaitingProvider = awaitingProvider;
_HandlersPool = new UpdateHandlersPool(_options, _options.GlobalCancellationToken); _HandlersPool = new UpdateHandlersPool(_options, _options.GlobalCancellationToken);
_botInfo = botInfo;
} }
/// <summary> /// <summary>
@@ -76,12 +64,14 @@ namespace Telegrator.Polling
/// <param name="awaitingProvider">The provider for awaiting handlers.</param> /// <param name="awaitingProvider">The provider for awaiting handlers.</param>
/// <param name="options">The bot configuration options.</param> /// <param name="options">The bot configuration options.</param>
/// <param name="handlersPool">The custom handlers pool to use.</param> /// <param name="handlersPool">The custom handlers pool to use.</param>
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, TelegramBotOptions options, IUpdateHandlersPool handlersPool) /// <param name="botInfo"></param>
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, TelegramBotOptions options, IUpdateHandlersPool handlersPool, ITelegramBotInfo botInfo)
{ {
_options = options; _options = options;
_handlersProvider = handlersProvider; _handlersProvider = handlersProvider;
_awaitingProvider = awaitingProvider; _awaitingProvider = awaitingProvider;
_HandlersPool = handlersPool; _HandlersPool = handlersPool;
_botInfo = botInfo;
} }
/// <summary> /// <summary>
@@ -112,38 +102,139 @@ namespace Telegrator.Polling
LeveledDebug.RouterWriteLine("Received Update ({0}) of type \"{1}\"", update.Id, update.Type); LeveledDebug.RouterWriteLine("Received Update ({0}) of type \"{1}\"", update.Id, update.Type);
LogUpdate(update); LogUpdate(update);
// Queuing handlers for execution
foreach (DescribedHandlerInfo handler in GetHandlers(botClient, update, cancellationToken))
HandlersPool.Enqueue(handler);
LeveledDebug.RouterWriteLine("Receiving Update ({0}) finished", update.Id);
return Task.CompletedTask;
}
private IEnumerable<DescribedHandlerInfo> GetHandlers(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
{
try try
{ {
// Getting handlers in update awaiting pool // Getting handlers in update awaiting pool
IEnumerable<DescribedHandlerInfo> handlers = AwaitingProvider.GetHandlers(this, botClient, update, cancellationToken); IEnumerable<DescribedHandlerInfo> handlers = GetHandlers(AwaitingProvider, this, botClient, update, cancellationToken);
if (handlers.Any() && Options.ExclusiveAwaitingHandlerRouting) if (handlers.Any())
return handlers; {
// Enqueuing found awiting handlers
HandlersPool.Enqueue(handlers);
return handlers.Concat(HandlersProvider.GetHandlers(this, botClient, update, cancellationToken)); // Chicking if awaiting handlers has exclusive routing
if (Options.ExclusiveAwaitingHandlerRouting)
{
LeveledDebug.RouterWriteLine("Receiving Update ({0}) completed with only awaiting handlers", update.Id);
return Task.CompletedTask;
}
}
// Queuing reagular handlers for execution
HandlersPool.Enqueue(GetHandlers(HandlersProvider, this, botClient, update, cancellationToken));
LeveledDebug.RouterWriteLine("Receiving Update ({0}) finished", update.Id);
return Task.CompletedTask;
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
_ = 0xBAD + 0xC0DE; LeveledDebug.RouterWriteLine("Receiving Update ({0}) cancelled", update.Id);
return []; return Task.CompletedTask;
} }
catch (Exception ex) catch (Exception ex)
{ {
LeveledDebug.RouterWriteLine("Receiving Update ({0}) finished with exception {1}", update.Id, ex.Message);
ExceptionHandler?.HandleException(botClient, ex, HandleErrorSource.PollingError, cancellationToken); ExceptionHandler?.HandleException(botClient, ex, HandleErrorSource.PollingError, cancellationToken);
return []; return Task.CompletedTask;
} }
} }
private static void LogUpdate(Update update) /// <summary>
/// Gets the handlers that match the specified update, using the provided router and client.
/// Searches for handlers by update type, falling back to Unknown type if no specific handlers are found.
/// </summary>
/// <param name="provider">The privode used to get handlers instance</param>
/// <param name="updateRouter">The update router for handler execution</param>
/// <param name="client">The Telegram bot client instance</param>
/// <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, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
{
LeveledDebug.ProviderWriteLine("Requested handlers for UpdateType.{0}", update.Type);
if (!provider.TryGetDescriptorList(update.Type, out HandlerDescriptorList? descriptors))
{
LeveledDebug.ProviderWriteLine("No registered, providing Any");
provider.TryGetDescriptorList(UpdateType.Unknown, out descriptors);
}
if (descriptors == null || descriptors.Count == 0)
{
LeveledDebug.ProviderWriteLine("No handlers provided");
return [];
}
IEnumerable<DescribedHandlerInfo> described = DescribeDescriptors(provider, descriptors, updateRouter, client, update, cancellationToken);
LeveledDebug.ProviderWriteLine("Described total of {0} handlers for Update ({1}) from {2} provider", described.Count(), update.Id, provider.GetType().Name);
LeveledDebug.ProviderWriteLine("Described handlers : {0}", string.Join(", ", described));
return described;
}
/// <summary>
/// Describes all handler descriptors for a given update context.
/// Processes descriptors in reverse order and respects the ExecuteOnlyFirstFoundHanlder option.
/// </summary>
/// <param name="provider">The privode used to get handlers instance</param>
/// <param name="descriptors">The list of handler descriptors to process</param>
/// <param name="updateRouter">The update router for handler execution</param>
/// <param name="client">The Telegram bot client instance</param>
/// <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, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
{
try
{
LeveledDebug.ProviderWriteLine("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, updateRouter, client, update, cancellationToken);
if (describedHandler == null)
continue;
yield return describedHandler;
if (Options.ExecuteOnlyFirstFoundHanlder)
break;
}
}
finally
{
LeveledDebug.ProviderWriteLine("Describing for Update ({0}) finished", update.Id);
}
}
/// <summary>
/// Describes a single handler descriptor for a given update context.
/// Validates the handler's filters against the update and creates a handler instance if validation passes.
/// </summary>
/// <param name="provider">The privode used to get handlers instance</param>
/// <param name="descriptor">The handler descriptor to process</param>
/// <param name="updateRouter">The update router for handler execution</param>
/// <param name="client">The Telegram bot client instance</param>
/// <param name="update">The incoming Telegram update to process</param>
/// <param name="cancellationToken"></param>
/// <returns>The described handler info if validation passes; otherwise, null</returns>
public virtual DescribedHandlerInfo? DescribeHandler(IHandlersProvider provider, HandlerDescriptor descriptor, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
Dictionary<string, object> data = new Dictionary<string, object>()
{
{ "handler_name", descriptor.ToString() }
};
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(_botInfo, update, update, data, []);
if (!descriptor.Filters.Validate(filterContext))
return null;
UpdateHandlerBase handlerInstance = provider.GetHandlerInstance(descriptor, cancellationToken);
return new DescribedHandlerInfo(updateRouter, client, handlerInstance, filterContext, descriptor.DisplayString);
}
/// <summary>
/// Methos used to log received <see cref="Update"/> object
/// </summary>
/// <param name="update"></param>
/// <exception cref="NullReferenceException"></exception>
protected static void LogUpdate(Update update)
{ {
switch (update.Type) switch (update.Type)
{ {
+5 -6
View File
@@ -1,5 +1,4 @@
using Telegram.Bot; using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types;
using Telegrator.Configuration; using Telegrator.Configuration;
using Telegrator.MadiatorCore; using Telegrator.MadiatorCore;
using Telegrator.MadiatorCore.Descriptors; using Telegrator.MadiatorCore.Descriptors;
@@ -11,8 +10,7 @@ namespace Telegrator.Providers
/// Extends HandlersProvider to provide functionality for creating and managing awaiter handlers. /// Extends HandlersProvider to provide functionality for creating and managing awaiter handlers.
/// </summary> /// </summary>
/// <param name="options">The bot configuration options.</param> /// <param name="options">The bot configuration options.</param>
/// <param name="botInfo">The bot information.</param> public class AwaitingProvider(TelegramBotOptions options) : HandlersProvider([], options), IAwaitingProvider
public class AwaitingProvider(TelegramBotOptions options, ITelegramBotInfo botInfo) : HandlersProvider([], options, botInfo), IAwaitingProvider
{ {
/// <summary> /// <summary>
/// List of handler descriptors for awaiting handlers. /// List of handler descriptors for awaiting handlers.
@@ -20,9 +18,10 @@ namespace Telegrator.Providers
protected readonly HandlerDescriptorList HandlersList = []; protected readonly HandlerDescriptorList HandlersList = [];
/// <inheritdoc/> /// <inheritdoc/>
public override IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default) public override bool TryGetDescriptorList(UpdateType updateType, out HandlerDescriptorList? list)
{ {
return DescribeDescriptors(HandlersList, updateRouter, client, update, cancellationToken); list = HandlersList;
return true;
} }
/// <inheritdoc/> /// <inheritdoc/>
+8 -135
View File
@@ -1,12 +1,7 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Reflection;
using Telegram.Bot;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
using Telegrator.Annotations;
using Telegrator.Configuration; using Telegrator.Configuration;
using Telegrator.Filters.Components;
using Telegrator.Handlers.Components; using Telegrator.Handlers.Components;
using Telegrator.MadiatorCore; using Telegrator.MadiatorCore;
using Telegrator.MadiatorCore.Descriptors; using Telegrator.MadiatorCore.Descriptors;
@@ -27,31 +22,24 @@ namespace Telegrator.Providers
/// Read-only dictionary mapping <see cref="UpdateType"/> to lists of handler descriptors. /// Read-only dictionary mapping <see cref="UpdateType"/> to lists of handler descriptors.
/// Each descriptor list is frozen to prevent modification after initialization. /// Each descriptor list is frozen to prevent modification after initialization.
/// </summary> /// </summary>
protected readonly ReadOnlyDictionary<UpdateType, HandlerDescriptorList> HandlersDictionary; public readonly ReadOnlyDictionary<UpdateType, HandlerDescriptorList> HandlersDictionary;
/// <summary> /// <summary>
/// Configuration options for the bot and handler execution behavior. /// Configuration options for the bot and handler execution behavior.
/// </summary> /// </summary>
protected readonly TelegramBotOptions Options; protected readonly TelegramBotOptions Options;
/// <summary>
/// Information about the Telegram bot instance, used for filter context creation.
/// </summary>
protected readonly ITelegramBotInfo BotInfo;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="HandlersProvider"/> with the specified handler collections and configuration. /// Initializes a new instance of <see cref="HandlersProvider"/> with the specified handler collections and configuration.
/// </summary> /// </summary>
/// <param name="handlers">Collection of handler descriptor lists organized by update type</param> /// <param name="handlers">Collection of handler descriptor lists organized by update type</param>
/// <param name="options">Configuration options for the bot and handler execution</param> /// <param name="options">Configuration options for the bot and handler execution</param>
/// <param name="botInfo">Information about the Telegram bot instance</param>
/// <exception cref="ArgumentNullException">Thrown when options or botInfo is null</exception> /// <exception cref="ArgumentNullException">Thrown when options or botInfo is null</exception>
public HandlersProvider(IHandlersCollection handlers, TelegramBotOptions options, ITelegramBotInfo botInfo) public HandlersProvider(IHandlersCollection handlers, TelegramBotOptions options)
{ {
AllowedTypes = handlers.AllowedTypes; AllowedTypes = handlers.AllowedTypes;
HandlersDictionary = handlers.Values.ForEach(list => list.Freeze()).ToReadOnlyDictionary(list => list.HandlingType); HandlersDictionary = handlers.Values.ForEach(list => list.Freeze()).ToReadOnlyDictionary(list => list.HandlingType);
Options = options ?? throw new ArgumentNullException(nameof(options)); Options = options ?? throw new ArgumentNullException(nameof(options));
BotInfo = botInfo ?? throw new ArgumentNullException(nameof(botInfo));
} }
/// <summary> /// <summary>
@@ -59,112 +47,15 @@ namespace Telegrator.Providers
/// </summary> /// </summary>
/// <param name="handlers">Collection of handler descriptor lists organized by update type</param> /// <param name="handlers">Collection of handler descriptor lists organized by update type</param>
/// <param name="options">Configuration options for the bot and handler execution</param> /// <param name="options">Configuration options for the bot and handler execution</param>
/// <param name="botInfo">Information about the Telegram bot instance</param>
/// <exception cref="ArgumentNullException">Thrown when options or botInfo is null</exception> /// <exception cref="ArgumentNullException">Thrown when options or botInfo is null</exception>
public HandlersProvider(IEnumerable<HandlerDescriptorList> handlers, TelegramBotOptions options, ITelegramBotInfo botInfo) public HandlersProvider(IEnumerable<HandlerDescriptorList> handlers, TelegramBotOptions options)
{ {
AllowedTypes = Update.AllTypes; AllowedTypes = Update.AllTypes;
HandlersDictionary = handlers.ForEach(list => list.Freeze()).ToReadOnlyDictionary(list => list.HandlingType); HandlersDictionary = handlers.ForEach(list => list.Freeze()).ToReadOnlyDictionary(list => list.HandlingType);
Options = options ?? throw new ArgumentNullException(nameof(options)); Options = options ?? throw new ArgumentNullException(nameof(options));
BotInfo = botInfo ?? throw new ArgumentNullException(nameof(botInfo));
} }
/// <summary> /// <inheritdoc/>
/// Gets the handlers that match the specified update, using the provided router and client.
/// Searches for handlers by update type, falling back to Unknown type if no specific handlers are found.
/// </summary>
/// <param name="updateRouter">The update router for handler execution</param>
/// <param name="client">The Telegram bot client instance</param>
/// <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>
public virtual IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
{
LeveledDebug.ProviderWriteLine("Requested handlers for UpdateType.{0}", update.Type);
if (!HandlersDictionary.TryGetValue(update.Type, out HandlerDescriptorList? descriptors))
{
LeveledDebug.ProviderWriteLine("No registered, providing Any");
HandlersDictionary.TryGetValue(UpdateType.Unknown, out descriptors);
}
if (descriptors == null || descriptors.Count == 0)
{
LeveledDebug.ProviderWriteLine("No handlers provided");
return [];
}
IEnumerable<DescribedHandlerInfo> described = DescribeDescriptors(descriptors, updateRouter, client, update, cancellationToken);
LeveledDebug.ProviderWriteLine("Described total of {0} handlers for Update ({1})", described.Count(), update.Id);
LeveledDebug.ProviderWriteLine("Described handlers : {0}", string.Join(", ", described));
return described;
}
/// <summary>
/// Describes all handler descriptors for a given update context.
/// Processes descriptors in reverse order and respects the ExecuteOnlyFirstFoundHanlder option.
/// </summary>
/// <param name="descriptors">The list of handler descriptors to process</param>
/// <param name="updateRouter">The update router for handler execution</param>
/// <param name="client">The Telegram bot client instance</param>
/// <param name="update">The incoming Telegram update to process</param>
/// <param name="cancellationToken"></param>
/// <returns>A collection of described handler information</returns>
public virtual IEnumerable<DescribedHandlerInfo> DescribeDescriptors(HandlerDescriptorList descriptors, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
{
try
{
LeveledDebug.ProviderWriteLine("Describing descriptors of descriptorsList.HandlingType.{0} for Update ({1})", descriptors.HandlingType, update.Id);
foreach (HandlerDescriptor descriptor in descriptors.Reverse())
{
cancellationToken.ThrowIfCancellationRequested();
DescribedHandlerInfo? describedHandler = DescribeHandler(descriptor, updateRouter, client, update, cancellationToken);
if (describedHandler == null)
continue;
yield return describedHandler;
if (Options.ExecuteOnlyFirstFoundHanlder)
break;
}
}
finally
{
LeveledDebug.ProviderWriteLine("Describing for Update ({0}) finished", update.Id);
}
}
/// <summary>
/// Describes a single handler descriptor for a given update context.
/// Validates the handler's filters against the update and creates a handler instance if validation passes.
/// </summary>
/// <param name="descriptor">The handler descriptor to process</param>
/// <param name="updateRouter">The update router for handler execution</param>
/// <param name="client">The Telegram bot client instance</param>
/// <param name="update">The incoming Telegram update to process</param>
/// <param name="cancellationToken"></param>
/// <returns>The described handler info if validation passes; otherwise, null</returns>
public virtual DescribedHandlerInfo? DescribeHandler(HandlerDescriptor descriptor, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
Dictionary<string, object> data = new Dictionary<string, object>()
{
{ "handler_name", descriptor.ToString() }
};
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(BotInfo, update, update, data, []);
if (!descriptor.Filters.Validate(filterContext))
return null;
UpdateHandlerBase handlerInstance = GetHandlerInstance(descriptor, cancellationToken);
return new DescribedHandlerInfo(updateRouter, client, handlerInstance, filterContext, descriptor.DisplayString);
}
/// <summary>
/// Instantiates a handler for the given descriptor, using the appropriate creation strategy based on descriptor type.
/// Supports singleton, implicit, keyed, and general descriptor types with different instantiation patterns.
/// </summary>
/// <param name="descriptor">The handler descriptor containing type and instantiation information</param>
/// <param name="cancellationToken"></param>
/// <returns>An instance of <see cref="UpdateHandlerBase"/> for the descriptor</returns>
/// <exception cref="Exception">Thrown when the descriptor type is not recognized</exception> /// <exception cref="Exception">Thrown when the descriptor type is not recognized</exception>
public virtual UpdateHandlerBase GetHandlerInstance(HandlerDescriptor descriptor, CancellationToken cancellationToken = default) public virtual UpdateHandlerBase GetHandlerInstance(HandlerDescriptor descriptor, CancellationToken cancellationToken = default)
{ {
@@ -192,31 +83,13 @@ namespace Telegrator.Providers
} }
} }
/// <summary> /// <inheritdoc/>
/// Gets the list of bot commands defined by all handler types with <see cref="CommandAlliasAttribute"/>. public virtual bool TryGetDescriptorList(UpdateType updateType, out HandlerDescriptorList? list)
/// Extracts command aliases and descriptions from message handlers for bot command registration.
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns>A collection of <see cref="BotCommand"/> objects for the bot</returns>
public IEnumerable<BotCommand> GetBotCommands(CancellationToken cancellationToken = default)
{ {
if (!HandlersDictionary.TryGetValue(UpdateType.Message, out HandlerDescriptorList? list)) return HandlersDictionary.TryGetValue(updateType, out list);
yield break;
foreach (BotCommand botCommand in list
.Select(descriptor => descriptor.HandlerType)
.SelectMany(handlerType => handlerType.GetCustomAttributes<CommandAlliasAttribute>()
.SelectMany(attribute => attribute.Alliases.Select(alias => new BotCommand(alias, attribute.Description)))))
{
cancellationToken.ThrowIfCancellationRequested();
yield return botCommand;
}
} }
/// <summary> /// <inheritdoc/>
/// Determines whether the provider contains any handlers.
/// </summary>
/// <returns>True if there are no handlers registered; otherwise, false</returns>
public virtual bool IsEmpty() public virtual bool IsEmpty()
{ {
return HandlersDictionary.Count == 0; return HandlersDictionary.Count == 0;
+2 -2
View File
@@ -30,7 +30,7 @@ namespace Telegrator.StateKeeping
/// <param name="_">The handler container (unused parameter for extension method syntax).</param> /// <param name="_">The handler container (unused parameter for extension method syntax).</param>
/// <returns>The enum state keeper instance.</returns> /// <returns>The enum state keeper instance.</returns>
public static EnumStateKeeper<TEnum> EnumStateKeeper<TEnum>(this IHandlerContainer _) where TEnum : Enum public static EnumStateKeeper<TEnum> EnumStateKeeper<TEnum>(this IHandlerContainer _) where TEnum : Enum
=> EnumStateAttribute<TEnum>.StateKeeper; => EnumStateAttribute<TEnum>.Shared;
/// <summary> /// <summary>
/// Creates a new enum state for the current update. /// Creates a new enum state for the current update.
@@ -55,7 +55,7 @@ namespace Telegrator.StateKeeping
/// <param name="container">The handler container.</param> /// <param name="container">The handler container.</param>
/// <param name="newState">The new state value. If null, uses the default state.</param> /// <param name="newState">The new state value. If null, uses the default state.</param>
public static void SetEnumState<TEnum>(this IHandlerContainer container, TEnum? newState) where TEnum : Enum public static void SetEnumState<TEnum>(this IHandlerContainer container, TEnum? newState) where TEnum : Enum
=> container.EnumStateKeeper<TEnum>().SetState(container.HandlingUpdate, newState ?? EnumStateAttribute<TEnum>.StateKeeper.DefaultState); => container.EnumStateKeeper<TEnum>().SetState(container.HandlingUpdate, newState ?? EnumStateAttribute<TEnum>.DefaultState);
/// <summary> /// <summary>
/// Moves the enum state forward to the next value in the enum sequence. /// Moves the enum state forward to the next value in the enum sequence.
@@ -50,7 +50,7 @@ namespace Telegrator.StateKeeping
/// <param name="_">The handler container instance</param> /// <param name="_">The handler container instance</param>
/// <returns>The <see cref="NumericStateKeeper"/> instance</returns> /// <returns>The <see cref="NumericStateKeeper"/> instance</returns>
public static NumericStateKeeper NumericStateKeeper(this IHandlerContainer _) public static NumericStateKeeper NumericStateKeeper(this IHandlerContainer _)
=> NumericStateAttribute.StateKeeper; => NumericStateAttribute.Shared;
/// <summary> /// <summary>
/// Creates a new numeric state for the current update being handled. /// Creates a new numeric state for the current update being handled.
@@ -73,7 +73,7 @@ namespace Telegrator.StateKeeping
/// <param name="container">The handler container instance</param> /// <param name="container">The handler container instance</param>
/// <param name="newState">The new numeric state to set, or null to use default</param> /// <param name="newState">The new numeric state to set, or null to use default</param>
public static void SetNumericState(this IHandlerContainer container, int? newState) public static void SetNumericState(this IHandlerContainer container, int? newState)
=> container.NumericStateKeeper().SetState(container.HandlingUpdate, newState ?? NumericStateAttribute.StateKeeper.DefaultState); => container.NumericStateKeeper().SetState(container.HandlingUpdate, newState ?? NumericStateAttribute.DefaultState);
/// <summary> /// <summary>
/// Moves the numeric state forward by incrementing the current value. /// Moves the numeric state forward by incrementing the current value.
+2 -3
View File
@@ -1,6 +1,5 @@
using Telegrator.Annotations.StateKeeping; using Telegrator.Annotations.StateKeeping;
using Telegrator.Handlers.Components; using Telegrator.Handlers.Components;
using Telegrator.StateKeeping;
namespace Telegrator.StateKeeping namespace Telegrator.StateKeeping
{ {
@@ -34,7 +33,7 @@ namespace Telegrator.StateKeeping
/// <param name="_">The handler container instance</param> /// <param name="_">The handler container instance</param>
/// <returns>The <see cref="StringStateKeeper"/> instance</returns> /// <returns>The <see cref="StringStateKeeper"/> instance</returns>
public static StringStateKeeper StringStateKeeper(this IHandlerContainer _) public static StringStateKeeper StringStateKeeper(this IHandlerContainer _)
=> StringStateAttribute.StateKeeper; => StringStateAttribute.Shared;
/// <summary> /// <summary>
/// Creates a new string state for the current update being handled. /// Creates a new string state for the current update being handled.
@@ -57,7 +56,7 @@ namespace Telegrator.StateKeeping
/// <param name="container">The handler container instance</param> /// <param name="container">The handler container instance</param>
/// <param name="newState">The new string state to set, or null to use default</param> /// <param name="newState">The new string state to set, or null to use default</param>
public static void SetStringState(this IHandlerContainer container, string? newState) public static void SetStringState(this IHandlerContainer container, string? newState)
=> container.StringStateKeeper().SetState(container.HandlingUpdate, newState ?? StringStateAttribute.StateKeeper.DefaultState); => container.StringStateKeeper().SetState(container.HandlingUpdate, newState ?? StringStateAttribute.DefaultState);
/// <summary> /// <summary>
/// Moves the string state forward to the next state in the sequence. /// Moves the string state forward to the next state in the sequence.
+1 -1
View File
@@ -17,7 +17,7 @@
<EnableNETAnalyzers>True</EnableNETAnalyzers> <EnableNETAnalyzers>True</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild> <EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<Version>1.0.2</Version> <Version>1.0.3</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
+3 -3
View File
@@ -63,10 +63,10 @@ namespace Telegrator
if (Options.GlobalCancellationToken == CancellationToken.None) if (Options.GlobalCancellationToken == CancellationToken.None)
Options.GlobalCancellationToken = cancellationToken; Options.GlobalCancellationToken = cancellationToken;
HandlersProvider handlerProvider = new HandlersProvider(Handlers, Options, BotInfo); HandlersProvider handlerProvider = new HandlersProvider(Handlers, Options);
AwaitingProvider awaitingProvider = new AwaitingProvider(Options, BotInfo); AwaitingProvider awaitingProvider = new AwaitingProvider(Options);
updateRouter = new UpdateRouter(handlerProvider, awaitingProvider, Options); updateRouter = new UpdateRouter(handlerProvider, awaitingProvider, Options, BotInfo);
StartReceivingInternal(receiverOptions, cancellationToken); StartReceivingInternal(receiverOptions, cancellationToken);
} }
+63 -1
View File
@@ -4,6 +4,7 @@ using System.Reflection;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.Payments; using Telegram.Bot.Types.Payments;
using Telegrator.Annotations;
using Telegrator.Annotations.StateKeeping; using Telegrator.Annotations.StateKeeping;
using Telegrator.Attributes; using Telegrator.Attributes;
using Telegrator.Filters.Components; using Telegrator.Filters.Components;
@@ -11,6 +12,7 @@ using Telegrator.Handlers.Building;
using Telegrator.Handlers.Building.Components; using Telegrator.Handlers.Building.Components;
using Telegrator.Handlers.Components; using Telegrator.Handlers.Components;
using Telegrator.MadiatorCore; using Telegrator.MadiatorCore;
using Telegrator.MadiatorCore.Descriptors;
using Telegrator.Providers; using Telegrator.Providers;
using Telegrator.StateKeeping; using Telegrator.StateKeeping;
using Telegrator.StateKeeping.Components; using Telegrator.StateKeeping.Components;
@@ -87,7 +89,7 @@ namespace Telegrator
/// <param name="_">The handler container (unused).</param> /// <param name="_">The handler container (unused).</param>
/// <returns>The state keeper instance.</returns> /// <returns>The state keeper instance.</returns>
public static TKeeper GetStateKeeper<TKey, TState, TKeeper>(this IHandlerContainer _) where TKey : notnull where TState : IEquatable<TState> where TKeeper : StateKeeperBase<TKey, TState>, new() public static TKeeper GetStateKeeper<TKey, TState, TKeeper>(this IHandlerContainer _) where TKey : notnull where TState : IEquatable<TState> where TKeeper : StateKeeperBase<TKey, TState>, new()
=> StateKeeperAttribute<TKey, TState, TKeeper>.StateKeeper; => StateKeeperAttribute<TKey, TState, TKeeper>.Shared;
} }
/// <summary> /// <summary>
@@ -161,6 +163,31 @@ namespace Telegrator
=> awaitingProvider.CreateAbstract<CallbackQuery>(UpdateType.CallbackQuery, handlingUpdate); => awaitingProvider.CreateAbstract<CallbackQuery>(UpdateType.CallbackQuery, handlingUpdate);
} }
/// <summary>
/// Extesions method for handlers providers
/// </summary>
public static class HandlersProviderExtensions
{
/// <summary>
/// Gets the list of bot commands supported by the provider.
/// </summary>
/// <returns>An enumerable of bot commands.</returns>
public static IEnumerable<BotCommand> GetBotCommands(this IHandlersProvider provider, CancellationToken cancellationToken = default)
{
if (!provider.TryGetDescriptorList(UpdateType.Message, out HandlerDescriptorList? list))
yield break;
foreach (BotCommand botCommand in list
.Select(descriptor => descriptor.HandlerType)
.SelectMany(handlerType => handlerType.GetCustomAttributes<CommandAlliasAttribute>()
.SelectMany(attribute => attribute.Alliases.Select(alias => new BotCommand(alias, attribute.Description)))))
{
cancellationToken.ThrowIfCancellationRequested();
yield return botCommand;
}
}
}
/// <summary> /// <summary>
/// Extension methods for handlers collections. /// Extension methods for handlers collections.
/// Provides convenient methods for creating implicit handlers. /// Provides convenient methods for creating implicit handlers.
@@ -850,6 +877,41 @@ namespace Telegrator
_ => update _ => update
}; };
/// <summary>
/// Selecting corresponding <see cref="UpdateType"/>s for <see cref="Update"/>'s sub-type
/// </summary>
/// <returns></returns>
public static UpdateType[] GetAllowedUpdateTypes(this Type type) => type.FullName switch
{
"Telegram.Bot.Types.Message" => UpdateTypeExtensions.MessageTypes,
"Telegram.Bot.Types.ChatMemberUpdated" => [UpdateType.MyChatMember, UpdateType.ChatMember],
"Telegram.Bot.Types.InlineQuery" => [UpdateType.InlineQuery],
"Telegram.Bot.Types.ChosenInlineResult" => [UpdateType.ChosenInlineResult],
"Telegram.Bot.Types.CallbackQuery" => [UpdateType.CallbackQuery],
"Telegram.Bot.Types.ShippingQuery" => [UpdateType.ShippingQuery],
"Telegram.Bot.Types.PreCheckoutQuery" => [UpdateType.PreCheckoutQuery],
"Telegram.Bot.Types.Poll" => [UpdateType.Poll],
"Telegram.Bot.Types.PollAnswer" => [UpdateType.PollAnswer],
"Telegram.Bot.Types.ChatJoinRequest" => [UpdateType.ChatJoinRequest],
"Telegram.Bot.Types.MessageReactionUpdated" => [UpdateType.MessageReaction],
"Telegram.Bot.TypesMessageReactionCountUpdated" => [UpdateType.MessageReactionCount],
"Telegram.Bot.Types.ChatBoostUpdated" => [UpdateType.ChatBoost],
"Telegram.Bot.Types.ChatBoostRemoved" => [UpdateType.RemovedChatBoost],
"Telegram.Bot.Types.BusinessConnection" => [UpdateType.BusinessConnection],
"Telegram.Bot.Types.BusinessMessagesDeleted" => [UpdateType.DeletedBusinessMessages],
"Telegram.Bot.Types.PaidMediaPurchased" => [UpdateType.PurchasedPaidMedia],
"Telegram.Bot.Types.Update" => Update.AllTypes,
_ => []
};
/// <summary>
/// Selecting corresponding <see cref="UpdateType"/>s for <see cref="Update"/>'s sub-type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static UpdateType[] GetAllowedUpdateTypes<T>() where T : class
=> GetAllowedUpdateTypes(typeof(T));
/// <summary> /// <summary>
/// Selects from <see cref="Update"/> an <typeparamref name="T"/> that contains information about the update /// Selects from <see cref="Update"/> an <typeparamref name="T"/> that contains information about the update
/// </summary> /// </summary>