* 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
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<Version>1.0.11</Version>
|
||||
<Version>1.0.12</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<Version>1.0.11</Version>
|
||||
<Version>1.0.12</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -100,12 +100,13 @@ namespace Telegrator.Hosting
|
||||
/// Adds a Microsoft.Extensions.Logging adapter to Alligator using a logger factory.
|
||||
/// </summary>
|
||||
/// <param name="host"></param>
|
||||
public static void AddLoggingAdapter(this ITelegramBotHost host)
|
||||
public static ITelegramBotHost AddLoggingAdapter(this ITelegramBotHost host)
|
||||
{
|
||||
ILoggerFactory loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
|
||||
ILogger logger = loggerFactory.CreateLogger("Telegrator");
|
||||
MicrosoftLoggingAdapter adapter = new MicrosoftLoggingAdapter(logger);
|
||||
Alligator.AddAdapter(adapter);
|
||||
return host;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace Telegrator.Tests.Filters
|
||||
var filter2 = Filter<Update>.If(_ => true);
|
||||
var filter3 = Filter<Update>.If(_ => false);
|
||||
|
||||
var compiledFilter = CompiledFilter<Update>.Compile(filter1, filter2, filter3);
|
||||
var compiledFilter = new CompiledFilter<Update>(filter1, filter2, filter3);
|
||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||
|
||||
// Act
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Telegrator.Attributes.Components
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IFilter{T}"/> that <see cref="UpdateHandlerBase"/> processing
|
||||
/// </summary>
|
||||
public abstract Filter<Update> AnonymousFilter { get; protected set; }
|
||||
public abstract IFilter<Update> AnonymousFilter { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filter modifiers that affect how this filter is combined with others.
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace Telegrator.Attributes.Components
|
||||
/// </summary>
|
||||
public int Priority { get; set; }
|
||||
|
||||
public bool FormReport { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="UpdateHandlerAttributeBase"/>
|
||||
/// </summary>
|
||||
|
||||
@@ -15,12 +15,12 @@ namespace Telegrator.Attributes
|
||||
/// <summary>
|
||||
/// Gets the compiled anonymous filter for this attribute.
|
||||
/// </summary>
|
||||
public override Filter<Update> AnonymousFilter { get; protected set; }
|
||||
public override IFilter<Update> AnonymousFilter { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compiled filter logic for the update target.
|
||||
/// </summary>
|
||||
public Filter<T> UpdateFilter { get; protected set; }
|
||||
public IFilter<T> UpdateFilter { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Empty constructor for internal using
|
||||
@@ -38,18 +38,20 @@ namespace Telegrator.Attributes
|
||||
/// <param name="filters">The filters to compose</param>
|
||||
protected UpdateFilterAttribute(params IFilter<T>[] filters)
|
||||
{
|
||||
UpdateFilter = CompiledFilter<T>.Compile(filters);
|
||||
AnonymousFilter = AnonymousTypeFilter.Compile(UpdateFilter, GetFilterringTarget);
|
||||
string name = GetType().Name;
|
||||
UpdateFilter = new CompiledFilter<T>(name, filters);
|
||||
AnonymousFilter = AnonymousTypeFilter.Compile(name, UpdateFilter, GetFilterringTarget);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the attribute with a precompiled filter for the update target.
|
||||
/// </summary>
|
||||
/// <param name="updateFilter">The compiled filter</param>
|
||||
protected UpdateFilterAttribute(Filter<T> updateFilter)
|
||||
protected UpdateFilterAttribute(IFilter<T> updateFilter)
|
||||
{
|
||||
string name = GetType().Name;
|
||||
UpdateFilter = updateFilter;
|
||||
AnonymousFilter = AnonymousTypeFilter.Compile(UpdateFilter, GetFilterringTarget);
|
||||
AnonymousFilter = AnonymousTypeFilter.Compile(name, UpdateFilter, GetFilterringTarget);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -60,13 +62,13 @@ namespace Telegrator.Attributes
|
||||
public override sealed bool ProcessModifiers(UpdateFilterAttributeBase? previous)
|
||||
{
|
||||
if (Modifiers.HasFlag(FilterModifier.Not))
|
||||
AnonymousFilter = AnonymousFilter.Not();
|
||||
AnonymousFilter = Filter<T>.Not(AnonymousFilter);
|
||||
|
||||
if (previous is not null)
|
||||
{
|
||||
if (previous.Modifiers.HasFlag(FilterModifier.OrNext))
|
||||
{
|
||||
AnonymousFilter = previous.AnonymousFilter.Or(AnonymousFilter);
|
||||
AnonymousFilter = Filter<Update>.Or(previous.AnonymousFilter, AnonymousFilter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
Sender
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Messages from where this filter was originated
|
||||
/// </summary>
|
||||
@@ -95,6 +96,7 @@
|
||||
/// </summary>
|
||||
Regualr
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Represents a compiled filter that applies a set of filters to an anonymous target type.
|
||||
/// </summary>
|
||||
public sealed class AnonymousCompiledFilter : AnonymousTypeFilter
|
||||
public class AnonymousCompiledFilter : Filter<Update>, INamedFilter
|
||||
{
|
||||
private readonly Func<FilterExecutionContext<Update>, object, bool> FilterAction;
|
||||
private readonly Func<Update, object?> GetFilterringTarget;
|
||||
private readonly string _name;
|
||||
|
||||
public virtual string Name => _name;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AnonymousCompiledFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="filterAction">The filter action delegate.</param>
|
||||
/// <param name="getFilterringTarget">The function to get the filtering target from an update.</param>
|
||||
private AnonymousCompiledFilter(Func<FilterExecutionContext<Update>, object, bool> filterAction, Func<Update, object?> getFilterringTarget)
|
||||
: base(filterAction, getFilterringTarget) { }
|
||||
private AnonymousCompiledFilter(string name, Func<Update, object?> getFilterringTarget, Func<FilterExecutionContext<Update>, object, bool> filterAction)
|
||||
{
|
||||
FilterAction = filterAction;
|
||||
GetFilterringTarget = getFilterringTarget;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compiles a set of filters into an <see cref="AnonymousCompiledFilter"/> for a specific target type.
|
||||
@@ -24,11 +34,28 @@ namespace Telegrator.Filters.Components
|
||||
/// <param name="filters">The list of filters to compile.</param>
|
||||
/// <param name="getFilterringTarget">The function to get the filtering target from an update.</param>
|
||||
/// <returns>The compiled filter.</returns>
|
||||
public static AnonymousCompiledFilter Compile<T>(IList<IFilter<T>> filters, Func<Update, object?> getFilterringTarget) where T : class
|
||||
public static AnonymousCompiledFilter Compile<T>(IEnumerable<IFilter<T>> filters, Func<Update, object?> 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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compiles a set of filters into an <see cref="AnonymousCompiledFilter"/> for a specific target type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the filtering target.</typeparam>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="filters">The list of filters to compile.</param>
|
||||
/// <param name="getFilterringTarget">The function to get the filtering target from an update.</param>
|
||||
/// <returns>The compiled filter.</returns>
|
||||
public static AnonymousCompiledFilter Compile<T>(string name, IEnumerable<IFilter<T>> filters, Func<Update, object?> getFilterringTarget) where T : class
|
||||
{
|
||||
return new AnonymousCompiledFilter(
|
||||
name,
|
||||
getFilterringTarget,
|
||||
(context, filterringTarget) => CanPassInternal(context, filters, filterringTarget));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,16 +66,15 @@ namespace Telegrator.Filters.Components
|
||||
/// <param name="updateContext">The filter execution context.</param>
|
||||
/// <param name="filterringTarget">The filtering target.</param>
|
||||
/// <returns>True if all filters pass; otherwise, false.</returns>
|
||||
private static bool CanPassInternal<T>(IList<IFilter<T>> filters, FilterExecutionContext<Update> updateContext, object filterringTarget) where T : class
|
||||
private static bool CanPassInternal<T>(FilterExecutionContext<Update> updateContext, IEnumerable<IFilter<T>> filters, object filterringTarget) where T : class
|
||||
{
|
||||
FilterExecutionContext<T> context = updateContext.CreateChild((T)filterringTarget);
|
||||
|
||||
foreach (IFilter<T> 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanPass(FilterExecutionContext<Update> context)
|
||||
{
|
||||
try
|
||||
{
|
||||
object? filterringTarget = GetFilterringTarget.Invoke(context.Input);
|
||||
if (filterringTarget == null)
|
||||
return false;
|
||||
|
||||
return FilterAction.Invoke(context, filterringTarget);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,20 +6,25 @@ namespace Telegrator.Filters.Components
|
||||
/// <summary>
|
||||
/// Represents a filter that applies a filter action to an anonymous target type extracted from an update.
|
||||
/// </summary>
|
||||
public class AnonymousTypeFilter : Filter<Update>
|
||||
public class AnonymousTypeFilter : Filter<Update>, INamedFilter
|
||||
{
|
||||
private readonly Func<FilterExecutionContext<Update>, object, bool> FilterAction;
|
||||
private readonly Func<Update, object?> GetFilterringTarget;
|
||||
private readonly string _name;
|
||||
|
||||
public virtual string Name => _name;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AnonymousTypeFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="filterAction">The filter action delegate.</param>
|
||||
/// <param name="getFilterringTarget">The function to get the filtering target from an update.</param>
|
||||
protected AnonymousTypeFilter(Func<FilterExecutionContext<Update>, object, bool> filterAction, Func<Update, object?> getFilterringTarget)
|
||||
public AnonymousTypeFilter(string name, Func<Update, object?> getFilterringTarget, Func<FilterExecutionContext<Update>, object, bool> filterAction)
|
||||
{
|
||||
FilterAction = filterAction;
|
||||
GetFilterringTarget = getFilterringTarget;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -32,8 +37,25 @@ namespace Telegrator.Filters.Components
|
||||
public static AnonymousTypeFilter Compile<T>(IFilter<T> filter, Func<Update, T?> getFilterringTarget) where T : class
|
||||
{
|
||||
return new AnonymousTypeFilter(
|
||||
(context, filterringTarget) => CanPassInternal(context, filter, filterringTarget),
|
||||
getFilterringTarget);
|
||||
filter.GetType().Name,
|
||||
getFilterringTarget,
|
||||
(context, filterringTarget) => CanPassInternal(context, filter, filterringTarget));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compiles a filter for a specific target type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the filtering target.</typeparam>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="filter">The filter to apply.</param>
|
||||
/// <param name="getFilterringTarget">The function to get the filtering target from an update.</param>
|
||||
/// <returns>The compiled filter.</returns>
|
||||
public static AnonymousTypeFilter Compile<T>(string name, IFilter<T> filter, Func<Update, T?> getFilterringTarget) where T : class
|
||||
{
|
||||
return new AnonymousTypeFilter(
|
||||
name,
|
||||
getFilterringTarget,
|
||||
(context, filterringTarget) => CanPassInternal(context, filter, filterringTarget));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -6,27 +6,32 @@ namespace Telegrator.Filters.Components
|
||||
/// Represents a filter that composes multiple filters and passes only if all of them pass.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the input for the filter.</typeparam>
|
||||
public class CompiledFilter<T> : Filter<T> where T : class
|
||||
public class CompiledFilter<T> : Filter<T>, INamedFilter where T : class
|
||||
{
|
||||
private readonly IFilter<T>[] Filters;
|
||||
private readonly string _name;
|
||||
|
||||
public virtual string Name => _name;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CompiledFilter{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="filters">The filters to compose.</param>
|
||||
private CompiledFilter(IFilter<T>[] filters)
|
||||
public CompiledFilter(params IFilter<T>[] filters)
|
||||
{
|
||||
_name = string.Join("+", filters.Select(fltr => fltr.GetType().Name));
|
||||
Filters = filters;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compiles multiple filters into a <see cref="CompiledFilter{T}"/>.
|
||||
/// Initializes a new instance of the <see cref="CompiledFilter{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="filters">The filters to compose.</param>
|
||||
/// <returns>A new <see cref="CompiledFilter{T}"/> instance.</returns>
|
||||
public static CompiledFilter<T> Compile(params IFilter<T>[] filters)
|
||||
public CompiledFilter(string name, params IFilter<T>[] filters)
|
||||
{
|
||||
return new CompiledFilter<T>(filters);
|
||||
_name = name;
|
||||
Filters = filters;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -41,7 +46,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
namespace Telegrator.Filters.Components
|
||||
{
|
||||
public interface INamedFilter
|
||||
{
|
||||
public string Name { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for filters that can be collected into a completed filters list.
|
||||
/// Provides information about whether a filter should be tracked during execution.
|
||||
|
||||
@@ -30,6 +30,14 @@ namespace Telegrator.Filters
|
||||
public Filter<T> Not()
|
||||
=> new ReverseFilter<T>(this);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a filter that inverts the result of this filter.
|
||||
/// </summary>
|
||||
/// <typeparam name="Q"></typeparam>
|
||||
/// <returns>A <see cref="ReverseFilter{T}"/> instance.</returns>
|
||||
public static Filter<Q> Not<Q>(IFilter<Q> filter) where Q : class
|
||||
=> new ReverseFilter<Q>(filter);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a filter that passes only if both this and the specified filter pass.
|
||||
/// </summary>
|
||||
@@ -46,6 +54,16 @@ namespace Telegrator.Filters
|
||||
public OrFilter<T> Or(IFilter<T> filter)
|
||||
=> new OrFilter<T>(this, filter);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a filter that passes if either this or the specified filter pass.
|
||||
/// </summary>
|
||||
/// <typeparam name="Q"></typeparam>
|
||||
/// <param name="left"></param>
|
||||
/// <param name="right"></param>
|
||||
/// <returns>An <see cref="OrFilter{Q}"/> instance.</returns>
|
||||
public static OrFilter<Q> Or<Q>(IFilter<Q> left, IFilter<Q> right) where Q : class
|
||||
=> new OrFilter<Q>(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this filter is collectible.
|
||||
/// </summary>
|
||||
|
||||
@@ -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<Update> context)
|
||||
{
|
||||
public HandlerDescriptor Descriptor { get; } = descriptor;
|
||||
|
||||
public FilterExecutionContext<Update> Context { get; } = context;
|
||||
|
||||
public FilterFallbackInfo? UpdateValidator { get; set; }
|
||||
|
||||
public FilterFallbackInfo? StateKeeperValidator { get; set; }
|
||||
|
||||
public List<FilterFallbackInfo> UpdateFilters { get; } = [];
|
||||
|
||||
/*
|
||||
public FilterFallbackInfo OfType<T>(int index = 0) where T : IFilter<Update>
|
||||
{
|
||||
return UpdateFilters.Where(info => info.Failed is T).ElementAt(index);
|
||||
}
|
||||
|
||||
public FilterFallbackInfo OfAttribute<T>(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<T>(int index = 0) where T : UpdateFilterAttributeBase
|
||||
{
|
||||
string name = typeof(T).Name;
|
||||
IEnumerable<FilterFallbackInfo> failed = UpdateFilters.Where(info => info.Failed);
|
||||
if (failed.Count() > 1)
|
||||
return false;
|
||||
|
||||
return failed.SingleOrDefault()?.Name == name;
|
||||
}
|
||||
}
|
||||
|
||||
public class FilterFallbackInfo(string name, IFilter<Update> filter, bool failed, Exception? exception)
|
||||
{
|
||||
public string Name { get; } = name;
|
||||
|
||||
public IFilter<Update> Filter { get; } = filter;
|
||||
|
||||
public bool Failed { get; } = failed;
|
||||
|
||||
public Exception? Exception { get; } = exception;
|
||||
}
|
||||
|
||||
public enum FallbackReason
|
||||
{
|
||||
NullTarget,
|
||||
|
||||
FailedFilter
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace Telegrator.MadiatorCore.Descriptors
|
||||
namespace Telegrator.Handlers.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a token that tracks the lifetime of a handler instance.
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Handles failed filters during handler describing.
|
||||
/// Use <see cref="Result"/> to control how router should treat this fail.
|
||||
/// <see cref="Result.Ok"/> to silently continue decribing.
|
||||
/// <see cref="Result.Fault"/> to stop\break decribing sequence.
|
||||
/// <see cref="Result.Next"/> to silently continue decribing.
|
||||
/// <see cref="Result.Fault"/> to stop\break desribing sequence.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="failedFilter"></param>
|
||||
/// <param name="origin"></param>
|
||||
/// <param name="report"></param>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public virtual Task<Result> FiltersFallback(FilterExecutionContext<Update> context, IFilter<Update> failedFilter, FilterOrigin origin)
|
||||
public virtual Task<Result> FiltersFallback(FiltersFallbackReport report, ITelegramBotClient client, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(Result.Ok());
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
/// <param name="filterContext">The filter execution context.</param>
|
||||
/// <param name="failedFilter"></param>
|
||||
/// <param name="origin"></param>
|
||||
/// <param name="formReport"></param>
|
||||
/// <param name="report"></param>
|
||||
/// <returns>True if all filters pass; otherwise, false.</returns>
|
||||
public bool Validate(FilterExecutionContext<Update> filterContext, out IFilter<Update> failedFilter, out FilterOrigin origin)
|
||||
public Result Validate(FilterExecutionContext<Update> 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<Update> 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<T>(IFilter<T> filter, FilterExecutionContext<T> context, out Exception? exception) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
exception = null;
|
||||
return filter.CanPass(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,12 @@ namespace Telegrator.MadiatorCore.Descriptors
|
||||
set;
|
||||
}
|
||||
|
||||
public bool FormReport
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The set of filters associated with this handler.
|
||||
/// </summary>
|
||||
@@ -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);
|
||||
|
||||
@@ -178,23 +178,21 @@ namespace Telegrator.Polling
|
||||
/// <returns>A collection of described handler information</returns>
|
||||
protected virtual IEnumerable<DescribedHandlerInfo> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -205,10 +203,12 @@ namespace Telegrator.Polling
|
||||
/// <param name="descriptor">The handler descriptor to process</param>
|
||||
/// <param name="client">The Telegram bot client instance</param>
|
||||
/// <param name="update">The incoming Telegram update to process</param>
|
||||
/// <param name="breakRouting"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>The described handler info if validation passes; otherwise, null</returns>
|
||||
public virtual DescribedHandlerInfo? DescribeHandler(IHandlersProvider provider, HandlerDescriptor descriptor, ITelegramBotClient client, Update update, 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<string, object> data = new Dictionary<string, object>()
|
||||
{
|
||||
@@ -216,15 +216,23 @@ namespace Telegrator.Polling
|
||||
};
|
||||
|
||||
UpdateHandlerBase handlerInstance = provider.GetHandlerInstance(descriptor, cancellationToken);
|
||||
|
||||
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(_botInfo, update, update, data, []);
|
||||
if (descriptor.Filters != null && !descriptor.Filters.Validate(filterContext, out IFilter<Update> 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);
|
||||
|
||||
@@ -37,7 +37,6 @@ namespace Telegrator
|
||||
/// Represents 'success'
|
||||
/// <list type="bullet">
|
||||
/// <item>Inside <see cref="IPreProcessor"/> - let handler's main block be executed</item>
|
||||
/// <item>Inside <see cref="UpdateHandlerBase.FiltersFallback(FilterExecutionContext{Update}, IFilter{Update}, FilterOrigin)"/> - let router continue describing</item>
|
||||
/// <item>Inside <see cref="UpdateHandlerBase.ExecuteInternal(IHandlerContainer, CancellationToken)"/> - tells <see cref="IUpdateRouter"/> that he can stop describing, as needed handler was found</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
@@ -49,7 +48,7 @@ namespace Telegrator
|
||||
/// Represents 'fault' or 'error'. Use cases:
|
||||
/// <list type="bullet">
|
||||
/// <item>Inside <see cref="IPreProcessor"/> - interupts execution of handler, main block and <see cref="IPostProcessor"/> wont be executed</item>
|
||||
/// <item>Inside <see cref="UpdateHandlerBase.FiltersFallback(FilterExecutionContext{Update}, IFilter{Update}, FilterOrigin)"/> - interupts router's describing sequence</item>
|
||||
/// <item>Inside <see cref="UpdateHandlerBase.FiltersFallback(FiltersFallbackReport, Telegram.Bot.ITelegramBotClient, CancellationToken)"/> - interupts <see cref="IUpdateRouter"/>'s describing sequence</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
@@ -59,6 +58,7 @@ namespace Telegrator
|
||||
/// <summary>
|
||||
/// Represents 'continue'. Use cases:
|
||||
/// <list type="bullet">
|
||||
/// <item>Inside <see cref="UpdateHandlerBase.FiltersFallback(FiltersFallbackReport, Telegram.Bot.ITelegramBotClient, CancellationToken)"/> - let router continue describing</item>
|
||||
/// <item>Inside <see cref="UpdateHandlerBase.ExecuteInternal(IHandlerContainer, CancellationToken)"/> - Tells <see cref="IUpdateRouter"/> to continue describing handlers</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<Version>1.0.11</Version>
|
||||
<Version>1.0.12</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user