* Removed default filters comparison methods from 'FiltersFallbackReport'
* Added 'ReportInspector' for patterned filters diagnostic * Moved some extension methods to corresponding namespaces and separated simple type extensions from complex types extensions
This commit is contained in:
@@ -9,7 +9,7 @@ namespace Telegrator.Attributes
|
||||
/// Reactive way to implement a new <see cref="UpdateFilterAttribute{T}"/> of type <typeparamref name="T"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class FilterAnnotation<T> : UpdateFilterAttribute<T>, IFilter<T> where T : class
|
||||
public abstract class FilterAnnotation<T> : UpdateFilterAttribute<T>, IFilter<T>, INamedFilter where T : class
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public virtual bool IsCollectible { get; } = false;
|
||||
@@ -17,6 +17,9 @@ namespace Telegrator.Attributes
|
||||
/// <inheritdoc/>
|
||||
public override UpdateType[] AllowedTypes { get; } = typeof(T).GetAllowedUpdateTypes();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Name => GetType().Name;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes new instance of <see cref="FilterAnnotation{T}"/>
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Annotations.StateKeeping;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.Handlers.Building.Components;
|
||||
using Telegrator.StateKeeping;
|
||||
using Telegrator.StateKeeping.Abstracts;
|
||||
using Telegrator.StateKeeping.Components;
|
||||
|
||||
namespace Telegrator.Handlers.Building
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for handler builders.
|
||||
/// Provides convenient methods for creating handlers and setting state keepers.
|
||||
/// </summary>
|
||||
public static partial class HandlerBuilderExtensions
|
||||
{
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetUpdateValidating(UpdateValidateAction)"/>
|
||||
public static TBuilder SetUpdateValidating<TBuilder>(this TBuilder handlerBuilder, UpdateValidateAction updateValidateAction)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetUpdateValidating(updateValidateAction);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetConcurreny(int)"/>
|
||||
public static TBuilder SetConcurreny<TBuilder>(this TBuilder handlerBuilder, int concurrency)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetConcurreny(concurrency);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetPriority(int)"/>
|
||||
public static TBuilder SetPriority<TBuilder>(this TBuilder handlerBuilder, int priority)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetPriority(priority);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetIndexer(int, int)"/>
|
||||
public static TBuilder SetIndexer<TBuilder>(this TBuilder handlerBuilder, int concurrency, int priority)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetIndexer(concurrency, priority);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.AddFilter(IFilter{Update})"/>
|
||||
public static TBuilder AddFilter<TBuilder>(this TBuilder handlerBuilder, IFilter<Update> filter)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.AddFilter(filter);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.AddFilters(IFilter{Update}[])"/>
|
||||
public static TBuilder AddFilters<TBuilder>(this TBuilder handlerBuilder, params IFilter<Update>[] filters)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.AddFilters(filters);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetStateKeeper{TKey, TState, TKeeper}(TState, IStateKeyResolver{TKey})"/>
|
||||
public static TBuilder SetStateKeeper<TBuilder, TKey, TState, TKeeper>(this TBuilder handlerBuilder, TState myState, IStateKeyResolver<TKey> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TKey : notnull
|
||||
where TState : IEquatable<TState>
|
||||
where TKeeper : StateKeeperBase<TKey, TState>, new()
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<TKey, TState, TKeeper>(myState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetStateKeeper{TKey, TState, TKeeper}(SpecialState, IStateKeyResolver{TKey})"/>
|
||||
public static TBuilder SetStateKeeper<TBuilder, TKey, TState, TKeeper>(this TBuilder handlerBuilder, SpecialState specialState, IStateKeyResolver<TKey> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TKey : notnull
|
||||
where TState : IEquatable<TState>
|
||||
where TKeeper : StateKeeperBase<TKey, TState>, new()
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<TKey, TState, TKeeper>(specialState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a targeted filter for a specific filter target type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder"></typeparam>
|
||||
/// <typeparam name="TFilterTarget">The type of the filter target.</typeparam>
|
||||
/// <param name="handlerBuilder"></param>
|
||||
/// <param name="getFilterringTarget">Function to get the filter target from an update.</param>
|
||||
/// <param name="filter">The filter to add.</param>
|
||||
/// <returns>The builder instance.</returns>
|
||||
public static TBuilder AddTargetedFilter<TBuilder, TFilterTarget>(this TBuilder handlerBuilder, Func<Update, TFilterTarget?> getFilterringTarget, IFilter<TFilterTarget> filter)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TFilterTarget : class
|
||||
{
|
||||
handlerBuilder.AddTargetedFilter(getFilterringTarget, filter);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple targeted filters for a specific filter target type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder"></typeparam>
|
||||
/// <typeparam name="TFilterTarget">The type of the filter target.</typeparam>
|
||||
/// <param name="handlerBuilder"></param>
|
||||
/// <param name="getFilterringTarget">Function to get the filter target from an update.</param>
|
||||
/// <param name="filters">The filters to add.</param>
|
||||
/// <returns>The builder instance.</returns>
|
||||
public static TBuilder AddTargetedFilters<TBuilder, TFilterTarget>(this TBuilder handlerBuilder, Func<Update, TFilterTarget?> getFilterringTarget, params IFilter<TFilterTarget>[] filters)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TFilterTarget : class
|
||||
{
|
||||
handlerBuilder.AddTargetedFilters(getFilterringTarget, filters);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a numeric state keeper with a custom key resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="myState">The numeric state value.</param>
|
||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, int myState, IStateKeyResolver<long> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(myState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a numeric state keeper with a special state and custom key resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="specialState">The special state value.</param>
|
||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, SpecialState specialState, IStateKeyResolver<long> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(specialState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a numeric state keeper with the default sender ID resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="myState">The numeric state value.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, int myState)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(myState, new SenderIdResolver());
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a numeric state keeper with a special state and the default sender ID resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="specialState">The special state value.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, SpecialState specialState)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(specialState, new SenderIdResolver());
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an enum state keeper with a custom key resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="myState">The enum state value.</param>
|
||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, TEnum myState, IStateKeyResolver<long> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TEnum : Enum, IEquatable<TEnum>
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(myState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an enum state keeper with a special state and custom key resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="specialState">The special state value.</param>
|
||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, SpecialState specialState, IStateKeyResolver<long> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TEnum : Enum, IEquatable<TEnum>
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(specialState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an enum state keeper with the default sender ID resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="myState">The enum state value.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, TEnum myState)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TEnum : Enum, IEquatable<TEnum>
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(myState, new SenderIdResolver());
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an enum state keeper with a special state and the default sender ID resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="specialState">The special state value.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, SpecialState specialState)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TEnum : Enum, IEquatable<TEnum>
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(specialState, new SenderIdResolver());
|
||||
return handlerBuilder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Attributes.Components;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.MadiatorCore.Descriptors;
|
||||
|
||||
namespace Telegrator.Handlers.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a report of filter fallback information for debugging and error handling.
|
||||
/// Contains detailed information about which filters failed and why during handler execution.
|
||||
/// </summary>
|
||||
/// <param name="descriptor">The handler descriptor that generated this report.</param>
|
||||
/// <param name="context">The filter execution context.</param>
|
||||
public class FiltersFallbackReport(HandlerDescriptor descriptor, FilterExecutionContext<Update> context)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the handler descriptor associated with this fallback report.
|
||||
/// </summary>
|
||||
public HandlerDescriptor Descriptor { get; } = descriptor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filter execution context that generated this report.
|
||||
/// </summary>
|
||||
public FilterExecutionContext<Update> Context { get; } = context;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fallback information for the update validator filter.
|
||||
/// </summary>
|
||||
public FilterFallbackInfo? UpdateValidator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fallback information for the state keeper validator filter.
|
||||
/// </summary>
|
||||
public FilterFallbackInfo? StateKeeperValidator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of fallback information for update filters that failed.
|
||||
/// </summary>
|
||||
public List<FilterFallbackInfo> UpdateFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the failure is due to a specific filter.
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public bool Only(string name, int index = 0)
|
||||
{
|
||||
FilterFallbackInfo? info = UpdateFilters.SingleSafe(info => info.Failed);
|
||||
if (info != null && info.Name != name)
|
||||
return false;
|
||||
|
||||
FilterFallbackInfo? target = UpdateFilters.ElementAtOrDefault(index);
|
||||
return ReferenceEquals(target, info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the failure is due to a specific filter.
|
||||
/// </summary>
|
||||
/// <param name="names"></param>
|
||||
/// <returns></returns>
|
||||
public bool Only(string[] names)
|
||||
{
|
||||
return UpdateFilters
|
||||
.Where(info => info.Failed)
|
||||
.Select(info => info.Name)
|
||||
.SequenceEqual(names);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the failure is due to all filters except one.
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public bool Except(string name, int index = 0)
|
||||
{
|
||||
FilterFallbackInfo? info = UpdateFilters.SingleSafe(info => !info.Failed);
|
||||
if (info != null && info.Name != name)
|
||||
return false;
|
||||
|
||||
FilterFallbackInfo? target = UpdateFilters.ElementAtOrDefault(index);
|
||||
return ReferenceEquals(target, info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the failure is due to all filters except one.
|
||||
/// </summary>
|
||||
/// <param name="names"></param>
|
||||
/// <returns></returns>
|
||||
public bool Except(string[] names)
|
||||
{
|
||||
return UpdateFilters
|
||||
.Where(info => !info.Failed)
|
||||
.Select(info => info.Name)
|
||||
.SequenceEqual(names);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the failure is due to aall attribute type, excluding one.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The attribute type to check for.</typeparam>
|
||||
/// <param name="index">The index of the filter to check (default: 0).</param>
|
||||
/// <returns>True if the failure is exclusively due to the specified attribute type; otherwise, false.</returns>
|
||||
public bool ExceptAttribute<T>(int index = 0) where T : UpdateFilterAttributeBase
|
||||
=> Except(nameof(T), index);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the failure is due to a specific attribute type, excluding other failures.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The attribute type to check for.</typeparam>
|
||||
/// <param name="index">The index of the filter to check (default: 0).</param>
|
||||
/// <returns>True if the failure is exclusively due to the specified attribute type; otherwise, false.</returns>
|
||||
public bool OnlyAttribute<T>(int index = 0) where T : UpdateFilterAttributeBase
|
||||
=> Only(nameof(T), index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains information about a filter that failed during execution.
|
||||
/// Provides details about the filter, its failure status, and any associated exception.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the filter.</param>
|
||||
/// <param name="filter">The filter instance that failed.</param>
|
||||
/// <param name="failed">Whether the filter failed.</param>
|
||||
/// <param name="exception">The exception that occurred during filter execution, if any.</param>
|
||||
public class FilterFallbackInfo(string name, IFilter<Update> filter, bool failed, Exception? exception)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the filter.
|
||||
/// </summary>
|
||||
public string Name { get; } = name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filter instance that failed.
|
||||
/// </summary>
|
||||
public IFilter<Update> Filter { get; } = filter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the filter failed.
|
||||
/// </summary>
|
||||
public bool Failed { get; } = failed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exception that occurred during filter execution, if any.
|
||||
/// </summary>
|
||||
public Exception? Exception { get; } = exception;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the reason for a filter fallback.
|
||||
/// </summary>
|
||||
public enum FallbackReason
|
||||
{
|
||||
/// <summary>
|
||||
/// The filter target was null.
|
||||
/// </summary>
|
||||
NullTarget,
|
||||
|
||||
/// <summary>
|
||||
/// The filter failed to pass.
|
||||
/// </summary>
|
||||
FailedFilter
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using Telegram.Bot.Polling;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.Handlers.Diagnostics;
|
||||
using Telegrator.MadiatorCore.Descriptors;
|
||||
|
||||
namespace Telegrator.Handlers.Components
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Filters.Components;
|
||||
|
||||
namespace Telegrator.Handlers.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about a filter that failed during execution.
|
||||
/// Provides details about the filter, its failure status, and any associated exception.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the filter.</param>
|
||||
/// <param name="filter">The filter instance that failed.</param>
|
||||
/// <param name="failed">Whether the filter failed.</param>
|
||||
/// <param name="exception">The exception that occurred during filter execution, if any.</param>
|
||||
public class FilterFallbackInfo(string name, IFilter<Update> filter, bool failed, Exception? exception)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the filter.
|
||||
/// </summary>
|
||||
public string Name { get; } = name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filter instance that failed.
|
||||
/// </summary>
|
||||
public IFilter<Update> Filter { get; } = filter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the filter failed.
|
||||
/// </summary>
|
||||
public bool Failed { get; } = failed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exception that occurred during filter execution, if any.
|
||||
/// </summary>
|
||||
public Exception? Exception { get; } = exception;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.MadiatorCore.Descriptors;
|
||||
|
||||
namespace Telegrator.Handlers.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a report of filter fallback information for debugging and error handling.
|
||||
/// Contains detailed information about which filters failed and why during handler execution.
|
||||
/// </summary>
|
||||
/// <param name="descriptor">The handler descriptor that generated this report.</param>
|
||||
/// <param name="context">The filter execution context.</param>
|
||||
public class FiltersFallbackReport(HandlerDescriptor descriptor, FilterExecutionContext<Update> context)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the handler descriptor associated with this fallback report.
|
||||
/// </summary>
|
||||
public HandlerDescriptor Descriptor { get; } = descriptor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filter execution context that generated this report.
|
||||
/// </summary>
|
||||
public FilterExecutionContext<Update> Context { get; } = context;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fallback information for the update validator filter.
|
||||
/// </summary>
|
||||
public FilterFallbackInfo? UpdateValidator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fallback information for the state keeper validator filter.
|
||||
/// </summary>
|
||||
public FilterFallbackInfo? StateKeeperValidator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of fallback information for update filters that failed.
|
||||
/// </summary>
|
||||
public List<FilterFallbackInfo> UpdateFilters { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Checks filter fail status by name
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public bool this[string name] => UpdateFilters.FirstOrDefault(f => f.Name == name)?.Failed ?? false;
|
||||
|
||||
/// <summary>
|
||||
/// Creates new instance of <see cref="ReportInspector"/> with default filter state as FAILED.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ReportInspector AllFailed()
|
||||
{
|
||||
return new ReportInspector(this, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new instance of <see cref="ReportInspector"/> with default filter state as PASSED.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ReportInspector AllPassed()
|
||||
{
|
||||
return new ReportInspector(this, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
namespace Telegrator.Handlers.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// A class builder for pattern checking of <see cref="FiltersFallbackReport"/>
|
||||
/// </summary>
|
||||
/// <param name="report"></param>
|
||||
/// <param name="defaulState"></param>
|
||||
public sealed class ReportInspector(FiltersFallbackReport report, bool defaulState)
|
||||
{
|
||||
private readonly FiltersFallbackReport _report = report;
|
||||
private readonly bool _defaulState = defaulState;
|
||||
|
||||
private readonly List<string> _ignore = [];
|
||||
private readonly List<string> _excepts = [];
|
||||
|
||||
/// <summary>
|
||||
/// Adds a filter to the exclusion list.
|
||||
/// Excluded filters are compared oppositely with the default state.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public ReportInspector Except(string name)
|
||||
{
|
||||
_excepts.Add(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a filter to the ignore list.
|
||||
/// Ignored filters are not checked and do not affect the final result.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public ReportInspector Whenever(string name)
|
||||
{
|
||||
_ignore.Add(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// It goes through the report and compares it with the specified filter pattern.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Match()
|
||||
{
|
||||
foreach (FilterFallbackInfo info in _report.UpdateFilters)
|
||||
{
|
||||
if (_ignore.Contains(info.Name))
|
||||
continue;
|
||||
|
||||
if (_excepts.Contains(info.Name))
|
||||
{
|
||||
if (_defaulState == true && !info.Failed)
|
||||
return false;
|
||||
|
||||
if (_defaulState == false && info.Failed)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!info.Failed != _defaulState)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Casts inspector by executing <see cref="Match"/>
|
||||
/// </summary>
|
||||
/// <param name="inspector"></param>
|
||||
public static implicit operator bool(ReportInspector inspector) => inspector.Match();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Telegrator.Attributes.Components;
|
||||
|
||||
namespace Telegrator.Handlers.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="ReportInspector"/>
|
||||
/// </summary>
|
||||
public static partial class ReportInspectorExtensions
|
||||
{
|
||||
/// <inheritdoc cref="ReportInspector.Whenever(string)"/>
|
||||
public static ReportInspector Whenever<TAttribute>(this ReportInspector inspector) where TAttribute : UpdateFilterAttributeBase
|
||||
=> inspector.Whenever(nameof(TAttribute));
|
||||
|
||||
/// <inheritdoc cref="ReportInspector.Except(string)"/>
|
||||
public static ReportInspector Except<TAttribute>(this ReportInspector inspector) where TAttribute : UpdateFilterAttributeBase
|
||||
=> inspector.Except(nameof(TAttribute));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.InlineQueryResults;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
|
||||
namespace Telegrator.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides usefull helper methods for abstract handler containers
|
||||
/// </summary>
|
||||
public static class AbstractHandlerContainerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Changes bot's reaction to message
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="reaction"></param>
|
||||
/// <param name="isBig"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task React(
|
||||
this IAbstractHandlerContainer<Message> container,
|
||||
ReactionType reaction,
|
||||
bool isBig = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> await container.Client.SetMessageReaction(
|
||||
container.ActualUpdate.Chat,
|
||||
container.ActualUpdate.Id,
|
||||
[reaction], isBig, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Changes bot's reaction to message
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="reactions"></param>
|
||||
/// <param name="isBig"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task React(
|
||||
this IAbstractHandlerContainer<Message> container,
|
||||
IEnumerable<ReactionType> reactions,
|
||||
bool isBig = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> await container.Client.SetMessageReaction(
|
||||
container.ActualUpdate.Chat,
|
||||
container.ActualUpdate.Id,
|
||||
reactions, isBig, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a reply message to the current message.
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="text">The text of the message to send.</param>
|
||||
/// <param name="parseMode">The parse mode for the message text.</param>
|
||||
/// <param name="replyMarkup">The reply markup for the message.</param>
|
||||
/// <param name="linkPreviewOptions">Options for link preview generation.</param>
|
||||
/// <param name="messageThreadId">The thread ID for forum topics.</param>
|
||||
/// <param name="entities">The message entities to include.</param>
|
||||
/// <param name="disableNotification">Whether to disable notification for the message.</param>
|
||||
/// <param name="protectContent">Whether to protect the message content.</param>
|
||||
/// <param name="messageEffectId">The message effect ID.</param>
|
||||
/// <param name="businessConnectionId">The business connection ID.</param>
|
||||
/// <param name="allowPaidBroadcast">Whether to allow paid broadcast.</param>
|
||||
/// <param name="directMessageTopicId"></param>
|
||||
/// <param name="suggestedPostParameters"></param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The sent message.</returns>
|
||||
public static async Task<Message> Reply(
|
||||
this IAbstractHandlerContainer<Message> container,
|
||||
string text,
|
||||
ParseMode parseMode = ParseMode.None,
|
||||
ReplyMarkup? replyMarkup = null,
|
||||
LinkPreviewOptions? linkPreviewOptions = null,
|
||||
int? messageThreadId = null,
|
||||
IEnumerable<MessageEntity>? entities = null,
|
||||
bool disableNotification = false,
|
||||
bool protectContent = false,
|
||||
string? messageEffectId = null,
|
||||
string? businessConnectionId = null,
|
||||
bool allowPaidBroadcast = false,
|
||||
int? directMessageTopicId = null,
|
||||
SuggestedPostParameters? suggestedPostParameters = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> await container.Client.SendMessage(
|
||||
container.ActualUpdate.Chat, text, parseMode, container.ActualUpdate,
|
||||
replyMarkup, linkPreviewOptions,
|
||||
messageThreadId, entities,
|
||||
disableNotification, protectContent,
|
||||
messageEffectId, businessConnectionId,
|
||||
allowPaidBroadcast, directMessageTopicId,
|
||||
suggestedPostParameters, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a response message to the current chat.
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="text">The text of the message to send.</param>
|
||||
/// <param name="parseMode">The parse mode for the message text.</param>
|
||||
/// <param name="replyParameters">The reply parameters for the message.</param>
|
||||
/// <param name="replyMarkup">The reply markup for the message.</param>
|
||||
/// <param name="linkPreviewOptions">Options for link preview generation.</param>
|
||||
/// <param name="messageThreadId">The thread ID for forum topics.</param>
|
||||
/// <param name="entities">The message entities to include.</param>
|
||||
/// <param name="disableNotification">Whether to disable notification for the message.</param>
|
||||
/// <param name="protectContent">Whether to protect the message content.</param>
|
||||
/// <param name="messageEffectId">The message effect ID.</param>
|
||||
/// <param name="businessConnectionId">The business connection ID.</param>
|
||||
/// <param name="allowPaidBroadcast">Whether to allow paid broadcast.</param>
|
||||
/// <param name="directMessageTopicId"></param>
|
||||
/// <param name="suggestedPostParameters"></param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The sent message.</returns>
|
||||
public static async Task<Message> Responce(
|
||||
this IAbstractHandlerContainer<Message> container,
|
||||
string text,
|
||||
ParseMode parseMode = ParseMode.None,
|
||||
ReplyParameters? replyParameters = null,
|
||||
ReplyMarkup? replyMarkup = null,
|
||||
LinkPreviewOptions? linkPreviewOptions = null,
|
||||
int? messageThreadId = null,
|
||||
IEnumerable<MessageEntity>? entities = null,
|
||||
bool disableNotification = false,
|
||||
bool protectContent = false,
|
||||
string? messageEffectId = null,
|
||||
string? businessConnectionId = null,
|
||||
bool allowPaidBroadcast = false,
|
||||
int? directMessageTopicId = null,
|
||||
SuggestedPostParameters? suggestedPostParameters = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> await container.Client.SendMessage(
|
||||
container.ActualUpdate.Chat, text, parseMode, replyParameters,
|
||||
replyMarkup, linkPreviewOptions,
|
||||
messageThreadId, entities,
|
||||
disableNotification, protectContent,
|
||||
messageEffectId, businessConnectionId,
|
||||
allowPaidBroadcast, directMessageTopicId,
|
||||
suggestedPostParameters, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Responnces to message that this CallbackQuery was originated from
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="parseMode"></param>
|
||||
/// <param name="replyParameters"></param>
|
||||
/// <param name="replyMarkup"></param>
|
||||
/// <param name="linkPreviewOptions"></param>
|
||||
/// <param name="messageThreadId"></param>
|
||||
/// <param name="entities"></param>
|
||||
/// <param name="disableNotification"></param>
|
||||
/// <param name="protectContent"></param>
|
||||
/// <param name="messageEffectId"></param>
|
||||
/// <param name="businessConnectionId"></param>
|
||||
/// <param name="allowPaidBroadcast"></param>
|
||||
/// <param name="directMessageTopicId"></param>
|
||||
/// <param name="suggestedPostParameters"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static async Task<Message> Responce(
|
||||
this IAbstractHandlerContainer<CallbackQuery> container,
|
||||
string text,
|
||||
ParseMode parseMode = ParseMode.None,
|
||||
ReplyParameters? replyParameters = null,
|
||||
ReplyMarkup? replyMarkup = null,
|
||||
LinkPreviewOptions? linkPreviewOptions = null,
|
||||
int? messageThreadId = null,
|
||||
IEnumerable<MessageEntity>? entities = null,
|
||||
bool disableNotification = false,
|
||||
bool protectContent = false,
|
||||
string? messageEffectId = null,
|
||||
string? businessConnectionId = null,
|
||||
bool allowPaidBroadcast = false,
|
||||
int? directMessageTopicId = null,
|
||||
SuggestedPostParameters? suggestedPostParameters = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
CallbackQuery query = container.ActualUpdate;
|
||||
if (query.Message == null)
|
||||
throw new Exception("Callback origin message not found!");
|
||||
|
||||
return await container.Client.SendMessage(
|
||||
query.Message.Chat, text, parseMode, replyParameters,
|
||||
replyMarkup, linkPreviewOptions,
|
||||
messageThreadId, entities,
|
||||
disableNotification, protectContent,
|
||||
messageEffectId, businessConnectionId,
|
||||
allowPaidBroadcast, directMessageTopicId,
|
||||
suggestedPostParameters, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edits message text that this CallbackQuery was originated from
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="parseMode"></param>
|
||||
/// <param name="replyMarkup"></param>
|
||||
/// <param name="entities"></param>
|
||||
/// <param name="linkPreviewOptions"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static async Task<Message> EditMessage(
|
||||
this IAbstractHandlerContainer<CallbackQuery> container,
|
||||
string text,
|
||||
ParseMode parseMode = ParseMode.None,
|
||||
InlineKeyboardMarkup? replyMarkup = null,
|
||||
IEnumerable<MessageEntity>? entities = null,
|
||||
LinkPreviewOptions? linkPreviewOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
CallbackQuery query = container.ActualUpdate;
|
||||
if (query.Message == null)
|
||||
throw new Exception("Callback origin message not found!");
|
||||
|
||||
return await container.Client.EditMessageText(
|
||||
query.Message.Chat,
|
||||
query.Message.MessageId,
|
||||
text: text,
|
||||
parseMode: parseMode,
|
||||
replyMarkup: replyMarkup,
|
||||
entities: entities,
|
||||
linkPreviewOptions: linkPreviewOptions,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this method to send answers to callback queries sent from <a href="https://core.telegram.org/bots/features#inline-keyboards">inline keyboards</a>.
|
||||
/// The answer will be displayed to the user as a notification at the top of the chat screen or as an alert
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alternatively, the user can be redirected to the specified Game URL.
|
||||
/// For this option to work, you must first create a game for your bot via <a href="https://t.me/botfather">@BotFather</a> and accept the terms.
|
||||
/// Otherwise, you may use links like <c>t.me/your_bot?start=XXXX</c> that open your bot with a parameter.
|
||||
/// </remarks>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="showAlert"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="cacheTime"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task AnswerCallbackQuery(
|
||||
this IAbstractHandlerContainer<CallbackQuery> container,
|
||||
string? text = null,
|
||||
bool showAlert = false,
|
||||
string? url = null,
|
||||
int cacheTime = 0,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> await container.Client.AnswerCallbackQuery(
|
||||
callbackQueryId: container.ActualUpdate.Id,
|
||||
text: text,
|
||||
showAlert: showAlert,
|
||||
url: url,
|
||||
cacheTime: cacheTime,
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Answers inline query
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="results"></param>
|
||||
/// <param name="cacheTime"></param>
|
||||
/// <param name="isPersonal"></param>
|
||||
/// <param name="nextOffset"></param>
|
||||
/// <param name="button"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task AnswerInlineQuery(
|
||||
this IAbstractHandlerContainer<InlineQuery> container,
|
||||
IEnumerable<InlineQueryResult> results,
|
||||
int? cacheTime = null,
|
||||
bool isPersonal = false,
|
||||
string? nextOffset = null,
|
||||
InlineQueryResultsButton? button = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
string id = container.ActualUpdate.Id;
|
||||
await container.Client.AnswerInlineQuery(id, results.Take(50), cacheTime, isPersonal, nextOffset, button, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.Handlers.Components;
|
||||
using Telegrator.Handlers.Diagnostics;
|
||||
using Telegrator.Logging;
|
||||
|
||||
namespace Telegrator.MadiatorCore.Descriptors
|
||||
|
||||
@@ -6,6 +6,7 @@ using Telegram.Bot.Types.Enums;
|
||||
using Telegrator.Configuration;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.Handlers.Components;
|
||||
using Telegrator.Handlers.Diagnostics;
|
||||
using Telegrator.Logging;
|
||||
using Telegrator.MadiatorCore;
|
||||
using Telegrator.MadiatorCore.Descriptors;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Telegrator.Aspects;
|
||||
using Telegrator.Handlers.Components;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.MadiatorCore;
|
||||
using Telegrator.Handlers.Diagnostics;
|
||||
|
||||
namespace Telegrator
|
||||
{
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reflection;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.Handlers.Components;
|
||||
using Telegrator.Providers;
|
||||
|
||||
namespace Telegrator
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for working with collections.
|
||||
/// </summary>
|
||||
public static partial class ColletionsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ReadOnlyDictionary{TKey, TValue}"/> from an <see cref="IEnumerable{TValue}"/>
|
||||
/// according to a specified key selector function.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="keySelector"></param>
|
||||
/// <returns></returns>
|
||||
public static ReadOnlyDictionary<TKey, TValue> ToReadOnlyDictionary<TKey, TValue>(this IEnumerable<TValue> source, Func<TValue, TKey> keySelector) where TKey : notnull
|
||||
{
|
||||
Dictionary<TKey, TValue> dictionary = source.ToDictionary(keySelector);
|
||||
return new ReadOnlyDictionary<TKey, TValue>(dictionary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates objects in a <paramref name="source"/> and executes an <paramref name="action"/> on each one
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<TValue> ForEach<TValue>(this IEnumerable<TValue> source, Action<TValue> action)
|
||||
{
|
||||
foreach (TValue value in source)
|
||||
action.Invoke(value);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a key in a dictionary, or if the key does not exist, adds it
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
public static void Set<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, TValue value)
|
||||
{
|
||||
if (source.ContainsKey(key))
|
||||
source[key] = value;
|
||||
else
|
||||
source.Add(key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a key in a dictionary, or if the key does not exist, adds its default value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="defaultValue"></param>
|
||||
public static void Set<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, TValue value, TValue defaultValue)
|
||||
{
|
||||
if (source.ContainsKey(key))
|
||||
source[key] = value;
|
||||
else
|
||||
source.Add(key, defaultValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the random object from <paramref name="source"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static TSource Random<TSource>(this IEnumerable<TSource> source)
|
||||
=> source.Random(new Random());
|
||||
|
||||
/// <summary>
|
||||
/// Return the random object from <paramref name="source"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="random"></param>
|
||||
/// <returns></returns>
|
||||
public static TSource Random<TSource>(this IEnumerable<TSource> source, Random random)
|
||||
=> source.ElementAt(random.Next(0, source.Count() - 1));
|
||||
|
||||
/// <summary>
|
||||
/// Adds a range of elements to collection if they dont already exist using default equality comparer
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <param name="list"></param>
|
||||
/// <param name="elements"></param>
|
||||
public static void UnionAdd<TSource>(this IList<TSource> list, params IEnumerable<TSource> elements)
|
||||
{
|
||||
foreach (TSource item in elements)
|
||||
{
|
||||
if (!list.Contains(item, EqualityComparer<TSource>.Default))
|
||||
list.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return index of first element that satisfies the condition
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
|
||||
{
|
||||
int index = 0;
|
||||
foreach (T item in source)
|
||||
{
|
||||
if (predicate.Invoke(item))
|
||||
return index;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerable that repeats the item multiple times.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="times"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> Repeat<T>(this T item, int times)
|
||||
=> Enumerable.Range(0, times).Select(_ => item);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the only element of a sequence, or a default value if the sequence is empty.
|
||||
/// This method returns default if there is more than one element in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static T? SingleSafe<T>(this IEnumerable<T> source)
|
||||
=> source.Count() == 1 ? source.ElementAt(0) : default;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the only element of a sequence that satisfies a specified condition or a default value if no such element exists.
|
||||
/// This method return default if more than one element satisfies the condition.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
public static T? SingleSafe<T>(this IEnumerable<T> source, Func<T, bool> predicate)
|
||||
{
|
||||
source = source.Where(predicate);
|
||||
return source.Count() == 1 ? source.ElementAt(0) : default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for reflection and type inspection.
|
||||
/// </summary>
|
||||
public static partial class ReflectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if a type implements the <see cref="ICustomDescriptorsProvider"/> interface.
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check.</param>
|
||||
/// <returns>True if the type implements ICustomDescriptorsProvider; otherwise, false.</returns>
|
||||
public static bool IsCustomDescriptorsProvider(this Type type)
|
||||
=> type.GetInterface(nameof(ICustomDescriptorsProvider)) != null;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if <paramref name="type"/> is a <see cref="IFilter{T}"/>
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsFilterType(this Type type)
|
||||
=> type.IsAssignableToGenericType(typeof(IFilter<>));
|
||||
|
||||
/// <summary>
|
||||
/// Checks if <paramref name="type"/> is a descendant of <see cref="UpdateHandlerBase"/> class
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsHandlerAbstract(this Type type)
|
||||
=> type.IsAbstract && typeof(UpdateHandlerBase).IsAssignableFrom(type);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if <paramref name="type"/> is an implementation of <see cref="UpdateHandlerBase"/> class or its descendants
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsHandlerRealization(this Type type)
|
||||
=> !type.IsAbstract && type != typeof(UpdateHandlerBase) && typeof(UpdateHandlerBase).IsAssignableFrom(type);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if <paramref name="type"/> has a parameterless constructor
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasParameterlessCtor(this Type type)
|
||||
=> type.GetConstructors().Any(ctor => ctor.GetParameters().Length == 0);
|
||||
|
||||
/// <summary>
|
||||
/// Checks is <paramref name="type"/> has public properties
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasPublicProperties(this Type type)
|
||||
=> type.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.Name != "IsCollectible").Any();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether an instance of a specified type can be assigned to an instance of the current type
|
||||
/// </summary>
|
||||
/// <param name="givenType"></param>
|
||||
/// <param name="genericType"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsAssignableToGenericType(this Type givenType, Type genericType)
|
||||
{
|
||||
if (givenType.GetInterfaces().Any(inter => inter.IsGenericType && inter.GetGenericTypeDefinition() == genericType))
|
||||
return true;
|
||||
|
||||
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
|
||||
return true;
|
||||
|
||||
if (givenType.BaseType == null)
|
||||
return false;
|
||||
|
||||
return givenType.BaseType.IsAssignableToGenericType(genericType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for string manipulation.
|
||||
/// </summary>
|
||||
public static partial class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Slices a <paramref name="source"/> string into a array of substrings of fixed <paramref name="length"/>
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<string> SliceBy(this string source, int length)
|
||||
{
|
||||
for (int start = 0; start < source.Length; start += length + 1)
|
||||
{
|
||||
int tillEnd = source.Length - start;
|
||||
int toSlice = tillEnd < length + 1 ? tillEnd : length + 1;
|
||||
|
||||
ReadOnlySpan<char> chunk = source.AsSpan().Slice(start, toSlice);
|
||||
yield return chunk.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return new string with first found letter set to upper case
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
public static string FirstLetterToUpper(this string target)
|
||||
{
|
||||
char[] chars = target.ToCharArray();
|
||||
int index = chars.IndexOf(char.IsLetter);
|
||||
chars[index] = char.ToUpper(chars[index]);
|
||||
return new string(chars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return new string with first found letter set to lower case
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
public static string FirstLetterToLower(this string target)
|
||||
{
|
||||
char[] chars = target.ToCharArray();
|
||||
int index = chars.IndexOf(char.IsLetter);
|
||||
chars[index] = char.ToLower(chars[index]);
|
||||
return new string(chars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if string contains a 'word'.
|
||||
/// 'Word' must be a separate member of the text, and not have any alphabetic characters next to it.
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="word"></param>
|
||||
/// <param name="comparison"></param>
|
||||
/// <param name="startIndex"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ContainsWord(this string source, string word, StringComparison comparison = StringComparison.InvariantCulture, int startIndex = 0)
|
||||
{
|
||||
int index = source.IndexOf(word, startIndex, comparison);
|
||||
if (index == -1)
|
||||
return false;
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
char prev = source[index - 1];
|
||||
if (char.IsLetter(prev))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index + word.Length < source.Length)
|
||||
{
|
||||
char post = source[index + word.Length];
|
||||
if (char.IsLetter(post))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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));
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reflection;
|
||||
using Telegram.Bot;
|
||||
using System.Reflection;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.InlineQueryResults;
|
||||
using Telegram.Bot.Types.Payments;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
using Telegrator.Annotations;
|
||||
using Telegrator.Annotations.StateKeeping;
|
||||
using Telegrator.Attributes;
|
||||
using Telegrator.Filters.Components;
|
||||
using Telegrator.Handlers;
|
||||
using Telegrator.Handlers.Building;
|
||||
using Telegrator.Handlers.Building.Components;
|
||||
using Telegrator.Handlers.Components;
|
||||
@@ -20,8 +12,6 @@ using Telegrator.MadiatorCore.Descriptors;
|
||||
using Telegrator.Providers;
|
||||
using Telegrator.StateKeeping;
|
||||
using Telegrator.StateKeeping.Abstracts;
|
||||
using Telegrator.StateKeeping.Components;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
|
||||
namespace Telegrator
|
||||
{
|
||||
@@ -171,282 +161,6 @@ namespace Telegrator
|
||||
=> StateKeeperAttribute<TKey, TState, TKeeper>.Shared;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides usefull helper methods for abstract handler containers
|
||||
/// </summary>
|
||||
public static class AbstractHandlerContainerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Changes bot's reaction to message
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="reaction"></param>
|
||||
/// <param name="isBig"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task React(
|
||||
this IAbstractHandlerContainer<Message> container,
|
||||
ReactionType reaction,
|
||||
bool isBig = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> await container.Client.SetMessageReaction(
|
||||
container.ActualUpdate.Chat,
|
||||
container.ActualUpdate.Id,
|
||||
[reaction], isBig, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Changes bot's reaction to message
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="reactions"></param>
|
||||
/// <param name="isBig"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task React(
|
||||
this IAbstractHandlerContainer<Message> container,
|
||||
IEnumerable<ReactionType> reactions,
|
||||
bool isBig = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> await container.Client.SetMessageReaction(
|
||||
container.ActualUpdate.Chat,
|
||||
container.ActualUpdate.Id,
|
||||
reactions, isBig, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a reply message to the current message.
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="text">The text of the message to send.</param>
|
||||
/// <param name="parseMode">The parse mode for the message text.</param>
|
||||
/// <param name="replyMarkup">The reply markup for the message.</param>
|
||||
/// <param name="linkPreviewOptions">Options for link preview generation.</param>
|
||||
/// <param name="messageThreadId">The thread ID for forum topics.</param>
|
||||
/// <param name="entities">The message entities to include.</param>
|
||||
/// <param name="disableNotification">Whether to disable notification for the message.</param>
|
||||
/// <param name="protectContent">Whether to protect the message content.</param>
|
||||
/// <param name="messageEffectId">The message effect ID.</param>
|
||||
/// <param name="businessConnectionId">The business connection ID.</param>
|
||||
/// <param name="allowPaidBroadcast">Whether to allow paid broadcast.</param>
|
||||
/// <param name="directMessageTopicId"></param>
|
||||
/// <param name="suggestedPostParameters"></param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The sent message.</returns>
|
||||
public static async Task<Message> Reply(
|
||||
this IAbstractHandlerContainer<Message> container,
|
||||
string text,
|
||||
ParseMode parseMode = ParseMode.None,
|
||||
ReplyMarkup? replyMarkup = null,
|
||||
LinkPreviewOptions? linkPreviewOptions = null,
|
||||
int? messageThreadId = null,
|
||||
IEnumerable<MessageEntity>? entities = null,
|
||||
bool disableNotification = false,
|
||||
bool protectContent = false,
|
||||
string? messageEffectId = null,
|
||||
string? businessConnectionId = null,
|
||||
bool allowPaidBroadcast = false,
|
||||
int? directMessageTopicId = null,
|
||||
SuggestedPostParameters? suggestedPostParameters = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> await container.Client.SendMessage(
|
||||
container.ActualUpdate.Chat, text, parseMode, container.ActualUpdate,
|
||||
replyMarkup, linkPreviewOptions,
|
||||
messageThreadId, entities,
|
||||
disableNotification, protectContent,
|
||||
messageEffectId, businessConnectionId,
|
||||
allowPaidBroadcast, directMessageTopicId,
|
||||
suggestedPostParameters, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a response message to the current chat.
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="text">The text of the message to send.</param>
|
||||
/// <param name="parseMode">The parse mode for the message text.</param>
|
||||
/// <param name="replyParameters">The reply parameters for the message.</param>
|
||||
/// <param name="replyMarkup">The reply markup for the message.</param>
|
||||
/// <param name="linkPreviewOptions">Options for link preview generation.</param>
|
||||
/// <param name="messageThreadId">The thread ID for forum topics.</param>
|
||||
/// <param name="entities">The message entities to include.</param>
|
||||
/// <param name="disableNotification">Whether to disable notification for the message.</param>
|
||||
/// <param name="protectContent">Whether to protect the message content.</param>
|
||||
/// <param name="messageEffectId">The message effect ID.</param>
|
||||
/// <param name="businessConnectionId">The business connection ID.</param>
|
||||
/// <param name="allowPaidBroadcast">Whether to allow paid broadcast.</param>
|
||||
/// <param name="directMessageTopicId"></param>
|
||||
/// <param name="suggestedPostParameters"></param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The sent message.</returns>
|
||||
public static async Task<Message> Responce(
|
||||
this IAbstractHandlerContainer<Message> container,
|
||||
string text,
|
||||
ParseMode parseMode = ParseMode.None,
|
||||
ReplyParameters? replyParameters = null,
|
||||
ReplyMarkup? replyMarkup = null,
|
||||
LinkPreviewOptions? linkPreviewOptions = null,
|
||||
int? messageThreadId = null,
|
||||
IEnumerable<MessageEntity>? entities = null,
|
||||
bool disableNotification = false,
|
||||
bool protectContent = false,
|
||||
string? messageEffectId = null,
|
||||
string? businessConnectionId = null,
|
||||
bool allowPaidBroadcast = false,
|
||||
int? directMessageTopicId = null,
|
||||
SuggestedPostParameters? suggestedPostParameters = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> await container.Client.SendMessage(
|
||||
container.ActualUpdate.Chat, text, parseMode, replyParameters,
|
||||
replyMarkup, linkPreviewOptions,
|
||||
messageThreadId, entities,
|
||||
disableNotification, protectContent,
|
||||
messageEffectId, businessConnectionId,
|
||||
allowPaidBroadcast, directMessageTopicId,
|
||||
suggestedPostParameters, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Responnces to message that this CallbackQuery was originated from
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="parseMode"></param>
|
||||
/// <param name="replyParameters"></param>
|
||||
/// <param name="replyMarkup"></param>
|
||||
/// <param name="linkPreviewOptions"></param>
|
||||
/// <param name="messageThreadId"></param>
|
||||
/// <param name="entities"></param>
|
||||
/// <param name="disableNotification"></param>
|
||||
/// <param name="protectContent"></param>
|
||||
/// <param name="messageEffectId"></param>
|
||||
/// <param name="businessConnectionId"></param>
|
||||
/// <param name="allowPaidBroadcast"></param>
|
||||
/// <param name="directMessageTopicId"></param>
|
||||
/// <param name="suggestedPostParameters"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static async Task<Message> Responce(
|
||||
this IAbstractHandlerContainer<CallbackQuery> container,
|
||||
string text,
|
||||
ParseMode parseMode = ParseMode.None,
|
||||
ReplyParameters? replyParameters = null,
|
||||
ReplyMarkup? replyMarkup = null,
|
||||
LinkPreviewOptions? linkPreviewOptions = null,
|
||||
int? messageThreadId = null,
|
||||
IEnumerable<MessageEntity>? entities = null,
|
||||
bool disableNotification = false,
|
||||
bool protectContent = false,
|
||||
string? messageEffectId = null,
|
||||
string? businessConnectionId = null,
|
||||
bool allowPaidBroadcast = false,
|
||||
int? directMessageTopicId = null,
|
||||
SuggestedPostParameters? suggestedPostParameters = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
CallbackQuery query = container.ActualUpdate;
|
||||
if (query.Message == null)
|
||||
throw new Exception("Callback origin message not found!");
|
||||
|
||||
return await container.Client.SendMessage(
|
||||
query.Message.Chat, text, parseMode, replyParameters,
|
||||
replyMarkup, linkPreviewOptions,
|
||||
messageThreadId, entities,
|
||||
disableNotification, protectContent,
|
||||
messageEffectId, businessConnectionId,
|
||||
allowPaidBroadcast, directMessageTopicId,
|
||||
suggestedPostParameters, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edits message text that this CallbackQuery was originated from
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="parseMode"></param>
|
||||
/// <param name="replyMarkup"></param>
|
||||
/// <param name="entities"></param>
|
||||
/// <param name="linkPreviewOptions"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static async Task<Message> EditMessage(
|
||||
this IAbstractHandlerContainer<CallbackQuery> container,
|
||||
string text,
|
||||
ParseMode parseMode = ParseMode.None,
|
||||
InlineKeyboardMarkup? replyMarkup = null,
|
||||
IEnumerable<MessageEntity>? entities = null,
|
||||
LinkPreviewOptions? linkPreviewOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
CallbackQuery query = container.ActualUpdate;
|
||||
if (query.Message == null)
|
||||
throw new Exception("Callback origin message not found!");
|
||||
|
||||
return await container.Client.EditMessageText(
|
||||
query.Message.Chat,
|
||||
query.Message.MessageId,
|
||||
text: text,
|
||||
parseMode: parseMode,
|
||||
replyMarkup: replyMarkup,
|
||||
entities: entities,
|
||||
linkPreviewOptions: linkPreviewOptions,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this method to send answers to callback queries sent from <a href="https://core.telegram.org/bots/features#inline-keyboards">inline keyboards</a>.
|
||||
/// The answer will be displayed to the user as a notification at the top of the chat screen or as an alert
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alternatively, the user can be redirected to the specified Game URL.
|
||||
/// For this option to work, you must first create a game for your bot via <a href="https://t.me/botfather">@BotFather</a> and accept the terms.
|
||||
/// Otherwise, you may use links like <c>t.me/your_bot?start=XXXX</c> that open your bot with a parameter.
|
||||
/// </remarks>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="showAlert"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="cacheTime"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task AnswerCallbackQuery(
|
||||
this IAbstractHandlerContainer<CallbackQuery> container,
|
||||
string? text = null,
|
||||
bool showAlert = false,
|
||||
string? url = null,
|
||||
int cacheTime = 0,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> await container.Client.AnswerCallbackQuery(
|
||||
callbackQueryId: container.ActualUpdate.Id,
|
||||
text: text,
|
||||
showAlert: showAlert,
|
||||
url: url,
|
||||
cacheTime: cacheTime,
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Answers inline query
|
||||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
/// <param name="results"></param>
|
||||
/// <param name="cacheTime"></param>
|
||||
/// <param name="isPersonal"></param>
|
||||
/// <param name="nextOffset"></param>
|
||||
/// <param name="button"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task AnswerInlineQuery(
|
||||
this IAbstractHandlerContainer<InlineQuery> container,
|
||||
IEnumerable<InlineQueryResult> results,
|
||||
int? cacheTime = null,
|
||||
bool isPersonal = false,
|
||||
string? nextOffset = null,
|
||||
InlineQueryResultsButton? button = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
string id = container.ActualUpdate.Id;
|
||||
await container.Client.AnswerInlineQuery(id, results.Take(50), cacheTime, isPersonal, nextOffset, button, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extensions methods for Awaiter Handler Builders
|
||||
/// </summary>
|
||||
@@ -673,620 +387,6 @@ namespace Telegrator
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for handler builders.
|
||||
/// Provides convenient methods for creating handlers and setting state keepers.
|
||||
/// </summary>
|
||||
public static partial class HandlerBuilderExtensions
|
||||
{
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetUpdateValidating(UpdateValidateAction)"/>
|
||||
public static TBuilder SetUpdateValidating<TBuilder>(this TBuilder handlerBuilder, UpdateValidateAction updateValidateAction)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetUpdateValidating(updateValidateAction);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetConcurreny(int)"/>
|
||||
public static TBuilder SetConcurreny<TBuilder>(this TBuilder handlerBuilder, int concurrency)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetConcurreny(concurrency);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetPriority(int)"/>
|
||||
public static TBuilder SetPriority<TBuilder>(this TBuilder handlerBuilder, int priority)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetPriority(priority);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetIndexer(int, int)"/>
|
||||
public static TBuilder SetIndexer<TBuilder>(this TBuilder handlerBuilder, int concurrency, int priority)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetIndexer(concurrency, priority);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.AddFilter(IFilter{Update})"/>
|
||||
public static TBuilder AddFilter<TBuilder>(this TBuilder handlerBuilder, IFilter<Update> filter)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.AddFilter(filter);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.AddFilters(IFilter{Update}[])"/>
|
||||
public static TBuilder AddFilters<TBuilder>(this TBuilder handlerBuilder, params IFilter<Update>[] filters)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.AddFilters(filters);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetStateKeeper{TKey, TState, TKeeper}(TState, IStateKeyResolver{TKey})"/>
|
||||
public static TBuilder SetStateKeeper<TBuilder, TKey, TState, TKeeper>(this TBuilder handlerBuilder, TState myState, IStateKeyResolver<TKey> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TKey : notnull
|
||||
where TState : IEquatable<TState>
|
||||
where TKeeper : StateKeeperBase<TKey, TState>, new()
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<TKey, TState, TKeeper>(myState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HandlerBuilderBase.SetStateKeeper{TKey, TState, TKeeper}(SpecialState, IStateKeyResolver{TKey})"/>
|
||||
public static TBuilder SetStateKeeper<TBuilder, TKey, TState, TKeeper>(this TBuilder handlerBuilder, SpecialState specialState, IStateKeyResolver<TKey> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TKey : notnull
|
||||
where TState : IEquatable<TState>
|
||||
where TKeeper : StateKeeperBase<TKey, TState>, new()
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<TKey, TState, TKeeper>(specialState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a targeted filter for a specific filter target type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder"></typeparam>
|
||||
/// <typeparam name="TFilterTarget">The type of the filter target.</typeparam>
|
||||
/// <param name="handlerBuilder"></param>
|
||||
/// <param name="getFilterringTarget">Function to get the filter target from an update.</param>
|
||||
/// <param name="filter">The filter to add.</param>
|
||||
/// <returns>The builder instance.</returns>
|
||||
public static TBuilder AddTargetedFilter<TBuilder, TFilterTarget>(this TBuilder handlerBuilder, Func<Update, TFilterTarget?> getFilterringTarget, IFilter<TFilterTarget> filter)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TFilterTarget : class
|
||||
{
|
||||
handlerBuilder.AddTargetedFilter(getFilterringTarget, filter);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple targeted filters for a specific filter target type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder"></typeparam>
|
||||
/// <typeparam name="TFilterTarget">The type of the filter target.</typeparam>
|
||||
/// <param name="handlerBuilder"></param>
|
||||
/// <param name="getFilterringTarget">Function to get the filter target from an update.</param>
|
||||
/// <param name="filters">The filters to add.</param>
|
||||
/// <returns>The builder instance.</returns>
|
||||
public static TBuilder AddTargetedFilters<TBuilder, TFilterTarget>(this TBuilder handlerBuilder, Func<Update, TFilterTarget?> getFilterringTarget, params IFilter<TFilterTarget>[] filters)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TFilterTarget : class
|
||||
{
|
||||
handlerBuilder.AddTargetedFilters(getFilterringTarget, filters);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a numeric state keeper with a custom key resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="myState">The numeric state value.</param>
|
||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, int myState, IStateKeyResolver<long> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(myState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a numeric state keeper with a special state and custom key resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="specialState">The special state value.</param>
|
||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, SpecialState specialState, IStateKeyResolver<long> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(specialState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a numeric state keeper with the default sender ID resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="myState">The numeric state value.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, int myState)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(myState, new SenderIdResolver());
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a numeric state keeper with a special state and the default sender ID resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="specialState">The special state value.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, SpecialState specialState)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(specialState, new SenderIdResolver());
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an enum state keeper with a custom key resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="myState">The enum state value.</param>
|
||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, TEnum myState, IStateKeyResolver<long> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TEnum : Enum, IEquatable<TEnum>
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(myState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an enum state keeper with a special state and custom key resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="specialState">The special state value.</param>
|
||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, SpecialState specialState, IStateKeyResolver<long> keyResolver)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TEnum : Enum, IEquatable<TEnum>
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(specialState, keyResolver);
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an enum state keeper with the default sender ID resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="myState">The enum state value.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, TEnum myState)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TEnum : Enum, IEquatable<TEnum>
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(myState, new SenderIdResolver());
|
||||
return handlerBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an enum state keeper with a special state and the default sender ID resolver.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
||||
/// <param name="handlerBuilder">The handler builder.</param>
|
||||
/// <param name="specialState">The special state value.</param>
|
||||
/// <returns>The handler builder for method chaining.</returns>
|
||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, SpecialState specialState)
|
||||
where TBuilder : HandlerBuilderBase
|
||||
where TEnum : Enum, IEquatable<TEnum>
|
||||
{
|
||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(specialState, new SenderIdResolver());
|
||||
return handlerBuilder;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for working with collections.
|
||||
/// </summary>
|
||||
public static partial class ColletionsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ReadOnlyDictionary{TKey, TValue}"/> from an <see cref="IEnumerable{TValue}"/>
|
||||
/// according to a specified key selector function.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="keySelector"></param>
|
||||
/// <returns></returns>
|
||||
public static ReadOnlyDictionary<TKey, TValue> ToReadOnlyDictionary<TKey, TValue>(this IEnumerable<TValue> source, Func<TValue, TKey> keySelector) where TKey : notnull
|
||||
{
|
||||
Dictionary<TKey, TValue> dictionary = source.ToDictionary(keySelector);
|
||||
return new ReadOnlyDictionary<TKey, TValue>(dictionary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates objects in a <paramref name="source"/> and executes an <paramref name="action"/> on each one
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<TValue> ForEach<TValue>(this IEnumerable<TValue> source, Action<TValue> action)
|
||||
{
|
||||
foreach (TValue value in source)
|
||||
action.Invoke(value);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a key in a dictionary, or if the key does not exist, adds it
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
public static void Set<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, TValue value)
|
||||
{
|
||||
if (source.ContainsKey(key))
|
||||
source[key] = value;
|
||||
else
|
||||
source.Add(key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a key in a dictionary, or if the key does not exist, adds its default value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="defaultValue"></param>
|
||||
public static void Set<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, TValue value, TValue defaultValue)
|
||||
{
|
||||
if (source.ContainsKey(key))
|
||||
source[key] = value;
|
||||
else
|
||||
source.Add(key, defaultValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the random object from <paramref name="source"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static TSource Random<TSource>(this IEnumerable<TSource> source)
|
||||
=> source.Random(new Random());
|
||||
|
||||
/// <summary>
|
||||
/// Return the random object from <paramref name="source"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="random"></param>
|
||||
/// <returns></returns>
|
||||
public static TSource Random<TSource>(this IEnumerable<TSource> source, Random random)
|
||||
=> source.ElementAt(random.Next(0, source.Count() - 1));
|
||||
|
||||
/// <summary>
|
||||
/// Adds a range of elements to collection if they dont already exist using default equality comparer
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource"></typeparam>
|
||||
/// <param name="list"></param>
|
||||
/// <param name="elements"></param>
|
||||
public static void UnionAdd<TSource>(this IList<TSource> list, params IEnumerable<TSource> elements)
|
||||
{
|
||||
foreach (TSource item in elements)
|
||||
{
|
||||
if (!list.Contains(item, EqualityComparer<TSource>.Default))
|
||||
list.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return index of first element that satisfies the condition
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
|
||||
{
|
||||
int index = 0;
|
||||
foreach (T item in source)
|
||||
{
|
||||
if (predicate.Invoke(item))
|
||||
return index;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerable that repeats the item multiple times.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="times"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> Repeat<T>(this T item, int times)
|
||||
=> Enumerable.Range(0, times).Select(_ => item);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the only element of a sequence, or a default value if the sequence is empty.
|
||||
/// This method returns default if there is more than one element in the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public static T? SingleSafe<T>(this IEnumerable<T> source)
|
||||
=> source.Count() == 1 ? source.ElementAt(0) : default;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the only element of a sequence that satisfies a specified condition or a default value if no such element exists.
|
||||
/// This method return default if more than one element satisfies the condition.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
public static T? SingleSafe<T>(this IEnumerable<T> source, Func<T, bool> predicate)
|
||||
{
|
||||
source = source.Where(predicate);
|
||||
return source.Count() == 1 ? source.ElementAt(0) : default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for reflection and type inspection.
|
||||
/// </summary>
|
||||
public static partial class ReflectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if a type implements the <see cref="ICustomDescriptorsProvider"/> interface.
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check.</param>
|
||||
/// <returns>True if the type implements ICustomDescriptorsProvider; otherwise, false.</returns>
|
||||
public static bool IsCustomDescriptorsProvider(this Type type)
|
||||
=> type.GetInterface(nameof(ICustomDescriptorsProvider)) != null;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if <paramref name="type"/> is a <see cref="IFilter{T}"/>
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsFilterType(this Type type)
|
||||
=> type.IsAssignableToGenericType(typeof(IFilter<>));
|
||||
|
||||
/// <summary>
|
||||
/// Checks if <paramref name="type"/> is a descendant of <see cref="UpdateHandlerBase"/> class
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsHandlerAbstract(this Type type)
|
||||
=> type.IsAbstract && typeof(UpdateHandlerBase).IsAssignableFrom(type);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if <paramref name="type"/> is an implementation of <see cref="UpdateHandlerBase"/> class or its descendants
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsHandlerRealization(this Type type)
|
||||
=> !type.IsAbstract && type != typeof(UpdateHandlerBase) && typeof(UpdateHandlerBase).IsAssignableFrom(type);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if <paramref name="type"/> has a parameterless constructor
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasParameterlessCtor(this Type type)
|
||||
=> type.GetConstructors().Any(ctor => ctor.GetParameters().Length == 0);
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Invokes a "<paramref name="methodName"/>" method of an object
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public static object? InvokeMethod(this object obj, string methodName, params object[]? args)
|
||||
=> obj.GetType().GetMethod(methodName, BindAll).InvokeMethod(obj, args);
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a method of <paramref name="methodInfo"/>
|
||||
/// </summary>
|
||||
/// <param name="methodInfo"></param>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public static object? InvokeMethod(this MethodInfo methodInfo, object obj, params object[]? args)
|
||||
=> methodInfo.Invoke(obj, args);
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a static method of <paramref name="methodInfo"/>
|
||||
/// </summary>
|
||||
/// <param name="methodInfo"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public static object? InvokeStaticMethod(this MethodInfo methodInfo, params object[]? parameters)
|
||||
=> methodInfo.Invoke(null, parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a static "<paramref name="methodName"/>" method of an object
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public static T? InvokeStaticMethod<T>(this object obj, string methodName, params object[]? parameters)
|
||||
=> (T?)obj.GetType().GetMethod(methodName, BindAll).InvokeStaticMethod(parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a generic method of <paramref name="methodInfo"/> with generic types in <paramref name="genericParameters"/>
|
||||
/// </summary>
|
||||
/// <param name="methodInfo"></param>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="genericParameters"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public static object InvokeGenericMethod(this MethodInfo methodInfo, object obj, Type[] genericParameters, params object[]? parameters)
|
||||
=> methodInfo.MakeGenericMethod(genericParameters).Invoke(obj, parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a generic <paramref name="methodName"/> method with generic types in <paramref name="genericParameters"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="genericParameters"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public static T? InvokeGenericMethod<T>(this object obj, string methodName, Type[] genericParameters, params object[]? parameters)
|
||||
=> (T?)obj.GetType().GetMethod(methodName).InvokeGenericMethod(obj, genericParameters, parameters);
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Checks is <paramref name="type"/> has public properties
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasPublicProperties(this Type type)
|
||||
=> type.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.Name != "IsCollectible").Any();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether an instance of a specified type can be assigned to an instance of the current type
|
||||
/// </summary>
|
||||
/// <param name="givenType"></param>
|
||||
/// <param name="genericType"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsAssignableToGenericType(this Type givenType, Type genericType)
|
||||
{
|
||||
if (givenType.GetInterfaces().Any(inter => inter.IsGenericType && inter.GetGenericTypeDefinition() == genericType))
|
||||
return true;
|
||||
|
||||
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
|
||||
return true;
|
||||
|
||||
if (givenType.BaseType == null)
|
||||
return false;
|
||||
|
||||
return givenType.BaseType.IsAssignableToGenericType(genericType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for string manipulation.
|
||||
/// </summary>
|
||||
public static partial class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Slices a <paramref name="source"/> string into a array of substrings of fixed <paramref name="length"/>
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<string> SliceBy(this string source, int length)
|
||||
{
|
||||
for (int start = 0; start < source.Length; start += length + 1)
|
||||
{
|
||||
int tillEnd = source.Length - start;
|
||||
int toSlice = tillEnd < length + 1 ? tillEnd : length + 1;
|
||||
|
||||
ReadOnlySpan<char> chunk = source.AsSpan().Slice(start, toSlice);
|
||||
yield return chunk.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return new string with first found letter set to upper case
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
public static string FirstLetterToUpper(this string target)
|
||||
{
|
||||
char[] chars = target.ToCharArray();
|
||||
int index = chars.IndexOf(char.IsLetter);
|
||||
chars[index] = char.ToUpper(chars[index]);
|
||||
return new string(chars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return new string with first found letter set to lower case
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
public static string FirstLetterToLower(this string target)
|
||||
{
|
||||
char[] chars = target.ToCharArray();
|
||||
int index = chars.IndexOf(char.IsLetter);
|
||||
chars[index] = char.ToLower(chars[index]);
|
||||
return new string(chars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if string contains a 'word'.
|
||||
/// 'Word' must be a separate member of the text, and not have any alphabetic characters next to it.
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="word"></param>
|
||||
/// <param name="comparison"></param>
|
||||
/// <param name="startIndex"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ContainsWord(this string source, string word, StringComparison comparison = StringComparison.InvariantCulture, int startIndex = 0)
|
||||
{
|
||||
int index = source.IndexOf(word, startIndex, comparison);
|
||||
if (index == -1)
|
||||
return false;
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
char prev = source[index - 1];
|
||||
if (char.IsLetter(prev))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index + word.Length < source.Length)
|
||||
{
|
||||
char post = source[index + word.Length];
|
||||
if (char.IsLetter(post))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for working with Telegram Update objects.
|
||||
/// </summary>
|
||||
@@ -1509,29 +609,4 @@ 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