* Added missing summaries

This commit is contained in:
2026-03-07 00:17:31 +04:00
parent 1ebf4ec22a
commit 3cdc058fb5
18 changed files with 1045 additions and 964 deletions
@@ -8,3 +8,4 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Style", "IDE0290")]
[assembly: SuppressMessage("Style", "IDE0090")]
[assembly: SuppressMessage("Usage", "CA2254")]
[assembly: SuppressMessage("Maintainability", "CA1510")]
@@ -49,11 +49,12 @@ namespace Telegrator.Hosting.Web
/// Initializes a new instance of the <see cref="WebApplicationBuilder"/> class.
/// </summary>
/// <param name="webApplicationBuilder">The proxied instance of host builder.</param>
/// <param name="handlers"></param>
public TelegramBotWebHost(WebApplicationBuilder webApplicationBuilder, IHandlersCollection handlers)
public TelegramBotWebHost(WebApplicationBuilder webApplicationBuilder)
{
// Registering this host in services for easy access
RegisterHostServices(webApplicationBuilder.Services, handlers);
webApplicationBuilder.Services.AddSingleton<ITelegramBotHost>(this);
webApplicationBuilder.Services.AddSingleton<ITelegramBotWebHost>(this);
webApplicationBuilder.Services.AddSingleton<ITelegratorBot>(this);
// Building proxy application
_innerApp = webApplicationBuilder.Build();
@@ -161,15 +162,5 @@ namespace Telegrator.Hosting.Web
GC.SuppressFinalize(this);
_disposed = true;
}
private void RegisterHostServices(IServiceCollection services, IHandlersCollection handlers)
{
//service.RemoveAll<IHost>();
//service.AddSingleton<IHost>(this);
services.AddSingleton<ITelegramBotHost>(this);
services.AddSingleton<ITelegramBotWebHost>(this);
services.AddSingleton<ITelegratorBot>(this);
}
}
}
@@ -46,6 +46,8 @@ namespace Telegrator.Hosting.Web
_innerBuilder = webApplicationBuilder ?? throw new ArgumentNullException(nameof(webApplicationBuilder));
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_handlers = new HostHandlersCollection(Services, _settings);
_innerBuilder.AddTelegratorWeb(settings);
}
/// <summary>
@@ -60,7 +62,7 @@ namespace Telegrator.Hosting.Web
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_handlers = handlers ?? throw new ArgumentNullException(nameof(settings));
_innerBuilder.AddTelegratorWeb(settings);
_innerBuilder.AddTelegratorWeb(settings, handlers);
}
/// <summary>
@@ -69,38 +71,7 @@ namespace Telegrator.Hosting.Web
/// <returns></returns>
public TelegramBotWebHost Build()
{
if (_handlers is IHostHandlersCollection hostHandlers)
{
foreach (PreBuildingRoutine preBuildRoutine in hostHandlers.PreBuilderRoutines)
{
try
{
preBuildRoutine.Invoke(this);
}
catch (NotImplementedException)
{
_ = 0xBAD + 0xC0DE;
}
}
}
if (!_settings.DisableAutoConfigure)
{
Services.Configure<TelegratorWebOptions>(Configuration.GetSection(nameof(TelegratorWebOptions)));
Services.Configure<TelegramBotClientOptions>(Configuration.GetSection(nameof(TelegramBotClientOptions)), new TelegramBotClientOptionsProxy());
}
else
{
if (null == Services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<TelegratorWebOptions>)))
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegratorWebOptions' wasn't registered. This configuration is runtime required!");
if (null == Services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<TelegramBotClientOptions>)))
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegramBotClientOptions' wasn't registered. This configuration is runtime required!");
}
Services.AddSingleton<IConfigurationManager>(Configuration);
Services.AddSingleton<IOptions<TelegratorOptions>>(Options.Create(_settings));
return new TelegramBotWebHost(_innerBuilder, _handlers);
return new TelegramBotWebHost(_innerBuilder);
}
}
}
+22 -2
View File
@@ -2,6 +2,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Diagnostics;
@@ -20,6 +21,19 @@ namespace Telegrator
/// </summary>
public static class ServicesCollectionExtensions
{
/// <summary>
/// The key used to store the <see cref="IHandlersCollection"/> in the builder properties.
/// </summary>
public const string HandlersCollectionPropertyKey = nameof(IHandlersCollection);
extension(IHostApplicationBuilder builder)
{
/// <summary>
/// Gets the <see cref="IHandlersCollection"/> from the builder properties.
/// </summary>
public IHandlersCollection Handlers => (IHandlersCollection)builder.Properties[HandlersCollectionPropertyKey];
}
/// <summary>
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
/// </summary>
@@ -32,6 +46,7 @@ namespace Telegrator
ConfigurationManager configuration = builder.Configuration;
handlers ??= new HostHandlersCollection(services, settings);
builder.Host.Properties.Add(HandlersCollectionPropertyKey, handlers);
if (handlers is IHostHandlersCollection hostHandlers)
{
@@ -53,6 +68,7 @@ namespace Telegrator
if (!settings.DisableAutoConfigure)
{
services.Configure<TelegratorWebOptions>(configuration.GetSection(nameof(TelegratorWebOptions)));
services.Configure<TelegratorWebOptions>(configuration.GetSection(nameof(TelegramBotClientOptions)));
}
else
{
@@ -92,8 +108,12 @@ namespace Telegrator
ILoggerFactory loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
ILogger logger = loggerFactory.CreateLogger("Telegrator.Hosting.Web.TelegratorHost");
logger.LogInformation("Telegrator Bot ASP.NET WebHost started");
logger.LogHandlers(handlers);
if (logger.IsEnabled(LogLevel.Information))
{
logger.LogInformation("Telegrator Bot ASP.NET WebHost started");
logger.LogInformation("Telegram Bot : {firstname}, @{usrname}, id:{id},", info.User.FirstName ?? "[NULL]", info.User.Username ?? "[NULL]", info.User.Id);
logger.LogHandlers(handlers);
}
return app;
}
@@ -8,3 +8,4 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Style", "IDE0290")]
[assembly: SuppressMessage("Style", "IDE0090")]
[assembly: SuppressMessage("Usage", "CA2254")]
[assembly: SuppressMessage("Maintainability", "CA1510")]
@@ -36,7 +36,8 @@ namespace Telegrator.Hosting
public TelegramBotHost(HostApplicationBuilder hostApplicationBuilder, IHandlersCollection handlers)
{
// Registering this host in services for easy access
RegisterHostServices(hostApplicationBuilder.Services, handlers);
hostApplicationBuilder.Services.AddSingleton<ITelegramBotHost>(this);
hostApplicationBuilder.Services.AddSingleton<ITelegratorBot>(this);
// Building proxy hoster
_innerHost = hostApplicationBuilder.Build();
@@ -122,14 +123,5 @@ namespace Telegrator.Hosting
GC.SuppressFinalize(this);
_disposed = true;
}
private void RegisterHostServices(IServiceCollection services, IHandlersCollection handlers)
{
//services.RemoveAll<IHost>();
//services.AddSingleton<IHost>(this);
services.AddSingleton<ITelegramBotHost>(this);
services.AddSingleton<ITelegratorBot>(this);
}
}
}
+232 -203
View File
@@ -21,228 +21,257 @@ using Telegrator.Logging;
using Telegrator.Polling;
using Telegrator.Providers;
namespace Telegrator
namespace Telegrator;
/// <summary>
/// Provides extension methods for <see cref="IHostApplicationBuilder"/> to configure Telegrator.
/// </summary>
public static class HostBuilderExtensions
{
public static class HostBuilderExtensions
/// <summary>
/// The key used to store the <see cref="IHandlersCollection"/> in the builder properties.
/// </summary>
public const string HandlersCollectionPropertyKey = nameof(IHandlersCollection);
extension (IHostApplicationBuilder builder)
{
/// <summary>
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
/// Gets the <see cref="IHandlersCollection"/> from the builder properties.
/// </summary>
public static IHostApplicationBuilder AddTelegrator(this IHostApplicationBuilder builder, TelegramBotHostBuilderSettings settings, IHandlersCollection? handlers = null)
public IHandlersCollection Handlers => (IHandlersCollection)builder.Properties[HandlersCollectionPropertyKey];
}
/// <summary>
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
/// </summary>
public static IHostApplicationBuilder AddTelegrator(this IHostApplicationBuilder builder, TelegramBotHostBuilderSettings settings, IHandlersCollection? handlers = null)
{
if (settings is null)
throw new ArgumentNullException(nameof(settings));
IServiceCollection services = builder.Services;
IConfigurationManager configuration = builder.Configuration;
handlers ??= new HostHandlersCollection(services, settings);
builder.Properties.Add(HandlersCollectionPropertyKey, handlers);
if (handlers is IHostHandlersCollection hostHandlers)
{
if (settings is null)
throw new ArgumentNullException(nameof(settings));
IServiceCollection services = builder.Services;
IConfigurationManager configuration = builder.Configuration;
handlers ??= new HostHandlersCollection(services, settings);
if (handlers is IHostHandlersCollection hostHandlers)
foreach (PreBuildingRoutine preBuildRoutine in hostHandlers.PreBuilderRoutines)
{
foreach (PreBuildingRoutine preBuildRoutine in hostHandlers.PreBuilderRoutines)
try
{
try
{
// TODO: fix
//preBuildRoutine.Invoke(builder);
Debug.WriteLine("Pre-Building routine was not executed");
}
catch (NotImplementedException)
{
_ = 0xBAD + 0xC0DE;
}
// TODO: fix
//preBuildRoutine.Invoke(builder);
Debug.WriteLine("Pre-Building routine was not executed");
}
catch (NotImplementedException)
{
_ = 0xBAD + 0xC0DE;
}
}
if (!settings.DisableAutoConfigure)
{
services.Configure<ReceiverOptions>(configuration.GetSection(nameof(ReceiverOptions)));
services.Configure(configuration.GetSection(nameof(TelegramBotClientOptions)), new TelegramBotClientOptionsProxy());
}
else
{
/*
if (null == Services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<ReceiverOptions>)))
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'ReceiverOptions' wasn't registered. This configuration is runtime required!");
*/
if (null == services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<TelegramBotClientOptions>)))
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegramBotClientOptions' wasn't registered. This configuration is runtime required!");
}
IOptions<TelegramBotHostBuilderSettings> options = Options.Create(settings);
services.AddSingleton((IOptions<TelegratorOptions>)options);
services.AddTelegramBotHostDefaults();
services.AddSingleton(options);
services.AddSingleton(handlers);
if (handlers is IHandlersManager manager)
{
ServiceDescriptor descriptor = new ServiceDescriptor(typeof(IHandlersProvider), manager);
services.Replace(descriptor);
services.AddSingleton(manager);
}
return builder;
}
}
/// <summary>
/// Contains extensions for <see cref="IServiceCollection"/>
/// Provides method to configure <see cref="ITelegramBotHost"/>
/// </summary>
public static class ServicesCollectionExtensions
{
/// <summary>
/// Registers a configuration instance that strongly-typed <typeparamref name="TOptions"/> will bind against using <see cref="ConfigureOptionsProxy{TOptions}"/>.
/// </summary>
/// <typeparam name="TOptions"></typeparam>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <param name="optionsProxy"></param>
/// <returns></returns>
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration configuration, ConfigureOptionsProxy<TOptions> optionsProxy) where TOptions : class
if (!settings.DisableAutoConfigure)
{
optionsProxy.Configure(services, configuration);
return services;
services.Configure<ReceiverOptions>(configuration.GetSection(nameof(ReceiverOptions)));
services.Configure(configuration.GetSection(nameof(TelegramBotClientOptions)), new TelegramBotClientOptionsProxy());
}
/// <summary>
/// Registers <see cref="TelegramBotHost"/> default services
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddTelegramBotHostDefaults(this IServiceCollection services)
else
{
services.AddLogging(builder => builder.AddConsole().AddDebug());
services.AddSingleton<IUpdateHandlersPool, HostUpdateHandlersPool>();
services.AddSingleton<IAwaitingProvider, HostAwaitingProvider>();
services.AddSingleton<IHandlersProvider, HostHandlersProvider>();
services.AddSingleton<IUpdateRouter, HostUpdateRouter>();
services.AddSingleton<ITelegramBotInfo, HostedTelegramBotInfo>();
if (null == services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<ReceiverOptions>)))
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'ReceiverOptions' wasn't registered. This configuration is runtime required!");
return services;
if (null == services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<TelegramBotClientOptions>)))
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegramBotClientOptions' wasn't registered. This configuration is runtime required!");
}
/// <summary>
/// Registers <see cref="ITelegramBotClient"/> service with <see cref="HostedUpdateReceiver"/> to receive updates using long polling
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddTelegramReceiver(this IServiceCollection services)
IOptions<TelegramBotHostBuilderSettings> options = Options.Create(settings);
services.AddSingleton((IOptions<TelegratorOptions>)options);
services.AddTelegramBotHostDefaults();
services.AddSingleton(options);
services.AddSingleton(handlers);
if (handlers is IHandlersManager manager)
{
services.AddHttpClient<ITelegramBotClient>("tgreceiver").RemoveAllLoggers().AddTypedClient(TypedTelegramBotClientFactory);
services.AddHostedService<HostedUpdateReceiver>();
return services;
ServiceDescriptor descriptor = new ServiceDescriptor(typeof(IHandlersProvider), manager);
services.Replace(descriptor);
services.AddSingleton(manager);
}
/// <summary>
/// <see cref="ITelegramBotClient"/> factory method
/// </summary>
/// <param name="httpClient"></param>
/// <param name="provider"></param>
/// <returns></returns>
private static ITelegramBotClient TypedTelegramBotClientFactory(HttpClient httpClient, IServiceProvider provider)
=> new TelegramBotClient(provider.GetRequiredService<IOptions<TelegramBotClientOptions>>().Value, httpClient);
}
/// <summary>
/// Provides useful methods to adjust <see cref="ITelegramBotHost"/>
/// </summary>
public static class TelegramBotHostExtensions
{
/// <summary>
/// Replaces the initialization logic from TelegramBotWebHost constructor.
/// Initializes the bot and logs handlers on application startup.
/// </summary>
public static IHost UseTelegrator(this IHost botHost)
{
ITelegramBotInfo info = botHost.Services.GetRequiredService<ITelegramBotInfo>();
IHandlersCollection handlers = botHost.Services.GetRequiredService<IHandlersCollection>();
ILoggerFactory loggerFactory = botHost.Services.GetRequiredService<ILoggerFactory>();
ILogger logger = loggerFactory.CreateLogger("Telegrator.Hosting.Web.TelegratorHost");
logger.LogInformation("Telegrator Bot .NET Host started");
logger.LogHandlers(handlers);
return botHost;
}
/// <summary>
/// Configures bots available commands depending on what handlers was registered
/// </summary>
/// <param name="botHost"></param>
/// <returns></returns>
public static IHost SetBotCommands(this IHost botHost)
{
ITelegramBotClient client = botHost.Services.GetRequiredService<ITelegramBotClient>();
IUpdateRouter router = botHost.Services.GetRequiredService<IUpdateRouter>();
IEnumerable<BotCommand> aliases = router.HandlersProvider.GetBotCommands();
client.SetMyCommands(aliases).Wait();
return botHost;
}
/// <summary>
/// Adds a Microsoft.Extensions.Logging adapter to Alligator using a logger factory.
/// </summary>
/// <param name="host"></param>
public static IHost AddLoggingAdapter(this IHost host)
{
ILoggerFactory loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
ILogger logger = loggerFactory.CreateLogger("Telegrator");
MicrosoftLoggingAdapter adapter = new MicrosoftLoggingAdapter(logger);
TelegratorLogging.AddAdapter(adapter);
return host;
}
}
/// <summary>
/// Provides extension methods for reflection and type inspection.
/// </summary>
public static class ReflectionExtensions
{
/// <summary>
/// Checks if a type implements the <see cref="IPreBuildingRoutine"/> interface.
/// </summary>
/// <param name="handlerType">The type to check.</param>
/// <param name="routineMethod"></param>
/// <returns>True if the type implements IPreBuildingRoutine; otherwise, false.</returns>
public static bool IsPreBuildingRoutine(this Type handlerType, [NotNullWhen(true)] out MethodInfo? routineMethod)
{
routineMethod = null;
if (handlerType.GetInterface(nameof(IPreBuildingRoutine)) == null)
return false;
routineMethod = handlerType.GetMethod(nameof(IPreBuildingRoutine.PreBuildingRoutine), BindingFlags.Static | BindingFlags.Public);
return routineMethod != null;
}
}
public static class LoggerExtensions
{
public static void LogHandlers(this ILogger logger, IHandlersCollection handlers)
{
StringBuilder logBuilder = new StringBuilder("Registered handlers : ");
if (!handlers.Keys.Any())
throw new Exception();
foreach (UpdateType updateType in handlers.Keys)
{
HandlerDescriptorList descriptors = handlers[updateType];
logBuilder.Append("\n\tUpdateType." + updateType + " :");
foreach (HandlerDescriptor descriptor in descriptors.Reverse())
{
logBuilder.AppendFormat("\n\t* {0} - {1}",
descriptor.Indexer.ToString(),
descriptor.ToString());
}
}
logger.LogInformation(logBuilder.ToString());
}
return builder;
}
}
/// <summary>
/// Contains extensions for <see cref="IServiceCollection"/>
/// Provides method to configure <see cref="ITelegramBotHost"/>
/// </summary>
public static class ServicesCollectionExtensions
{
/// <summary>
/// Registers a configuration instance that strongly-typed <typeparamref name="TOptions"/> will bind against using <see cref="ConfigureOptionsProxy{TOptions}"/>.
/// </summary>
/// <typeparam name="TOptions"></typeparam>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <param name="optionsProxy"></param>
/// <returns></returns>
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration configuration, ConfigureOptionsProxy<TOptions> optionsProxy) where TOptions : class
{
optionsProxy.Configure(services, configuration);
return services;
}
/// <summary>
/// Registers <see cref="TelegramBotHost"/> default services
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddTelegramBotHostDefaults(this IServiceCollection services)
{
services.AddLogging(builder => builder.AddConsole().AddDebug());
services.AddSingleton<IUpdateHandlersPool, HostUpdateHandlersPool>();
services.AddSingleton<IAwaitingProvider, HostAwaitingProvider>();
services.AddSingleton<IHandlersProvider, HostHandlersProvider>();
services.AddSingleton<IUpdateRouter, HostUpdateRouter>();
services.AddSingleton<ITelegramBotInfo, HostedTelegramBotInfo>();
return services;
}
/// <summary>
/// Registers <see cref="ITelegramBotClient"/> service with <see cref="HostedUpdateReceiver"/> to receive updates using long polling
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddTelegramReceiver(this IServiceCollection services)
{
services.AddHttpClient<ITelegramBotClient>("tgreceiver").RemoveAllLoggers().AddTypedClient(TypedTelegramBotClientFactory);
services.AddHostedService<HostedUpdateReceiver>();
return services;
}
/// <summary>
/// <see cref="ITelegramBotClient"/> factory method
/// </summary>
/// <param name="httpClient"></param>
/// <param name="provider"></param>
/// <returns></returns>
private static ITelegramBotClient TypedTelegramBotClientFactory(HttpClient httpClient, IServiceProvider provider)
=> new TelegramBotClient(provider.GetRequiredService<IOptions<TelegramBotClientOptions>>().Value, httpClient);
}
/// <summary>
/// Provides useful methods to adjust <see cref="ITelegramBotHost"/>
/// </summary>
public static class TelegramBotHostExtensions
{
/// <summary>
/// Replaces the initialization logic from TelegramBotWebHost constructor.
/// Initializes the bot and logs handlers on application startup.
/// </summary>
public static IHost UseTelegrator(this IHost botHost)
{
ITelegramBotInfo info = botHost.Services.GetRequiredService<ITelegramBotInfo>();
IHandlersCollection handlers = botHost.Services.GetRequiredService<IHandlersCollection>();
ILoggerFactory loggerFactory = botHost.Services.GetRequiredService<ILoggerFactory>();
ILogger logger = loggerFactory.CreateLogger("Telegrator.Hosting.Web.TelegratorHost");
if (logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information))
{
logger.LogInformation("Telegrator Bot .NET Host started");
logger.LogInformation("Telegram Bot : {firstname}, @{usrname}, id:{id},", info.User.FirstName ?? "[NULL]", info.User.Username ?? "[NULL]", info.User.Id);
logger.LogHandlers(handlers);
}
return botHost;
}
/// <summary>
/// Configures bots available commands depending on what handlers was registered
/// </summary>
/// <param name="botHost"></param>
/// <returns></returns>
public static IHost SetBotCommands(this IHost botHost)
{
ITelegramBotClient client = botHost.Services.GetRequiredService<ITelegramBotClient>();
IUpdateRouter router = botHost.Services.GetRequiredService<IUpdateRouter>();
IEnumerable<BotCommand> aliases = router.HandlersProvider.GetBotCommands();
client.SetMyCommands(aliases).Wait();
return botHost;
}
/// <summary>
/// Adds a Microsoft.Extensions.Logging adapter to Alligator using a logger factory.
/// </summary>
/// <param name="host"></param>
public static IHost AddLoggingAdapter(this IHost host)
{
ILoggerFactory loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
ILogger logger = loggerFactory.CreateLogger("Telegrator");
MicrosoftLoggingAdapter adapter = new MicrosoftLoggingAdapter(logger);
TelegratorLogging.AddAdapter(adapter);
return host;
}
}
/// <summary>
/// Provides extension methods for reflection and type inspection.
/// </summary>
public static class ReflectionExtensions
{
/// <summary>
/// Checks if a type implements the <see cref="IPreBuildingRoutine"/> interface.
/// </summary>
/// <param name="handlerType">The type to check.</param>
/// <param name="routineMethod"></param>
/// <returns>True if the type implements IPreBuildingRoutine; otherwise, false.</returns>
public static bool IsPreBuildingRoutine(this Type handlerType, [NotNullWhen(true)] out MethodInfo? routineMethod)
{
routineMethod = null;
if (handlerType.GetInterface(nameof(IPreBuildingRoutine)) == null)
return false;
routineMethod = handlerType.GetMethod(nameof(IPreBuildingRoutine.PreBuildingRoutine), BindingFlags.Static | BindingFlags.Public);
return routineMethod != null;
}
}
/// <summary>
/// Provides extension methods for logging Telegrator-related information.
/// </summary>
public static class LoggerExtensions
{
/// <summary>
/// Logs the registered handlers to the specified logger.
/// </summary>
/// <param name="logger">The logger to write to.</param>
/// <param name="handlers">The collection of handlers to log.</param>
public static void LogHandlers(this ILogger logger, IHandlersCollection handlers)
{
if (!logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information))
return;
StringBuilder logBuilder = new StringBuilder("Registered handlers : ");
if (!handlers.Keys.Any())
throw new Exception();
foreach (UpdateType updateType in handlers.Keys)
{
HandlerDescriptorList descriptors = handlers[updateType];
logBuilder.Append("\n\tUpdateType." + updateType + " :");
foreach (HandlerDescriptor descriptor in descriptors.Reverse())
{
logBuilder.AppendFormat("\n\t* {0} - {1}",
descriptor.Indexer.ToString(),
descriptor.ToString());
}
}
logger.LogInformation(logBuilder.ToString());
}
}
@@ -2,10 +2,9 @@
using Telegram.Bot.Types;
using Telegrator.Core.Handlers;
namespace Telegrator.Localized
namespace Telegrator.Localized;
public interface ILocalizedHandler<T> : IAbstractUpdateHandler<Message> where T : class
{
public interface ILocalizedHandler<T> : IAbstractUpdateHandler<Message> where T : class
{
public IStringLocalizer LocalizationProvider { get; }
}
public IStringLocalizer LocalizationProvider { get; }
}
@@ -1,9 +1,8 @@
using Telegram.Bot.Types;
namespace Telegrator.Localized
{
public interface ILocalizedMessageHandler : ILocalizedHandler<Message>
{
namespace Telegrator.Localized;
public interface ILocalizedMessageHandler : ILocalizedHandler<Message>
{
}
}
+22
View File
@@ -5,6 +5,9 @@ using Telegrator.Logging;
namespace Telegrator;
/// <summary>
/// Represents a continuous chat action that runs in the background until cancelled or disposed.
/// </summary>
public class ContinuousAction : IDisposable
{
private readonly ITelegramBotClient _client;
@@ -17,6 +20,14 @@ public class ContinuousAction : IDisposable
private int _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="ContinuousAction"/> class.
/// </summary>
/// <param name="client">The Telegram bot client.</param>
/// <param name="chat">The target chat.</param>
/// <param name="action">The action to perform continuously.</param>
/// <param name="delay">The delay between actions. Defaults to 4 seconds.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public ContinuousAction(ITelegramBotClient client, ChatId chat, ChatAction action, TimeSpan? delay = null, CancellationToken cancellationToken = default)
{
_client = client;
@@ -52,6 +63,9 @@ public class ContinuousAction : IDisposable
}
}
/// <summary>
/// Cancels the continuous action.
/// </summary>
public void Cancel()
{
if (Interlocked.CompareExchange(ref _disposed, 0, 0) == 0)
@@ -67,11 +81,17 @@ public class ContinuousAction : IDisposable
}
}
/// <summary>
/// Waits for the background worker task to complete.
/// </summary>
public async Task WaitAsync()
{
await _workerTask.ConfigureAwait(false);
}
/// <summary>
/// Disposes the instance and stops the continuous action.
/// </summary>
public void Dispose()
{
if (Interlocked.Exchange(ref _disposed, 1) == 1)
@@ -85,5 +105,7 @@ public class ContinuousAction : IDisposable
{
_linkedCts.Dispose();
}
GC.SuppressFinalize(this);
}
}
@@ -14,7 +14,7 @@ namespace Telegrator.Core.Descriptors
private readonly ManualResetEventSlim ResetEvent = new ManualResetEventSlim(false);
/// <summary>
/// descriptor from that handler was described from
/// Descriptor from that handler was described from.
/// </summary>
public HandlerDescriptor From { get; }
@@ -31,7 +31,7 @@ namespace Telegrator.Core.Descriptors
/// <summary>
/// The Telegram bot client used for this handler.
/// </summary>
public ITelegramBotClient Client { get; }
public ITelegramBotClient Client { get; }
/// <summary>
/// The handler instance being described.
@@ -71,9 +71,9 @@ namespace Telegrator.Core.Descriptors
/// <summary>
/// Initializes a new instance of the <see cref="DescribedHandlerDescriptor"/> class.
/// </summary>
/// <param name="fromDescriptor">descriptor from that handler was described from</param>
/// <param name="awaitingProvider"></param>
/// <param name="fromDescriptor">The descriptor from which this handler was described.</param>
/// <param name="updateRouter">The update router.</param>
/// <param name="awaitingProvider">The awaiting provider.</param>
/// <param name="client">The Telegram bot client.</param>
/// <param name="handlerInstance">The handler instance.</param>
/// <param name="filterContext">The filter execution context.</param>
@@ -98,6 +98,10 @@ namespace Telegrator.Core.Descriptors
DisplayString = displayString ?? fromDescriptor.HandlerType.Name;
}
/// <summary>
/// Waits for the handler execution result.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
public async Task AwaitResult(CancellationToken cancellationToken)
{
await Task.Yield();
@@ -105,6 +109,10 @@ namespace Telegrator.Core.Descriptors
ResetEvent.Wait(cancellationToken);
}
/// <summary>
/// Reports the execution result and signals completion.
/// </summary>
/// <param name="result">The execution result.</param>
public void ReportResult(Result? result)
{
if (result != null)
+2 -2
View File
@@ -4,8 +4,8 @@ using Telegram.Bot.Types;
namespace Telegrator.Core;
/// <summary>
/// Requests new <see cref="Update"/>s and processes them using provided <see cref="IUpdateHandler"/> instance<
/// /summary>
/// Requests new <see cref="Update"/>s and processes them using provided <see cref="IUpdateHandler"/> instance.
/// </summary>
public interface IUpdateReceiver
{
/// <summary>
@@ -18,8 +18,14 @@ namespace Telegrator.Mediation
/// </summary>
protected readonly object SyncObj = new object();
/// <summary>
/// The task responsible for reading and processing handlers from the channel.
/// </summary>
protected readonly Task ChannelReaderTask;
/// <summary>
/// The channel used to queue handlers for execution.
/// </summary>
protected readonly Channel<DescribedHandlerDescriptor> ExecutionChannel;
/// <summary>
@@ -27,6 +33,9 @@ namespace Telegrator.Mediation
/// </summary>
protected readonly SemaphoreSlim? ExecutionLimiter;
/// <summary>
/// The update router associated with this pool.
/// </summary>
protected readonly IUpdateRouter UpdateRouter;
/// <summary>