Files
Telegrator/Telegrator/Providers/HandlersCollection.cs
T
Rikitav 92df5e9c79 * hotfix of aspects inspection
* Added IHandlersManager. interface that allows both collecting and providing handlers
2025-08-19 12:10:40 +04:00

135 lines
5.5 KiB
C#

using System.Reflection;
using Telegram.Bot.Types.Enums;
using Telegrator.Annotations;
using Telegrator.Attributes;
using Telegrator.Configuration;
using Telegrator.Handlers.Components;
using Telegrator.MadiatorCore;
using Telegrator.MadiatorCore.Descriptors;
namespace Telegrator.Providers
{
/// <summary>
/// Collection class for managing handler descriptors organized by update type.
/// Provides functionality for collecting, adding, and organizing handlers.
/// </summary>
/// <param name="options">Optional configuration options for handler collecting.</param>
public class HandlersCollection(ITelegratorOptions? options) : IHandlersCollection
{
/// <summary>
/// Gets the collection of <see cref="UpdateType"/>'s allowed by registered handlers
/// </summary>
protected readonly List<UpdateType> _allowedTypes = [];
/// <summary>
/// Dictionary that organizes handler descriptors by update type.
/// </summary>
protected readonly Dictionary<UpdateType, HandlerDescriptorList> InnerDictionary = [];
/// <summary>
/// Configuration options for handler collecting.
/// </summary>
protected readonly ITelegratorOptions? Options = options;
/// <summary>
/// Gets whether handlers must have a parameterless constructor.
/// </summary>
protected virtual bool MustHaveParameterlessCtor => true;
/// <summary>
/// List of command aliases that have been registered.
/// </summary>
public readonly List<string> CommandAliasses = [];
/// <inheritdoc/>
public IEnumerable<UpdateType> AllowedTypes => _allowedTypes;
/// <inheritdoc/>
public IEnumerable<UpdateType> Keys
{
get => InnerDictionary.Keys;
}
/// <inheritdoc/>
public IEnumerable<HandlerDescriptorList> Values
{
get => InnerDictionary.Values;
}
/// <inheritdoc/>
public HandlerDescriptorList this[UpdateType updateType]
{
get => InnerDictionary[updateType];
}
/// <summary>
/// Adds a handler descriptor to the collection.
/// </summary>
/// <param name="descriptor">The handler descriptor to add.</param>
/// <returns>This collection instance for method chaining.</returns>
/// <exception cref="Exception">Thrown when the handler type doesn't have a parameterless constructor and MustHaveParameterlessCtor is true.</exception>
public virtual IHandlersCollection AddDescriptor(HandlerDescriptor descriptor)
{
if (MustHaveParameterlessCtor && !descriptor.HandlerType.HasParameterlessCtor())
throw new Exception("This handler (" + descriptor.HandlerType.FullName + "), must contain constructor without parameters.");
_allowedTypes.UnionAdd(descriptor.UpdateType);
MightAwaitAttribute? mightAwait = descriptor.HandlerType.GetCustomAttribute<MightAwaitAttribute>();
if (mightAwait != null)
_allowedTypes.UnionAdd(mightAwait.UpdateTypes);
IntersectCommands(descriptor);
HandlerDescriptorList list = GetDescriptorList(descriptor);
if (descriptor.UpdateType == UpdateType.InlineQuery || descriptor.UpdateType == UpdateType.ChosenInlineResult)
{
if (list.Count > 0)
throw new Exception("Bot cannot have more than one InlineQuery handler");
}
list.Add(descriptor);
return this;
}
/// <summary>
/// Gets the <see cref="HandlerDescriptorList"/> for the specified <see cref="HandlerDescriptor"/>.
/// </summary>
/// <param name="descriptor">The handler descriptor.</param>
/// <returns>The handler descriptor list containing the descriptor.</returns>
protected virtual HandlerDescriptorList GetDescriptorList(HandlerDescriptor descriptor)
{
UpdateType updateType = UpdateTypeExtensions.SuppressTypes.TryGetValue(descriptor.UpdateType, out UpdateType suppressType)
? suppressType
: descriptor.UpdateType;
if (!InnerDictionary.TryGetValue(updateType, out HandlerDescriptorList? list))
{
list = new HandlerDescriptorList(updateType, Options);
InnerDictionary.Add(updateType, list);
}
return list;
}
/// <summary>
/// Checks for intersecting command aliases and handles them according to configuration.
/// </summary>
/// <param name="descriptor">The handler descriptor to check for command aliases.</param>
/// <exception cref="Exception">Thrown when intersecting command aliases are found and ExceptIntersectingCommandAliases is enabled.</exception>
protected void IntersectCommands(HandlerDescriptor descriptor)
{
if (Options == null)
return;
CommandAlliasAttribute? alliasAttribute = descriptor.HandlerType.GetCustomAttribute<CommandAlliasAttribute>();
if (alliasAttribute == null)
return;
if (Options.ExceptIntersectingCommandAliases && CommandAliasses.Intersect(alliasAttribute.Alliases, StringComparer.InvariantCultureIgnoreCase).Any())
throw new Exception(descriptor.HandlerType.FullName);
CommandAliasses.AddRange(alliasAttribute.Alliases);
}
}
}