From bf66431089224802e2c1cb427b2ea93d97f2390d Mon Sep 17 00:00:00 2001 From: Rikitav Date: Fri, 8 Aug 2025 17:04:56 +0400 Subject: [PATCH] * Added "INamedFilter" to tagging filters. Required for fallbacking system * AnonymousCompiledFilter now has undependent realization * All internal filters now implement INamedFilter interface * Added new static methods to "Filter" class * Added Filters fallbacking system * Added "FiltersFallback" method in "UpdatehandlerBase" * Changed filters validating system to be able to report failed filters * Added "FiltersFallbackReport" class * Added "FormReport" Property to "UpdateHandlerAttributeBase" * Version incremented --- .../Telegrator.Hosting.Web.csproj | 2 +- Telegrator.Hosting/Telegrator.Hosting.csproj | 2 +- Telegrator.Hosting/TypesExtensions.cs | 3 +- Telegrator.Tests/Filters/FilterTests.cs | 2 +- .../Components/UpdateFilterAttributeBase.cs | 2 +- .../Components/UpdateHandlerAttributeBase.cs | 2 + .../Attributes/UpdateFilterAttribute.cs | 18 +-- Telegrator/Enums.cs | 2 + .../Components/AnonymousCompiledFilter.cs | 65 +++++++++-- .../Filters/Components/AnonymousTypeFilter.cs | 30 ++++- .../Filters/Components/CompiledFilter.cs | 21 ++-- Telegrator/Filters/Components/IFilter.cs | 5 + Telegrator/Filters/Filter.cs | 18 +++ .../Components/FiltersFallbackReport.cs | 66 +++++++++++ .../Components}/HandlerLifetimeToken.cs | 2 +- .../Handlers/Components/UpdateHandlerBase.cs | 27 +++-- .../Descriptors/DescriptorFiltersSet.cs | 107 +++++++++++++----- .../Descriptors/HandlerDescriptor.cs | 7 ++ Telegrator/Polling/UpdateRouter.cs | 52 +++++---- Telegrator/Result.cs | 4 +- Telegrator/Telegrator.csproj | 2 +- 21 files changed, 338 insertions(+), 101 deletions(-) create mode 100644 Telegrator/Handlers/Components/FiltersFallbackReport.cs rename Telegrator/{MadiatorCore/Descriptors => Handlers/Components}/HandlerLifetimeToken.cs (93%) diff --git a/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj b/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj index 211d0b5..ea76a57 100644 --- a/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj +++ b/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj @@ -15,7 +15,7 @@ True LICENSE README.md - 1.0.11 + 1.0.12 diff --git a/Telegrator.Hosting/Telegrator.Hosting.csproj b/Telegrator.Hosting/Telegrator.Hosting.csproj index 49af79d..555872d 100644 --- a/Telegrator.Hosting/Telegrator.Hosting.csproj +++ b/Telegrator.Hosting/Telegrator.Hosting.csproj @@ -16,7 +16,7 @@ True LICENSE README.md - 1.0.11 + 1.0.12 diff --git a/Telegrator.Hosting/TypesExtensions.cs b/Telegrator.Hosting/TypesExtensions.cs index d188ac8..d245c30 100644 --- a/Telegrator.Hosting/TypesExtensions.cs +++ b/Telegrator.Hosting/TypesExtensions.cs @@ -100,12 +100,13 @@ namespace Telegrator.Hosting /// Adds a Microsoft.Extensions.Logging adapter to Alligator using a logger factory. /// /// - public static void AddLoggingAdapter(this ITelegramBotHost host) + public static ITelegramBotHost AddLoggingAdapter(this ITelegramBotHost host) { ILoggerFactory loggerFactory = host.Services.GetRequiredService(); ILogger logger = loggerFactory.CreateLogger("Telegrator"); MicrosoftLoggingAdapter adapter = new MicrosoftLoggingAdapter(logger); Alligator.AddAdapter(adapter); + return host; } } diff --git a/Telegrator.Tests/Filters/FilterTests.cs b/Telegrator.Tests/Filters/FilterTests.cs index 98b8f30..e764f19 100644 --- a/Telegrator.Tests/Filters/FilterTests.cs +++ b/Telegrator.Tests/Filters/FilterTests.cs @@ -121,7 +121,7 @@ namespace Telegrator.Tests.Filters var filter2 = Filter.If(_ => true); var filter3 = Filter.If(_ => false); - var compiledFilter = CompiledFilter.Compile(filter1, filter2, filter3); + var compiledFilter = new CompiledFilter(filter1, filter2, filter3); var context = new FilterExecutionContext(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary(), new CompletedFiltersList()); // Act diff --git a/Telegrator/Attributes/Components/UpdateFilterAttributeBase.cs b/Telegrator/Attributes/Components/UpdateFilterAttributeBase.cs index f2dec42..30c74d1 100644 --- a/Telegrator/Attributes/Components/UpdateFilterAttributeBase.cs +++ b/Telegrator/Attributes/Components/UpdateFilterAttributeBase.cs @@ -20,7 +20,7 @@ namespace Telegrator.Attributes.Components /// /// Gets the that processing /// - public abstract Filter AnonymousFilter { get; protected set; } + public abstract IFilter AnonymousFilter { get; protected set; } /// /// Gets or sets the filter modifiers that affect how this filter is combined with others. diff --git a/Telegrator/Attributes/Components/UpdateHandlerAttributeBase.cs b/Telegrator/Attributes/Components/UpdateHandlerAttributeBase.cs index 1e85896..c765d8b 100644 --- a/Telegrator/Attributes/Components/UpdateHandlerAttributeBase.cs +++ b/Telegrator/Attributes/Components/UpdateHandlerAttributeBase.cs @@ -35,6 +35,8 @@ namespace Telegrator.Attributes.Components /// public int Priority { get; set; } + public bool FormReport { get; set; } + /// /// Creates a new instance of /// diff --git a/Telegrator/Attributes/UpdateFilterAttribute.cs b/Telegrator/Attributes/UpdateFilterAttribute.cs index 8cd145b..dfda0b0 100644 --- a/Telegrator/Attributes/UpdateFilterAttribute.cs +++ b/Telegrator/Attributes/UpdateFilterAttribute.cs @@ -15,12 +15,12 @@ namespace Telegrator.Attributes /// /// Gets the compiled anonymous filter for this attribute. /// - public override Filter AnonymousFilter { get; protected set; } + public override IFilter AnonymousFilter { get; protected set; } /// /// Gets the compiled filter logic for the update target. /// - public Filter UpdateFilter { get; protected set; } + public IFilter UpdateFilter { get; protected set; } /// /// Empty constructor for internal using @@ -38,18 +38,20 @@ namespace Telegrator.Attributes /// The filters to compose protected UpdateFilterAttribute(params IFilter[] filters) { - UpdateFilter = CompiledFilter.Compile(filters); - AnonymousFilter = AnonymousTypeFilter.Compile(UpdateFilter, GetFilterringTarget); + string name = GetType().Name; + UpdateFilter = new CompiledFilter(name, filters); + AnonymousFilter = AnonymousTypeFilter.Compile(name, UpdateFilter, GetFilterringTarget); } /// /// Initializes the attribute with a precompiled filter for the update target. /// /// The compiled filter - protected UpdateFilterAttribute(Filter updateFilter) + protected UpdateFilterAttribute(IFilter updateFilter) { + string name = GetType().Name; UpdateFilter = updateFilter; - AnonymousFilter = AnonymousTypeFilter.Compile(UpdateFilter, GetFilterringTarget); + AnonymousFilter = AnonymousTypeFilter.Compile(name, UpdateFilter, GetFilterringTarget); } /// @@ -60,13 +62,13 @@ namespace Telegrator.Attributes public override sealed bool ProcessModifiers(UpdateFilterAttributeBase? previous) { if (Modifiers.HasFlag(FilterModifier.Not)) - AnonymousFilter = AnonymousFilter.Not(); + AnonymousFilter = Filter.Not(AnonymousFilter); if (previous is not null) { if (previous.Modifiers.HasFlag(FilterModifier.OrNext)) { - AnonymousFilter = previous.AnonymousFilter.Or(AnonymousFilter); + AnonymousFilter = Filter.Or(previous.AnonymousFilter, AnonymousFilter); } } diff --git a/Telegrator/Enums.cs b/Telegrator/Enums.cs index 6c43850..ca875f2 100644 --- a/Telegrator/Enums.cs +++ b/Telegrator/Enums.cs @@ -70,6 +70,7 @@ Sender } + /* /// /// Messages from where this filter was originated /// @@ -95,6 +96,7 @@ /// Regualr } + */ /* /// diff --git a/Telegrator/Filters/Components/AnonymousCompiledFilter.cs b/Telegrator/Filters/Components/AnonymousCompiledFilter.cs index 2a7a090..2de7496 100644 --- a/Telegrator/Filters/Components/AnonymousCompiledFilter.cs +++ b/Telegrator/Filters/Components/AnonymousCompiledFilter.cs @@ -1,5 +1,4 @@ -using Telegram.Bot; -using Telegram.Bot.Types; +using Telegram.Bot.Types; using Telegrator.Logging; namespace Telegrator.Filters.Components @@ -7,15 +6,26 @@ namespace Telegrator.Filters.Components /// /// Represents a compiled filter that applies a set of filters to an anonymous target type. /// - public sealed class AnonymousCompiledFilter : AnonymousTypeFilter + public class AnonymousCompiledFilter : Filter, INamedFilter { + private readonly Func, object, bool> FilterAction; + private readonly Func GetFilterringTarget; + private readonly string _name; + + public virtual string Name => _name; + /// /// Initializes a new instance of the class. /// + /// /// The filter action delegate. /// The function to get the filtering target from an update. - private AnonymousCompiledFilter(Func, object, bool> filterAction, Func getFilterringTarget) - : base(filterAction, getFilterringTarget) { } + private AnonymousCompiledFilter(string name, Func getFilterringTarget, Func, object, bool> filterAction) + { + FilterAction = filterAction; + GetFilterringTarget = getFilterringTarget; + _name = name; + } /// /// Compiles a set of filters into an for a specific target type. @@ -24,11 +34,28 @@ namespace Telegrator.Filters.Components /// The list of filters to compile. /// The function to get the filtering target from an update. /// The compiled filter. - public static AnonymousCompiledFilter Compile(IList> filters, Func getFilterringTarget) where T : class + public static AnonymousCompiledFilter Compile(IEnumerable> filters, Func getFilterringTarget) where T : class { return new AnonymousCompiledFilter( - (context, filterringTarget) => CanPassInternal(filters, context, filterringTarget), - getFilterringTarget); + string.Join("+", filters.Select(fltr => fltr.GetType().Name)), + getFilterringTarget, + (context, filterringTarget) => CanPassInternal(context, filters, filterringTarget)); + } + + /// + /// Compiles a set of filters into an for a specific target type. + /// + /// The type of the filtering target. + /// + /// The list of filters to compile. + /// The function to get the filtering target from an update. + /// The compiled filter. + public static AnonymousCompiledFilter Compile(string name, IEnumerable> filters, Func getFilterringTarget) where T : class + { + return new AnonymousCompiledFilter( + name, + getFilterringTarget, + (context, filterringTarget) => CanPassInternal(context, filters, filterringTarget)); } /// @@ -39,16 +66,15 @@ namespace Telegrator.Filters.Components /// The filter execution context. /// The filtering target. /// True if all filters pass; otherwise, false. - private static bool CanPassInternal(IList> filters, FilterExecutionContext updateContext, object filterringTarget) where T : class + private static bool CanPassInternal(FilterExecutionContext updateContext, IEnumerable> filters, object filterringTarget) where T : class { FilterExecutionContext context = updateContext.CreateChild((T)filterringTarget); - foreach (IFilter filter in filters) { if (!filter.CanPass(context)) { if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter) - Alligator.LogDebug("{0} filter of {1} didnt pass!", filter.GetType().Name, context.Data["handler_name"]); + Alligator.LogDebug("{0} filter of {1} didnt pass! (Compiled anonymous)", filter.GetType().Name, context.Data["handler_name"]); return false; } @@ -58,5 +84,22 @@ namespace Telegrator.Filters.Components return true; } + + /// + public override bool CanPass(FilterExecutionContext context) + { + try + { + object? filterringTarget = GetFilterringTarget.Invoke(context.Input); + if (filterringTarget == null) + return false; + + return FilterAction.Invoke(context, filterringTarget); + } + catch + { + return false; + } + } } } diff --git a/Telegrator/Filters/Components/AnonymousTypeFilter.cs b/Telegrator/Filters/Components/AnonymousTypeFilter.cs index f2bb65e..ad13a45 100644 --- a/Telegrator/Filters/Components/AnonymousTypeFilter.cs +++ b/Telegrator/Filters/Components/AnonymousTypeFilter.cs @@ -6,20 +6,25 @@ namespace Telegrator.Filters.Components /// /// Represents a filter that applies a filter action to an anonymous target type extracted from an update. /// - public class AnonymousTypeFilter : Filter + public class AnonymousTypeFilter : Filter, INamedFilter { private readonly Func, object, bool> FilterAction; private readonly Func GetFilterringTarget; + private readonly string _name; + + public virtual string Name => _name; /// /// Initializes a new instance of the class. /// + /// /// The filter action delegate. /// The function to get the filtering target from an update. - protected AnonymousTypeFilter(Func, object, bool> filterAction, Func getFilterringTarget) + public AnonymousTypeFilter(string name, Func getFilterringTarget, Func, object, bool> filterAction) { FilterAction = filterAction; GetFilterringTarget = getFilterringTarget; + _name = name; } /// @@ -32,8 +37,25 @@ namespace Telegrator.Filters.Components public static AnonymousTypeFilter Compile(IFilter filter, Func getFilterringTarget) where T : class { return new AnonymousTypeFilter( - (context, filterringTarget) => CanPassInternal(context, filter, filterringTarget), - getFilterringTarget); + filter.GetType().Name, + getFilterringTarget, + (context, filterringTarget) => CanPassInternal(context, filter, filterringTarget)); + } + + /// + /// Compiles a filter for a specific target type. + /// + /// The type of the filtering target. + /// + /// The filter to apply. + /// The function to get the filtering target from an update. + /// The compiled filter. + public static AnonymousTypeFilter Compile(string name, IFilter filter, Func getFilterringTarget) where T : class + { + return new AnonymousTypeFilter( + name, + getFilterringTarget, + (context, filterringTarget) => CanPassInternal(context, filter, filterringTarget)); } /// diff --git a/Telegrator/Filters/Components/CompiledFilter.cs b/Telegrator/Filters/Components/CompiledFilter.cs index cbe4c97..51a5dfc 100644 --- a/Telegrator/Filters/Components/CompiledFilter.cs +++ b/Telegrator/Filters/Components/CompiledFilter.cs @@ -6,27 +6,32 @@ namespace Telegrator.Filters.Components /// Represents a filter that composes multiple filters and passes only if all of them pass. /// /// The type of the input for the filter. - public class CompiledFilter : Filter where T : class + public class CompiledFilter : Filter, INamedFilter where T : class { private readonly IFilter[] Filters; + private readonly string _name; + + public virtual string Name => _name; /// /// Initializes a new instance of the class. /// /// The filters to compose. - private CompiledFilter(IFilter[] filters) + public CompiledFilter(params IFilter[] filters) { + _name = string.Join("+", filters.Select(fltr => fltr.GetType().Name)); Filters = filters; } /// - /// Compiles multiple filters into a . + /// Initializes a new instance of the class. /// + /// /// The filters to compose. - /// A new instance. - public static CompiledFilter Compile(params IFilter[] filters) + public CompiledFilter(string name, params IFilter[] filters) { - return new CompiledFilter(filters); + _name = name; + Filters = filters; } /// @@ -41,14 +46,14 @@ namespace Telegrator.Filters.Components if (!filter.CanPass(context)) { if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter) - Alligator.LogDebug("{0} filter of {1} didnt pass!", filter.GetType().Name, context.Data["handler_name"]); + Alligator.LogDebug("{0} filter of {1} didnt pass! (Compiled)", filter.GetType().Name, context.Data["handler_name"]); return false; } context.CompletedFilters.Add(filter); } - + return true; } } diff --git a/Telegrator/Filters/Components/IFilter.cs b/Telegrator/Filters/Components/IFilter.cs index fc4c86a..4b39096 100644 --- a/Telegrator/Filters/Components/IFilter.cs +++ b/Telegrator/Filters/Components/IFilter.cs @@ -1,5 +1,10 @@ namespace Telegrator.Filters.Components { + public interface INamedFilter + { + public string Name { get; } + } + /// /// Interface for filters that can be collected into a completed filters list. /// Provides information about whether a filter should be tracked during execution. diff --git a/Telegrator/Filters/Filter.cs b/Telegrator/Filters/Filter.cs index 8417a9e..f8802e7 100644 --- a/Telegrator/Filters/Filter.cs +++ b/Telegrator/Filters/Filter.cs @@ -30,6 +30,14 @@ namespace Telegrator.Filters public Filter Not() => new ReverseFilter(this); + /// + /// Creates a filter that inverts the result of this filter. + /// + /// + /// A instance. + public static Filter Not(IFilter filter) where Q : class + => new ReverseFilter(filter); + /// /// Creates a filter that passes only if both this and the specified filter pass. /// @@ -46,6 +54,16 @@ namespace Telegrator.Filters public OrFilter Or(IFilter filter) => new OrFilter(this, filter); + /// + /// Creates a filter that passes if either this or the specified filter pass. + /// + /// + /// + /// + /// An instance. + public static OrFilter Or(IFilter left, IFilter right) where Q : class + => new OrFilter(left, right); + /// /// Gets a value indicating whether this filter is collectible. /// diff --git a/Telegrator/Handlers/Components/FiltersFallbackReport.cs b/Telegrator/Handlers/Components/FiltersFallbackReport.cs new file mode 100644 index 0000000..f5d8bcf --- /dev/null +++ b/Telegrator/Handlers/Components/FiltersFallbackReport.cs @@ -0,0 +1,66 @@ +using Telegram.Bot; +using Telegram.Bot.Types; +using Telegrator.Attributes.Components; +using Telegrator.Filters.Components; +using Telegrator.MadiatorCore.Descriptors; + +namespace Telegrator.Handlers.Components +{ + public class FiltersFallbackReport(HandlerDescriptor descriptor, FilterExecutionContext context) + { + public HandlerDescriptor Descriptor { get; } = descriptor; + + public FilterExecutionContext Context { get; } = context; + + public FilterFallbackInfo? UpdateValidator { get; set; } + + public FilterFallbackInfo? StateKeeperValidator { get; set; } + + public List UpdateFilters { get; } = []; + + /* + public FilterFallbackInfo OfType(int index = 0) where T : IFilter + { + return UpdateFilters.Where(info => info.Failed is T).ElementAt(index); + } + + public FilterFallbackInfo OfAttribute(int index = 0) where T : UpdateFilterAttributeBase + { + return UpdateFilters.Where(info => info.Name == typeof(T).Name).ElementAt(index); + } + + public FilterFallbackInfo OfName(string name, int index = 0) + { + return UpdateFilters.Where(info => info.Name == name).ElementAt(index); + } + */ + + public bool ExceptAttribute(int index = 0) where T : UpdateFilterAttributeBase + { + string name = typeof(T).Name; + IEnumerable failed = UpdateFilters.Where(info => info.Failed); + if (failed.Count() > 1) + return false; + + return failed.SingleOrDefault()?.Name == name; + } + } + + public class FilterFallbackInfo(string name, IFilter filter, bool failed, Exception? exception) + { + public string Name { get; } = name; + + public IFilter Filter { get; } = filter; + + public bool Failed { get; } = failed; + + public Exception? Exception { get; } = exception; + } + + public enum FallbackReason + { + NullTarget, + + FailedFilter + } +} diff --git a/Telegrator/MadiatorCore/Descriptors/HandlerLifetimeToken.cs b/Telegrator/Handlers/Components/HandlerLifetimeToken.cs similarity index 93% rename from Telegrator/MadiatorCore/Descriptors/HandlerLifetimeToken.cs rename to Telegrator/Handlers/Components/HandlerLifetimeToken.cs index bbafb27..8c9777e 100644 --- a/Telegrator/MadiatorCore/Descriptors/HandlerLifetimeToken.cs +++ b/Telegrator/Handlers/Components/HandlerLifetimeToken.cs @@ -1,4 +1,4 @@ -namespace Telegrator.MadiatorCore.Descriptors +namespace Telegrator.Handlers.Components { /// /// Represents a token that tracks the lifetime of a handler instance. diff --git a/Telegrator/Handlers/Components/UpdateHandlerBase.cs b/Telegrator/Handlers/Components/UpdateHandlerBase.cs index f243695..fd48988 100644 --- a/Telegrator/Handlers/Components/UpdateHandlerBase.cs +++ b/Telegrator/Handlers/Components/UpdateHandlerBase.cs @@ -1,4 +1,5 @@ -using Telegram.Bot.Polling; +using Telegram.Bot; +using Telegram.Bot.Polling; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegrator.Filters.Components; @@ -41,7 +42,10 @@ namespace Telegrator.Handlers.Components // Executing pre processor if (aspects != null) { - Result? preResult = await aspects.ExecutePre(this, container, cancellationToken).ConfigureAwait(false); + Result? preResult = await aspects + .ExecutePre(this, container, cancellationToken) + .ConfigureAwait(false); + if (!preResult.Positive) return preResult; } @@ -54,7 +58,10 @@ namespace Telegrator.Handlers.Components // Executing post processor if (aspects != null) { - Result postResult = await aspects.ExecutePost(this, container, cancellationToken).ConfigureAwait(false); + Result postResult = await aspects + .ExecutePost(this, container, cancellationToken) + .ConfigureAwait(false); + if (!postResult.Positive) return postResult; } @@ -82,7 +89,7 @@ namespace Telegrator.Handlers.Components } } - private IHandlerContainer GetContainer(DescribedHandlerInfo handlerInfo) + internal IHandlerContainer GetContainer(DescribedHandlerInfo handlerInfo) { if (this is IHandlerContainerFactory handlerDefainedContainerFactory) return handlerDefainedContainerFactory.CreateContainer(handlerInfo); @@ -114,14 +121,14 @@ namespace Telegrator.Handlers.Components /// /// Handles failed filters during handler describing. /// Use to control how router should treat this fail. - /// to silently continue decribing. - /// to stop\break decribing sequence. + /// to silently continue decribing. + /// to stop\break desribing sequence. /// - /// - /// - /// + /// + /// + /// /// - public virtual Task FiltersFallback(FilterExecutionContext context, IFilter failedFilter, FilterOrigin origin) + public virtual Task FiltersFallback(FiltersFallbackReport report, ITelegramBotClient client, CancellationToken cancellationToken = default) { return Task.FromResult(Result.Ok()); } diff --git a/Telegrator/MadiatorCore/Descriptors/DescriptorFiltersSet.cs b/Telegrator/MadiatorCore/Descriptors/DescriptorFiltersSet.cs index a38b48d..bf9a2e1 100644 --- a/Telegrator/MadiatorCore/Descriptors/DescriptorFiltersSet.cs +++ b/Telegrator/MadiatorCore/Descriptors/DescriptorFiltersSet.cs @@ -1,5 +1,7 @@ using Telegram.Bot.Types; +using Telegrator.Filters; using Telegrator.Filters.Components; +using Telegrator.Handlers.Components; using Telegrator.Logging; namespace Telegrator.MadiatorCore.Descriptors @@ -41,61 +43,108 @@ namespace Telegrator.MadiatorCore.Descriptors /// Validates the filter context using all filters in the set. /// /// The filter execution context. - /// - /// + /// + /// /// True if all filters pass; otherwise, false. - public bool Validate(FilterExecutionContext filterContext, out IFilter failedFilter, out FilterOrigin origin) + public Result Validate(FilterExecutionContext filterContext, bool formReport, ref FiltersFallbackReport report) { + bool anyErrors = false; + bool anyMatches = false; + if (UpdateValidator != null) { - if (!UpdateValidator.CanPass(filterContext)) + bool result = ExecuteFilter(UpdateValidator, filterContext, out Exception? exc); + + if (formReport) { - Alligator.LogDebug("(E) UpdateValidator filter of {0} for Update ({1}) didnt pass!", filterContext.Data["handler_name"], filterContext.Update.Id); - failedFilter = UpdateValidator; - origin = FilterOrigin.Validator; - return false; + report.UpdateValidator = new FilterFallbackInfo("Validator", UpdateValidator, !result, exc); } - //LeveledDebug.FilterWriteLine("UpdateValidator of {0} for Update ({2}) passed", filterContext.Data["handler_name"]); - filterContext.CompletedFilters.Add(UpdateValidator); + if (!result) + { + anyErrors = true; + Alligator.LogDebug("(E) UpdateValidator filter of '{0}' for Update ({1}) didnt pass!", filterContext.Data["handler_name"], filterContext.Update.Id); + + if (!formReport) + return Result.Fault(); + } + else + { + //anyMatches = true; // DO NOT COUNT + filterContext.CompletedFilters.Add(UpdateValidator); + } } if (StateKeeperValidator != null) { - if (!StateKeeperValidator.CanPass(filterContext)) + bool result = ExecuteFilter(StateKeeperValidator, filterContext, out Exception? exc); + + if (formReport) { - Alligator.LogDebug("(E) StateKeeperValidator filter of {0} for Update ({1}) didnt pass!", filterContext.Data["handler_name"], filterContext.Update.Id); - failedFilter = StateKeeperValidator; - origin = FilterOrigin.StateKeeper; - return false; + report.StateKeeperValidator = new FilterFallbackInfo("State", StateKeeperValidator, !result, exc); } - //LeveledDebug.FilterWriteLine("StateKeeperValidator of {0} for Update ({2}) passed", filterContext.Data["handler_name"]); - filterContext.CompletedFilters.Add(StateKeeperValidator); + if (!result) + { + anyErrors = true; + Alligator.LogDebug("(E) StateKeeperValidator filter of '{0}' for Update ({1}) didnt pass!", filterContext.Data["handler_name"], filterContext.Update.Id); + + if (!formReport) + return Result.Fault(); + } + else + { + anyMatches = true; + filterContext.CompletedFilters.Add(StateKeeperValidator); + } } if (UpdateFilters != null) { foreach (IFilter filter in UpdateFilters) { - if (!filter.CanPass(filterContext)) - { - if (filter is not AnonymousCompiledFilter && filter is not AnonymousTypeFilter) - Alligator.LogDebug("(E) {0} filter of {1} for Update ({2}) didnt pass!", filter.GetType().Name, filterContext.Data["handler_name"], filterContext.Update.Id); + bool result = ExecuteFilter(filter, filterContext, out Exception? exc); + string filterName = filter is INamedFilter named ? named.Name : filter.GetType().Name; - failedFilter = filter; - origin = FilterOrigin.Regualr; - return false; + if (formReport) + { + report.UpdateFilters.Add(new FilterFallbackInfo(filterName, filter, !result, exc)); } - //LeveledDebug.FilterWriteLine("{0} filter of {1} for Update ({2}) passed", filter.GetType().Name, filterContext.Data["handler_name"]); - filterContext.CompletedFilters.Add(filter); + if (!result) + { + anyErrors = true; + Alligator.LogDebug("(E) '{0}' filter of '{1}' for Update ({2}) didnt pass!", filterName, filterContext.Data["handler_name"], filterContext.Update.Id); + + if (!formReport) + return Result.Fault(); + } + else + { + anyMatches = true; + filterContext.CompletedFilters.Add(filter); + } } } - failedFilter = null!; - origin = FilterOrigin.None; - return true; + if (!anyErrors) + return Result.Ok(); + + return formReport ? Result.Next() : Result.Fault(); + } + + private static bool ExecuteFilter(IFilter filter, FilterExecutionContext context, out Exception? exception) where T : class + { + try + { + exception = null; + return filter.CanPass(context); + } + catch (Exception ex) + { + exception = ex; + return false; + } } } } diff --git a/Telegrator/MadiatorCore/Descriptors/HandlerDescriptor.cs b/Telegrator/MadiatorCore/Descriptors/HandlerDescriptor.cs index 873513a..b66dfb1 100644 --- a/Telegrator/MadiatorCore/Descriptors/HandlerDescriptor.cs +++ b/Telegrator/MadiatorCore/Descriptors/HandlerDescriptor.cs @@ -73,6 +73,12 @@ namespace Telegrator.MadiatorCore.Descriptors set; } + public bool FormReport + { + get; + set; + } + /// /// The set of filters associated with this handler. /// @@ -163,6 +169,7 @@ namespace Telegrator.MadiatorCore.Descriptors UpdateType = handlerAttribute.Type; Indexer = handlerAttribute.GetIndexer(); + FormReport = handlerAttribute.FormReport; Filters = new DescriptorFiltersSet(handlerAttribute, stateKeeperAttribute, filters); Aspects = HandlerInspector.GetAspects(handlerType); DisplayString = HandlerInspector.GetDisplayName(handlerType); diff --git a/Telegrator/Polling/UpdateRouter.cs b/Telegrator/Polling/UpdateRouter.cs index 3e55b21..9209c8c 100644 --- a/Telegrator/Polling/UpdateRouter.cs +++ b/Telegrator/Polling/UpdateRouter.cs @@ -178,23 +178,21 @@ namespace Telegrator.Polling /// A collection of described handler information protected virtual IEnumerable DescribeDescriptors(IHandlersProvider provider, HandlerDescriptorList descriptors, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default) { - try + Alligator.LogDebug("Describing descriptors of descriptorsList.HandlingType.{0} for Update ({1})", descriptors.HandlingType, update.Id); + foreach (HandlerDescriptor descriptor in descriptors.Reverse()) { - Alligator.LogDebug("Describing descriptors of descriptorsList.HandlingType.{0} for Update ({1})", descriptors.HandlingType, update.Id); - foreach (HandlerDescriptor descriptor in descriptors.Reverse()) - { - cancellationToken.ThrowIfCancellationRequested(); - DescribedHandlerInfo? describedHandler = DescribeHandler(provider, descriptor, client, update, cancellationToken); - if (describedHandler == null) - continue; + cancellationToken.ThrowIfCancellationRequested(); + DescribedHandlerInfo? describedHandler = DescribeHandler(provider, descriptor, client, update, out bool breakRouting, cancellationToken); + if (breakRouting) + yield break; - yield return describedHandler; - } - } - finally - { - Alligator.LogDebug("Describing for Update ({0}) finished", update.Id); + if (describedHandler == null) + continue; + + yield return describedHandler; } + + Alligator.LogDebug("Describing for Update ({0}) finished", update.Id); } /// @@ -205,10 +203,12 @@ namespace Telegrator.Polling /// The handler descriptor to process /// The Telegram bot client instance /// The incoming Telegram update to process + /// /// /// The described handler info if validation passes; otherwise, null - public virtual DescribedHandlerInfo? DescribeHandler(IHandlersProvider provider, HandlerDescriptor descriptor, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default) + public virtual DescribedHandlerInfo? DescribeHandler(IHandlersProvider provider, HandlerDescriptor descriptor, ITelegramBotClient client, Update update, out bool breakRouting, CancellationToken cancellationToken = default) { + breakRouting = false; cancellationToken.ThrowIfCancellationRequested(); Dictionary data = new Dictionary() { @@ -216,15 +216,23 @@ namespace Telegrator.Polling }; UpdateHandlerBase handlerInstance = provider.GetHandlerInstance(descriptor, cancellationToken); - FilterExecutionContext filterContext = new FilterExecutionContext(_botInfo, update, update, data, []); - if (descriptor.Filters != null && !descriptor.Filters.Validate(filterContext, out IFilter failedFilter, out FilterOrigin origin)) - { - Result fallbackResult = handlerInstance.FiltersFallback(filterContext, failedFilter, origin).Result; - if (!fallbackResult.Positive) - throw new BreakDescribingException(); - return null; + if (descriptor.Filters != null) + { + FiltersFallbackReport report = new FiltersFallbackReport(descriptor, filterContext); + Result filtersResult = descriptor.Filters.Validate(filterContext, descriptor.FormReport, ref report); + + if (filtersResult.RouteNext) + { + Result fallbackResult = handlerInstance.FiltersFallback(report, client, cancellationToken).Result; + breakRouting = !fallbackResult.RouteNext; + return null; + } + else if (!filtersResult.Positive) + { + return null; + } } return new DescribedHandlerInfo(descriptor, this, AwaitingProvider, client, handlerInstance, filterContext, descriptor.DisplayString); diff --git a/Telegrator/Result.cs b/Telegrator/Result.cs index 4d025b1..ac745fb 100644 --- a/Telegrator/Result.cs +++ b/Telegrator/Result.cs @@ -37,7 +37,6 @@ namespace Telegrator /// Represents 'success' /// /// Inside - let handler's main block be executed - /// Inside - let router continue describing /// Inside - tells that he can stop describing, as needed handler was found /// /// @@ -49,7 +48,7 @@ namespace Telegrator /// Represents 'fault' or 'error'. Use cases: /// /// Inside - interupts execution of handler, main block and wont be executed - /// Inside - interupts router's describing sequence + /// Inside - interupts 's describing sequence /// /// /// @@ -59,6 +58,7 @@ namespace Telegrator /// /// Represents 'continue'. Use cases: /// + /// Inside - let router continue describing /// Inside - Tells to continue describing handlers /// /// diff --git a/Telegrator/Telegrator.csproj b/Telegrator/Telegrator.csproj index 2ee8b14..7032b0a 100644 --- a/Telegrator/Telegrator.csproj +++ b/Telegrator/Telegrator.csproj @@ -17,7 +17,7 @@ True True LICENSE - 1.0.11 + 1.0.12