* Renamed "concurrency" parameters in handlers attributes to more relevant and intuitive "importance" parameter
* Added debug logging helper that use default Debug tracer to trace filter, providers, routers and pool execution * Added debug logging for failing during handlers resolving, filters * Added new "CreateEmptyBuilder" methods for TelegramBotHost class * Addede ability to name handlers using "DisplayNameAttribute". This name is writed to descriptor's "DisplayString" property * Fixed missing summaries inside Hosting library
This commit is contained in:
@@ -6,11 +6,12 @@ using Telegrator.Polling;
|
||||
|
||||
namespace Telegrator.Hosting.Polling
|
||||
{
|
||||
public class HostUpdateHandlersPool(IOptions<TelegramBotOptions> options, ILogger<HostUpdateHandlersPool> logger)
|
||||
: UpdateHandlersPool(options.Value, options.Value.GlobalCancellationToken)
|
||||
/// <inheritdoc/>
|
||||
public class HostUpdateHandlersPool(IOptions<TelegramBotOptions> options, ILogger<HostUpdateHandlersPool> logger) : UpdateHandlersPool(options.Value, options.Value.GlobalCancellationToken)
|
||||
{
|
||||
private readonly ILogger<HostUpdateHandlersPool> _logger = logger;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteHandlerWrapper(DescribedHandlerInfo enqueuedHandler)
|
||||
{
|
||||
_logger.LogInformation("Handler \"{0}\" has entered execution pool", enqueuedHandler.DisplayString);
|
||||
|
||||
@@ -10,23 +10,38 @@ using Telegrator.Polling;
|
||||
|
||||
namespace Telegrator.Hosting.Polling
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public class HostUpdateRouter : UpdateRouter
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="ILogger"/> of this router
|
||||
/// </summary>
|
||||
protected readonly ILogger<HostUpdateRouter> Logger;
|
||||
|
||||
public HostUpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, IOptions<TelegramBotOptions> options, IUpdateHandlersPool handlersPool, ILogger<HostUpdateRouter> logger)
|
||||
: base(handlersProvider, awaitingProvider, options.Value, handlersPool)
|
||||
// Ehat a mess :/
|
||||
/// <inheritdoc/>
|
||||
public HostUpdateRouter(
|
||||
IHandlersProvider handlersProvider,
|
||||
IAwaitingProvider awaitingProvider,
|
||||
IOptions<TelegramBotOptions> options,
|
||||
IUpdateHandlersPool handlersPool,
|
||||
ILogger<HostUpdateRouter> logger) : base(handlersProvider, awaitingProvider, options.Value, handlersPool)
|
||||
{
|
||||
Logger = logger;
|
||||
ExceptionHandler = new HostExceptionHandler(logger);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
|
||||
{
|
||||
Logger.LogInformation("Received update of type \"{type}\"", update.Type);
|
||||
return base.HandleUpdateAsync(botClient, update, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default exception handler of this router
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
private class HostExceptionHandler(ILogger<HostUpdateRouter> logger) : IRouterExceptionHandler
|
||||
{
|
||||
public void HandleException(ITelegramBotClient botClient, Exception exception, HandleErrorSource source, CancellationToken cancellationToken)
|
||||
@@ -34,7 +49,7 @@ namespace Telegrator.Hosting.Polling
|
||||
if (exception is HandlerFaultedException handlerFaultedException)
|
||||
{
|
||||
logger.LogError("\"{handler}\" handler's execution was faulted :\n{exception}",
|
||||
handlerFaultedException.HandlerInfo.DisplayString,
|
||||
handlerFaultedException.HandlerInfo.ToString(),
|
||||
handlerFaultedException.InnerException?.ToString() ?? "No inner exception");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -9,17 +9,26 @@ using Telegrator.Polling;
|
||||
|
||||
namespace Telegrator.Hosting.Polling
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for receiving updates for Hosted telegram bots
|
||||
/// </summary>
|
||||
/// <param name="botHost"></param>
|
||||
/// <param name="botClient"></param>
|
||||
/// <param name="updateRouter"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="logger"></param>
|
||||
public class HostedUpdateReceiver(ITelegramBotHost botHost, ITelegramBotClient botClient, IUpdateRouter updateRouter, IOptions<ReceiverOptions> options, ILogger<HostedUpdateReceiver> logger) : BackgroundService
|
||||
{
|
||||
private readonly ReceiverOptions ReceiverOptions = options.Value;
|
||||
private readonly IUpdateRouter UpdateRouter = updateRouter;
|
||||
private readonly ReceiverOptions _receiverOptions = options.Value;
|
||||
private readonly IUpdateRouter _updateRouter = updateRouter;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
logger.LogInformation("Starting receiving updates via long-polling");
|
||||
ReceiverOptions.AllowedUpdates = botHost.UpdateRouter.HandlersProvider.AllowedTypes.ToArray();
|
||||
ReactiveUpdateReceiver updateReceiver = new ReactiveUpdateReceiver(botClient, ReceiverOptions);
|
||||
await updateReceiver.ReceiveAsync(UpdateRouter, stoppingToken).ConfigureAwait(false);
|
||||
_receiverOptions.AllowedUpdates = botHost.UpdateRouter.HandlersProvider.AllowedTypes.ToArray();
|
||||
ReactiveUpdateReceiver updateReceiver = new ReactiveUpdateReceiver(botClient, _receiverOptions);
|
||||
await updateReceiver.ReceiveAsync(_updateRouter, stoppingToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ using Telegrator.Providers;
|
||||
|
||||
namespace Telegrator.Hosting.Providers
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public class HostAwaitingProvider(IOptions<TelegramBotOptions> options, ITelegramBotInfo botInfo, ILogger<HostAwaitingProvider> logger) : AwaitingProvider(options.Value, botInfo)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
||||
{
|
||||
IEnumerable<DescribedHandlerInfo> handlers = base.GetHandlers(updateRouter, client, update, cancellationToken).ToArray();
|
||||
|
||||
@@ -8,28 +8,35 @@ using Telegrator.Providers;
|
||||
|
||||
namespace Telegrator.Hosting.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// Pre host building task
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
public delegate void PreBuildingRoutine(TelegramBotHostBuilder builder);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public class HostHandlersCollection(IServiceCollection hostServiceColletion, IHandlersCollectingOptions options) : HandlersCollection(options)
|
||||
{
|
||||
private readonly IServiceCollection Services = hostServiceColletion;
|
||||
public readonly List<Action<TelegramBotHostBuilder>> PreBuilderRoutines = [];
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool MustHaveParameterlessCtor => false;
|
||||
|
||||
/// <summary>
|
||||
/// List of tasks that should be completed right before building the bot
|
||||
/// </summary>
|
||||
public readonly List<PreBuildingRoutine> PreBuilderRoutines = [];
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IHandlersCollection AddHandler(Type handlerType)
|
||||
{
|
||||
//
|
||||
if (handlerType.GetInterface(nameof(IPreBuildingRoutine)) != null)
|
||||
{
|
||||
MethodInfo? methodInfo = handlerType.GetMethod(nameof(IPreBuildingRoutine.PreBuildingRoutine), BindingFlags.Static | BindingFlags.Public);
|
||||
if (methodInfo != null)
|
||||
{
|
||||
Action<TelegramBotHostBuilder> routineDelegate = methodInfo.CreateDelegate<Action<TelegramBotHostBuilder>>(null);
|
||||
PreBuilderRoutines.Add(routineDelegate);
|
||||
}
|
||||
}
|
||||
if (handlerType.IsPreBuildingRoutine(out MethodInfo? routineMethod))
|
||||
PreBuilderRoutines.Add(routineMethod.CreateDelegate<PreBuildingRoutine>(null));
|
||||
|
||||
return base.AddHandler(handlerType);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IHandlersCollection AddDescriptor(HandlerDescriptor descriptor)
|
||||
{
|
||||
switch (descriptor.Type)
|
||||
|
||||
@@ -11,18 +11,25 @@ using Telegrator.Providers;
|
||||
|
||||
namespace Telegrator.Hosting.Providers
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public class HostHandlersProvider : HandlersProvider
|
||||
{
|
||||
private readonly IServiceProvider Services;
|
||||
private readonly ILogger<HostHandlersProvider> Logger;
|
||||
|
||||
public HostHandlersProvider(IHandlersCollection handlers, IOptions<TelegramBotOptions> options, ITelegramBotInfo botInfo, IServiceProvider serviceProvider, ILogger<HostHandlersProvider> logger)
|
||||
: base(handlers, options.Value, botInfo)
|
||||
/// <inheritdoc/>
|
||||
public HostHandlersProvider(
|
||||
IHandlersCollection handlers,
|
||||
IOptions<TelegramBotOptions> options,
|
||||
ITelegramBotInfo botInfo,
|
||||
IServiceProvider serviceProvider,
|
||||
ILogger<HostHandlersProvider> logger) : base(handlers, options.Value, botInfo)
|
||||
{
|
||||
Services = serviceProvider;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IEnumerable<DescribedHandlerInfo> GetHandlers(IUpdateRouter updateRouter, ITelegramBotClient client, Update update, CancellationToken cancellationToken = default)
|
||||
{
|
||||
IEnumerable<DescribedHandlerInfo> handlers = base.GetHandlers(updateRouter, client, update, cancellationToken).ToArray();
|
||||
@@ -30,6 +37,7 @@ namespace Telegrator.Hosting.Providers
|
||||
return handlers;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override UpdateHandlerBase GetHandlerInstance(HandlerDescriptor descriptor, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -10,6 +10,9 @@ using Telegrator.MadiatorCore.Descriptors;
|
||||
|
||||
namespace Telegrator.Hosting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a hosted telegram bot
|
||||
/// </summary>
|
||||
public class TelegramBotHost : ITelegramBotHost
|
||||
{
|
||||
private readonly IHost _innerHost;
|
||||
@@ -32,7 +35,8 @@ namespace Telegrator.Hosting
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TelegramBotHost"/> class.
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider">The service provider.</param>
|
||||
/// <param name="hostApplicationBuilder">The service provider.</param>
|
||||
/// <param name="handlers"></param>
|
||||
internal TelegramBotHost(HostApplicationBuilder hostApplicationBuilder, HostHandlersCollection handlers)
|
||||
{
|
||||
RegisterHostServices(hostApplicationBuilder, handlers);
|
||||
@@ -44,22 +48,52 @@ namespace Telegrator.Hosting
|
||||
LogHandlers(handlers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new <see cref="TelegramBotHostBuilder"/> with default configuration, services and long-polling update receiving scheme
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static TelegramBotHostBuilder CreateBuilder()
|
||||
{
|
||||
TelegramBotHostBuilder builder = new TelegramBotHostBuilder(null);
|
||||
HostApplicationBuilder innerBuilder = new HostApplicationBuilder(settings: null);
|
||||
TelegramBotHostBuilder builder = new TelegramBotHostBuilder(innerBuilder, null);
|
||||
builder.Services.AddTelegramBotHostDefaults();
|
||||
builder.Services.AddTelegramReceiver();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new <see cref="TelegramBotHostBuilder"/> with default services and long-polling update receiving scheme
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static TelegramBotHostBuilder CreateBuilder(TelegramBotHostBuilderSettings? settings)
|
||||
{
|
||||
TelegramBotHostBuilder builder = new TelegramBotHostBuilder(settings);
|
||||
HostApplicationBuilder innerBuilder = new HostApplicationBuilder(settings?.ToApplicationBuilderSettings());
|
||||
TelegramBotHostBuilder builder = new TelegramBotHostBuilder(innerBuilder, settings);
|
||||
builder.Services.AddTelegramBotHostDefaults();
|
||||
builder.Services.AddTelegramReceiver();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new EMPTY <see cref="TelegramBotHostBuilder"/> WITHOUT any services or update receiving schemes
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static TelegramBotHostBuilder CreateEmptyBuilder()
|
||||
{
|
||||
HostApplicationBuilder innerBuilder = Host.CreateEmptyApplicationBuilder(null);
|
||||
return new TelegramBotHostBuilder(innerBuilder, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new EMPTY <see cref="TelegramBotHostBuilder"/> WITHOUT any services or update receiving schemes
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static TelegramBotHostBuilder CreateEmptyBuilder(TelegramBotHostBuilderSettings? settings)
|
||||
{
|
||||
HostApplicationBuilder innerBuilder = Host.CreateEmptyApplicationBuilder(null);
|
||||
return new TelegramBotHostBuilder(innerBuilder, settings);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task StartAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,9 @@ using Telegrator.MadiatorCore;
|
||||
#pragma warning disable IDE0001
|
||||
namespace Telegrator.Hosting
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a hosted telegram bots and services builder that helps manage configuration, logging, lifetime, and more.
|
||||
/// </summary>
|
||||
public class TelegramBotHostBuilder : ITelegramBotHostBuilder
|
||||
{
|
||||
private readonly HostApplicationBuilder _innerBuilder;
|
||||
@@ -38,10 +41,12 @@ namespace Telegrator.Hosting
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TelegramBotHostBuilder"/> class.
|
||||
/// </summary>
|
||||
internal TelegramBotHostBuilder(TelegramBotHostBuilderSettings? settings = null)
|
||||
/// <param name="hostApplicationBuilder"></param>
|
||||
/// <param name="settings"></param>
|
||||
internal TelegramBotHostBuilder(HostApplicationBuilder hostApplicationBuilder, TelegramBotHostBuilderSettings? settings = null)
|
||||
{
|
||||
_innerBuilder = hostApplicationBuilder;
|
||||
_settings = settings ?? new TelegramBotHostBuilderSettings();
|
||||
_innerBuilder = new HostApplicationBuilder(settings?.ToApplicationBuilderSettings());
|
||||
_handlers = new HostHandlersCollection(Services, _settings);
|
||||
|
||||
Services.Configure<TelegramBotOptions>(Configuration.GetSection(nameof(TelegramBotOptions)));
|
||||
@@ -55,7 +60,7 @@ namespace Telegrator.Hosting
|
||||
/// <returns></returns>
|
||||
public TelegramBotHost Build()
|
||||
{
|
||||
foreach (var preBuildRoutine in _handlers.PreBuilderRoutines)
|
||||
foreach (PreBuildingRoutine preBuildRoutine in _handlers.PreBuilderRoutines)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ using Telegrator.Configuration;
|
||||
namespace Telegrator.Hosting
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// Settings os hosted Telegram bot
|
||||
/// </summary>
|
||||
public class TelegramBotHostBuilderSettings() : IHandlersCollectingOptions
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<Version>1.0.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegrator;
|
||||
using Telegrator.Configuration;
|
||||
using Telegrator.Hosting.Components;
|
||||
using Telegrator.Hosting.Configuration;
|
||||
@@ -14,14 +16,31 @@ using Telegrator.MadiatorCore;
|
||||
|
||||
namespace Telegrator.Hosting
|
||||
{
|
||||
/// <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());
|
||||
@@ -34,6 +53,11 @@ namespace Telegrator.Hosting
|
||||
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);
|
||||
@@ -41,12 +65,26 @@ namespace Telegrator.Hosting
|
||||
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>
|
||||
/// Configures bots available commands depending on what handlers was registered
|
||||
/// </summary>
|
||||
/// <param name="botHost"></param>
|
||||
/// <returns></returns>
|
||||
public static ITelegramBotHost SetBotCommands(this ITelegramBotHost botHost)
|
||||
{
|
||||
ITelegramBotClient client = botHost.Services.GetRequiredService<ITelegramBotClient>();
|
||||
@@ -55,4 +93,26 @@ namespace Telegrator.Hosting
|
||||
return botHost;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user