* Renamed "concurrency" parameters in handlers attributes to more relevant and intuitive "importance" parameter
* Added debug logging helper that use default Debug tracer to trace filter, providers, routers and pool execution * Added debug logging for failing during handlers resolving, filters * Added new "CreateEmptyBuilder" methods for TelegramBotHost class * Addede ability to name handlers using "DisplayNameAttribute". This name is writed to descriptor's "DisplayString" property * Fixed missing summaries inside Hosting library
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegrator.Attributes;
|
||||
using Telegrator.Filters;
|
||||
using Telegrator.Filters.Components;
|
||||
|
||||
namespace Telegrator.Annotations
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base attribute for filtering callback-based updates.
|
||||
/// Supports various message types including regular messages, edited messages, channel posts, and business messages.
|
||||
/// </summary>
|
||||
/// <param name="filters">The filters to apply to messages</param>
|
||||
public abstract class CallbackQueryAttribute(params IFilter<CallbackQuery>[] filters) : UpdateFilterAttribute<CallbackQuery>(filters)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the allowed update types that this filter can process.
|
||||
/// </summary>
|
||||
public override UpdateType[] AllowedTypes => [UpdateType.CallbackQuery];
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the message from various types of updates.
|
||||
/// </summary>
|
||||
/// <param name="update">The Telegram update</param>
|
||||
/// <returns>The message from the update, or null if not present</returns>
|
||||
public override CallbackQuery? GetFilterringTarget(Update update)
|
||||
=> update.CallbackQuery;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attribute for filtering <see cref="CallbackQuery"/>'s data
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public class CallbackDataAttribute(string data)
|
||||
: CallbackQueryAttribute(new CallbackDataFilter(data))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Attribute to check if <see cref="CallbackQuery"/> belongs to a specific message by its ID
|
||||
/// </summary>
|
||||
public class CallbackInlineIdAttribute(string inlineMessageId)
|
||||
: CallbackQueryAttribute(new CallbackInlineIdFilter(inlineMessageId))
|
||||
{ }
|
||||
}
|
||||
@@ -47,7 +47,7 @@ namespace Telegrator.Annotations
|
||||
/// </summary>
|
||||
/// <param name="alliases">The command aliases to match against.</param>
|
||||
public CommandAlliasAttribute(params string[] alliases)
|
||||
: base(new CommandAlliasFilter(alliases)) => Alliases = alliases;
|
||||
: base(new CommandAlliasFilter(alliases)) => Alliases = alliases.Select(c => c.TrimStart('/')).ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filtering target (Message) from the update.
|
||||
|
||||
@@ -10,9 +10,18 @@ namespace Telegrator.Annotations
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Attribute for filtering messages in reply chain.
|
||||
/// Attribute for checking message's reply chain.
|
||||
/// </summary>
|
||||
public class MessageRepliedAttribute(int replyDepth = 1)
|
||||
: MessageFilterAttribute(new MessageRepliedFilter(replyDepth))
|
||||
public class HasReplyAttribute(int replyDepth = 1)
|
||||
: MessageFilterAttribute(new MessageHasReplyFilter(replyDepth))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Helper filter class for filters that operate on replied messages.
|
||||
/// Provides functionality to traverse reply chains and access replied message content.
|
||||
/// </summary>
|
||||
/// <param name="replyDepth"></param>
|
||||
public class FromReplyChainAttribute(int replyDepth = 1)
|
||||
: MessageFilterAttribute(new FromReplyChainFilter(replyDepth))
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegrator.Filters;
|
||||
|
||||
namespace Telegrator.Annotations
|
||||
@@ -12,7 +13,8 @@ namespace Telegrator.Annotations
|
||||
/// <summary>
|
||||
/// Creates new instance of <see cref="WelcomeAttribute"/>
|
||||
/// </summary>
|
||||
public WelcomeAttribute() : base(new MessageChatTypeFilter(ChatType.Private), new CommandAlliasFilter("start"))
|
||||
/// <param name="onlyFirst"></param>
|
||||
public WelcomeAttribute(bool onlyFirst = false) : base(new MessageChatTypeFilter(ChatType.Private), new CommandAlliasFilter("start"), Filter<Message>.If(ctx => !onlyFirst || ctx.Input.Id == 0))
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ namespace Telegrator.Attributes.Components
|
||||
public UpdateType Type { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets concurrency of this <see cref="UpdateHandlerBase"/> in same <see cref="UpdateType"/> pool
|
||||
/// Gets or sets importance of this <see cref="UpdateHandlerBase"/> in same <see cref="UpdateType"/> pool
|
||||
/// </summary>
|
||||
public int Concurrency { get; set; }
|
||||
public int Importance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets priority of this <see cref="UpdateHandlerBase"/> in same type handlers pool
|
||||
@@ -40,11 +40,11 @@ namespace Telegrator.Attributes.Components
|
||||
/// </summary>
|
||||
/// <param name="expectingHandlerType"></param>
|
||||
/// <param name="updateType"></param>
|
||||
/// <param name="concurrency"></param>
|
||||
/// <param name="importance"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
/// <exception cref="Exception"></exception>
|
||||
protected internal UpdateHandlerAttributeBase(Type[] expectingHandlerType, UpdateType updateType, int concurrency = 0)
|
||||
protected internal UpdateHandlerAttributeBase(Type[] expectingHandlerType, UpdateType updateType, int importance = 0)
|
||||
{
|
||||
if (expectingHandlerType == null)
|
||||
throw new ArgumentNullException(nameof(expectingHandlerType));
|
||||
@@ -57,11 +57,11 @@ namespace Telegrator.Attributes.Components
|
||||
|
||||
ExpectingHandlerType = expectingHandlerType;
|
||||
Type = updateType;
|
||||
Concurrency = concurrency;
|
||||
Importance = importance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="DescriptorIndexer"/> of this <see cref="UpdateHandlerAttributeBase"/> from <see cref="Concurrency"/> and <see cref="Priority"/>
|
||||
/// Gets an <see cref="DescriptorIndexer"/> of this <see cref="UpdateHandlerAttributeBase"/> from <see cref="Importance"/> and <see cref="Priority"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public DescriptorIndexer GetIndexer()
|
||||
|
||||
@@ -6,13 +6,13 @@ namespace Telegrator.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base attribute for marking update handler classes.
|
||||
/// Provides a type-safe way to associate handler types with specific update types and concurrency settings.
|
||||
/// Provides a type-safe way to associate handler types with specific update types and importance settings.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the update handler that this attribute is applied to.</typeparam>
|
||||
/// <param name="updateType">The type of update that this handler can process.</param>
|
||||
/// <param name="concurrency">The concurrency level for this handler (default: 0 for unlimited).</param>
|
||||
public abstract class UpdateHandlerAttribute<T>(UpdateType updateType, int concurrency = 0)
|
||||
: UpdateHandlerAttributeBase([typeof(T)], updateType, concurrency) where T : UpdateHandlerBase
|
||||
/// <param name="importance">The importance level for this handler (default: 0 for unlimited).</param>
|
||||
public abstract class UpdateHandlerAttribute<T>(UpdateType updateType, int importance = 0)
|
||||
: UpdateHandlerAttributeBase([typeof(T)], updateType, importance) where T : UpdateHandlerBase
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Filters.Components;
|
||||
|
||||
namespace Telegrator.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Filter thet checks <see cref="CallbackQuery"/>'s data
|
||||
/// </summary>
|
||||
public class CallbackDataFilter : Filter<CallbackQuery>
|
||||
{
|
||||
private readonly string _data;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize new instance of <see cref="CallbackDataFilter"/>
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public CallbackDataFilter(string data)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanPass(FilterExecutionContext<CallbackQuery> context)
|
||||
{
|
||||
return context.Input.Data == _data;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filter that checks if <see cref="CallbackQuery"/> belongs to a specific message
|
||||
/// </summary>
|
||||
public class CallbackInlineIdFilter : Filter<CallbackQuery>
|
||||
{
|
||||
private readonly string _inlineMessageId;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize new instance of <see cref="CallbackInlineIdFilter"/>
|
||||
/// </summary>
|
||||
/// <param name="inlineMessageId"></param>
|
||||
public CallbackInlineIdFilter(string inlineMessageId)
|
||||
{
|
||||
_inlineMessageId = inlineMessageId;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanPass(FilterExecutionContext<CallbackQuery> context)
|
||||
{
|
||||
return context.Input.InlineMessageId == _inlineMessageId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,12 @@ namespace Telegrator.Filters.Components
|
||||
foreach (IFilter<T> filter in filters)
|
||||
{
|
||||
if (!filter.CanPass(context))
|
||||
{
|
||||
if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter)
|
||||
LeveledDebug.FilterWriteLine("(E) {0} filter of {1} didnt pass!", filter.GetType().Name, context.Data["handler_name"]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
context.CompletedFilters.Add(filter);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,12 @@ namespace Telegrator.Filters.Components
|
||||
{
|
||||
FilterExecutionContext<T> context = updateContext.CreateChild((T)filterringTarget);
|
||||
if (!filter.CanPass(context))
|
||||
{
|
||||
if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter)
|
||||
LeveledDebug.FilterWriteLine("(E) {0} filter of {1} didnt pass!", filter.GetType().Name, context.Data["handler_name"]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
context.CompletedFilters.Add(filter);
|
||||
return true;
|
||||
|
||||
@@ -37,7 +37,12 @@
|
||||
foreach (IFilter<T> filter in Filters)
|
||||
{
|
||||
if (!filter.CanPass(context))
|
||||
{
|
||||
if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter)
|
||||
LeveledDebug.FilterWriteLine("(E) {0} filter of {1} didnt pass!", filter.GetType().Name, context.Data["handler_name"]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
context.CompletedFilters.Add(filter);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Reflection;
|
||||
using Telegrator;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.Filters.Components;
|
||||
|
||||
namespace Telegrator.Filters
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Telegrator.Filters
|
||||
/// <param name="mention">The username to check for in the mention.</param>
|
||||
public MentionedFilter(string mention)
|
||||
{
|
||||
Mention = mention;
|
||||
Mention = mention.TrimStart('@');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Telegrator.Filters
|
||||
/// <inheritdoc/>
|
||||
public override bool CanPass(FilterExecutionContext<Message> context)
|
||||
{
|
||||
MessageRepliedFilter? repliedFilter = context.CompletedFilters.Get<MessageRepliedFilter>().SingleOrDefault();
|
||||
FromReplyChainFilter? repliedFilter = context.CompletedFilters.Get<FromReplyChainFilter>().SingleOrDefault();
|
||||
Target = repliedFilter?.Reply ?? context.Input;
|
||||
return CanPassNext(context);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,51 @@ using Telegrator.Filters.Components;
|
||||
namespace Telegrator.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base class for filters that operate on replied messages.
|
||||
/// Provides functionality to traverse reply chains and access replied message content.
|
||||
/// Filter that checks if message has appropriate reply chain.
|
||||
/// DOES NOT SHiFT MESSAGE FILTERS TARGET
|
||||
/// </summary>
|
||||
/// <param name="replyDepth">The depth of reply chain to traverse (default: 1).</param>
|
||||
public class MessageRepliedFilter(int replyDepth = 1) : Filter<Message>
|
||||
public class MessageHasReplyFilter(int replyDepth = 1) : Filter<Message>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the replied message at the specified depth in the reply chain.
|
||||
/// </summary>
|
||||
public Message Reply { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the depth of reply chain traversal.
|
||||
/// </summary>
|
||||
public int ReplyDepth { get; private set; } = replyDepth;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the message can pass through the filter by first validating
|
||||
/// the reply chain and then applying specific filter logic.
|
||||
/// </summary>
|
||||
/// <param name="context">The filter execution context containing the message.</param>
|
||||
/// <returns>True if the message passes both reply validation and specific filter criteria; otherwise, false.</returns>
|
||||
public override bool CanPass(FilterExecutionContext<Message> context)
|
||||
{
|
||||
Message reply = context.Input;
|
||||
for (int i = ReplyDepth; i > 0; i--)
|
||||
{
|
||||
if (reply.ReplyToMessage is not { Id: > 0 } replyMessage)
|
||||
return false;
|
||||
|
||||
reply = replyMessage;
|
||||
}
|
||||
|
||||
Reply = reply;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper filter class for filters that operate on replied messages.
|
||||
/// Provides functionality to traverse reply chains and access replied message content
|
||||
/// and shifts any next message filter to filter the content of found reply.
|
||||
/// </summary>
|
||||
/// <param name="replyDepth"></param>
|
||||
public class FromReplyChainFilter(int replyDepth = 1) : Filter<Message>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the replied message at the specified depth in the reply chain.
|
||||
@@ -44,7 +84,7 @@ namespace Telegrator.Filters
|
||||
|
||||
/// <summary>
|
||||
/// Filter that checks if the replied message was sent by the bot itself.
|
||||
/// <para>( ! ): REQUIRES <see cref="MessageRepliedFilter"/> before</para>
|
||||
/// <para>( ! ): REQUIRES <see cref="MessageHasReplyFilter"/> before</para>
|
||||
/// </summary>
|
||||
public class MeRepliedFilter : Filter<Message>
|
||||
{
|
||||
@@ -55,7 +95,7 @@ namespace Telegrator.Filters
|
||||
/// <returns>True if the replied message was sent by the bot; otherwise, false.</returns>
|
||||
public override bool CanPass(FilterExecutionContext<Message> context)
|
||||
{
|
||||
MessageRepliedFilter repliedFilter = context.CompletedFilters.Get<MessageRepliedFilter>(0);
|
||||
MessageHasReplyFilter repliedFilter = context.CompletedFilters.Get<MessageHasReplyFilter>(0);
|
||||
return context.BotInfo.User == repliedFilter.Reply.From;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Telegrator.Filters
|
||||
if (!base.CanPass(context))
|
||||
return false;
|
||||
|
||||
Message = context.Update.Message!;
|
||||
Message = context.Input!;
|
||||
if (Message is not { Id: > 0 })
|
||||
return false;
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Telegrator.Handlers
|
||||
/// Attribute that marks a handler to process any type of update.
|
||||
/// This handler will be triggered for all incoming updates regardless of their type.
|
||||
/// </summary>
|
||||
/// <param name="concurrency">The maximum number of concurrent executions allowed (default: -1 for unlimited).</param>
|
||||
public class AnyUpdateHandlerAttribute(int concurrency = -1) : UpdateHandlerAttribute<AnyUpdateHandler>(UpdateType.Unknown, concurrency)
|
||||
/// <param name="importance"></param>
|
||||
public class AnyUpdateHandlerAttribute(int importance = -1) : UpdateHandlerAttribute<AnyUpdateHandler>(UpdateType.Unknown, importance)
|
||||
{
|
||||
/// <summary>
|
||||
/// Always returns true, allowing any update to pass through this filter.
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace Telegrator.Handlers.Building.Components
|
||||
/// <returns>The builder instance.</returns>
|
||||
public void SetConcurreny(int concurrency)
|
||||
{
|
||||
Indexer = Indexer.UpdateConcurrency(concurrency);
|
||||
Indexer = Indexer.UpdateImportance(concurrency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -10,15 +10,15 @@ namespace Telegrator.Handlers
|
||||
/// Attribute that marks a handler to process callback query updates.
|
||||
/// This handler will be triggered when users interact with inline keyboards or other callback mechanisms.
|
||||
/// </summary>
|
||||
/// <param name="concurrency">The maximum number of concurrent executions allowed (default: 0 for unlimited).</param>
|
||||
public sealed class CallbackQueryHandlerAttribute(int concurrency = 0) : UpdateHandlerAttribute<CallbackQueryHandler>(UpdateType.CallbackQuery, concurrency)
|
||||
/// <param name="importance"></param>
|
||||
public sealed class CallbackQueryHandlerAttribute(int importance = 0) : UpdateHandlerAttribute<CallbackQueryHandler>(UpdateType.CallbackQuery, importance)
|
||||
{
|
||||
/// <summary>
|
||||
/// Always returns true, allowing any callback query update to pass through this filter.
|
||||
/// </summary>
|
||||
/// <param name="context">The filter execution context (unused).</param>
|
||||
/// <returns>Always returns true to allow any callback query update.</returns>
|
||||
public override bool CanPass(FilterExecutionContext<Update> context) => true;
|
||||
public override bool CanPass(FilterExecutionContext<Update> context) => context.Input is { CallbackQuery: { } };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -9,8 +9,7 @@ namespace Telegrator.Handlers
|
||||
/// Attribute that marks a handler to process command messages.
|
||||
/// This handler will be triggered when users send bot commands (messages starting with '/').
|
||||
/// </summary>
|
||||
/// <param name="concurrency">The maximum number of concurrent executions allowed (default: 1).</param>
|
||||
public class CommandHandlerAttribute(int concurrency = 1) : UpdateHandlerAttribute<CommandHandler>(UpdateType.Message, concurrency)
|
||||
public class CommandHandlerAttribute(int importance = 1) : UpdateHandlerAttribute<CommandHandler>(UpdateType.Message, importance)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the command that was extracted from the message (without the '/' prefix and bot username).
|
||||
|
||||
@@ -12,8 +12,7 @@ namespace Telegrator.Handlers
|
||||
/// Attribute that marks a handler to process message updates.
|
||||
/// This handler will be triggered when users send messages in chats.
|
||||
/// </summary>
|
||||
/// <param name="concurrency">The maximum number of concurrent executions allowed (default: 0 for unlimited).</param>
|
||||
public class MessageHandlerAttribute(int concurrency = 0) : UpdateHandlerAttribute<MessageHandler>(UpdateType.Message, concurrency)
|
||||
public class MessageHandlerAttribute(int importance = 0) : UpdateHandlerAttribute<MessageHandler>(UpdateType.Message, importance)
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the update contains a valid message.
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Telegrator
|
||||
{
|
||||
/// <summary>
|
||||
/// Telegrator's Debug logger helper
|
||||
/// </summary>
|
||||
public static class LeveledDebug
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes debug message if Indent level has Router flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void RouterWriteLine(string message)
|
||||
{
|
||||
if (Debug.IndentLevel.HasFlag(DebugLevel.Router))
|
||||
Debug.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes debug message if Indent level has Router flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void RouterWriteLine(string message, params object[] args)
|
||||
{
|
||||
if (Debug.IndentLevel.HasFlag(DebugLevel.Router))
|
||||
Debug.WriteLine(message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes debug message if Indent level has Providers flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void ProviderWriteLine(string message)
|
||||
{
|
||||
if (Debug.IndentLevel.HasFlag(DebugLevel.Providers))
|
||||
Debug.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes debug message if Indent level has Providers flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void ProviderWriteLine(string message, params object[] args)
|
||||
{
|
||||
if (Debug.IndentLevel.HasFlag(DebugLevel.Providers))
|
||||
Debug.WriteLine(message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes debug message if Indent level has Filters flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void FilterWriteLine(string message)
|
||||
{
|
||||
if (Debug.IndentLevel.HasFlag(DebugLevel.Filters))
|
||||
Debug.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes debug message if Indent level has Filters flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void FilterWriteLine(string message, params object[] args)
|
||||
{
|
||||
if (Debug.IndentLevel.HasFlag(DebugLevel.Filters))
|
||||
Debug.WriteLine(message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes debug message if Indent level has Pool flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public static void PoolWriteLine(string message)
|
||||
{
|
||||
if (Debug.IndentLevel.HasFlag(DebugLevel.HandlersPool))
|
||||
Debug.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes debug message if Indent level has Pool flag
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void PoolWriteLine(string message, params object[] args)
|
||||
{
|
||||
if (Debug.IndentLevel.HasFlag(DebugLevel.HandlersPool))
|
||||
Debug.WriteLine(message, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Levels of debug writing
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DebugLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Write debug messages from filters execution
|
||||
/// </summary>
|
||||
Filters = 0x1,
|
||||
|
||||
/// <summary>
|
||||
/// Write debug messages from handlers providers execution
|
||||
/// </summary>
|
||||
Providers = 0x2,
|
||||
|
||||
/// <summary>
|
||||
/// Write debug messages from update router's execution
|
||||
/// </summary>
|
||||
Router = 0x4,
|
||||
|
||||
/// <summary>
|
||||
/// Write debug messages from handlers pool execution
|
||||
/// </summary>
|
||||
HandlersPool = 0x8
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using Telegram.Bot.Polling;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.Handlers.Components;
|
||||
using Telegrator.MadiatorCore;
|
||||
|
||||
namespace Telegrator.MadiatorCore.Descriptors
|
||||
{
|
||||
@@ -111,5 +110,9 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
=> DisplayString ?? HandlerInstance.GetType().Name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,16 +46,24 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
if (UpdateValidator != null)
|
||||
{
|
||||
if (!UpdateValidator.CanPass(filterContext))
|
||||
{
|
||||
LeveledDebug.FilterWriteLine("(E) UpdateValidator filter of {0} for Update ({2}) didnt pass!", filterContext.Data["handler_name"]);
|
||||
return false;
|
||||
}
|
||||
|
||||
//LeveledDebug.FilterWriteLine("UpdateValidator of {0} for Update ({2}) passed", filterContext.Data["handler_name"]);
|
||||
filterContext.CompletedFilters.Add(UpdateValidator);
|
||||
}
|
||||
|
||||
if (StateKeeperValidator != null)
|
||||
{
|
||||
if (!StateKeeperValidator.CanPass(filterContext))
|
||||
{
|
||||
LeveledDebug.FilterWriteLine("(E) StateKeeperValidator filter of {0} for Update ({2}) didnt pass!", filterContext.Data["handler_name"]);
|
||||
return false;
|
||||
}
|
||||
|
||||
//LeveledDebug.FilterWriteLine("StateKeeperValidator of {0} for Update ({2}) passed", filterContext.Data["handler_name"]);
|
||||
filterContext.CompletedFilters.Add(StateKeeperValidator);
|
||||
}
|
||||
|
||||
@@ -64,8 +72,14 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
foreach (IFilter<Update> filter in UpdateFilters)
|
||||
{
|
||||
if (!filter.CanPass(filterContext))
|
||||
return false;
|
||||
{
|
||||
if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter)
|
||||
LeveledDebug.FilterWriteLine("(E) {0} filter of {1} didnt pass!", filter.GetType().Name, filterContext.Data["handler_name"]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//LeveledDebug.FilterWriteLine("{0} filter of {1} for Update ({2}) passed", filter.GetType().Name, filterContext.Data["handler_name"]);
|
||||
filterContext.CompletedFilters.Add(filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace Telegrator.MadiatorCore.Descriptors
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an indexer for handler descriptors, containing concurrency and priority information.
|
||||
/// Represents an indexer for handler descriptors, containing importance and priority information.
|
||||
/// </summary>
|
||||
public readonly struct DescriptorIndexer(int routerIndex, int concurrency, int priority) : IComparable<DescriptorIndexer>
|
||||
public readonly struct DescriptorIndexer(int routerIndex, int importance, int priority) : IComparable<DescriptorIndexer>
|
||||
{
|
||||
/// <summary>
|
||||
/// Index of this descriptor when it was added to router
|
||||
@@ -15,7 +15,7 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
/// <summary>
|
||||
/// Of this handlert type
|
||||
/// </summary>
|
||||
public readonly int Importance = concurrency;
|
||||
public readonly int Importance = importance;
|
||||
|
||||
/// <summary>
|
||||
/// The priority of the handler.
|
||||
@@ -28,7 +28,7 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
/// <param name="routerIndex"></param>
|
||||
/// <param name="pollingHandler">The handler attribute.</param>
|
||||
public DescriptorIndexer(int routerIndex, UpdateHandlerAttributeBase pollingHandler)
|
||||
: this(routerIndex, pollingHandler.Concurrency, pollingHandler.Priority) { }
|
||||
: this(routerIndex, pollingHandler.Importance, pollingHandler.Priority) { }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="DescriptorIndexer"/> with updated priority.
|
||||
@@ -39,12 +39,12 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
=> new DescriptorIndexer(RouterIndex, Importance, priority);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="DescriptorIndexer"/> with updated concurrency.
|
||||
/// Returns a new <see cref="DescriptorIndexer"/> with updated importance.
|
||||
/// </summary>
|
||||
/// <param name="concurrency">The new concurrency value.</param>
|
||||
/// <param name="importance">The new importance value.</param>
|
||||
/// <returns>A new <see cref="DescriptorIndexer"/> instance.</returns>
|
||||
public DescriptorIndexer UpdateConcurrency(int concurrency)
|
||||
=> new DescriptorIndexer(RouterIndex, concurrency, Priority);
|
||||
public DescriptorIndexer UpdateImportance(int importance)
|
||||
=> new DescriptorIndexer(RouterIndex, importance, Priority);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="DescriptorIndexer"/> with updated RouterIndex.
|
||||
@@ -79,7 +79,7 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
/// <summary>
|
||||
/// Returns a string representation of the indexer.
|
||||
/// </summary>
|
||||
/// <returns>A string in the format (C:concurrency, P:priority).</returns>
|
||||
/// <returns>A string in the format (C:importance, P:priority).</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("(I:{0}, C:{1}, P:{2})", RouterIndex, Importance, Priority);
|
||||
|
||||
@@ -139,6 +139,7 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
UpdateType = handlerAttribute.Type;
|
||||
Indexer = handlerAttribute.GetIndexer();
|
||||
Filters = new DescriptorFiltersSet(handlerAttribute, stateKeeperAttribute, filters);
|
||||
DisplayString = HandlerInspector.GetDisplayName(handlerType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -402,5 +403,9 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
ServiceKey = serviceKey ?? throw new ArgumentNullException(nameof(serviceKey));
|
||||
InstanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
=> DisplayString ?? HandlerType.Name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System.Collections;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegrator;
|
||||
using Telegrator.Configuration;
|
||||
using Telegrator.MadiatorCore;
|
||||
|
||||
namespace Telegrator.MadiatorCore.Descriptors
|
||||
{
|
||||
@@ -29,6 +27,11 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
/// </summary>
|
||||
public UpdateType HandlingType => _handlingType;
|
||||
|
||||
/// <summary>
|
||||
/// Gets count of registered handlers in list
|
||||
/// </summary>
|
||||
public int Count => _innerCollection.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="HandlerDescriptor"/> at the specified index.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Reflection;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegrator.Attributes.Components;
|
||||
@@ -11,6 +12,16 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
/// </summary>
|
||||
public static class HandlerInspector
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets handler's display name
|
||||
/// </summary>
|
||||
/// <param name="handlerType"></param>
|
||||
/// <returns></returns>
|
||||
public static string? GetDisplayName(MemberInfo handlerType)
|
||||
{
|
||||
return handlerType.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the handler attribute from the specified member info.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Telegram.Bot;
|
||||
using System;
|
||||
using System.Text;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Polling;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Polling;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegrator.Configuration;
|
||||
using Telegrator.Handlers.Components;
|
||||
using Telegrator.MadiatorCore;
|
||||
@@ -92,6 +94,7 @@ namespace Telegrator.Polling
|
||||
/// <returns>A task representing the asynchronous error handling operation.</returns>
|
||||
public virtual Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, HandleErrorSource source, CancellationToken cancellationToken)
|
||||
{
|
||||
LeveledDebug.RouterWriteLine("Handling exception {0}", exception.GetType().Name);
|
||||
ExceptionHandler?.HandleException(botClient, exception, source, cancellationToken);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -105,21 +108,60 @@ namespace Telegrator.Polling
|
||||
/// <returns>A task representing the asynchronous update handling operation.</returns>
|
||||
public virtual Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
|
||||
{
|
||||
// Logging
|
||||
LeveledDebug.RouterWriteLine("Received Update ({0}) of type \"{1}\"", update.Id, update.Type);
|
||||
LogUpdate(update);
|
||||
|
||||
// Queuing handlers for execution
|
||||
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)
|
||||
{
|
||||
// Getting handlers in update awaiting pool
|
||||
IEnumerable<DescribedHandlerInfo> handlers = AwaitingProvider.GetHandlers(this, botClient, update, cancellationToken);
|
||||
if (handlers.Any() && Options.ExclusiveAwaitingHandlerRouting)
|
||||
return handlers;
|
||||
try
|
||||
{
|
||||
// Getting handlers in update awaiting pool
|
||||
IEnumerable<DescribedHandlerInfo> handlers = AwaitingProvider.GetHandlers(this, botClient, update, cancellationToken);
|
||||
if (handlers.Any() && Options.ExclusiveAwaitingHandlerRouting)
|
||||
return handlers;
|
||||
|
||||
return handlers.Concat(HandlersProvider.GetHandlers(this, botClient, update, cancellationToken));
|
||||
return handlers.Concat(HandlersProvider.GetHandlers(this, botClient, update, cancellationToken));
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_ = 0xBAD + 0xC0DE;
|
||||
return [];
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionHandler?.HandleException(botClient, ex, HandleErrorSource.PollingError, cancellationToken);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogUpdate(Update update)
|
||||
{
|
||||
switch (update.Type)
|
||||
{
|
||||
case UpdateType.Message:
|
||||
{
|
||||
Message msg = update.Message ?? throw new NullReferenceException();
|
||||
StringBuilder sb = new StringBuilder("Update.Message");
|
||||
|
||||
if (msg.From != null)
|
||||
sb.AppendFormat(" from {0} ({1})", msg.From.Username, msg.From.Id);
|
||||
|
||||
if (msg.Text != null)
|
||||
sb.AppendFormat("'{0}'", msg.Text);
|
||||
|
||||
LeveledDebug.RouterWriteLine(sb.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types;
|
||||
@@ -79,16 +80,23 @@ namespace Telegrator.Providers
|
||||
/// <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))
|
||||
{
|
||||
if (!HandlersDictionary.TryGetValue(UpdateType.Unknown, out descriptors))
|
||||
return [];
|
||||
LeveledDebug.ProviderWriteLine("No registered, providing Any");
|
||||
HandlersDictionary.TryGetValue(UpdateType.Unknown, out descriptors);
|
||||
}
|
||||
|
||||
if (descriptors == null || !descriptors.Any())
|
||||
if (descriptors == null || descriptors.Count == 0)
|
||||
{
|
||||
LeveledDebug.ProviderWriteLine("No handlers provided");
|
||||
return [];
|
||||
}
|
||||
|
||||
return DescribeDescriptors(descriptors, updateRouter, client, update, cancellationToken);
|
||||
IEnumerable<DescribedHandlerInfo> described = DescribeDescriptors(descriptors, updateRouter, client, update, cancellationToken);
|
||||
LeveledDebug.ProviderWriteLine("Described total of {0} handlers for Update ({1})", described.Count(), update.Id);
|
||||
LeveledDebug.ProviderWriteLine("Described handlers : {0}", string.Join(", ", described));
|
||||
return described;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -103,16 +111,24 @@ namespace Telegrator.Providers
|
||||
/// <returns>A collection of described handler information</returns>
|
||||
public virtual IEnumerable<DescribedHandlerInfo> DescribeDescriptors(HandlerDescriptorList descriptors, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
||||
{
|
||||
foreach (HandlerDescriptor descriptor in descriptors.Reverse())
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
DescribedHandlerInfo? describedHandler = DescribeHandler(descriptor, updateRouter, client, update, cancellationToken);
|
||||
if (describedHandler == null)
|
||||
continue;
|
||||
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;
|
||||
yield return describedHandler;
|
||||
if (Options.ExecuteOnlyFirstFoundHanlder)
|
||||
break;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
LeveledDebug.ProviderWriteLine("Describing for Update ({0}) finished", update.Id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +145,12 @@ namespace Telegrator.Providers
|
||||
public virtual DescribedHandlerInfo? DescribeHandler(HandlerDescriptor descriptor, IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(BotInfo, update, update);
|
||||
Dictionary<string, object> data = new Dictionary<string, object>()
|
||||
{
|
||||
{ "handler_name", descriptor.ToString() }
|
||||
};
|
||||
|
||||
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(BotInfo, update, update, data, []);
|
||||
if (!descriptor.Filters.Validate(filterContext))
|
||||
return null;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<Version>1.0.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.Payments;
|
||||
@@ -902,4 +903,28 @@ namespace Telegrator
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains extension method for number types
|
||||
/// </summary>
|
||||
public static class NumbersExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if int value has int flag using bit compare
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="flag"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasFlag(this int value, int flag)
|
||||
=> (value & flag) == flag;
|
||||
|
||||
/// <summary>
|
||||
/// Check if int value has enum flag using bit compare
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="flag"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasFlag<T>(this int value, T flag) where T : Enum
|
||||
=> value.HasFlag(Convert.ToInt32(flag));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user