* Added integration addon wit WTelegramBot (WIP)

* Added some extensions methods
* Refactored Result behaviour
* Added missing exception messages
* Removed telegrator-specific host builder (obsolete)
* Code cleanup and bug fixes
This commit is contained in:
gutii
2026-04-27 09:56:44 +04:00
parent 06a021de49
commit aba9cf4037
36 changed files with 814 additions and 990 deletions
+1
View File
@@ -4,6 +4,7 @@
<Project Path="src/Telegrator.Analyzers/Telegrator.Analyzers.csproj" /> <Project Path="src/Telegrator.Analyzers/Telegrator.Analyzers.csproj" />
<Project Path="src/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj" /> <Project Path="src/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj" />
<Project Path="src/Telegrator.Hosting/Telegrator.Hosting.csproj" /> <Project Path="src/Telegrator.Hosting/Telegrator.Hosting.csproj" />
<Project Path="src/Telegrator.Hosting.WideBot/Telegrator.Hosting.WideBot.csproj" />
<Project Path="src/Telegrator.Localized/Telegrator.Localized.csproj" /> <Project Path="src/Telegrator.Localized/Telegrator.Localized.csproj" />
<Project Path="src/Telegrator/Telegrator.csproj" /> <Project Path="src/Telegrator/Telegrator.csproj" />
<Project Path="tests/Telegrator.Tests/Telegrator.Tests.csproj" /> <Project Path="tests/Telegrator.Tests/Telegrator.Tests.csproj" />
@@ -6,3 +6,4 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Style", "IDE0090")] [assembly: SuppressMessage("Style", "IDE0090")]
[assembly: SuppressMessage("Roslynator", "RCS1037")]
@@ -112,7 +112,7 @@ public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
{ {
try try
{ {
PrimaryConstructorBaseTypeSyntax primaryConstructor = (PrimaryConstructorBaseTypeSyntax)classDeclaration.BaseList.Types.ElementAt(0); PrimaryConstructorBaseTypeSyntax primaryConstructor = (PrimaryConstructorBaseTypeSyntax)classDeclaration.BaseList.Types[0];
MethodDeclarationSyntax genExtension = GeneratedExtensionsMethod(classDeclaration, classDeclaration.ParameterList, primaryConstructor.ArgumentList, targeter); MethodDeclarationSyntax genExtension = GeneratedExtensionsMethod(classDeclaration, classDeclaration.ParameterList, primaryConstructor.ArgumentList, targeter);
extensions.Add(genExtension); extensions.Add(genExtension);
} }
@@ -239,7 +239,7 @@ public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
if (targeters.TryGetValue(classDeclaration.Identifier.ValueText, out MethodDeclarationSyntax targeter)) if (targeters.TryGetValue(classDeclaration.Identifier.ValueText, out MethodDeclarationSyntax targeter))
return targeter; return targeter;
if (classDeclaration.BaseList != null && targeters.TryGetValue(classDeclaration.BaseList.Types.ElementAt(0).Type.ToString(), out targeter)) if (classDeclaration.BaseList != null && targeters.TryGetValue(classDeclaration.BaseList.Types[0].Type.ToString(), out targeter))
return targeter; return targeter;
return null; return null;
@@ -1,5 +1,5 @@
namespace Telegrator.RoslynGenerators.RoslynExtensions namespace Telegrator.RoslynGenerators.RoslynExtensions;
{
public static class CollectionsExtensions public static class CollectionsExtensions
{ {
public static IEnumerable<TSource> Combine<TSource>(params IEnumerable<TSource>[] collections) public static IEnumerable<TSource> Combine<TSource>(params IEnumerable<TSource>[] collections)
@@ -61,4 +61,3 @@
public static IEnumerable<T> Repeat<T>(this T item, int times) public static IEnumerable<T> Repeat<T>(this T item, int times)
=> Enumerable.Range(0, times).Select(_ => item); => Enumerable.Range(0, times).Select(_ => item);
} }
}
+11 -144
View File
@@ -4,131 +4,6 @@
<name>Telegrator.Hosting.Web</name> <name>Telegrator.Hosting.Web</name>
</assembly> </assembly>
<members> <members>
<member name="T:Telegrator.Hosting.Web.TelegramBotWebHost">
<summary>
Represents a web hosted telegram bot
</summary>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHost.Services">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHost.UpdateRouter">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHost.DataSources">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHost.Lifetime">
<summary>
Allows consumers to be notified of application lifetime events.
</summary>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHost.Logger">
<summary>
This application's logger
</summary>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHost.Properties">
<inheritdoc/>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.#ctor(Microsoft.AspNetCore.Builder.WebApplicationBuilder)">
<summary>
Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.Builder.WebApplicationBuilder"/> class.
</summary>
<param name="webApplicationBuilder">The proxied instance of host builder.</param>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.CreateBuilder(Microsoft.AspNetCore.Builder.WebApplicationOptions)">
<summary>
Creates new <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> with default services and webhook update receiving scheme
</summary>
<returns></returns>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.CreateSlimBuilder(Microsoft.AspNetCore.Builder.WebApplicationOptions)">
<summary>
Creates new SLIM <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> with default services and webhook update receiving scheme
</summary>
<returns></returns>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.CreateEmptyBuilder(Microsoft.AspNetCore.Builder.WebApplicationOptions)">
<summary>
Creates new EMPTY <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> WITHOUT any services or update receiving schemes
</summary>
<returns></returns>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.StartAsync(System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.StopAsync(System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.CreateApplicationBuilder">
<inheritdoc/>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.Use(System.Func{Microsoft.AspNetCore.Http.RequestDelegate,Microsoft.AspNetCore.Http.RequestDelegate})">
<inheritdoc/>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.New">
<inheritdoc/>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.Build">
<inheritdoc/>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.DisposeAsync">
<summary>
Disposes the host.
</summary>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.Dispose">
<summary>
Disposes the host.
</summary>
</member>
<member name="T:Telegrator.Hosting.Web.TelegramBotWebHostBuilder">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHostBuilder.Handlers">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHostBuilder.Configuration">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHostBuilder.Logging">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHostBuilder.Services">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHostBuilder.Environment">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHostBuilder.Properties">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.Web.TelegramBotWebHostBuilder.Metrics">
<inheritdoc/>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHostBuilder.#ctor(Microsoft.AspNetCore.Builder.WebApplicationBuilder)">
<summary>
Initializes a new instance of the <see cref="T:Telegrator.Hosting.Web.TelegramBotWebHostBuilder"/> class.
</summary>
<param name="webApplicationBuilder"></param>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHostBuilder.#ctor(Microsoft.AspNetCore.Builder.WebApplicationBuilder,Telegrator.Core.IHandlersCollection)">
<summary>
Initializes a new instance of the <see cref="T:Telegrator.Hosting.Web.TelegramBotWebHostBuilder"/> class.
</summary>
<param name="webApplicationBuilder"></param>
<param name="handlers"></param>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHostBuilder.Build">
<summary>
Builds the host.
</summary>
<returns></returns>
</member>
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHostBuilder.ConfigureContainer``1(Microsoft.Extensions.DependencyInjection.IServiceProviderFactory{``0},System.Action{``0})">
<inheritdoc/>
</member>
<member name="T:Telegrator.Hosting.Web.WebhookerOptions"> <member name="T:Telegrator.Hosting.Web.WebhookerOptions">
<summary> <summary>
Configuration options for Telegram bot behavior and execution settings. Configuration options for Telegram bot behavior and execution settings.
@@ -200,20 +75,12 @@
Provides method to configure Telegram Bot WebHost Provides method to configure Telegram Bot WebHost
</summary> </summary>
</member> </member>
<member name="F:Telegrator.ServicesCollectionExtensions.HandlersCollectionPropertyKey"> <member name="M:Telegrator.ServicesCollectionExtensions.AddTelegratorWeb(Microsoft.Extensions.Hosting.IHostApplicationBuilder,Telegrator.TelegratorOptions,Telegrator.Core.IHandlersCollection)">
<summary>
The key used to store the <see cref="T:Telegrator.Core.IHandlersCollection"/> in the builder properties.
</summary>
</member>
<member name="M:Telegrator.ServicesCollectionExtensions.get_Handlers(Microsoft.AspNetCore.Builder.WebApplicationBuilder)">
<inheritdoc cref="P:Telegrator.ServicesCollectionExtensions.&lt;G&gt;$41F16C2D39AF52899E745C9C9F42FF83.Handlers"/>
</member>
<member name="M:Telegrator.ServicesCollectionExtensions.AddTelegratorWeb(Telegrator.Hosting.ITelegramBotHostBuilder,Telegrator.TelegratorOptions,Telegrator.Core.IHandlersCollection,System.Action{Telegrator.Hosting.ITelegramBotHostBuilder})">
<summary> <summary>
Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers. Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
</summary> </summary>
</member> </member>
<member name="M:Telegrator.ServicesCollectionExtensions.AddTelegratorWeb(Microsoft.AspNetCore.Builder.WebApplicationBuilder,Telegrator.TelegratorOptions,Telegrator.Core.IHandlersCollection,System.Action{Telegrator.Hosting.ITelegramBotHostBuilder})"> <member name="M:Telegrator.ServicesCollectionExtensions.AddTelegratorWeb(Microsoft.Extensions.Hosting.IHostApplicationBuilder,Telegrator.TelegratorOptions,Telegrator.Core.IHandlersCollection,System.Action{Telegrator.Hosting.ITelegramBotHostBuilder})">
<summary> <summary>
Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers. Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
</summary> </summary>
@@ -223,7 +90,12 @@
Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers. Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
</summary> </summary>
</member> </member>
<member name="M:Telegrator.ServicesCollectionExtensions.TryFindWebhooker(System.IServiceProvider,Telegrator.Mediation.HostedUpdateWebhooker@)"> <member name="T:Telegrator.TelegramBotHostExtensions">
<summary>
Provides useful methods to adjust Telegram bot Host
</summary>
</member>
<member name="M:Telegrator.TelegramBotHostExtensions.TryFindWebhooker(System.IServiceProvider,Telegrator.Mediation.HostedUpdateWebhooker@)">
<summary> <summary>
Searchs for <see cref="T:Telegrator.Mediation.HostedUpdateWebhooker"/> hosted service inside hosts services Searchs for <see cref="T:Telegrator.Mediation.HostedUpdateWebhooker"/> hosted service inside hosts services
</summary> </summary>
@@ -231,13 +103,13 @@
<param name="webhooker"></param> <param name="webhooker"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Telegrator.ServicesCollectionExtensions.UseTelegratorWeb``1(``0,System.Boolean)"> <member name="M:Telegrator.TelegramBotHostExtensions.UseTelegratorWeb``1(``0,System.Boolean)">
<summary> <summary>
Replaces the initialization logic from TelegramBotWebHost constructor. Replaces the initialization logic from TelegramBotWebHost constructor.
Initializes the bot and logs handlers on application startup. Initializes the bot and logs handlers on application startup.
</summary> </summary>
</member> </member>
<member name="M:Telegrator.ServicesCollectionExtensions.RemapWebhook``1(``0,System.String)"> <member name="M:Telegrator.TelegramBotHostExtensions.RemapWebhook``1(``0,System.String)">
<summary> <summary>
Allows to remap receiving webhook endpoint and map new route to webhost. Allows to remap receiving webhook endpoint and map new route to webhost.
</summary> </summary>
@@ -246,17 +118,12 @@
<returns></returns> <returns></returns>
<exception cref="T:System.ArgumentException"></exception> <exception cref="T:System.ArgumentException"></exception>
</member> </member>
<member name="M:Telegrator.ServicesCollectionExtensions.AddTelegramWebhook(Microsoft.Extensions.DependencyInjection.IServiceCollection)"> <member name="M:Telegrator.TelegramBotHostExtensions.AddTelegramWebhook(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary> <summary>
Registers <see cref="T:Telegram.Bot.ITelegramBotClient"/> service with <see cref="T:Telegrator.Mediation.HostedUpdateWebhooker"/> to receive updates using webhook Registers <see cref="T:Telegram.Bot.ITelegramBotClient"/> service with <see cref="T:Telegrator.Mediation.HostedUpdateWebhooker"/> to receive updates using webhook
</summary> </summary>
<param name="services"></param> <param name="services"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="P:Telegrator.ServicesCollectionExtensions.&lt;G&gt;$41F16C2D39AF52899E745C9C9F42FF83.Handlers">
<summary>
Gets the <see cref="T:Telegrator.Core.IHandlersCollection"/> from the builder properties.
</summary>
</member>
</members> </members>
</doc> </doc>
+18 -81
View File
@@ -40,63 +40,6 @@
Represents a hosted telegram bots and services builder that helps manage configuration, logging, lifetime, and more. Represents a hosted telegram bots and services builder that helps manage configuration, logging, lifetime, and more.
</summary> </summary>
</member> </member>
<member name="T:Telegrator.Hosting.TelegramBotHost">
<summary>
Represents a hosted telegram bot
</summary>
</member>
<member name="P:Telegrator.Hosting.TelegramBotHost.Services">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.TelegramBotHost.UpdateRouter">
<inheritdoc/>
</member>
<member name="P:Telegrator.Hosting.TelegramBotHost.Logger">
<summary>
This application's logger
</summary>
</member>
<member name="M:Telegrator.Hosting.TelegramBotHost.#ctor(Microsoft.Extensions.Hosting.HostApplicationBuilder)">
<summary>
Initializes a new instance of the <see cref="T:Telegrator.Hosting.TelegramBotHost"/> class.
</summary>
<param name="hostApplicationBuilder">The proxied instance of host builder.</param>
</member>
<member name="M:Telegrator.Hosting.TelegramBotHost.CreateBuilder">
<summary>
Creates new <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> with default configuration, services and long-polling update receiving scheme
</summary>
<returns></returns>
</member>
<member name="M:Telegrator.Hosting.TelegramBotHost.CreateBuilder(Microsoft.Extensions.Hosting.HostApplicationBuilderSettings)">
<summary>
Creates new <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> with default services and long-polling update receiving scheme
</summary>
<returns></returns>
</member>
<member name="M:Telegrator.Hosting.TelegramBotHost.CreateEmptyBuilder">
<summary>
Creates new EMPTY <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> WITHOUT any services or update receiving schemes
</summary>
<returns></returns>
</member>
<member name="M:Telegrator.Hosting.TelegramBotHost.CreateEmptyBuilder(Microsoft.Extensions.Hosting.HostApplicationBuilderSettings)">
<summary>
Creates new EMPTY <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> WITHOUT any services or update receiving schemes
</summary>
<returns></returns>
</member>
<member name="M:Telegrator.Hosting.TelegramBotHost.StartAsync(System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Telegrator.Hosting.TelegramBotHost.StopAsync(System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Telegrator.Hosting.TelegramBotHost.Dispose">
<summary>
Disposes the host.
</summary>
</member>
<member name="T:Telegrator.Hosting.TelegramBotHostBuilder"> <member name="T:Telegrator.Hosting.TelegramBotHostBuilder">
<inheritdoc/> <inheritdoc/>
</member> </member>
@@ -121,25 +64,19 @@
<member name="P:Telegrator.Hosting.TelegramBotHostBuilder.Metrics"> <member name="P:Telegrator.Hosting.TelegramBotHostBuilder.Metrics">
<inheritdoc/> <inheritdoc/>
</member> </member>
<member name="M:Telegrator.Hosting.TelegramBotHostBuilder.#ctor(Microsoft.Extensions.Hosting.HostApplicationBuilder)"> <member name="M:Telegrator.Hosting.TelegramBotHostBuilder.#ctor(Microsoft.Extensions.Hosting.IHostApplicationBuilder)">
<summary> <summary>
Initializes a new instance of the <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> class. Initializes a new instance of the <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> class.
</summary> </summary>
<param name="hostApplicationBuilder"></param> <param name="hostApplicationBuilder"></param>
</member> </member>
<member name="M:Telegrator.Hosting.TelegramBotHostBuilder.#ctor(Microsoft.Extensions.Hosting.HostApplicationBuilder,Telegrator.Core.IHandlersCollection)"> <member name="M:Telegrator.Hosting.TelegramBotHostBuilder.#ctor(Microsoft.Extensions.Hosting.IHostApplicationBuilder,Telegrator.Core.IHandlersCollection)">
<summary> <summary>
Initializes a new instance of the <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> class. Initializes a new instance of the <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> class.
</summary> </summary>
<param name="hostApplicationBuilder"></param> <param name="hostApplicationBuilder"></param>
<param name="handlers"></param> <param name="handlers"></param>
</member> </member>
<member name="M:Telegrator.Hosting.TelegramBotHostBuilder.Build">
<summary>
Builds the host.
</summary>
<returns></returns>
</member>
<member name="M:Telegrator.Hosting.TelegramBotHostBuilder.ConfigureContainer``1(Microsoft.Extensions.DependencyInjection.IServiceProviderFactory{``0},System.Action{``0})"> <member name="M:Telegrator.Hosting.TelegramBotHostBuilder.ConfigureContainer``1(Microsoft.Extensions.DependencyInjection.IServiceProviderFactory{``0},System.Action{``0})">
<inheritdoc/> <inheritdoc/>
</member> </member>
@@ -158,7 +95,7 @@
<member name="M:Telegrator.Logging.MicrosoftLoggingAdapter.Log(Telegrator.Logging.LogLevel,System.String,System.Exception)"> <member name="M:Telegrator.Logging.MicrosoftLoggingAdapter.Log(Telegrator.Logging.LogLevel,System.String,System.Exception)">
<inheritdoc/> <inheritdoc/>
</member> </member>
<member name="T:Telegrator.Polling.HostedUpdateReceiver"> <member name="T:Telegrator.Mediation.HostedUpdateReceiver">
<summary> <summary>
Service for receiving updates for Hosted telegram bots Service for receiving updates for Hosted telegram bots
</summary> </summary>
@@ -167,7 +104,7 @@
<param name="options"></param> <param name="options"></param>
<param name="logger"></param> <param name="logger"></param>
</member> </member>
<member name="M:Telegrator.Polling.HostedUpdateReceiver.#ctor(Telegram.Bot.ITelegramBotClient,Telegrator.Core.IUpdateRouter,Microsoft.Extensions.Options.IOptions{Telegram.Bot.Polling.ReceiverOptions},Microsoft.Extensions.Logging.ILogger{Telegrator.Polling.HostedUpdateReceiver})"> <member name="M:Telegrator.Mediation.HostedUpdateReceiver.#ctor(Telegram.Bot.ITelegramBotClient,Telegrator.Core.IUpdateRouter,Microsoft.Extensions.Options.IOptions{Telegram.Bot.Polling.ReceiverOptions},Microsoft.Extensions.Logging.ILogger{Telegrator.Mediation.HostedUpdateReceiver})">
<summary> <summary>
Service for receiving updates for Hosted telegram bots Service for receiving updates for Hosted telegram bots
</summary> </summary>
@@ -176,24 +113,24 @@
<param name="options"></param> <param name="options"></param>
<param name="logger"></param> <param name="logger"></param>
</member> </member>
<member name="M:Telegrator.Polling.HostedUpdateReceiver.ExecuteAsync(System.Threading.CancellationToken)"> <member name="M:Telegrator.Mediation.HostedUpdateReceiver.ExecuteAsync(System.Threading.CancellationToken)">
<inheritdoc/> <inheritdoc/>
</member> </member>
<member name="T:Telegrator.Polling.HostUpdateRouter"> <member name="T:Telegrator.Mediation.HostUpdateRouter">
<inheritdoc/> <inheritdoc/>
</member> </member>
<member name="F:Telegrator.Polling.HostUpdateRouter.Logger"> <member name="F:Telegrator.Mediation.HostUpdateRouter.Logger">
<summary> <summary>
<see cref="T:Microsoft.Extensions.Logging.ILogger"/> of this router <see cref="T:Microsoft.Extensions.Logging.ILogger"/> of this router
</summary> </summary>
</member> </member>
<member name="M:Telegrator.Polling.HostUpdateRouter.#ctor(Telegrator.Core.IHandlersProvider,Telegrator.Core.IAwaitingProvider,Telegrator.Core.States.IStateStorage,Microsoft.Extensions.Options.IOptions{Telegrator.TelegratorOptions},Telegrator.Core.ITelegramBotInfo,Microsoft.Extensions.Logging.ILogger{Telegrator.Polling.HostUpdateRouter})"> <member name="M:Telegrator.Mediation.HostUpdateRouter.#ctor(Telegrator.Core.IHandlersProvider,Telegrator.Core.IAwaitingProvider,Telegrator.Core.States.IStateStorage,Microsoft.Extensions.Options.IOptions{Telegrator.TelegratorOptions},Telegrator.Core.ITelegramBotInfo,Microsoft.Extensions.Logging.ILogger{Telegrator.Mediation.HostUpdateRouter})">
<inheritdoc/> <inheritdoc/>
</member> </member>
<member name="M:Telegrator.Polling.HostUpdateRouter.HandleUpdateAsync(Telegram.Bot.ITelegramBotClient,Telegram.Bot.Types.Update,System.Threading.CancellationToken)"> <member name="M:Telegrator.Mediation.HostUpdateRouter.HandleUpdateAsync(Telegram.Bot.ITelegramBotClient,Telegram.Bot.Types.Update,System.Threading.CancellationToken)">
<inheritdoc/> <inheritdoc/>
</member> </member>
<member name="M:Telegrator.Polling.HostUpdateRouter.HandleException(Telegram.Bot.ITelegramBotClient,System.Exception,Telegram.Bot.Polling.HandleErrorSource,System.Threading.CancellationToken)"> <member name="M:Telegrator.Mediation.HostUpdateRouter.HandleException(Telegram.Bot.ITelegramBotClient,System.Exception,Telegram.Bot.Polling.HandleErrorSource,System.Threading.CancellationToken)">
<summary> <summary>
Default exception handler of this router Default exception handler of this router
</summary> </summary>
@@ -239,12 +176,7 @@
The key used to store the <see cref="T:Telegrator.Core.IHandlersCollection"/> in the builder properties. The key used to store the <see cref="T:Telegrator.Core.IHandlersCollection"/> in the builder properties.
</summary> </summary>
</member> </member>
<member name="M:Telegrator.HostBuilderExtensions.AddTelegrator(Telegrator.Hosting.ITelegramBotHostBuilder,Telegrator.TelegratorOptions,Telegrator.Core.IHandlersCollection,System.Action{Telegrator.Hosting.ITelegramBotHostBuilder})"> <member name="M:Telegrator.HostBuilderExtensions.AddTelegrator(Microsoft.Extensions.Hosting.IHostApplicationBuilder,Telegrator.TelegratorOptions,Telegrator.Core.IHandlersCollection,System.Action{Telegrator.Hosting.ITelegramBotHostBuilder})">
<summary>
Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
</summary>
</member>
<member name="M:Telegrator.HostBuilderExtensions.AddTelegrator(Microsoft.Extensions.Hosting.HostApplicationBuilder,Telegrator.TelegratorOptions,Telegrator.Core.IHandlersCollection,System.Action{Telegrator.Hosting.ITelegramBotHostBuilder})">
<summary> <summary>
Replaces TelegramBotHostBuilder. Configures DI, options, and handlers. Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
</summary> </summary>
@@ -259,6 +191,11 @@
Replaces TelegramBotHostBuilder. Configures DI, options, and handlers. Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
</summary> </summary>
</member> </member>
<member name="M:Telegrator.HostBuilderExtensions.AddTelegrator(Microsoft.Extensions.Hosting.IHostBuilder,Telegrator.TelegratorOptions,Telegrator.Core.IHandlersCollection,System.Action{Telegrator.Core.IHandlersCollection})">
<summary>
Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
</summary>
</member>
<member name="M:Telegrator.HostBuilderExtensions.AddTelegratorInternal(Microsoft.Extensions.DependencyInjection.IServiceCollection,Microsoft.Extensions.Configuration.IConfiguration,System.Collections.Generic.IDictionary{System.Object,System.Object},Telegrator.Core.IHandlersCollection@,Telegrator.TelegratorOptions)"> <member name="M:Telegrator.HostBuilderExtensions.AddTelegratorInternal(Microsoft.Extensions.DependencyInjection.IServiceCollection,Microsoft.Extensions.Configuration.IConfiguration,System.Collections.Generic.IDictionary{System.Object,System.Object},Telegrator.Core.IHandlersCollection@,Telegrator.TelegratorOptions)">
<summary> <summary>
Replaces TelegramBotHostBuilder. Configures DI, options, and handlers. Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
@@ -280,14 +217,14 @@
</member> </member>
<member name="M:Telegrator.ServicesCollectionExtensions.AddTelegramBotHostDefaults(Microsoft.Extensions.DependencyInjection.IServiceCollection)"> <member name="M:Telegrator.ServicesCollectionExtensions.AddTelegramBotHostDefaults(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary> <summary>
Registers <see cref="T:Telegrator.Hosting.TelegramBotHost"/> default services Registers <see cref="N:Telegrator"/> default services
</summary> </summary>
<param name="services"></param> <param name="services"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Telegrator.ServicesCollectionExtensions.AddTelegramReceiver(Microsoft.Extensions.DependencyInjection.IServiceCollection)"> <member name="M:Telegrator.ServicesCollectionExtensions.AddTelegramReceiver(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary> <summary>
Registers <see cref="T:Telegram.Bot.ITelegramBotClient"/> service with <see cref="T:Telegrator.Polling.HostedUpdateReceiver"/> to receive updates using long polling Registers <see cref="T:Telegram.Bot.ITelegramBotClient"/> service with <see cref="T:Telegrator.Mediation.HostedUpdateReceiver"/> to receive updates using long polling
</summary> </summary>
<param name="services"></param> <param name="services"></param>
<returns></returns> <returns></returns>
+23 -12
View File
@@ -6027,6 +6027,9 @@
Manages the distribution of updates between regular handlers and awaiting handlers. Manages the distribution of updates between regular handlers and awaiting handlers.
</summary> </summary>
</member> </member>
<member name="P:Telegrator.Mediation.UpdateRouter.Options">
<inheritdoc/>
</member>
<member name="P:Telegrator.Mediation.UpdateRouter.HandlersProvider"> <member name="P:Telegrator.Mediation.UpdateRouter.HandlersProvider">
<inheritdoc/> <inheritdoc/>
</member> </member>
@@ -6036,9 +6039,6 @@
<member name="P:Telegrator.Mediation.UpdateRouter.StateStorage"> <member name="P:Telegrator.Mediation.UpdateRouter.StateStorage">
<inheritdoc/> <inheritdoc/>
</member> </member>
<member name="P:Telegrator.Mediation.UpdateRouter.Options">
<inheritdoc/>
</member>
<member name="P:Telegrator.Mediation.UpdateRouter.HandlersPool"> <member name="P:Telegrator.Mediation.UpdateRouter.HandlersPool">
<inheritdoc/> <inheritdoc/>
</member> </member>
@@ -6377,14 +6377,9 @@
Represents handler results, allowing to communicate with router and control aspect execution Represents handler results, allowing to communicate with router and control aspect execution
</summary> </summary>
</member> </member>
<member name="P:Telegrator.Result.Positive"> <member name="P:Telegrator.Result.InterruptRouter">
<summary> <summary>
Is result positive Tell router to stop describing
</summary>
</member>
<member name="P:Telegrator.Result.RouteNext">
<summary>
Should router search for next matching handler
</summary> </summary>
</member> </member>
<member name="P:Telegrator.Result.NextType"> <member name="P:Telegrator.Result.NextType">
@@ -6449,6 +6444,14 @@
<param name="keySelector"></param> <param name="keySelector"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Telegrator.ColletionsExtensions.Squeeze``1(System.Collections.Generic.IEnumerable{``0})">
<summary>
Remove all <see langword="null"/> values and returns collection without nullable type.
</summary>
<typeparam name="T"></typeparam>
<param name="source"></param>
<returns></returns>
</member>
<member name="M:Telegrator.ColletionsExtensions.ForEach``1(System.Collections.Generic.IEnumerable{``0},System.Action{``0})"> <member name="M:Telegrator.ColletionsExtensions.ForEach``1(System.Collections.Generic.IEnumerable{``0},System.Action{``0})">
<summary> <summary>
Enumerates objects in a <paramref name="source"/> and executes an <paramref name="action"/> on each one Enumerates objects in a <paramref name="source"/> and executes an <paramref name="action"/> on each one
@@ -6558,7 +6561,7 @@
<param name="type"></param> <param name="type"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Telegrator.ReflectionExtensions.IsHandlerRealization(System.Type)"> <member name="M:Telegrator.ReflectionExtensions.IsHandlerImplementation(System.Type)">
<summary> <summary>
Checks if <paramref name="type"/> is an implementation of <see cref="T:Telegrator.Core.Handlers.UpdateHandlerBase"/> class or its descendants Checks if <paramref name="type"/> is an implementation of <see cref="T:Telegrator.Core.Handlers.UpdateHandlerBase"/> class or its descendants
</summary> </summary>
@@ -6802,7 +6805,7 @@
Initializes the update router and begins polling for updates. Initializes the update router and begins polling for updates.
</summary> </summary>
<param name="receiverOptions">Optional receiver options for configuring update polling.</param> <param name="receiverOptions">Optional receiver options for configuring update polling.</param>
<param name="cancellationToken">The cancellation token to stop receiving updates.</param> <param name="globalCancellationToken">The cancellation token to stop receiving updates.</param>
</member> </member>
<member name="M:Telegrator.TelegratorClient.StartReceivingInternal(Telegram.Bot.Polling.ReceiverOptions,System.Threading.CancellationToken)"> <member name="M:Telegrator.TelegratorClient.StartReceivingInternal(Telegram.Bot.Polling.ReceiverOptions,System.Threading.CancellationToken)">
<summary> <summary>
@@ -7113,6 +7116,14 @@
Provides convenient methods for creating implicit handlers. Provides convenient methods for creating implicit handlers.
</summary> </summary>
</member> </member>
<member name="M:Telegrator.HandlersCollectionExtensions.CollectHandlers(Telegrator.Core.IHandlersCollection)">
<summary>
Collects all handlers from current app domain.
Scans for handlers exported by analyzer into class `Telegrator.Analyzers.AnalyzerExport` in each assembly and registers them to the collection.
</summary>
<param name="handlers"></param>
<returns></returns>
</member>
<member name="M:Telegrator.HandlersCollectionExtensions.CollectHandlersDomainWide(Telegrator.Core.IHandlersCollection)"> <member name="M:Telegrator.HandlersCollectionExtensions.CollectHandlersDomainWide(Telegrator.Core.IHandlersCollection)">
<summary> <summary>
Collects all public handlers from the current app domain. Collects all public handlers from the current app domain.
@@ -10,6 +10,8 @@ namespace Telegrator.Analyzers;
[Generator(LanguageNames.CSharp)] [Generator(LanguageNames.CSharp)]
public class DeveloperHelperAnalyzer : IIncrementalGenerator public class DeveloperHelperAnalyzer : IIncrementalGenerator
{ {
internal record class HandlerDeclarationModel(string ClassName, string NamespaceName, string? AttributeName, string? BaseClassName, Location Location);
private static readonly DiagnosticDescriptor MissingBaseClassWarning = new( private static readonly DiagnosticDescriptor MissingBaseClassWarning = new(
id: "TLG101", id: "TLG101",
title: "Missing handlers base class", title: "Missing handlers base class",
@@ -131,9 +133,8 @@ public class DeveloperHelperAnalyzer : IIncrementalGenerator
private static FieldDeclarationSyntax GenerateTypeField(HandlerDeclarationModel handler) private static FieldDeclarationSyntax GenerateTypeField(HandlerDeclarationModel handler)
{ {
string fullTypeName = handler.Namespace == "Global" string fullTypeName = handler.NamespaceName == "Global"
? handler.ClassName ? handler.ClassName : $"{handler.NamespaceName}.{handler.ClassName}";
: $"{handler.Namespace}.{handler.ClassName}";
TypeOfExpressionSyntax typeofExpression = SyntaxFactory.TypeOfExpression(SyntaxFactory.ParseTypeName(fullTypeName)); TypeOfExpressionSyntax typeofExpression = SyntaxFactory.TypeOfExpression(SyntaxFactory.ParseTypeName(fullTypeName));
VariableDeclaratorSyntax variableDeclarator = SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier($"{handler.ClassName}Type")) VariableDeclaratorSyntax variableDeclarator = SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier($"{handler.ClassName}Type"))
@@ -149,15 +150,6 @@ public class DeveloperHelperAnalyzer : IIncrementalGenerator
} }
} }
internal class HandlerDeclarationModel(string className, string namespaceName, string? attributeName, string? baseClassName, Location location)
{
public readonly string ClassName = className;
public readonly string Namespace = namespaceName;
public readonly string? AttributeName = attributeName;
public readonly string? BaseClassName = baseClassName;
public readonly Location Location = location;
}
internal static class DeveloperHelperAnalyzerExtensions internal static class DeveloperHelperAnalyzerExtensions
{ {
private static readonly string[] HandlersNames = private static readonly string[] HandlersNames =
@@ -8,8 +8,12 @@ using System.Text;
namespace Telegrator.Analyzers; namespace Telegrator.Analyzers;
[Generator(LanguageNames.CSharp)] [Generator(LanguageNames.CSharp)]
public class GeneratedKeyboardMarkupGenerator : IIncrementalGenerator public class KeyboardMarkupGenerator : IIncrementalGenerator
{ {
// Records
private record class GeneratedMarkupMethodModel(MethodDeclarationSyntax OriginalMethod, FieldDeclarationSyntax GeneratedField, MethodDeclarationSyntax GeneratedMethod);
private record class GeneratedMarkupPropertyModel(PropertyDeclarationSyntax OriginalProperty, PropertyDeclarationSyntax GeneratedProperty);
// Return types // Return types
private const string InlineReturnType = "InlineKeyboardMarkup"; private const string InlineReturnType = "InlineKeyboardMarkup";
private const string ReplyReturnType = "ReplyKeyboardMarkup"; private const string ReplyReturnType = "ReplyKeyboardMarkup";
@@ -459,30 +463,4 @@ public class GeneratedKeyboardMarkupGenerator : IIncrementalGenerator
SyntaxFactory.IdentifierName(className), SyntaxFactory.IdentifierName(className),
SyntaxFactory.IdentifierName(methodName)); SyntaxFactory.IdentifierName(methodName));
} }
private class GeneratedMarkupMethodModel
{
public MethodDeclarationSyntax OriginalMethod { get; }
public FieldDeclarationSyntax GeneratedField { get; }
public MethodDeclarationSyntax GeneratedMethod { get; }
public GeneratedMarkupMethodModel(MethodDeclarationSyntax originalMethod, FieldDeclarationSyntax generatedField, MethodDeclarationSyntax generatedMethod)
{
OriginalMethod = originalMethod;
GeneratedField = generatedField;
GeneratedMethod = generatedMethod;
}
}
private class GeneratedMarkupPropertyModel
{
public PropertyDeclarationSyntax OriginalProperty { get; }
public PropertyDeclarationSyntax GeneratedProperty { get; }
public GeneratedMarkupPropertyModel(PropertyDeclarationSyntax originalProperty, PropertyDeclarationSyntax generatedProperty)
{
OriginalProperty = originalProperty;
GeneratedProperty = generatedProperty;
}
}
} }
@@ -1,7 +1,7 @@
namespace Telegrator.Analyzers.RoslynExtensions; namespace Telegrator.Analyzers.RoslynExtensions;
public class TargteterNotFoundException() : Exception() { } #pragma warning disable RCS1194 // Implement exception constructors
public class TargteterNotFoundException() : Exception();
public class BaseClassTypeNotFoundException() : Exception() { } public class BaseClassTypeNotFoundException() : Exception();
public class AncestorNotFoundException() : Exception();
public class AncestorNotFoundException : Exception { } #pragma warning restore RCS1194 // Implement exception constructors
@@ -10,3 +10,5 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Usage", "CA2254")] [assembly: SuppressMessage("Usage", "CA2254")]
[assembly: SuppressMessage("Maintainability", "CA1510")] [assembly: SuppressMessage("Maintainability", "CA1510")]
[assembly: SuppressMessage("Style", "IDE0270")] [assembly: SuppressMessage("Style", "IDE0270")]
[assembly: SuppressMessage("Roslynator", "RCS1037")]
[assembly: SuppressMessage("Roslynator", "RCS1224")]
@@ -1,159 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Telegrator.Core;
namespace Telegrator.Hosting.Web;
/// <summary>
/// Represents a web hosted telegram bot
/// </summary>
public class TelegramBotWebHost : IHost, IApplicationBuilder, IEndpointRouteBuilder, IAsyncDisposable
{
private readonly WebApplication _innerApp;
private readonly IUpdateRouter _updateRouter;
private readonly ILogger<TelegramBotWebHost> _logger;
private bool _disposed;
/// <inheritdoc/>
public IServiceProvider Services => _innerApp.Services;
/// <inheritdoc/>
public IUpdateRouter UpdateRouter => _updateRouter;
/// <inheritdoc/>
public ICollection<EndpointDataSource> DataSources => ((IEndpointRouteBuilder)_innerApp).DataSources;
/// <summary>
/// Allows consumers to be notified of application lifetime events.
/// </summary>
public IHostApplicationLifetime Lifetime => _innerApp.Lifetime;
/// <summary>
/// This application's logger
/// </summary>
public ILogger<TelegramBotWebHost> Logger => _logger;
/// <inheritdoc/>
public IDictionary<string, object?> Properties => ((IApplicationBuilder)_innerApp).Properties;
// Private interface fields
IServiceProvider IEndpointRouteBuilder.ServiceProvider => Services;
IServiceProvider IApplicationBuilder.ApplicationServices { get => Services; set => throw new NotImplementedException(); }
IFeatureCollection IApplicationBuilder.ServerFeatures => ((IApplicationBuilder)_innerApp).ServerFeatures;
/// <summary>
/// Initializes a new instance of the <see cref="WebApplicationBuilder"/> class.
/// </summary>
/// <param name="webApplicationBuilder">The proxied instance of host builder.</param>
public TelegramBotWebHost(WebApplicationBuilder webApplicationBuilder)
{
// Building proxy application
_innerApp = webApplicationBuilder.Build();
// Reruesting services for this host
_updateRouter = Services.GetRequiredService<IUpdateRouter>();
_logger = Services.GetRequiredService<ILogger<TelegramBotWebHost>>();
}
/// <summary>
/// Creates new <see cref="TelegramBotHostBuilder"/> with default services and webhook update receiving scheme
/// </summary>
/// <returns></returns>
public static TelegramBotWebHostBuilder CreateBuilder(WebApplicationOptions? settings)
{
ArgumentNullException.ThrowIfNull(settings, nameof(settings));
WebApplicationBuilder innerApp = WebApplication.CreateBuilder(settings);
TelegramBotWebHostBuilder builder = new TelegramBotWebHostBuilder(innerApp);
builder.AddTelegratorWeb();
return builder;
}
/// <summary>
/// Creates new SLIM <see cref="TelegramBotHostBuilder"/> with default services and webhook update receiving scheme
/// </summary>
/// <returns></returns>
public static TelegramBotWebHostBuilder CreateSlimBuilder(WebApplicationOptions? settings)
{
ArgumentNullException.ThrowIfNull(settings, nameof(settings));
WebApplicationBuilder innerApp = WebApplication.CreateSlimBuilder(settings);
TelegramBotWebHostBuilder builder = new TelegramBotWebHostBuilder(innerApp);
builder.AddTelegratorWeb();
return builder;
}
/// <summary>
/// Creates new EMPTY <see cref="TelegramBotHostBuilder"/> WITHOUT any services or update receiving schemes
/// </summary>
/// <returns></returns>
public static TelegramBotWebHostBuilder CreateEmptyBuilder(WebApplicationOptions? settings)
{
ArgumentNullException.ThrowIfNull(settings, nameof(settings));
WebApplicationBuilder innerApp = WebApplication.CreateEmptyBuilder(settings);
TelegramBotWebHostBuilder builder = new TelegramBotWebHostBuilder(innerApp);
builder.AddTelegratorWeb();
return builder;
}
/// <inheritdoc/>
public async Task StartAsync(CancellationToken cancellationToken = default)
{
await _innerApp.StartAsync(cancellationToken);
}
/// <inheritdoc/>
public async Task StopAsync(CancellationToken cancellationToken = default)
{
await _innerApp.StopAsync(cancellationToken);
}
/// <inheritdoc/>
public IApplicationBuilder CreateApplicationBuilder()
=> ((IEndpointRouteBuilder)_innerApp).CreateApplicationBuilder();
/// <inheritdoc/>
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
=> _innerApp.Use(middleware);
/// <inheritdoc/>
public IApplicationBuilder New()
=> ((IApplicationBuilder)_innerApp).New();
/// <inheritdoc/>
public RequestDelegate Build()
=> ((IApplicationBuilder)_innerApp).Build();
/// <summary>
/// Disposes the host.
/// </summary>
public async ValueTask DisposeAsync()
{
if (_disposed)
return;
await _innerApp.DisposeAsync();
GC.SuppressFinalize(this);
_disposed = true;
}
/// <summary>
/// Disposes the host.
/// </summary>
public void Dispose()
{
if (_disposed)
return;
ValueTask disposeTask = _innerApp.DisposeAsync();
disposeTask.AsTask().Wait();
GC.SuppressFinalize(this);
_disposed = true;
}
}
@@ -1,75 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.Metrics;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Telegrator.Core;
#pragma warning disable IDE0001
namespace Telegrator.Hosting.Web;
/// <inheritdoc/>
public class TelegramBotWebHostBuilder : ITelegramBotHostBuilder
{
private readonly WebApplicationBuilder _innerBuilder;
internal IHandlersCollection _handlers = null!;
/// <inheritdoc/>
public IHandlersCollection Handlers => _handlers;
/// <inheritdoc/>
public IConfigurationManager Configuration => _innerBuilder.Configuration;
/// <inheritdoc/>
public ILoggingBuilder Logging => _innerBuilder.Logging;
/// <inheritdoc/>
public IServiceCollection Services => _innerBuilder.Services;
/// <inheritdoc/>
public IHostEnvironment Environment => _innerBuilder.Environment;
/// <inheritdoc/>
public IDictionary<object, object> Properties => ((IHostApplicationBuilder)_innerBuilder).Properties;
/// <inheritdoc/>
public IMetricsBuilder Metrics => _innerBuilder.Metrics;
/// <summary>
/// Initializes a new instance of the <see cref="TelegramBotWebHostBuilder"/> class.
/// </summary>
/// <param name="webApplicationBuilder"></param>
public TelegramBotWebHostBuilder(WebApplicationBuilder webApplicationBuilder)
{
_innerBuilder = webApplicationBuilder ?? throw new ArgumentNullException(nameof(webApplicationBuilder));
}
/// <summary>
/// Initializes a new instance of the <see cref="TelegramBotWebHostBuilder"/> class.
/// </summary>
/// <param name="webApplicationBuilder"></param>
/// <param name="handlers"></param>
public TelegramBotWebHostBuilder(WebApplicationBuilder webApplicationBuilder, IHandlersCollection handlers)
{
_innerBuilder = webApplicationBuilder ?? throw new ArgumentNullException(nameof(webApplicationBuilder));
_handlers = handlers ?? throw new ArgumentNullException(nameof(handlers));
}
/// <summary>
/// Builds the host.
/// </summary>
/// <returns></returns>
public TelegramBotWebHost Build()
{
TelegramBotWebHost host = new TelegramBotWebHost(_innerBuilder);
host.UseTelegratorWeb();
return host;
}
/// <inheritdoc/>
public void ConfigureContainer<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory, Action<TContainerBuilder>? configure = null) where TContainerBuilder : notnull
{
((IHostApplicationBuilder)_innerBuilder).ConfigureContainer(factory, configure);
}
}
+2 -40
View File
@@ -6,25 +6,8 @@ using Telegrator.Hosting.Web;
namespace Telegrator; namespace Telegrator;
internal class Program internal static class Program
{ {
public static void TelegramBotWebHostBuilder_Example(string[] args)
{
TelegramBotWebHostBuilder builder = TelegramBotWebHost.CreateBuilder(new WebApplicationOptions()
{
Args = args,
ApplicationName = "TelegramBotWebHost example",
});
builder.Handlers
.CollectHandlersAssemblyWide();
builder.Build()
.AddLoggingAdapter()
.SetBotCommands()
.Run();
}
public static void WebApplicationBuilder_Example(string[] args) public static void WebApplicationBuilder_Example(string[] args)
{ {
WebApplicationBuilder builder = WebApplication.CreateBuilder(new WebApplicationOptions() WebApplicationBuilder builder = WebApplication.CreateBuilder(new WebApplicationOptions()
@@ -38,28 +21,7 @@ internal class Program
builder.Build() builder.Build()
.UseTelegratorWeb(dontMap: true) .UseTelegratorWeb(dontMap: true)
.RemapWebhook("https://awesome-butt-sex.cloudpub.ru/") .RemapWebhook("https://amazing-butt-sex.cloudpub.ru/")
.AddLoggingAdapter()
.SetBotCommands()
.Run();
}
public static void TelegramBotHostBuilder_Example(string[] args)
{
ConfigurationManager configuration = new ConfigurationManager();
configuration.AddJsonFile("appsettings.json");
TelegramBotHostBuilder builder = TelegramBotHost.CreateBuilder(new HostApplicationBuilderSettings()
{
Args = args,
ApplicationName = "TelegramBotHost example",
Configuration = configuration
});
builder.Handlers
.CollectHandlersAssemblyWide();
builder.Build()
.AddLoggingAdapter() .AddLoggingAdapter()
.SetBotCommands() .SetBotCommands()
.Run(); .Run();
+39 -33
View File
@@ -8,45 +8,43 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Polling;
using Telegrator.Core; using Telegrator.Core;
using Telegrator.Hosting; using Telegrator.Hosting;
using Telegrator.Hosting.Web; using Telegrator.Hosting.Web;
using Telegrator.Mediation; using Telegrator.Mediation;
using Telegrator.Providers; using Telegrator.Providers;
namespace Telegrator namespace Telegrator;
{
/// <summary> /// <summary>
/// Contains extensions for <see cref="IServiceCollection"/> /// Contains extensions for <see cref="IServiceCollection"/>
/// Provides method to configure Telegram Bot WebHost /// Provides method to configure Telegram Bot WebHost
/// </summary> /// </summary>
public static class ServicesCollectionExtensions public static class ServicesCollectionExtensions
{ {
/// <summary> public static IServiceCollection ConfigureWebhooker(this IServiceCollection services, WebhookerOptions options)
/// The key used to store the <see cref="IHandlersCollection"/> in the builder properties. {
/// </summary> services.AddSingleton(Options.Create(options));
public const string HandlersCollectionPropertyKey = nameof(IHandlersCollection); return services;
}
/// <summary> /// <summary>
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers. /// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
/// </summary> /// </summary>
public static ITelegramBotHostBuilder AddTelegratorWeb(this ITelegramBotHostBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<ITelegramBotHostBuilder>? action = null) public static IHostApplicationBuilder AddTelegratorWeb(this IHostApplicationBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null)
{ {
AddTelegratorWebInternal(builder.Services, builder.Configuration, builder.Properties, ref handlers, options); AddTelegratorWebInternal(builder.Services, builder.Configuration, builder.Properties, ref handlers, options);
if (builder is TelegramBotWebHostBuilder telegramBotHostBuilder)
telegramBotHostBuilder._handlers = handlers;
action?.Invoke(builder);
return builder; return builder;
} }
/// <summary> /// <summary>
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers. /// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
/// </summary> /// </summary>
public static IHostApplicationBuilder AddTelegratorWeb(this WebApplicationBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<ITelegramBotHostBuilder>? action = null) public static IHostApplicationBuilder AddTelegratorWeb(this IHostApplicationBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<ITelegramBotHostBuilder>? action = null)
{ {
AddTelegratorWebInternal(builder.Services, builder.Configuration, ((IHostApplicationBuilder)builder).Properties, ref handlers, options); AddTelegratorWebInternal(builder.Services, builder.Configuration, builder.Properties, ref handlers, options);
action?.Invoke(new TelegramBotWebHostBuilder(builder, handlers)); action?.Invoke(new TelegramBotHostBuilder(builder, handlers));
return builder; return builder;
} }
@@ -55,11 +53,14 @@ namespace Telegrator
/// </summary> /// </summary>
internal static void AddTelegratorWebInternal(IServiceCollection services, IConfiguration configuration, IDictionary<object, object> properties, [NotNull] ref IHandlersCollection? handlers, TelegratorOptions? options = null) internal static void AddTelegratorWebInternal(IServiceCollection services, IConfiguration configuration, IDictionary<object, object> properties, [NotNull] ref IHandlersCollection? handlers, TelegratorOptions? options = null)
{ {
if (options == null) if (services.Any(srvc => srvc.ServiceType == typeof(HostedUpdateReceiver)))
throw new InvalidOperationException("`HostedUpdateReceiver` found in services. WebHost extension is not compatible with long-polling receiving. Please, remove `AddTelegrator` invocation from your WebApp configuration.");
if (options is null)
{ {
options = configuration.GetSection(nameof(TelegratorOptions)).Get<TelegratorOptions>(); options = configuration.GetSection(nameof(TelegratorOptions)).Get<TelegratorOptions>();
if (options == null) if (options is null)
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegratorOptions' wasn't registered. This configuration is runtime required!"); throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegratorOptions' was registered. This configuration is runtime required!");
} }
CancellationTokenSource globallCancell = new CancellationTokenSource(); CancellationTokenSource globallCancell = new CancellationTokenSource();
@@ -67,25 +68,22 @@ namespace Telegrator
services.AddSingleton(Options.Create(options)); services.AddSingleton(Options.Create(options));
services.AddKeyedSingleton("cancell", globallCancell); services.AddKeyedSingleton("cancell", globallCancell);
if (handlers != null) if (handlers is not null && handlers is IHandlersManager manager)
{
if (handlers is IHandlersManager manager)
{ {
ServiceDescriptor descriptor = new ServiceDescriptor(typeof(IHandlersProvider), manager); ServiceDescriptor descriptor = new ServiceDescriptor(typeof(IHandlersProvider), manager);
services.Replace(descriptor); services.Replace(descriptor);
services.AddSingleton(manager); services.AddSingleton(manager);
} }
}
handlers ??= new HostHandlersCollection(services, options); handlers ??= new HostHandlersCollection(services, options);
services.AddSingleton(handlers); services.AddSingleton(handlers);
properties.Add(HandlersCollectionPropertyKey, handlers); properties.Add(HostBuilderExtensions.HandlersCollectionPropertyKey, handlers);
if (!services.Any(srvc => srvc.ImplementationType == typeof(IOptions<WebhookerOptions>))) if (!services.Any(srvc => srvc.ServiceType == typeof(IOptions<WebhookerOptions>)))
{ {
WebhookerOptions? webhookerOptions = configuration.GetSection(nameof(WebhookerOptions)).Get<WebhookerOptions>(); WebhookerOptions? webhookerOptions = configuration.GetSection(nameof(WebhookerOptions)).Get<WebhookerOptions>();
if (webhookerOptions == null) if (webhookerOptions == null)
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'WebhookerOptions' wasn't registered. This configuration is runtime required!"); throw new MissingMemberException("Auto configuration disabled, yet no options of type 'WebhookerOptions' was registered. This configuration is runtime required!");
services.AddSingleton(Options.Create(webhookerOptions)); services.AddSingleton(Options.Create(webhookerOptions));
} }
@@ -102,7 +100,13 @@ namespace Telegrator
services.AddTelegramBotHostDefaults(); services.AddTelegramBotHostDefaults();
services.AddTelegramWebhook(); services.AddTelegramWebhook();
} }
}
/// <summary>
/// Provides useful methods to adjust Telegram bot Host
/// </summary>
public static class TelegramBotHostExtensions
{
/// <summary> /// <summary>
/// Searchs for <see cref="HostedUpdateWebhooker"/> hosted service inside hosts services /// Searchs for <see cref="HostedUpdateWebhooker"/> hosted service inside hosts services
/// </summary> /// </summary>
@@ -119,27 +123,30 @@ namespace Telegrator
/// Replaces the initialization logic from TelegramBotWebHost constructor. /// Replaces the initialization logic from TelegramBotWebHost constructor.
/// Initializes the bot and logs handlers on application startup. /// Initializes the bot and logs handlers on application startup.
/// </summary> /// </summary>
public static T UseTelegratorWeb<T>(this T app, bool dontMap = false) where T : IEndpointRouteBuilder, IHost public static T UseTelegratorWeb<T>(this T botHost, bool dontMap = false) where T : IEndpointRouteBuilder, IHost
{ {
if (!app.ServiceProvider.TryFindWebhooker(out HostedUpdateWebhooker? webhooker)) if (!botHost.ServiceProvider.TryFindWebhooker(out HostedUpdateWebhooker? webhooker))
throw new InvalidOperationException("No service for type 'Telegrator.Mediation.HostedUpdateWebhooker' has been registered."); throw new InvalidOperationException("No service for type 'Telegrator.Mediation.HostedUpdateWebhooker' has been registered.");
ITelegramBotInfo info = app.ServiceProvider.GetRequiredService<ITelegramBotInfo>(); ITelegramBotInfo info = botHost.ServiceProvider.GetRequiredService<ITelegramBotInfo>();
IHandlersCollection handlers = app.ServiceProvider.GetRequiredService<IHandlersCollection>(); IHandlersCollection handlers = botHost.ServiceProvider.GetRequiredService<IHandlersCollection>();
ILoggerFactory loggerFactory = app.ServiceProvider.GetRequiredService<ILoggerFactory>(); ILoggerFactory loggerFactory = botHost.ServiceProvider.GetRequiredService<ILoggerFactory>();
ILogger logger = loggerFactory.CreateLogger("Telegrator.Hosting.Web.TelegratorHost"); ILogger logger = loggerFactory.CreateLogger("Telegrator.Hosting.Web.TelegratorHost");
if (logger.IsEnabled(LogLevel.Information)) if (logger.IsEnabled(LogLevel.Information))
{ {
logger.LogInformation("Telegrator Bot ASP.NET WebHost started"); logger.LogInformation("Telegrator Bot Host started (ASP.NET WebHost)");
logger.LogInformation("Receiving mode : WEB-HOOKING");
logger.LogInformation("Telegram Bot : {firstname}, @{usrname}, id:{id},", info.User.FirstName ?? "[NULL]", info.User.Username ?? "[NULL]", info.User.Id); logger.LogInformation("Telegram Bot : {firstname}, @{usrname}, id:{id},", info.User.FirstName ?? "[NULL]", info.User.Username ?? "[NULL]", info.User.Id);
logger.LogHandlers(handlers); logger.LogHandlers(handlers);
} }
if (!dontMap) if (!dontMap)
webhooker.MapWebhook(app); webhooker.MapWebhook(botHost);
return app; botHost.AddLoggingAdapter();
botHost.SetBotCommands();
return botHost;
} }
/// <summary> /// <summary>
@@ -173,4 +180,3 @@ namespace Telegrator
private static ITelegramBotClient TypedTelegramBotClientFactory(HttpClient httpClient, IServiceProvider provider) private static ITelegramBotClient TypedTelegramBotClientFactory(HttpClient httpClient, IServiceProvider provider)
=> new TelegramBotClient(provider.GetRequiredService<IOptions<TelegramBotClientOptions>>().Value, httpClient); => new TelegramBotClient(provider.GetRequiredService<IOptions<TelegramBotClientOptions>>().Value, httpClient);
} }
}
@@ -0,0 +1,8 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Roslynator", "RCS1037")]
@@ -0,0 +1,35 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Threading;
using System.Threading.Tasks;
using Telegram.Bot;
using Telegram.Bot.Polling;
using Telegrator.Core;
namespace Telegrator.Mediation;
//Hosting.WideBot
public class HostedWideBotUpdateReceiver(ILogger<HostedWideBotUpdateReceiver> logger, ITelegramBotClient botClient, IUpdateRouter updateRouter, IOptions<ReceiverOptions>? options) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
if (botClient is not WTelegramBotClient wideBotClient)
throw new Exception("Registered ITelegramBotClient was not a wide client (WTelegramBotClient)! Please, use `AddWideTelegrator` instead.");
if (options?.Value.DropPendingUpdates is true)
await wideBotClient.DropPendingUpdates();
logger.LogInformation("Starting receiving updates via MTProto");
// UIP (understanding in progress)
//_receiverOptions.AllowedUpdates = updateRouter.HandlersProvider.AllowedTypes.ToArray();
botClient.DeleteWebhook(options?.Value.DropPendingUpdates ?? false, cancellationToken: stoppingToken)
.ConfigureAwait(false).GetAwaiter().GetResult();
WideUpdateReceiver updateReceiver = new WideUpdateReceiver(wideBotClient);
await updateReceiver.ReceiveAsync(updateRouter, stoppingToken).ConfigureAwait(false);
}
}
@@ -0,0 +1,50 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Telegram.Bot;
using Telegram.Bot.Polling;
using Telegrator.Core;
using WUpdate = WTelegram.Types.Update;
namespace Telegrator.Mediation;
public class WideUpdateReceiver(WTelegramBotClient client) : IUpdateReceiver
{
private readonly WTelegramBotClient _client = client;
private IUpdateHandler? _updateHandler = null;
private CancellationToken _cancellation = default;
public async Task ReceiveAsync(IUpdateHandler updateHandler, CancellationToken cancellationToken = default)
{
_updateHandler = updateHandler;
_cancellation = cancellationToken;
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
await using CancellationTokenRegistration registration = cancellationToken.Register(() => tcs.TrySetResult(null!));
try
{
_client.OnUpdate += OnUpdate;
await tcs.Task.ConfigureAwait(false);
}
finally
{
_client.OnUpdate -= OnUpdate;
}
}
private async Task OnUpdate(WUpdate update)
{
if (_updateHandler == null)
throw new Exception("Router not initialized (got null)");
try
{
await _updateHandler.HandleUpdateAsync(_client, update, _cancellation).ConfigureAwait(false);
}
catch (Exception ex)
{
await _updateHandler.HandleErrorAsync(_client, ex, HandleErrorSource.HandleUpdateError, _cancellation).ConfigureAwait(false);
}
}
}
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>Telegrator</RootNamespace>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="WTelegramBot" Version="9.5.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Telegrator.Hosting\Telegrator.Hosting.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,76 @@
// Maybe later...
/*
using System;
using System.Net.Http;
using System.Threading;
using Telegram.Bot;
using Telegram.Bot.Polling;
using Telegrator.Core;
using Telegrator.Logging;
using Telegrator.Mediation;
using Telegrator.Providers;
using Telegrator.States;
namespace Telegrator;
public class TelegratorWClient : WTelegramBotClient, ITelegratorBot, ICollectingProvider
{
private IUpdateRouter? _updateRouter = null;
public TelegratorOptions Options { get; }
public IHandlersCollection Handlers { get; }
public ITelegramBotInfo BotInfo { get; }
public IUpdateRouter UpdateRouter => _updateRouter ?? throw new InvalidOperationException("Router's not created yet. Invoke `StartReceiving` to initialize this property.");
public TelegratorWClient(WTelegramBotClientOptions wOptions, TelegratorOptions? telegratorOptions = null, HttpClient? httpClient = null, CancellationToken cancellationToken = default)
: base(wOptions, httpClient, cancellationToken)
{
Options = telegratorOptions ?? new TelegratorOptions();
Handlers = new HandlersCollection(default);
BotInfo = new TelegramBotInfo(GetMe(cancellationToken).Result);
}
public void StartReceiving(CancellationToken cancellationToken = default)
{
if (Options.GlobalCancellationToken == CancellationToken.None)
Options.GlobalCancellationToken = cancellationToken;
HandlersProvider handlerProvider = new HandlersProvider(Handlers, Options);
AwaitingProvider awaitingProvider = new AwaitingProvider(Options);
DefaultStateStorage stateStorage = new DefaultStateStorage();
_updateRouter = new UpdateRouter(handlerProvider, awaitingProvider, stateStorage, Options, BotInfo);
TelegratorLogging.LogInformation($"TelegratorW bot starting up - BotId: {BotInfo.User.Id}, Username: {BotInfo.User.Username}");
StartReceivingInternal(Options.GlobalCancellationToken);
}
private async void StartReceivingInternal(CancellationToken cancellationToken)
{
try
{
try
{
await new HostedWideBotUpdateReceiver(this)
.ReceiveAsync(UpdateRouter, cancellationToken)
.ConfigureAwait(false);
}
catch (Exception exception)
{
await UpdateRouter
.HandleErrorAsync(this, exception, HandleErrorSource.FatalError, cancellationToken)
.ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
// Cancelled
TelegratorLogging.LogInformation("Telegrator bot stopped (cancelled)");
}
}
}
*/
@@ -0,0 +1,181 @@
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;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http;
using System.Threading;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegrator.Core;
using Telegrator.Core.Handlers;
using Telegrator.Hosting;
using Telegrator.Mediation;
using Telegrator.Providers;
using TLUpdate = TL.Update;
using WUpdate = WTelegram.Types.Update;
namespace Telegrator;
public static class HandlersExtensions
{
extension<TUpdate>(AbstractUpdateHandler<TUpdate> handler) where TUpdate : class
{
public WUpdate WUpdate
{
get
{
object? update = typeof(AbstractUpdateHandler<TUpdate>).GetField("HandlingUpdate")?.GetValue(handler);
if (update is not WUpdate wUpdate)
throw new InvalidCastException();
return wUpdate;
}
}
public TLUpdate? TLUpdate
{
get => handler.WUpdate.TLUpdate;
}
}
public static WUpdate AsWUpdate(this Update update)
{
return update as WUpdate
?? throw new InvalidCastException("Update is not assignable to `WTelegram.Types.Update`");
}
}
/// <summary>
/// Provides extension methods for <see cref="IHostApplicationBuilder"/> to configure Telegrator.
/// </summary>
public static class ServiceCollectionExtensions
{
public static IServiceCollection ConfigureWideTelegram(this IServiceCollection services, WTelegramBotClientOptions options)
{
services.AddSingleton(Options.Create(options));
return services;
}
/// <summary>
/// Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
/// </summary>
public static IHostApplicationBuilder AddWideTelegrator(this IHostApplicationBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<ITelegramBotHostBuilder>? action = null)
{
AddTelegratorInternal(builder.Services, builder.Configuration, builder.Properties, ref handlers, options);
action?.Invoke(new TelegramBotHostBuilder(builder, handlers));
return builder;
}
/// <summary>
/// Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
/// </summary>
public static IHostApplicationBuilder AddWideTelegrator(this IHostApplicationBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null)
{
AddTelegratorInternal(builder.Services, builder.Configuration, builder.Properties, ref handlers, options);
return builder;
}
/// <summary>
/// Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
/// </summary>
internal static void AddTelegratorInternal(IServiceCollection services, IConfiguration configuration, IDictionary<object, object> properties, [NotNull] ref IHandlersCollection? handlers, TelegratorOptions? options = null)
{
if (services.Any(srvc => srvc.ServiceType == typeof(HostedUpdateReceiver)))
throw new InvalidOperationException("`HostedUpdateReceiver` found in services. WideHost extension is not compatible with long-polling receiving. Please, remove `AddTelegrator` invocation from your WebApp configuration.");
if (options == null)
{
options = configuration.GetSection(nameof(TelegratorOptions)).Get<TelegratorOptions>();
if (options == null)
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegratorOptions' was registered. This configuration is runtime required!");
}
CancellationTokenSource globallCancell = new CancellationTokenSource();
options.GlobalCancellationToken = globallCancell.Token;
services.AddSingleton(Options.Create(options));
services.AddKeyedSingleton("cancell", globallCancell);
if (handlers != null)
{
if (handlers is IHandlersManager manager)
{
ServiceDescriptor descriptor = new ServiceDescriptor(typeof(IHandlersProvider), manager);
services.Replace(descriptor);
services.AddSingleton(manager);
}
}
handlers ??= new HostHandlersCollection(services, options);
services.AddSingleton(handlers);
properties.Add(HostBuilderExtensions.HandlersCollectionPropertyKey, handlers);
if (!services.Any(srvc => srvc.ImplementationType == typeof(IOptions<WTelegramBotClientOptions>)))
{
// For now, there's no way to configure this from IConfiguration, use `ConfigureWideTelegram` instead
throw new MissingMemberException("No options of type 'WTelegramBotClientOptions' was registered. This configuration is runtime required! Use `ConfigureWideTelegram` to register options.");
/*
services.AddSingleton(Options.Create(new WTelegramBotClientOptions(options.Token, options.BaseUrl, options.UseTestEnvironment)
{
RetryCount = options.RetryCount,
RetryThreshold = options.RetryThreshold
}));
*/
}
services.AddTelegramBotHostDefaults();
services.AddMTProtoUpdateReceiver();
}
public static IServiceCollection AddMTProtoUpdateReceiver(this IServiceCollection services)
{
services.AddHttpClient<WTelegramBotClient>("tgmtproto").RemoveAllLoggers().AddTypedClient(TypedTelegramBotClientFactory);
services.AddSingleton<ITelegramBotClient>(sp => sp.GetRequiredService<WTelegramBotClient>());
services.AddHostedService<HostedWideBotUpdateReceiver>();
return services;
}
private static WTelegramBotClient TypedTelegramBotClientFactory(HttpClient httpClient, IServiceProvider provider)
=> new WTelegramBotClient(provider.GetRequiredService<IOptions<WTelegramBotClientOptions>>().Value, httpClient);
}
/// <summary>
/// Provides useful methods to adjust Telegram bot Host
/// </summary>
public static class TelegramBotHostExtensions
{
public static IHost UseWideTelegrator(this IHost botHost)
{
if (!botHost.Services.TryFindWTelegramBotClient())
throw new InvalidOperationException("No service for type 'Telegram.Bot.WTelegramBotClient' has been registered. Invoke `AddWideTelegrator`");
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(LogLevel.Information))
{
logger.LogInformation("Telegrator WIDE Bot Host started (Generic Host)");
logger.LogInformation("Receiving mode : MTProto");
logger.LogInformation("Telegram Bot : {firstname}, @{usrname}, id:{id},", info.User.FirstName ?? "[NULL]", info.User.Username ?? "[NULL]", info.User.Id);
logger.LogHandlers(handlers);
}
botHost.AddLoggingAdapter();
botHost.SetBotCommands();
return botHost;
}
private static bool TryFindWTelegramBotClient(this IServiceProvider services)
{
return services.GetServices<IHostedService>().Any(s => s is HostedWideBotUpdateReceiver);
}
}
@@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Telegrator;
public class WideReceiverOptions
{
}
@@ -10,3 +10,4 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Usage", "CA2254")] [assembly: SuppressMessage("Usage", "CA2254")]
[assembly: SuppressMessage("Maintainability", "CA1510")] [assembly: SuppressMessage("Maintainability", "CA1510")]
[assembly: SuppressMessage("Style", "IDE0270")] [assembly: SuppressMessage("Style", "IDE0270")]
[assembly: SuppressMessage("Roslynator", "RCS1037")]
@@ -6,7 +6,4 @@ namespace Telegrator.Hosting;
/// <summary> /// <summary>
/// Represents a hosted telegram bots and services builder that helps manage configuration, logging, lifetime, and more. /// Represents a hosted telegram bots and services builder that helps manage configuration, logging, lifetime, and more.
/// </summary> /// </summary>
public interface ITelegramBotHostBuilder : IHostApplicationBuilder, ICollectingProvider public interface ITelegramBotHostBuilder : IHostApplicationBuilder, ICollectingProvider;
{
}
@@ -1,114 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Telegrator.Core;
namespace Telegrator.Hosting;
/// <summary>
/// Represents a hosted telegram bot
/// </summary>
public class TelegramBotHost : IHost, ITelegratorBot
{
private readonly IHost _innerHost;
private readonly IUpdateRouter _updateRouter;
private readonly ILogger<TelegramBotHost> _logger;
private bool _disposed;
/// <inheritdoc/>
public IServiceProvider Services => _innerHost.Services;
/// <inheritdoc/>
public IUpdateRouter UpdateRouter => _updateRouter;
/// <summary>
/// This application's logger
/// </summary>
public ILogger<TelegramBotHost> Logger => _logger;
/// <summary>
/// Initializes a new instance of the <see cref="TelegramBotHost"/> class.
/// </summary>
/// <param name="hostApplicationBuilder">The proxied instance of host builder.</param>
public TelegramBotHost(HostApplicationBuilder hostApplicationBuilder)
{
// Registering this host in services for easy access
hostApplicationBuilder.Services.AddSingleton<ITelegratorBot>(this);
// Building proxy hoster
_innerHost = hostApplicationBuilder.Build();
// Reruesting services for this host
_updateRouter = Services.GetRequiredService<IUpdateRouter>();
_logger = Services.GetRequiredService<ILogger<TelegramBotHost>>();
}
/// <summary>
/// Creates new <see cref="TelegramBotHostBuilder"/> with default configuration, services and long-polling update receiving scheme
/// </summary>
/// <returns></returns>
public static TelegramBotHostBuilder CreateBuilder()
{
HostApplicationBuilder innerBuilder = new HostApplicationBuilder(settings: null);
TelegramBotHostBuilder builder = new TelegramBotHostBuilder(innerBuilder);
return builder;
}
/// <summary>
/// Creates new <see cref="TelegramBotHostBuilder"/> with default services and long-polling update receiving scheme
/// </summary>
/// <returns></returns>
public static TelegramBotHostBuilder CreateBuilder(HostApplicationBuilderSettings? settings)
{
HostApplicationBuilder innerBuilder = new HostApplicationBuilder(settings);
TelegramBotHostBuilder builder = new TelegramBotHostBuilder(innerBuilder);
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);
}
/// <summary>
/// Creates new EMPTY <see cref="TelegramBotHostBuilder"/> WITHOUT any services or update receiving schemes
/// </summary>
/// <returns></returns>
public static TelegramBotHostBuilder CreateEmptyBuilder(HostApplicationBuilderSettings? settings)
{
HostApplicationBuilder innerBuilder = Host.CreateEmptyApplicationBuilder(settings);
return new TelegramBotHostBuilder(innerBuilder);
}
/// <inheritdoc/>
public async Task StartAsync(CancellationToken cancellationToken = default)
{
await _innerHost.StartAsync(cancellationToken);
}
/// <inheritdoc/>
public async Task StopAsync(CancellationToken cancellationToken = default)
{
await _innerHost.StopAsync(cancellationToken);
}
/// <summary>
/// Disposes the host.
/// </summary>
public void Dispose()
{
if (_disposed)
return;
_innerHost.Dispose();
GC.SuppressFinalize(this);
_disposed = true;
}
}
@@ -11,7 +11,7 @@ namespace Telegrator.Hosting;
/// <inheritdoc/> /// <inheritdoc/>
public class TelegramBotHostBuilder : ITelegramBotHostBuilder public class TelegramBotHostBuilder : ITelegramBotHostBuilder
{ {
private readonly HostApplicationBuilder _innerBuilder; private readonly IHostApplicationBuilder _innerBuilder;
internal IHandlersCollection _handlers = null!; internal IHandlersCollection _handlers = null!;
/// <inheritdoc/> /// <inheritdoc/>
@@ -30,7 +30,7 @@ public class TelegramBotHostBuilder : ITelegramBotHostBuilder
public IHostEnvironment Environment => _innerBuilder.Environment; public IHostEnvironment Environment => _innerBuilder.Environment;
/// <inheritdoc/> /// <inheritdoc/>
public IDictionary<object, object> Properties => ((IHostApplicationBuilder)_innerBuilder).Properties; public IDictionary<object, object> Properties => _innerBuilder.Properties;
/// <inheritdoc/> /// <inheritdoc/>
public IMetricsBuilder Metrics => _innerBuilder.Metrics; public IMetricsBuilder Metrics => _innerBuilder.Metrics;
@@ -39,7 +39,7 @@ public class TelegramBotHostBuilder : ITelegramBotHostBuilder
/// Initializes a new instance of the <see cref="TelegramBotHostBuilder"/> class. /// Initializes a new instance of the <see cref="TelegramBotHostBuilder"/> class.
/// </summary> /// </summary>
/// <param name="hostApplicationBuilder"></param> /// <param name="hostApplicationBuilder"></param>
public TelegramBotHostBuilder(HostApplicationBuilder hostApplicationBuilder) public TelegramBotHostBuilder(IHostApplicationBuilder hostApplicationBuilder)
{ {
_innerBuilder = hostApplicationBuilder ?? throw new ArgumentNullException(nameof(hostApplicationBuilder)); _innerBuilder = hostApplicationBuilder ?? throw new ArgumentNullException(nameof(hostApplicationBuilder));
} }
@@ -49,23 +49,12 @@ public class TelegramBotHostBuilder : ITelegramBotHostBuilder
/// </summary> /// </summary>
/// <param name="hostApplicationBuilder"></param> /// <param name="hostApplicationBuilder"></param>
/// <param name="handlers"></param> /// <param name="handlers"></param>
public TelegramBotHostBuilder(HostApplicationBuilder hostApplicationBuilder, IHandlersCollection handlers) public TelegramBotHostBuilder(IHostApplicationBuilder hostApplicationBuilder, IHandlersCollection handlers)
{ {
_innerBuilder = hostApplicationBuilder ?? throw new ArgumentNullException(nameof(hostApplicationBuilder)); _innerBuilder = hostApplicationBuilder ?? throw new ArgumentNullException(nameof(hostApplicationBuilder));
_handlers = handlers ?? throw new ArgumentNullException(nameof(handlers)); _handlers = handlers ?? throw new ArgumentNullException(nameof(handlers));
} }
/// <summary>
/// Builds the host.
/// </summary>
/// <returns></returns>
public TelegramBotHost Build()
{
TelegramBotHost host = new TelegramBotHost(_innerBuilder);
host.UseTelegrator();
return host;
}
/// <inheritdoc/> /// <inheritdoc/>
public void ConfigureContainer<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory, Action<TContainerBuilder>? configure = null) where TContainerBuilder : notnull public void ConfigureContainer<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory, Action<TContainerBuilder>? configure = null) where TContainerBuilder : notnull
{ {
@@ -5,9 +5,8 @@ using Telegram.Bot.Polling;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Telegrator.Core; using Telegrator.Core;
using Telegrator.Core.States; using Telegrator.Core.States;
using Telegrator.Mediation;
namespace Telegrator.Polling; namespace Telegrator.Mediation;
/// <inheritdoc/> /// <inheritdoc/>
public class HostUpdateRouter : UpdateRouter public class HostUpdateRouter : UpdateRouter
@@ -4,9 +4,8 @@ using Microsoft.Extensions.Options;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Polling; using Telegram.Bot.Polling;
using Telegrator.Core; using Telegrator.Core;
using Telegrator.Mediation;
namespace Telegrator.Polling; namespace Telegrator.Mediation;
/// <summary> /// <summary>
/// Service for receiving updates for Hosted telegram bots /// Service for receiving updates for Hosted telegram bots
@@ -26,7 +25,8 @@ public class HostedUpdateReceiver(ITelegramBotClient botClient, IUpdateRouter up
logger.LogInformation("Starting receiving updates via long-polling"); logger.LogInformation("Starting receiving updates via long-polling");
_receiverOptions.AllowedUpdates = _updateRouter.HandlersProvider.AllowedTypes.ToArray(); _receiverOptions.AllowedUpdates = _updateRouter.HandlersProvider.AllowedTypes.ToArray();
botClient.DeleteWebhook(options.Value.DropPendingUpdates).Wait(); botClient.DeleteWebhook(options.Value.DropPendingUpdates, cancellationToken: stoppingToken)
.ConfigureAwait(false).GetAwaiter().GetResult();
DefaultUpdateReceiver updateReceiver = new DefaultUpdateReceiver(botClient, _receiverOptions); DefaultUpdateReceiver updateReceiver = new DefaultUpdateReceiver(botClient, _receiverOptions);
await updateReceiver.ReceiveAsync(_updateRouter, stoppingToken).ConfigureAwait(false); await updateReceiver.ReceiveAsync(_updateRouter, stoppingToken).ConfigureAwait(false);
@@ -17,41 +17,60 @@ public class HostHandlersCollection(IServiceCollection hostServiceColletion, Tel
{ {
switch (descriptor.Type) switch (descriptor.Type)
{ {
default:
throw new Exception("Unknown descriptor type");
case DescriptorType.General: case DescriptorType.General:
{ {
if (descriptor.InstanceFactory != null) if (descriptor.InstanceFactory != null)
{
Services.AddScoped(descriptor.HandlerType, _ => descriptor.InstanceFactory.Invoke()); Services.AddScoped(descriptor.HandlerType, _ => descriptor.InstanceFactory.Invoke());
else break;
Services.AddScoped(descriptor.HandlerType); }
Services.AddScoped(descriptor.HandlerType);
break; break;
} }
case DescriptorType.Keyed: case DescriptorType.Keyed:
{ {
if (descriptor.InstanceFactory != null) if (descriptor.InstanceFactory != null)
{
Services.AddKeyedScoped(descriptor.HandlerType, descriptor.ServiceKey, (_, _) => descriptor.InstanceFactory.Invoke()); Services.AddKeyedScoped(descriptor.HandlerType, descriptor.ServiceKey, (_, _) => descriptor.InstanceFactory.Invoke());
else break;
Services.AddKeyedScoped(descriptor.HandlerType, descriptor.ServiceKey); }
Services.AddKeyedScoped(descriptor.HandlerType, descriptor.ServiceKey);
break; break;
} }
case DescriptorType.Singleton: case DescriptorType.Singleton:
{ {
Services.AddSingleton(descriptor.HandlerType, descriptor.SingletonInstance ?? (descriptor.InstanceFactory != null if (descriptor.SingletonInstance != null)
? descriptor.InstanceFactory.Invoke() {
: throw new Exception())); Services.AddSingleton(descriptor.HandlerType, descriptor.SingletonInstance);
break;
}
if (descriptor.InstanceFactory == null)
throw new InvalidOperationException("Singleton handler descriptor without singleton instance should implement `InstanceFactory`");
Services.AddSingleton(descriptor.HandlerType, descriptor.InstanceFactory.Invoke());
break; break;
} }
case DescriptorType.Implicit: case DescriptorType.Implicit:
{ {
Services.AddKeyedSingleton(descriptor.HandlerType, descriptor.ServiceKey, descriptor.SingletonInstance ?? (descriptor.InstanceFactory != null if (descriptor.SingletonInstance != null)
? descriptor.InstanceFactory.Invoke() {
: throw new Exception())); Services.AddKeyedSingleton(descriptor.HandlerType, descriptor.ServiceKey, descriptor.SingletonInstance);
break;
}
if (descriptor.InstanceFactory == null)
throw new InvalidOperationException("Implicit handler descriptor without singleton instance should implement `InstanceFactory`");
Services.AddKeyedSingleton(descriptor.HandlerType, descriptor.ServiceKey, descriptor.InstanceFactory.Invoke());
break; break;
} }
} }
+39 -22
View File
@@ -15,7 +15,7 @@ using Telegrator.Core.Descriptors;
using Telegrator.Core.States; using Telegrator.Core.States;
using Telegrator.Hosting; using Telegrator.Hosting;
using Telegrator.Logging; using Telegrator.Logging;
using Telegrator.Polling; using Telegrator.Mediation;
using Telegrator.Providers; using Telegrator.Providers;
using Telegrator.States; using Telegrator.States;
@@ -34,20 +34,7 @@ public static class HostBuilderExtensions
/// <summary> /// <summary>
/// Replaces TelegramBotHostBuilder. Configures DI, options, and handlers. /// Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
/// </summary> /// </summary>
public static ITelegramBotHostBuilder AddTelegrator(this ITelegramBotHostBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<ITelegramBotHostBuilder>? action = null) public static IHostApplicationBuilder AddTelegrator(this IHostApplicationBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<ITelegramBotHostBuilder>? action = null)
{
AddTelegratorInternal(builder.Services, builder.Configuration, builder.Properties, ref handlers, options);
if (builder is TelegramBotHostBuilder telegramBotHostBuilder)
telegramBotHostBuilder._handlers = handlers;
action?.Invoke(builder);
return builder;
}
/// <summary>
/// Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
/// </summary>
public static IHostApplicationBuilder AddTelegrator(this HostApplicationBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<ITelegramBotHostBuilder>? action = null)
{ {
AddTelegratorInternal(builder.Services, builder.Configuration, ((IHostApplicationBuilder)builder).Properties, ref handlers, options); AddTelegratorInternal(builder.Services, builder.Configuration, ((IHostApplicationBuilder)builder).Properties, ref handlers, options);
action?.Invoke(new TelegramBotHostBuilder(builder, handlers)); action?.Invoke(new TelegramBotHostBuilder(builder, handlers));
@@ -72,6 +59,16 @@ public static class HostBuilderExtensions
return builder; return builder;
} }
/// <summary>
/// Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
/// </summary>
public static IHostBuilder AddTelegrator(this IHostBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<IHandlersCollection>? action = null)
{
builder.ConfigureServices((ctx, sp) => AddTelegratorInternal(sp, ctx.Configuration, builder.Properties, ref handlers, options));
action?.Invoke(handlers!); // AddTelegratorInternal initializes `handlers`
return builder;
}
/// <summary> /// <summary>
/// Replaces TelegramBotHostBuilder. Configures DI, options, and handlers. /// Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
/// </summary> /// </summary>
@@ -81,7 +78,7 @@ public static class HostBuilderExtensions
{ {
options = configuration.GetSection(nameof(TelegratorOptions)).Get<TelegratorOptions>(); options = configuration.GetSection(nameof(TelegratorOptions)).Get<TelegratorOptions>();
if (options == null) if (options == null)
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegratorOptions' wasn't registered. This configuration is runtime required!"); throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegratorOptions' was registered. This configuration is runtime required!");
} }
CancellationTokenSource globallCancell = new CancellationTokenSource(); CancellationTokenSource globallCancell = new CancellationTokenSource();
@@ -103,11 +100,11 @@ public static class HostBuilderExtensions
services.AddSingleton(handlers); services.AddSingleton(handlers);
properties.Add(HandlersCollectionPropertyKey, handlers); properties.Add(HandlersCollectionPropertyKey, handlers);
if (!services.Any(srvc => srvc.ImplementationType == typeof(IOptions<ReceiverOptions>))) if (!services.Any(srvc => srvc.ServiceType == typeof(IOptions<ReceiverOptions>)))
{ {
ReceiverOptions? receiverOptions = configuration.GetSection(nameof(ReceiverOptions)).Get<ReceiverOptions>(); ReceiverOptions? receiverOptions = configuration.GetSection(nameof(ReceiverOptions)).Get<ReceiverOptions>();
if (receiverOptions == null) if (receiverOptions == null)
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'ReceiverOptions' wasn't registered. This configuration is runtime required!"); throw new MissingMemberException("Auto configuration disabled, yet no options of type 'ReceiverOptions' was registered. This configuration is runtime required!");
services.AddSingleton(Options.Create(receiverOptions)); services.AddSingleton(Options.Create(receiverOptions));
} }
@@ -132,6 +129,18 @@ public static class HostBuilderExtensions
/// </summary> /// </summary>
public static class ServicesCollectionExtensions public static class ServicesCollectionExtensions
{ {
public static IServiceCollection ConfigureTelegram(this IServiceCollection services, TelegramBotClientOptions options)
{
services.AddSingleton(Options.Create(options));
return services;
}
public static IServiceCollection ConfigureReceiver(this IServiceCollection services, ReceiverOptions options)
{
services.AddSingleton(Options.Create(options));
return services;
}
/// <summary> /// <summary>
/// Registers <see cref="IStateStorage"/> service /// Registers <see cref="IStateStorage"/> service
/// </summary> /// </summary>
@@ -145,7 +154,7 @@ public static class ServicesCollectionExtensions
} }
/// <summary> /// <summary>
/// Registers <see cref="TelegramBotHost"/> default services /// Registers <see cref="Telegrator"/> default services
/// </summary> /// </summary>
/// <param name="services"></param> /// <param name="services"></param>
/// <returns></returns> /// <returns></returns>
@@ -200,11 +209,14 @@ public static class TelegramBotHostExtensions
if (logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information)) if (logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information))
{ {
logger.LogInformation("Telegrator Bot .NET Host started"); logger.LogInformation("Telegrator Bot Host started (Generic Host)");
logger.LogInformation("Receiving mode : LONG-POLLING");
logger.LogInformation("Telegram Bot : {firstname}, @{usrname}, id:{id},", info.User.FirstName ?? "[NULL]", info.User.Username ?? "[NULL]", info.User.Id); logger.LogInformation("Telegram Bot : {firstname}, @{usrname}, id:{id},", info.User.FirstName ?? "[NULL]", info.User.Username ?? "[NULL]", info.User.Id);
logger.LogHandlers(handlers); logger.LogHandlers(handlers);
} }
botHost.AddLoggingAdapter();
botHost.SetBotCommands();
return botHost; return botHost;
} }
@@ -219,7 +231,12 @@ public static class TelegramBotHostExtensions
IUpdateRouter router = botHost.Services.GetRequiredService<IUpdateRouter>(); IUpdateRouter router = botHost.Services.GetRequiredService<IUpdateRouter>();
IEnumerable<BotCommand> aliases = router.HandlersProvider.GetBotCommands(); IEnumerable<BotCommand> aliases = router.HandlersProvider.GetBotCommands();
client.SetMyCommands(aliases).Wait(); if (aliases.Any())
{
client.SetMyCommands(aliases)
.ConfigureAwait(false).GetAwaiter().GetResult();
}
return botHost; return botHost;
} }
@@ -255,7 +272,7 @@ public static class LoggerExtensions
StringBuilder logBuilder = new StringBuilder("Registered handlers : "); StringBuilder logBuilder = new StringBuilder("Registered handlers : ");
if (!handlers.Keys.Any()) if (!handlers.Keys.Any())
throw new Exception(); throw new Exception("No update types were registered");
foreach (UpdateType updateType in handlers.Keys) foreach (UpdateType updateType in handlers.Keys)
{ {
@@ -56,7 +56,7 @@ public abstract class UpdateHandlerBase(UpdateType handlingUpdateType) : IUpdate
.ExecutePre(this, container, cancellationToken) .ExecutePre(this, container, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
if (!preResult.Positive) if (!preResult.InterruptRouter)
return preResult; return preResult;
} }
catch (NotImplementedException) catch (NotImplementedException)
@@ -69,7 +69,7 @@ public abstract class UpdateHandlerBase(UpdateType handlingUpdateType) : IUpdate
{ {
// Executing handler // Executing handler
Result execResult = await ExecuteInternal(container, cancellationToken).ConfigureAwait(false); Result execResult = await ExecuteInternal(container, cancellationToken).ConfigureAwait(false);
if (!execResult.Positive) if (!execResult.InterruptRouter)
return execResult; return execResult;
} }
catch (NotImplementedException) catch (NotImplementedException)
@@ -86,7 +86,7 @@ public abstract class UpdateHandlerBase(UpdateType handlingUpdateType) : IUpdate
.ExecutePost(this, container, cancellationToken) .ExecutePost(this, container, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
if (!postResult.Positive) if (!postResult.InterruptRouter)
return postResult; return postResult;
} }
} }
+16 -27
View File
@@ -19,27 +19,22 @@ namespace Telegrator.Mediation;
/// </summary> /// </summary>
public class UpdateRouter : IUpdateRouter public class UpdateRouter : IUpdateRouter
{ {
private readonly TelegratorOptions _options;
private readonly IHandlersProvider _handlersProvider;
private readonly IAwaitingProvider _awaitingProvider;
private readonly IStateStorage _stateStorage;
private readonly IUpdateHandlersPool _HandlersPool;
private readonly ITelegramBotInfo _botInfo; private readonly ITelegramBotInfo _botInfo;
/// <inheritdoc/> /// <inheritdoc/>
public IHandlersProvider HandlersProvider => _handlersProvider; public TelegratorOptions Options { get; }
/// <inheritdoc/> /// <inheritdoc/>
public IAwaitingProvider AwaitingProvider => _awaitingProvider; public IHandlersProvider HandlersProvider { get; }
/// <inheritdoc/> /// <inheritdoc/>
public IStateStorage StateStorage => _stateStorage; public IAwaitingProvider AwaitingProvider { get; }
/// <inheritdoc/> /// <inheritdoc/>
public TelegratorOptions Options => _options; public IStateStorage StateStorage { get; }
/// <inheritdoc/> /// <inheritdoc/>
public IUpdateHandlersPool HandlersPool => _HandlersPool; public IUpdateHandlersPool HandlersPool { get; }
/// <inheritdoc/> /// <inheritdoc/>
public IRouterExceptionHandler? ExceptionHandler { get; set; } public IRouterExceptionHandler? ExceptionHandler { get; set; }
@@ -57,11 +52,11 @@ public class UpdateRouter : IUpdateRouter
/// <param name="botInfo"></param> /// <param name="botInfo"></param>
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, IStateStorage stateStorage, TelegratorOptions options, ITelegramBotInfo botInfo) public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, IStateStorage stateStorage, TelegratorOptions options, ITelegramBotInfo botInfo)
{ {
_options = options; Options = options;
_handlersProvider = handlersProvider; HandlersProvider = handlersProvider;
_awaitingProvider = awaitingProvider; AwaitingProvider = awaitingProvider;
_stateStorage = stateStorage; StateStorage = stateStorage;
_HandlersPool = new UpdateHandlersPool(this, _options, _options.GlobalCancellationToken); HandlersPool = new UpdateHandlersPool(this, Options, Options.GlobalCancellationToken);
_botInfo = botInfo; _botInfo = botInfo;
} }
@@ -116,7 +111,7 @@ public class UpdateRouter : IUpdateRouter
if (lastResult == null) if (lastResult == null)
break; // Smth went horribly wrong, better to stop routing break; // Smth went horribly wrong, better to stop routing
if (lastResult != null && !lastResult.RouteNext) if (lastResult.InterruptRouter)
break; break;
TelegratorLogging.LogTrace("Handler '{0}' requested route continuation (Update {1})", handlerInfo.DisplayString, handlerInfo.HandlingUpdate.Id); TelegratorLogging.LogTrace("Handler '{0}' requested route continuation (Update {1})", handlerInfo.DisplayString, handlerInfo.HandlingUpdate.Id);
@@ -146,7 +141,7 @@ public class UpdateRouter : IUpdateRouter
if (lastResult == null) if (lastResult == null)
break; // Smth went horribly wrong, better to stop routing break; // Smth went horribly wrong, better to stop routing
if (lastResult != null && !lastResult.RouteNext) if (lastResult.InterruptRouter)
break; break;
TelegratorLogging.LogTrace("Handler '{0}' requested route continuation (Update {1})", handlerInfo.DisplayString, handlerInfo.HandlingUpdate.Id); TelegratorLogging.LogTrace("Handler '{0}' requested route continuation (Update {1})", handlerInfo.DisplayString, handlerInfo.HandlingUpdate.Id);
@@ -249,17 +244,13 @@ public class UpdateRouter : IUpdateRouter
FiltersFallbackReport report = new FiltersFallbackReport(descriptor, filterContext); FiltersFallbackReport report = new FiltersFallbackReport(descriptor, filterContext);
Result filtersResult = descriptor.Filters.Validate(filterContext, descriptor.FormReport, ref report); Result filtersResult = descriptor.Filters.Validate(filterContext, descriptor.FormReport, ref report);
if (filtersResult.RouteNext) if (filtersResult.InterruptRouter)
{ return null;
Result fallbackResult = handlerInstance.FiltersFallback(report, client, cancellationToken).Result; Result fallbackResult = handlerInstance.FiltersFallback(report, client, cancellationToken).Result;
breakRouting = !fallbackResult.RouteNext; breakRouting = fallbackResult.InterruptRouter;
return null; return null;
} }
else if (!filtersResult.Positive)
{
return null;
}
}
return new DescribedHandlerDescriptor(descriptor, this, AwaitingProvider, StateStorage, client, handlerInstance, filterContext, descriptor.DisplayString); return new DescribedHandlerDescriptor(descriptor, this, AwaitingProvider, StateStorage, client, handlerInstance, filterContext, descriptor.DisplayString);
} }
@@ -307,6 +298,4 @@ public class UpdateRouter : IUpdateRouter
TelegratorLogging.LogTrace(sb.ToString()); TelegratorLogging.LogTrace(sb.ToString());
} }
private class BreakDescribingException : Exception { }
} }
+8 -14
View File
@@ -10,29 +10,23 @@ namespace Telegrator;
/// </summary> /// </summary>
public sealed class Result public sealed class Result
{ {
private static readonly Result ok = new Result(true, false, null); private static readonly Result ok = new Result(true, null);
private static readonly Result fault = new Result(false, false, null); private static readonly Result fault = new Result(true, null);
private static readonly Result next = new Result(true, true, null); private static readonly Result next = new Result(false, null);
/// <summary> /// <summary>
/// Is result positive /// Tell router to stop describing
/// </summary> /// </summary>
public bool Positive { get; } public bool InterruptRouter { get; }
/// <summary>
/// Should router search for next matching handler
/// </summary>
public bool RouteNext { get; }
/// <summary> /// <summary>
/// Exact type that router should search /// Exact type that router should search
/// </summary> /// </summary>
public Type? NextType { get; } public Type? NextType { get; }
internal Result(bool positive, bool routeNext, Type? nextType) internal Result(bool interruptRouter, Type? nextType)
{ {
Positive = positive; InterruptRouter = interruptRouter;
RouteNext = routeNext;
NextType = nextType; NextType = nextType;
} }
@@ -79,5 +73,5 @@ public sealed class Result
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
public static Result Next<T>() public static Result Next<T>()
=> new Result(true, true, typeof(T)); => new Result(false, typeof(T));
} }
+18 -1
View File
@@ -26,6 +26,21 @@ public static partial class ColletionsExtensions
return new ReadOnlyDictionary<TKey, TValue>(dictionary); return new ReadOnlyDictionary<TKey, TValue>(dictionary);
} }
/// <summary>
/// Remove all <see langword="null"/> values and returns collection without nullable type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static IEnumerable<T> Squeeze<T>(this IEnumerable<T?> source)
{
foreach (T? item in source)
{
if (item is not null)
yield return item;
}
}
/// <summary> /// <summary>
/// Enumerates objects in a <paramref name="source"/> and executes an <paramref name="action"/> on each one /// Enumerates objects in a <paramref name="source"/> and executes an <paramref name="action"/> on each one
/// </summary> /// </summary>
@@ -188,7 +203,7 @@ public static partial class ReflectionExtensions
/// </summary> /// </summary>
/// <param name="type"></param> /// <param name="type"></param>
/// <returns></returns> /// <returns></returns>
public static bool IsHandlerRealization(this Type type) public static bool IsHandlerImplementation(this Type type)
=> !type.IsAbstract && type != typeof(UpdateHandlerBase) && typeof(UpdateHandlerBase).IsAssignableFrom(type); => !type.IsAbstract && type != typeof(UpdateHandlerBase) && typeof(UpdateHandlerBase).IsAssignableFrom(type);
/// <summary> /// <summary>
@@ -260,6 +275,7 @@ public static partial class StringExtensions
{ {
char[] chars = target.ToCharArray(); char[] chars = target.ToCharArray();
int index = chars.IndexOf(char.IsLetter); int index = chars.IndexOf(char.IsLetter);
chars[index] = char.ToUpper(chars[index]); chars[index] = char.ToUpper(chars[index]);
return new string(chars); return new string(chars);
} }
@@ -273,6 +289,7 @@ public static partial class StringExtensions
{ {
char[] chars = target.ToCharArray(); char[] chars = target.ToCharArray();
int index = chars.IndexOf(char.IsLetter); int index = chars.IndexOf(char.IsLetter);
chars[index] = char.ToLower(chars[index]); chars[index] = char.ToLower(chars[index]);
return new string(chars); return new string(chars);
} }
+5 -8
View File
@@ -29,7 +29,7 @@ public class TelegratorClient : TelegramBotClient, ITelegratorBot, ICollectingPr
public ITelegramBotInfo BotInfo { get; private set; } public ITelegramBotInfo BotInfo { get; private set; }
/// <inheritdoc/> /// <inheritdoc/>
public IUpdateRouter UpdateRouter { get => updateRouter ?? throw new Exception(); } public IUpdateRouter UpdateRouter => updateRouter ?? throw new InvalidOperationException("Router's not created yet. Invoke `StartReceiving` to initialize this property.");
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TelegratorClient"/> class with a bot token. /// Initializes a new instance of the <see cref="TelegratorClient"/> class with a bot token.
@@ -68,11 +68,11 @@ public class TelegratorClient : TelegramBotClient, ITelegratorBot, ICollectingPr
/// Initializes the update router and begins polling for updates. /// Initializes the update router and begins polling for updates.
/// </summary> /// </summary>
/// <param name="receiverOptions">Optional receiver options for configuring update polling.</param> /// <param name="receiverOptions">Optional receiver options for configuring update polling.</param>
/// <param name="cancellationToken">The cancellation token to stop receiving updates.</param> /// <param name="globalCancellationToken">The cancellation token to stop receiving updates.</param>
public void StartReceiving(ReceiverOptions? receiverOptions = null, CancellationToken cancellationToken = default) public void StartReceiving(ReceiverOptions? receiverOptions = null, CancellationToken globalCancellationToken = default)
{ {
if (Options.GlobalCancellationToken == CancellationToken.None) if (Options.GlobalCancellationToken == CancellationToken.None)
Options.GlobalCancellationToken = cancellationToken; Options.GlobalCancellationToken = globalCancellationToken;
HandlersProvider handlerProvider = new HandlersProvider(Handlers, Options); HandlersProvider handlerProvider = new HandlersProvider(Handlers, Options);
AwaitingProvider awaitingProvider = new AwaitingProvider(Options); AwaitingProvider awaitingProvider = new AwaitingProvider(Options);
@@ -82,8 +82,7 @@ public class TelegratorClient : TelegramBotClient, ITelegratorBot, ICollectingPr
// Log startup // Log startup
TelegratorLogging.LogInformation($"Telegrator bot starting up - BotId: {BotInfo.User.Id}, Username: {BotInfo.User.Username}, MaxParallelHandlers: {Options.MaximumParallelWorkingHandlers ?? -1}"); TelegratorLogging.LogInformation($"Telegrator bot starting up - BotId: {BotInfo.User.Id}, Username: {BotInfo.User.Username}, MaxParallelHandlers: {Options.MaximumParallelWorkingHandlers ?? -1}");
StartReceivingInternal(receiverOptions, globalCancellationToken);
StartReceivingInternal(receiverOptions, cancellationToken);
} }
/// <summary> /// <summary>
@@ -115,6 +114,4 @@ public class TelegratorClient : TelegramBotClient, ITelegratorBot, ICollectingPr
TelegratorLogging.LogInformation("Telegrator bot stopped (cancelled)"); TelegratorLogging.LogInformation("Telegrator bot stopped (cancelled)");
} }
} }
} }
+22 -2
View File
@@ -412,6 +412,26 @@ public static partial class HandlersCollectionExtensions
"Ocelot", "BouncyCastle", "IdentityModel", "Telegrator" "Ocelot", "BouncyCastle", "IdentityModel", "Telegrator"
]; ];
/// <summary>
/// Collects all handlers from current app domain.
/// Scans for handlers exported by analyzer into class `Telegrator.Analyzers.AnalyzerExport` in each assembly and registers them to the collection.
/// </summary>
/// <param name="handlers"></param>
/// <returns></returns>
public static IHandlersCollection CollectHandlers(this IHandlersCollection handlers)
{
const string exportClassName = "Telegrator.Analyzers.AnalyzerExport";
AppDomain.CurrentDomain.GetAssemblies()
.Select(ass => ass.GetType(exportClassName))
.Squeeze()
.SelectMany(t => t.GetFields())
.Select(f => f.GetValue(null) as Type)
.Squeeze()
.ForEach(v => handlers.AddHandler(v));
return handlers;
}
/// <summary> /// <summary>
/// Collects all public handlers from the current app domain. /// Collects all public handlers from the current app domain.
/// Scans for types that implement handlers and adds them to the collection. /// Scans for types that implement handlers and adds them to the collection.
@@ -438,7 +458,7 @@ public static partial class HandlersCollectionExtensions
{ {
(collectingTarget ?? Assembly.GetCallingAssembly()) (collectingTarget ?? Assembly.GetCallingAssembly())
.GetExportedTypes() .GetExportedTypes()
.Where(type => type.GetCustomAttribute<DontCollectAttribute>() == null && type.IsHandlerRealization()) .Where(type => type.GetCustomAttribute<DontCollectAttribute>() == null && type.IsHandlerImplementation())
.ForEach(type => handlers.AddHandler(type)); .ForEach(type => handlers.AddHandler(type));
return handlers; return handlers;
@@ -496,7 +516,7 @@ public static partial class HandlersCollectionExtensions
/// <exception cref="Exception">Thrown when the type is not a valid handler implementation.</exception> /// <exception cref="Exception">Thrown when the type is not a valid handler implementation.</exception>
public static IHandlersCollection AddHandler(this IHandlersCollection handlers, Type handlerType) public static IHandlersCollection AddHandler(this IHandlersCollection handlers, Type handlerType)
{ {
if (!handlerType.IsHandlerRealization()) if (!handlerType.IsHandlerImplementation())
throw new Exception(); throw new Exception();
if (handlerType.IsCustomDescriptorsProvider()) if (handlerType.IsCustomDescriptorsProvider())