* Changed public API overview generator behaviour, now working only in DEBUG builds

* Fixed wrong LeveldDebug method calls after moving logic from providers to router
* Added independent "IndentFlags" property to inner debugger class
* Fixed debug logging in few places
* Removed "ICollectingOptions" and merged it with new options abstract "ITelegratorOptions"
* Added WebHook version of hosting class
This commit is contained in:
2025-07-28 20:35:48 +04:00
parent 4e53337496
commit 5320c9ec20
47 changed files with 873 additions and 148 deletions
@@ -64,36 +64,6 @@ namespace Telegrator.Attributes
SpecialState = specialState;
}
/*
/// <summary>
/// Initializes the attribute with a custom state keeper, a specific state, and a custom key resolver.
/// </summary>
/// <param name="keeper">The state keeper instance</param>
/// <param name="myState">The state value to associate</param>
/// <param name="keyResolver">The key resolver for state keeping</param>
protected StateKeeperAttribute(TKeeper keeper, TState myState, IStateKeyResolver<TKey> keyResolver) : base(typeof(TKeeper))
{
StateKeeper ??= keeper;
StateKeeper.KeyResolver = keyResolver;
MyState = myState;
SpecialState = SpecialState.None;
}
/// <summary>
/// Initializes the attribute with a custom state keeper, a special state, and a custom key resolver.
/// </summary>
/// <param name="keeper">The state keeper instance</param>
/// <param name="specialState">The special state mode</param>
/// <param name="keyResolver">The key resolver for state keeping</param>
protected StateKeeperAttribute(TKeeper keeper, SpecialState specialState, IStateKeyResolver<TKey> keyResolver) : base(typeof(TKeeper))
{
StateKeeper ??= keeper;
StateKeeper.KeyResolver = keyResolver;
MyState = StateKeeper.DefaultState;
SpecialState = specialState;
}
*/
/// <summary>
/// Determines whether the current update context passes the state filter.
/// </summary>
@@ -1,19 +0,0 @@
namespace Telegrator.Configuration
{
/// <summary>
/// Interface for configuring handler collection behavior.
/// Defines options that control how handlers are collected and processed during initialization.
/// </summary>
public interface IHandlersCollectingOptions
{
/// <summary>
/// Gets or sets a value indicating whether to descend the indexr of handler's index on register. ('false' by default)
/// </summary>
public bool DescendDescriptorIndex { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to exclude intersecting command aliases.
/// </summary>
public bool ExceptIntersectingCommandAliases { get; set; }
}
}
@@ -1,10 +1,10 @@
namespace Telegrator.Configuration
{
/// <summary>
/// Configuration options for Telegram bot behavior and execution settings.
/// Controls various aspects of bot operation including concurrency, routing, and execution policies.
/// Interface for configuring Telegram bot behavior and execution settings.
/// Controls various aspects of bot operation including concurrency, routing, collecting, and execution policies.
/// </summary>
public class TelegramBotOptions
public interface ITelegratorOptions
{
/// <summary>
/// Gets or sets a value indicating whether only the first found handler should be executed for each update.
@@ -25,5 +25,15 @@
/// Gets or sets the global cancellation token for all bot operations.
/// </summary>
public CancellationToken GlobalCancellationToken { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to descend the indexr of handler's index on register. ('false' by default)
/// </summary>
public bool DescendDescriptorIndex { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to exclude intersecting command aliases.
/// </summary>
public bool ExceptIntersectingCommandAliases { get; set; }
}
}
+5
View File
@@ -76,6 +76,11 @@
[Flags]
public enum DebugLevel
{
/// <summary>
/// None to write
/// </summary>
None = 0x0,
/// <summary>
/// Write debug messages from filters execution
/// </summary>
+13 -9
View File
@@ -1,5 +1,4 @@
using System.Diagnostics;
using Telegram.Bot.Types.Enums;
namespace Telegrator
{
@@ -8,13 +7,18 @@ namespace Telegrator
/// </summary>
public static class LeveledDebug
{
/// <summary>
/// Gets or sets flags of what debug messages to write
/// </summary>
public static DebugLevel IndentFlags { get; set; } = DebugLevel.None;
/// <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))
if (IndentFlags.HasFlag(DebugLevel.Router))
Debug.WriteLine(message);
}
@@ -25,7 +29,7 @@ namespace Telegrator
/// <param name="args"></param>
public static void RouterWriteLine(string message, params object[] args)
{
if (Debug.IndentLevel.HasFlag(DebugLevel.Router))
if (IndentFlags.HasFlag(DebugLevel.Router))
Debug.WriteLine(message, args);
}
@@ -35,7 +39,7 @@ namespace Telegrator
/// <param name="message"></param>
public static void ProviderWriteLine(string message)
{
if (Debug.IndentLevel.HasFlag(DebugLevel.Providers))
if (IndentFlags.HasFlag(DebugLevel.Providers))
Debug.WriteLine(message);
}
@@ -46,7 +50,7 @@ namespace Telegrator
/// <param name="args"></param>
public static void ProviderWriteLine(string message, params object[] args)
{
if (Debug.IndentLevel.HasFlag(DebugLevel.Providers))
if (IndentFlags.HasFlag(DebugLevel.Providers))
Debug.WriteLine(message, args);
}
@@ -56,7 +60,7 @@ namespace Telegrator
/// <param name="message"></param>
public static void FilterWriteLine(string message)
{
if (Debug.IndentLevel.HasFlag(DebugLevel.Filters))
if (IndentFlags.HasFlag(DebugLevel.Filters))
Debug.WriteLine(message);
}
@@ -67,7 +71,7 @@ namespace Telegrator
/// <param name="args"></param>
public static void FilterWriteLine(string message, params object[] args)
{
if (Debug.IndentLevel.HasFlag(DebugLevel.Filters))
if (IndentFlags.HasFlag(DebugLevel.Filters))
Debug.WriteLine(message, args);
}
@@ -77,7 +81,7 @@ namespace Telegrator
/// <param name="message"></param>
public static void PoolWriteLine(string message)
{
if (Debug.IndentLevel.HasFlag(DebugLevel.HandlersPool))
if (IndentFlags.HasFlag(DebugLevel.HandlersPool))
Debug.WriteLine(message);
}
@@ -88,7 +92,7 @@ namespace Telegrator
/// <param name="args"></param>
public static void PoolWriteLine(string message, params object[] args)
{
if (Debug.IndentLevel.HasFlag(DebugLevel.HandlersPool))
if (IndentFlags.HasFlag(DebugLevel.HandlersPool))
Debug.WriteLine(message, args);
}
}
@@ -47,7 +47,7 @@ namespace Telegrator.MadiatorCore.Descriptors
{
if (!UpdateValidator.CanPass(filterContext))
{
LeveledDebug.FilterWriteLine("(E) UpdateValidator filter of {0} for Update ({2}) didnt pass!", filterContext.Data["handler_name"]);
LeveledDebug.FilterWriteLine("(E) UpdateValidator filter of {0} for Update ({1}) didnt pass!", filterContext.Data["handler_name"], filterContext.Update.Id);
return false;
}
@@ -59,7 +59,7 @@ namespace Telegrator.MadiatorCore.Descriptors
{
if (!StateKeeperValidator.CanPass(filterContext))
{
LeveledDebug.FilterWriteLine("(E) StateKeeperValidator filter of {0} for Update ({2}) didnt pass!", filterContext.Data["handler_name"]);
LeveledDebug.FilterWriteLine("(E) StateKeeperValidator filter of {0} for Update ({1}) didnt pass!", filterContext.Data["handler_name"], filterContext.Update.Id);
return false;
}
@@ -74,7 +74,7 @@ namespace Telegrator.MadiatorCore.Descriptors
if (!filter.CanPass(filterContext))
{
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"]);
LeveledDebug.FilterWriteLine("(E) {0} filter of {1} for Update ({2}) didnt pass!", filter.GetType().Name, filterContext.Data["handler_name"], filterContext.Update.Id);
return false;
}
@@ -12,7 +12,7 @@ namespace Telegrator.MadiatorCore.Descriptors
{
private readonly object _lock = new object();
private readonly SortedList<DescriptorIndexer, HandlerDescriptor> _innerCollection;
private readonly IHandlersCollectingOptions? _options;
private readonly ITelegratorOptions? _options;
private readonly UpdateType _handlingType;
private int count;
@@ -54,7 +54,7 @@ namespace Telegrator.MadiatorCore.Descriptors
/// </summary>
/// <param name="updateType">The update type for the handlers.</param>
/// <param name="options">The collecting options.</param>
public HandlerDescriptorList(UpdateType updateType, IHandlersCollectingOptions? options)
public HandlerDescriptorList(UpdateType updateType, ITelegratorOptions? options)
{
_innerCollection = [];
_handlingType = updateType;
+2 -3
View File
@@ -1,5 +1,4 @@
using Telegram.Bot.Polling;
using Telegrator.Configuration;
using Telegrator.Handlers.Components;
namespace Telegrator.MadiatorCore
@@ -11,9 +10,9 @@ namespace Telegrator.MadiatorCore
public interface IUpdateRouter : IUpdateHandler, IPollingProvider
{
/// <summary>
/// Gets the <see cref="TelegramBotOptions"/> for the router.
/// Gets the <see cref="TelegratorOptions"/> for the router.
/// </summary>
public TelegramBotOptions Options { get; }
public TelegratorOptions Options { get; }
/// <summary>
/// Gets the <see cref="IUpdateHandlersPool"/> that manages handler execution.
+2 -3
View File
@@ -1,5 +1,4 @@
using System.Collections.Concurrent;
using Telegrator.Configuration;
using Telegrator.MadiatorCore;
using Telegrator.MadiatorCore.Descriptors;
@@ -39,7 +38,7 @@ namespace Telegrator.Polling
/// <summary>
/// The bot configuration options.
/// </summary>
protected readonly TelegramBotOptions Options;
protected readonly TelegratorOptions Options;
/// <summary>
/// The global cancellation token for stopping all operations.
@@ -62,7 +61,7 @@ namespace Telegrator.Polling
/// </summary>
/// <param name="options">The bot configuration options.</param>
/// <param name="globalCancellationToken">The global cancellation token.</param>
public UpdateHandlersPool(TelegramBotOptions options, CancellationToken globalCancellationToken)
public UpdateHandlersPool(TelegratorOptions options, CancellationToken globalCancellationToken)
{
Options = options;
GlobalCancellationToken = globalCancellationToken;
+30 -12
View File
@@ -17,7 +17,7 @@ namespace Telegrator.Polling
/// </summary>
public class UpdateRouter : IUpdateRouter
{
private readonly TelegramBotOptions _options;
private readonly TelegratorOptions _options;
private readonly IHandlersProvider _handlersProvider;
private readonly IAwaitingProvider _awaitingProvider;
private readonly IUpdateHandlersPool _HandlersPool;
@@ -30,7 +30,7 @@ namespace Telegrator.Polling
public IAwaitingProvider AwaitingProvider => _awaitingProvider;
/// <inheritdoc/>
public TelegramBotOptions Options => _options;
public TelegratorOptions Options => _options;
/// <inheritdoc/>
public IUpdateHandlersPool HandlersPool => _HandlersPool;
@@ -48,7 +48,7 @@ namespace Telegrator.Polling
/// <param name="awaitingProvider">The provider for awaiting handlers.</param>
/// <param name="options">The bot configuration options.</param>
/// <param name="botInfo"></param>
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, TelegramBotOptions options, ITelegramBotInfo botInfo)
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, TelegratorOptions options, ITelegramBotInfo botInfo)
{
_options = options;
_handlersProvider = handlersProvider;
@@ -65,7 +65,7 @@ namespace Telegrator.Polling
/// <param name="options">The bot configuration options.</param>
/// <param name="handlersPool">The custom handlers pool to use.</param>
/// <param name="botInfo"></param>
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, TelegramBotOptions options, IUpdateHandlersPool handlersPool, ITelegramBotInfo botInfo)
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, TelegratorOptions options, IUpdateHandlersPool handlersPool, ITelegramBotInfo botInfo)
{
_options = options;
_handlersProvider = handlersProvider;
@@ -149,22 +149,22 @@ namespace Telegrator.Polling
/// <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);
LeveledDebug.RouterWriteLine("Requested handlers for UpdateType.{0}", update.Type);
if (!provider.TryGetDescriptorList(update.Type, out HandlerDescriptorList? descriptors))
{
LeveledDebug.ProviderWriteLine("No registered, providing Any");
LeveledDebug.RouterWriteLine("No registered, providing Any");
provider.TryGetDescriptorList(UpdateType.Unknown, out descriptors);
}
if (descriptors == null || descriptors.Count == 0)
{
LeveledDebug.ProviderWriteLine("No handlers provided");
LeveledDebug.RouterWriteLine("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));
LeveledDebug.RouterWriteLine("Described total of {0} handlers for Update ({1}) from {2} provider", described.Count(), update.Id, provider.GetType().Name);
LeveledDebug.RouterWriteLine("Described handlers : {0}", string.Join(", ", described));
return described;
}
@@ -183,7 +183,7 @@ namespace Telegrator.Polling
{
try
{
LeveledDebug.ProviderWriteLine("Describing descriptors of descriptorsList.HandlingType.{0} for Update ({1})", descriptors.HandlingType, update.Id);
LeveledDebug.RouterWriteLine("Describing descriptors of descriptorsList.HandlingType.{0} for Update ({1})", descriptors.HandlingType, update.Id);
foreach (HandlerDescriptor descriptor in descriptors.Reverse())
{
cancellationToken.ThrowIfCancellationRequested();
@@ -198,7 +198,7 @@ namespace Telegrator.Polling
}
finally
{
LeveledDebug.ProviderWriteLine("Describing for Update ({0}) finished", update.Id);
LeveledDebug.RouterWriteLine("Describing for Update ({0}) finished", update.Id);
}
}
@@ -247,7 +247,25 @@ namespace Telegrator.Polling
sb.AppendFormat(" from {0} ({1})", msg.From.Username, msg.From.Id);
if (msg.Text != null)
sb.AppendFormat("'{0}'", msg.Text);
sb.AppendFormat(" with text '{0}'", msg.Text);
if (msg.Sticker != null)
sb.AppendFormat(" with sticker '{0}'", msg.Sticker.Emoji);
LeveledDebug.RouterWriteLine(sb.ToString());
break;
}
case UpdateType.CallbackQuery:
{
CallbackQuery cq = update.CallbackQuery ?? throw new NullReferenceException();
StringBuilder sb = new StringBuilder("Update.CallbackQuery");
if (cq.From != null)
sb.AppendFormat(" from {0} ({1})", cq.From.Username, cq.From.Id);
if (cq.From != null)
sb.AppendFormat(" with data '{0}'", cq.Data);
LeveledDebug.RouterWriteLine(sb.ToString());
break;
+1 -2
View File
@@ -1,5 +1,4 @@
using Telegram.Bot.Types.Enums;
using Telegrator.Configuration;
using Telegrator.MadiatorCore;
using Telegrator.MadiatorCore.Descriptors;
@@ -10,7 +9,7 @@ namespace Telegrator.Providers
/// Extends HandlersProvider to provide functionality for creating and managing awaiter handlers.
/// </summary>
/// <param name="options">The bot configuration options.</param>
public class AwaitingProvider(TelegramBotOptions options) : HandlersProvider([], options), IAwaitingProvider
public class AwaitingProvider(TelegratorOptions options) : HandlersProvider([], options), IAwaitingProvider
{
/// <summary>
/// List of handler descriptors for awaiting handlers.
+2 -2
View File
@@ -14,7 +14,7 @@ namespace Telegrator.Providers
/// Provides functionality for collecting, adding, and organizing handlers.
/// </summary>
/// <param name="options">Optional configuration options for handler collecting.</param>
public class HandlersCollection(IHandlersCollectingOptions? options) : IHandlersCollection
public class HandlersCollection(ITelegratorOptions? options) : IHandlersCollection
{
private readonly List<UpdateType> _allowedTypes = [];
@@ -26,7 +26,7 @@ namespace Telegrator.Providers
/// <summary>
/// Configuration options for handler collecting.
/// </summary>
protected readonly IHandlersCollectingOptions? Options = options;
protected readonly ITelegratorOptions? Options = options;
/// <summary>
/// Gets whether handlers must have a parameterless constructor.
+22 -13
View File
@@ -2,7 +2,6 @@
using System.Runtime.CompilerServices;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegrator.Configuration;
using Telegrator.Handlers.Components;
using Telegrator.MadiatorCore;
using Telegrator.MadiatorCore.Descriptors;
@@ -28,7 +27,7 @@ namespace Telegrator.Providers
/// <summary>
/// Configuration options for the bot and handler execution behavior.
/// </summary>
protected readonly TelegramBotOptions Options;
protected readonly TelegratorOptions Options;
/// <summary>
/// Initializes a new instance of <see cref="HandlersProvider"/> with the specified handler collections and configuration.
@@ -36,11 +35,12 @@ namespace Telegrator.Providers
/// <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>
/// <exception cref="ArgumentNullException">Thrown when options or botInfo is null</exception>
public HandlersProvider(IHandlersCollection handlers, TelegramBotOptions options)
public HandlersProvider(IHandlersCollection handlers, TelegratorOptions options)
{
AllowedTypes = handlers.AllowedTypes;
HandlersDictionary = handlers.Values.ForEach(list => list.Freeze()).ToReadOnlyDictionary(list => list.HandlingType);
Options = options ?? throw new ArgumentNullException(nameof(options));
LeveledDebug.ProviderWriteLine("{0} created!", GetType().Name);
}
/// <summary>
@@ -49,27 +49,36 @@ namespace Telegrator.Providers
/// <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>
/// <exception cref="ArgumentNullException">Thrown when options or botInfo is null</exception>
public HandlersProvider(IEnumerable<HandlerDescriptorList> handlers, TelegramBotOptions options)
public HandlersProvider(IEnumerable<HandlerDescriptorList> handlers, TelegratorOptions options)
{
AllowedTypes = Update.AllTypes;
HandlersDictionary = handlers.ForEach(list => list.Freeze()).ToReadOnlyDictionary(list => list.HandlingType);
Options = options ?? throw new ArgumentNullException(nameof(options));
LeveledDebug.ProviderWriteLine("{0} created!", GetType().Name);
}
/// <inheritdoc/>
/// <exception cref="Exception">Thrown when the descriptor type is not recognized</exception>
public virtual UpdateHandlerBase GetHandlerInstance(HandlerDescriptor descriptor, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
bool useSingleton = UseSingleton(descriptor);
try
{
cancellationToken.ThrowIfCancellationRequested();
bool useSingleton = UseSingleton(descriptor);
if (useSingleton && descriptor.SingletonInstance != null)
return descriptor.SingletonInstance;
if (useSingleton && descriptor.SingletonInstance != null)
return descriptor.SingletonInstance;
UpdateHandlerBase instance = GetHandlerInstanceInternal(descriptor);
descriptor.SingletonInstance = useSingleton ? instance : null;
descriptor.LazyInitialization?.Invoke(instance);
return instance;
UpdateHandlerBase instance = GetHandlerInstanceInternal(descriptor);
descriptor.SingletonInstance = useSingleton ? instance : null;
descriptor.LazyInitialization?.Invoke(instance);
return instance;
}
catch
{
LeveledDebug.ProviderWriteLine("Failed to create instance of {0}", descriptor.ToString());
throw;
}
}
private static UpdateHandlerBase GetHandlerInstanceInternal(HandlerDescriptor descriptor)
@@ -84,7 +93,7 @@ namespace Telegrator.Providers
{
DescriptorType.General or DescriptorType.Keyed => false,
DescriptorType.Implicit or DescriptorType.Singleton => true,
_ => throw new Exception()
_ => throw new Exception("Unknown decriptor type")
};
/// <inheritdoc/>
+1 -1
View File
@@ -17,7 +17,7 @@
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Version>1.0.6</Version>
<Version>1.0.7</Version>
</PropertyGroup>
<ItemGroup>
+2 -2
View File
@@ -19,7 +19,7 @@ namespace Telegrator
private IUpdateRouter? updateRouter = null;
/// <inheritdoc/>
public TelegramBotOptions Options { get; private set; }
public TelegratorOptions Options { get; private set; }
/// <inheritdoc/>
public IHandlersCollection Handlers { get; private set; }
@@ -47,7 +47,7 @@ namespace Telegrator
/// <param name="cancellationToken">The cancellation token.</param>
public TelegratorClient(TelegramBotClientOptions options, HttpClient? httpClient = null, CancellationToken cancellationToken = default) : base(options, httpClient, cancellationToken)
{
Options = new TelegramBotOptions();
Options = new TelegratorOptions();
Handlers = new HandlersCollection(default);
BotInfo = new TelegramBotInfo(this.GetMe(cancellationToken).Result);
}
+30
View File
@@ -0,0 +1,30 @@
using Telegrator.Configuration;
namespace Telegrator
{
/// <summary>
/// Configuration options for Telegram bot behavior and execution settings.
/// Controls various aspects of bot operation including concurrency, routing, and execution policies.
/// </summary>
public class TelegratorOptions : ITelegratorOptions
{
/// <inheritdoc/>
public bool ExecuteOnlyFirstFoundHanlder { get; set; }
/// <inheritdoc/>
public int? MaximumParallelWorkingHandlers { get; set; }
/// <inheritdoc/>
public bool ExclusiveAwaitingHandlerRouting { get; set; }
/// <inheritdoc/>
public CancellationToken GlobalCancellationToken { get; set; }
/// <inheritdoc/>
public bool DescendDescriptorIndex { get; set; } = true;
/// <inheritdoc/>
public bool ExceptIntersectingCommandAliases { get; set; } = true;
}
}