* 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.Hosting.Web/Telegrator.Hosting.Web.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/Telegrator.csproj" />
<Project Path="tests/Telegrator.Tests/Telegrator.Tests.csproj" />
@@ -6,3 +6,4 @@
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Style", "IDE0090")]
[assembly: SuppressMessage("Roslynator", "RCS1037")]
@@ -112,7 +112,7 @@ public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
{
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);
extensions.Add(genExtension);
}
@@ -239,7 +239,7 @@ public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
if (targeters.TryGetValue(classDeclaration.Identifier.ValueText, out MethodDeclarationSyntax 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 null;
@@ -1,64 +1,63 @@
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)
=> collections.SelectMany(x => x);
public static IEnumerable<TSource> IntersectBy<TSource, TValue>(this IEnumerable<TSource> first, IEnumerable<TValue> second, Func<TSource, TValue> selector)
{
public static IEnumerable<TSource> Combine<TSource>(params IEnumerable<TSource>[] collections)
=> collections.SelectMany(x => x);
public static IEnumerable<TSource> IntersectBy<TSource, TValue>(this IEnumerable<TSource> first, IEnumerable<TValue> second, Func<TSource, TValue> selector)
foreach (TSource item in first)
{
foreach (TSource item in first)
{
TValue value = selector(item);
if (second.Contains(value))
yield return item;
}
TValue value = selector(item);
if (second.Contains(value))
yield return item;
}
public static IList<TValue> UnionAdd<TValue>(this IList<TValue> source, IEnumerable<TValue> toUnion, IEqualityComparer<TValue> comparer)
{
foreach (TValue toUnionValue in toUnion)
{
if (!source.Contains(toUnionValue, comparer))
source.Add(toUnionValue);
}
return source;
}
public static void UnionAdd<TSource>(this ICollection<TSource> collection, IEnumerable<TSource> target)
{
foreach (TSource item in target)
{
if (!collection.Contains(item))
collection.Add(item);
}
}
public static void UnionAdd<TSource>(this SortedList<TSource, TSource> collection, IEnumerable<TSource> target)
{
foreach (TSource item in target)
{
if (!collection.Values.Contains(item))
collection.Add(item, item);
}
}
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
int index = 0;
foreach (T item in source)
{
if (predicate.Invoke(item))
return index;
index++;
}
return -1;
}
public static IEnumerable<T> Repeat<T>(this T item, int times)
=> Enumerable.Range(0, times).Select(_ => item);
}
public static IList<TValue> UnionAdd<TValue>(this IList<TValue> source, IEnumerable<TValue> toUnion, IEqualityComparer<TValue> comparer)
{
foreach (TValue toUnionValue in toUnion)
{
if (!source.Contains(toUnionValue, comparer))
source.Add(toUnionValue);
}
return source;
}
public static void UnionAdd<TSource>(this ICollection<TSource> collection, IEnumerable<TSource> target)
{
foreach (TSource item in target)
{
if (!collection.Contains(item))
collection.Add(item);
}
}
public static void UnionAdd<TSource>(this SortedList<TSource, TSource> collection, IEnumerable<TSource> target)
{
foreach (TSource item in target)
{
if (!collection.Values.Contains(item))
collection.Add(item, item);
}
}
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
int index = 0;
foreach (T item in source)
{
if (predicate.Invoke(item))
return index;
index++;
}
return -1;
}
public static IEnumerable<T> Repeat<T>(this T item, int times)
=> Enumerable.Range(0, times).Select(_ => item);
}
+11 -144
View File
@@ -4,131 +4,6 @@
<name>Telegrator.Hosting.Web</name>
</assembly>
<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">
<summary>
Configuration options for Telegram bot behavior and execution settings.
@@ -200,20 +75,12 @@
Provides method to configure Telegram Bot WebHost
</summary>
</member>
<member name="F:Telegrator.ServicesCollectionExtensions.HandlersCollectionPropertyKey">
<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})">
<member name="M:Telegrator.ServicesCollectionExtensions.AddTelegratorWeb(Microsoft.Extensions.Hosting.IHostApplicationBuilder,Telegrator.TelegratorOptions,Telegrator.Core.IHandlersCollection)">
<summary>
Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
</summary>
</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>
Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
</summary>
@@ -223,7 +90,12 @@
Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
</summary>
</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>
Searchs for <see cref="T:Telegrator.Mediation.HostedUpdateWebhooker"/> hosted service inside hosts services
</summary>
@@ -231,13 +103,13 @@
<param name="webhooker"></param>
<returns></returns>
</member>
<member name="M:Telegrator.ServicesCollectionExtensions.UseTelegratorWeb``1(``0,System.Boolean)">
<member name="M:Telegrator.TelegramBotHostExtensions.UseTelegratorWeb``1(``0,System.Boolean)">
<summary>
Replaces the initialization logic from TelegramBotWebHost constructor.
Initializes the bot and logs handlers on application startup.
</summary>
</member>
<member name="M:Telegrator.ServicesCollectionExtensions.RemapWebhook``1(``0,System.String)">
<member name="M:Telegrator.TelegramBotHostExtensions.RemapWebhook``1(``0,System.String)">
<summary>
Allows to remap receiving webhook endpoint and map new route to webhost.
</summary>
@@ -246,17 +118,12 @@
<returns></returns>
<exception cref="T:System.ArgumentException"></exception>
</member>
<member name="M:Telegrator.ServicesCollectionExtensions.AddTelegramWebhook(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<member name="M:Telegrator.TelegramBotHostExtensions.AddTelegramWebhook(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
Registers <see cref="T:Telegram.Bot.ITelegramBotClient"/> service with <see cref="T:Telegrator.Mediation.HostedUpdateWebhooker"/> to receive updates using webhook
</summary>
<param name="services"></param>
<returns></returns>
</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>
</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.
</summary>
</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">
<inheritdoc/>
</member>
@@ -121,25 +64,19 @@
<member name="P:Telegrator.Hosting.TelegramBotHostBuilder.Metrics">
<inheritdoc/>
</member>
<member name="M:Telegrator.Hosting.TelegramBotHostBuilder.#ctor(Microsoft.Extensions.Hosting.HostApplicationBuilder)">
<member name="M:Telegrator.Hosting.TelegramBotHostBuilder.#ctor(Microsoft.Extensions.Hosting.IHostApplicationBuilder)">
<summary>
Initializes a new instance of the <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> class.
</summary>
<param name="hostApplicationBuilder"></param>
</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>
Initializes a new instance of the <see cref="T:Telegrator.Hosting.TelegramBotHostBuilder"/> class.
</summary>
<param name="hostApplicationBuilder"></param>
<param name="handlers"></param>
</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})">
<inheritdoc/>
</member>
@@ -158,7 +95,7 @@
<member name="M:Telegrator.Logging.MicrosoftLoggingAdapter.Log(Telegrator.Logging.LogLevel,System.String,System.Exception)">
<inheritdoc/>
</member>
<member name="T:Telegrator.Polling.HostedUpdateReceiver">
<member name="T:Telegrator.Mediation.HostedUpdateReceiver">
<summary>
Service for receiving updates for Hosted telegram bots
</summary>
@@ -167,7 +104,7 @@
<param name="options"></param>
<param name="logger"></param>
</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>
Service for receiving updates for Hosted telegram bots
</summary>
@@ -176,24 +113,24 @@
<param name="options"></param>
<param name="logger"></param>
</member>
<member name="M:Telegrator.Polling.HostedUpdateReceiver.ExecuteAsync(System.Threading.CancellationToken)">
<member name="M:Telegrator.Mediation.HostedUpdateReceiver.ExecuteAsync(System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="T:Telegrator.Polling.HostUpdateRouter">
<member name="T:Telegrator.Mediation.HostUpdateRouter">
<inheritdoc/>
</member>
<member name="F:Telegrator.Polling.HostUpdateRouter.Logger">
<member name="F:Telegrator.Mediation.HostUpdateRouter.Logger">
<summary>
<see cref="T:Microsoft.Extensions.Logging.ILogger"/> of this router
</summary>
</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/>
</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/>
</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>
Default exception handler of this router
</summary>
@@ -239,12 +176,7 @@
The key used to store the <see cref="T:Telegrator.Core.IHandlersCollection"/> in the builder properties.
</summary>
</member>
<member name="M:Telegrator.HostBuilderExtensions.AddTelegrator(Telegrator.Hosting.ITelegramBotHostBuilder,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})">
<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>
@@ -259,6 +191,11 @@
Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
</summary>
</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)">
<summary>
Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
@@ -280,14 +217,14 @@
</member>
<member name="M:Telegrator.ServicesCollectionExtensions.AddTelegramBotHostDefaults(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
Registers <see cref="T:Telegrator.Hosting.TelegramBotHost"/> default services
Registers <see cref="N:Telegrator"/> default services
</summary>
<param name="services"></param>
<returns></returns>
</member>
<member name="M:Telegrator.ServicesCollectionExtensions.AddTelegramReceiver(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<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>
<param name="services"></param>
<returns></returns>
+23 -12
View File
@@ -6027,6 +6027,9 @@
Manages the distribution of updates between regular handlers and awaiting handlers.
</summary>
</member>
<member name="P:Telegrator.Mediation.UpdateRouter.Options">
<inheritdoc/>
</member>
<member name="P:Telegrator.Mediation.UpdateRouter.HandlersProvider">
<inheritdoc/>
</member>
@@ -6036,9 +6039,6 @@
<member name="P:Telegrator.Mediation.UpdateRouter.StateStorage">
<inheritdoc/>
</member>
<member name="P:Telegrator.Mediation.UpdateRouter.Options">
<inheritdoc/>
</member>
<member name="P:Telegrator.Mediation.UpdateRouter.HandlersPool">
<inheritdoc/>
</member>
@@ -6377,14 +6377,9 @@
Represents handler results, allowing to communicate with router and control aspect execution
</summary>
</member>
<member name="P:Telegrator.Result.Positive">
<member name="P:Telegrator.Result.InterruptRouter">
<summary>
Is result positive
</summary>
</member>
<member name="P:Telegrator.Result.RouteNext">
<summary>
Should router search for next matching handler
Tell router to stop describing
</summary>
</member>
<member name="P:Telegrator.Result.NextType">
@@ -6449,6 +6444,14 @@
<param name="keySelector"></param>
<returns></returns>
</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})">
<summary>
Enumerates objects in a <paramref name="source"/> and executes an <paramref name="action"/> on each one
@@ -6558,7 +6561,7 @@
<param name="type"></param>
<returns></returns>
</member>
<member name="M:Telegrator.ReflectionExtensions.IsHandlerRealization(System.Type)">
<member name="M:Telegrator.ReflectionExtensions.IsHandlerImplementation(System.Type)">
<summary>
Checks if <paramref name="type"/> is an implementation of <see cref="T:Telegrator.Core.Handlers.UpdateHandlerBase"/> class or its descendants
</summary>
@@ -6802,7 +6805,7 @@
Initializes the update router and begins polling for updates.
</summary>
<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 name="M:Telegrator.TelegratorClient.StartReceivingInternal(Telegram.Bot.Polling.ReceiverOptions,System.Threading.CancellationToken)">
<summary>
@@ -7113,6 +7116,14 @@
Provides convenient methods for creating implicit handlers.
</summary>
</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)">
<summary>
Collects all public handlers from the current app domain.
@@ -10,6 +10,8 @@ namespace Telegrator.Analyzers;
[Generator(LanguageNames.CSharp)]
public class DeveloperHelperAnalyzer : IIncrementalGenerator
{
internal record class HandlerDeclarationModel(string ClassName, string NamespaceName, string? AttributeName, string? BaseClassName, Location Location);
private static readonly DiagnosticDescriptor MissingBaseClassWarning = new(
id: "TLG101",
title: "Missing handlers base class",
@@ -131,9 +133,8 @@ public class DeveloperHelperAnalyzer : IIncrementalGenerator
private static FieldDeclarationSyntax GenerateTypeField(HandlerDeclarationModel handler)
{
string fullTypeName = handler.Namespace == "Global"
? handler.ClassName
: $"{handler.Namespace}.{handler.ClassName}";
string fullTypeName = handler.NamespaceName == "Global"
? handler.ClassName : $"{handler.NamespaceName}.{handler.ClassName}";
TypeOfExpressionSyntax typeofExpression = SyntaxFactory.TypeOfExpression(SyntaxFactory.ParseTypeName(fullTypeName));
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
{
private static readonly string[] HandlersNames =
@@ -8,8 +8,12 @@ using System.Text;
namespace Telegrator.Analyzers;
[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
private const string InlineReturnType = "InlineKeyboardMarkup";
private const string ReplyReturnType = "ReplyKeyboardMarkup";
@@ -459,30 +463,4 @@ public class GeneratedKeyboardMarkupGenerator : IIncrementalGenerator
SyntaxFactory.IdentifierName(className),
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;
public class TargteterNotFoundException() : Exception() { }
public class BaseClassTypeNotFoundException() : Exception() { }
public class AncestorNotFoundException : Exception { }
#pragma warning disable RCS1194 // Implement exception constructors
public class TargteterNotFoundException() : Exception();
public class BaseClassTypeNotFoundException() : 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("Maintainability", "CA1510")]
[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;
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)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(new WebApplicationOptions()
@@ -38,28 +21,7 @@ internal class Program
builder.Build()
.UseTelegratorWeb(dontMap: true)
.RemapWebhook("https://awesome-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()
.RemapWebhook("https://amazing-butt-sex.cloudpub.ru/")
.AddLoggingAdapter()
.SetBotCommands()
.Run();
+151 -145
View File
@@ -8,169 +8,175 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Diagnostics.CodeAnalysis;
using Telegram.Bot;
using Telegram.Bot.Polling;
using Telegrator.Core;
using Telegrator.Hosting;
using Telegrator.Hosting.Web;
using Telegrator.Mediation;
using Telegrator.Providers;
namespace Telegrator
namespace Telegrator;
/// <summary>
/// Contains extensions for <see cref="IServiceCollection"/>
/// Provides method to configure Telegram Bot WebHost
/// </summary>
public static class ServicesCollectionExtensions
{
/// <summary>
/// Contains extensions for <see cref="IServiceCollection"/>
/// Provides method to configure Telegram Bot WebHost
/// </summary>
public static class ServicesCollectionExtensions
public static IServiceCollection ConfigureWebhooker(this IServiceCollection services, WebhookerOptions options)
{
/// <summary>
/// The key used to store the <see cref="IHandlersCollection"/> in the builder properties.
/// </summary>
public const string HandlersCollectionPropertyKey = nameof(IHandlersCollection);
services.AddSingleton(Options.Create(options));
return services;
}
/// <summary>
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
/// </summary>
public static ITelegramBotHostBuilder AddTelegratorWeb(this ITelegramBotHostBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<ITelegramBotHostBuilder>? action = null)
/// <summary>
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
/// </summary>
public static IHostApplicationBuilder AddTelegratorWeb(this IHostApplicationBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null)
{
AddTelegratorWebInternal(builder.Services, builder.Configuration, builder.Properties, ref handlers, options);
return builder;
}
/// <summary>
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
/// </summary>
public static IHostApplicationBuilder AddTelegratorWeb(this IHostApplicationBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<ITelegramBotHostBuilder>? action = null)
{
AddTelegratorWebInternal(builder.Services, builder.Configuration, builder.Properties, ref handlers, options);
action?.Invoke(new TelegramBotHostBuilder(builder, handlers));
return builder;
}
/// <summary>
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
/// </summary>
internal static void AddTelegratorWebInternal(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. WebHost extension is not compatible with long-polling receiving. Please, remove `AddTelegrator` invocation from your WebApp configuration.");
if (options is null)
{
AddTelegratorWebInternal(builder.Services, builder.Configuration, builder.Properties, ref handlers, options);
if (builder is TelegramBotWebHostBuilder telegramBotHostBuilder)
telegramBotHostBuilder._handlers = handlers;
action?.Invoke(builder);
return builder;
options = configuration.GetSection(nameof(TelegratorOptions)).Get<TelegratorOptions>();
if (options is null)
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegratorOptions' was registered. This configuration is runtime required!");
}
/// <summary>
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
/// </summary>
public static IHostApplicationBuilder AddTelegratorWeb(this WebApplicationBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<ITelegramBotHostBuilder>? action = null)
CancellationTokenSource globallCancell = new CancellationTokenSource();
options.GlobalCancellationToken = globallCancell.Token;
services.AddSingleton(Options.Create(options));
services.AddKeyedSingleton("cancell", globallCancell);
if (handlers is not null && handlers is IHandlersManager manager)
{
AddTelegratorWebInternal(builder.Services, builder.Configuration, ((IHostApplicationBuilder)builder).Properties, ref handlers, options);
action?.Invoke(new TelegramBotWebHostBuilder(builder, handlers));
return builder;
ServiceDescriptor descriptor = new ServiceDescriptor(typeof(IHandlersProvider), manager);
services.Replace(descriptor);
services.AddSingleton(manager);
}
/// <summary>
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
/// </summary>
internal static void AddTelegratorWebInternal(IServiceCollection services, IConfiguration configuration, IDictionary<object, object> properties, [NotNull] ref IHandlersCollection? handlers, TelegratorOptions? options = null)
handlers ??= new HostHandlersCollection(services, options);
services.AddSingleton(handlers);
properties.Add(HostBuilderExtensions.HandlersCollectionPropertyKey, handlers);
if (!services.Any(srvc => srvc.ServiceType == typeof(IOptions<WebhookerOptions>)))
{
if (options == null)
WebhookerOptions? webhookerOptions = configuration.GetSection(nameof(WebhookerOptions)).Get<WebhookerOptions>();
if (webhookerOptions == null)
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'WebhookerOptions' was registered. This configuration is runtime required!");
services.AddSingleton(Options.Create(webhookerOptions));
}
if (!services.Any(srvc => srvc.ImplementationType == typeof(IOptions<TelegramBotClientOptions>)))
{
services.AddSingleton(Options.Create(new TelegramBotClientOptions(options.Token, options.BaseUrl, options.UseTestEnvironment)
{
options = configuration.GetSection(nameof(TelegratorOptions)).Get<TelegratorOptions>();
if (options == null)
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegratorOptions' wasn't 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(HandlersCollectionPropertyKey, handlers);
if (!services.Any(srvc => srvc.ImplementationType == typeof(IOptions<WebhookerOptions>)))
{
WebhookerOptions? webhookerOptions = configuration.GetSection(nameof(WebhookerOptions)).Get<WebhookerOptions>();
if (webhookerOptions == null)
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'WebhookerOptions' wasn't registered. This configuration is runtime required!");
services.AddSingleton(Options.Create(webhookerOptions));
}
if (!services.Any(srvc => srvc.ImplementationType == typeof(IOptions<TelegramBotClientOptions>)))
{
services.AddSingleton(Options.Create(new TelegramBotClientOptions(options.Token, options.BaseUrl, options.UseTestEnvironment)
{
RetryCount = options.RetryCount,
RetryThreshold = options.RetryThreshold
}));
}
services.AddTelegramBotHostDefaults();
services.AddTelegramWebhook();
RetryCount = options.RetryCount,
RetryThreshold = options.RetryThreshold
}));
}
/// <summary>
/// Searchs for <see cref="HostedUpdateWebhooker"/> hosted service inside hosts services
/// </summary>
/// <param name="services"></param>
/// <param name="webhooker"></param>
/// <returns></returns>
public static bool TryFindWebhooker(this IServiceProvider services, [NotNullWhen(true)] out HostedUpdateWebhooker? webhooker)
{
webhooker = services.GetServices<IHostedService>().FirstOrDefault(s => s is HostedUpdateWebhooker) as HostedUpdateWebhooker;
return webhooker != null;
}
/// <summary>
/// Replaces the initialization logic from TelegramBotWebHost constructor.
/// Initializes the bot and logs handlers on application startup.
/// </summary>
public static T UseTelegratorWeb<T>(this T app, bool dontMap = false) where T : IEndpointRouteBuilder, IHost
{
if (!app.ServiceProvider.TryFindWebhooker(out HostedUpdateWebhooker? webhooker))
throw new InvalidOperationException("No service for type 'Telegrator.Mediation.HostedUpdateWebhooker' has been registered.");
ITelegramBotInfo info = app.ServiceProvider.GetRequiredService<ITelegramBotInfo>();
IHandlersCollection handlers = app.ServiceProvider.GetRequiredService<IHandlersCollection>();
ILoggerFactory loggerFactory = app.ServiceProvider.GetRequiredService<ILoggerFactory>();
ILogger logger = loggerFactory.CreateLogger("Telegrator.Hosting.Web.TelegratorHost");
if (logger.IsEnabled(LogLevel.Information))
{
logger.LogInformation("Telegrator Bot ASP.NET WebHost started");
logger.LogInformation("Telegram Bot : {firstname}, @{usrname}, id:{id},", info.User.FirstName ?? "[NULL]", info.User.Username ?? "[NULL]", info.User.Id);
logger.LogHandlers(handlers);
}
if (!dontMap)
webhooker.MapWebhook(app);
return app;
}
/// <summary>
/// Allows to remap receiving webhook endpoint and map new route to webhost.
/// </summary>
/// <param name="app"></param>
/// <param name="webhookUri"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static T RemapWebhook<T>(this T app, string webhookUri) where T : IEndpointRouteBuilder, IHost
{
if (!app.ServiceProvider.TryFindWebhooker(out HostedUpdateWebhooker? webhooker))
throw new InvalidOperationException("No service for type 'Telegrator.Mediation.HostedUpdateWebhooker' has been registered.");
webhooker.RemapWebhook(app, webhookUri, default).GetAwaiter().GetResult();
return app;
}
/// <summary>
/// Registers <see cref="ITelegramBotClient"/> service with <see cref="HostedUpdateWebhooker"/> to receive updates using webhook
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddTelegramWebhook(this IServiceCollection services)
{
services.AddHttpClient<ITelegramBotClient>("tgwebhook").RemoveAllLoggers().AddTypedClient(TypedTelegramBotClientFactory);
services.AddHostedService<HostedUpdateWebhooker>();
return services;
}
private static ITelegramBotClient TypedTelegramBotClientFactory(HttpClient httpClient, IServiceProvider provider)
=> new TelegramBotClient(provider.GetRequiredService<IOptions<TelegramBotClientOptions>>().Value, httpClient);
services.AddTelegramBotHostDefaults();
services.AddTelegramWebhook();
}
}
/// <summary>
/// Provides useful methods to adjust Telegram bot Host
/// </summary>
public static class TelegramBotHostExtensions
{
/// <summary>
/// Searchs for <see cref="HostedUpdateWebhooker"/> hosted service inside hosts services
/// </summary>
/// <param name="services"></param>
/// <param name="webhooker"></param>
/// <returns></returns>
public static bool TryFindWebhooker(this IServiceProvider services, [NotNullWhen(true)] out HostedUpdateWebhooker? webhooker)
{
webhooker = services.GetServices<IHostedService>().FirstOrDefault(s => s is HostedUpdateWebhooker) as HostedUpdateWebhooker;
return webhooker != null;
}
/// <summary>
/// Replaces the initialization logic from TelegramBotWebHost constructor.
/// Initializes the bot and logs handlers on application startup.
/// </summary>
public static T UseTelegratorWeb<T>(this T botHost, bool dontMap = false) where T : IEndpointRouteBuilder, IHost
{
if (!botHost.ServiceProvider.TryFindWebhooker(out HostedUpdateWebhooker? webhooker))
throw new InvalidOperationException("No service for type 'Telegrator.Mediation.HostedUpdateWebhooker' has been registered.");
ITelegramBotInfo info = botHost.ServiceProvider.GetRequiredService<ITelegramBotInfo>();
IHandlersCollection handlers = botHost.ServiceProvider.GetRequiredService<IHandlersCollection>();
ILoggerFactory loggerFactory = botHost.ServiceProvider.GetRequiredService<ILoggerFactory>();
ILogger logger = loggerFactory.CreateLogger("Telegrator.Hosting.Web.TelegratorHost");
if (logger.IsEnabled(LogLevel.Information))
{
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.LogHandlers(handlers);
}
if (!dontMap)
webhooker.MapWebhook(botHost);
botHost.AddLoggingAdapter();
botHost.SetBotCommands();
return botHost;
}
/// <summary>
/// Allows to remap receiving webhook endpoint and map new route to webhost.
/// </summary>
/// <param name="app"></param>
/// <param name="webhookUri"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static T RemapWebhook<T>(this T app, string webhookUri) where T : IEndpointRouteBuilder, IHost
{
if (!app.ServiceProvider.TryFindWebhooker(out HostedUpdateWebhooker? webhooker))
throw new InvalidOperationException("No service for type 'Telegrator.Mediation.HostedUpdateWebhooker' has been registered.");
webhooker.RemapWebhook(app, webhookUri, default).GetAwaiter().GetResult();
return app;
}
/// <summary>
/// Registers <see cref="ITelegramBotClient"/> service with <see cref="HostedUpdateWebhooker"/> to receive updates using webhook
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddTelegramWebhook(this IServiceCollection services)
{
services.AddHttpClient<ITelegramBotClient>("tgwebhook").RemoveAllLoggers().AddTypedClient(TypedTelegramBotClientFactory);
services.AddHostedService<HostedUpdateWebhooker>();
return services;
}
private static ITelegramBotClient TypedTelegramBotClientFactory(HttpClient httpClient, IServiceProvider provider)
=> 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("Maintainability", "CA1510")]
[assembly: SuppressMessage("Style", "IDE0270")]
[assembly: SuppressMessage("Roslynator", "RCS1037")]
@@ -6,7 +6,4 @@ namespace Telegrator.Hosting;
/// <summary>
/// Represents a hosted telegram bots and services builder that helps manage configuration, logging, lifetime, and more.
/// </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/>
public class TelegramBotHostBuilder : ITelegramBotHostBuilder
{
private readonly HostApplicationBuilder _innerBuilder;
private readonly IHostApplicationBuilder _innerBuilder;
internal IHandlersCollection _handlers = null!;
/// <inheritdoc/>
@@ -30,7 +30,7 @@ public class TelegramBotHostBuilder : ITelegramBotHostBuilder
public IHostEnvironment Environment => _innerBuilder.Environment;
/// <inheritdoc/>
public IDictionary<object, object> Properties => ((IHostApplicationBuilder)_innerBuilder).Properties;
public IDictionary<object, object> Properties => _innerBuilder.Properties;
/// <inheritdoc/>
public IMetricsBuilder Metrics => _innerBuilder.Metrics;
@@ -39,7 +39,7 @@ public class TelegramBotHostBuilder : ITelegramBotHostBuilder
/// Initializes a new instance of the <see cref="TelegramBotHostBuilder"/> class.
/// </summary>
/// <param name="hostApplicationBuilder"></param>
public TelegramBotHostBuilder(HostApplicationBuilder hostApplicationBuilder)
public TelegramBotHostBuilder(IHostApplicationBuilder hostApplicationBuilder)
{
_innerBuilder = hostApplicationBuilder ?? throw new ArgumentNullException(nameof(hostApplicationBuilder));
}
@@ -49,23 +49,12 @@ public class TelegramBotHostBuilder : ITelegramBotHostBuilder
/// </summary>
/// <param name="hostApplicationBuilder"></param>
/// <param name="handlers"></param>
public TelegramBotHostBuilder(HostApplicationBuilder hostApplicationBuilder, IHandlersCollection handlers)
public TelegramBotHostBuilder(IHostApplicationBuilder hostApplicationBuilder, IHandlersCollection handlers)
{
_innerBuilder = hostApplicationBuilder ?? throw new ArgumentNullException(nameof(hostApplicationBuilder));
_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/>
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 Telegrator.Core;
using Telegrator.Core.States;
using Telegrator.Mediation;
namespace Telegrator.Polling;
namespace Telegrator.Mediation;
/// <inheritdoc/>
public class HostUpdateRouter : UpdateRouter
@@ -4,9 +4,8 @@ using Microsoft.Extensions.Options;
using Telegram.Bot;
using Telegram.Bot.Polling;
using Telegrator.Core;
using Telegrator.Mediation;
namespace Telegrator.Polling;
namespace Telegrator.Mediation;
/// <summary>
/// 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");
_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);
await updateReceiver.ReceiveAsync(_updateRouter, stoppingToken).ConfigureAwait(false);
@@ -17,41 +17,60 @@ public class HostHandlersCollection(IServiceCollection hostServiceColletion, Tel
{
switch (descriptor.Type)
{
default:
throw new Exception("Unknown descriptor type");
case DescriptorType.General:
{
if (descriptor.InstanceFactory != null)
{
Services.AddScoped(descriptor.HandlerType, _ => descriptor.InstanceFactory.Invoke());
else
Services.AddScoped(descriptor.HandlerType);
break;
}
Services.AddScoped(descriptor.HandlerType);
break;
}
case DescriptorType.Keyed:
{
if (descriptor.InstanceFactory != null)
{
Services.AddKeyedScoped(descriptor.HandlerType, descriptor.ServiceKey, (_, _) => descriptor.InstanceFactory.Invoke());
else
Services.AddKeyedScoped(descriptor.HandlerType, descriptor.ServiceKey);
break;
}
Services.AddKeyedScoped(descriptor.HandlerType, descriptor.ServiceKey);
break;
}
case DescriptorType.Singleton:
{
Services.AddSingleton(descriptor.HandlerType, descriptor.SingletonInstance ?? (descriptor.InstanceFactory != null
? descriptor.InstanceFactory.Invoke()
: throw new Exception()));
if (descriptor.SingletonInstance != null)
{
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;
}
case DescriptorType.Implicit:
{
Services.AddKeyedSingleton(descriptor.HandlerType, descriptor.ServiceKey, descriptor.SingletonInstance ?? (descriptor.InstanceFactory != null
? descriptor.InstanceFactory.Invoke()
: throw new Exception()));
if (descriptor.SingletonInstance != null)
{
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;
}
}
+39 -22
View File
@@ -15,7 +15,7 @@ using Telegrator.Core.Descriptors;
using Telegrator.Core.States;
using Telegrator.Hosting;
using Telegrator.Logging;
using Telegrator.Polling;
using Telegrator.Mediation;
using Telegrator.Providers;
using Telegrator.States;
@@ -34,20 +34,7 @@ public static class HostBuilderExtensions
/// <summary>
/// Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
/// </summary>
public static ITelegramBotHostBuilder AddTelegrator(this ITelegramBotHostBuilder 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)
public static IHostApplicationBuilder AddTelegrator(this IHostApplicationBuilder builder, TelegratorOptions? options = null, IHandlersCollection? handlers = null, Action<ITelegramBotHostBuilder>? action = null)
{
AddTelegratorInternal(builder.Services, builder.Configuration, ((IHostApplicationBuilder)builder).Properties, ref handlers, options);
action?.Invoke(new TelegramBotHostBuilder(builder, handlers));
@@ -72,6 +59,16 @@ public static class HostBuilderExtensions
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>
/// Replaces TelegramBotHostBuilder. Configures DI, options, and handlers.
/// </summary>
@@ -81,7 +78,7 @@ public static class HostBuilderExtensions
{
options = configuration.GetSection(nameof(TelegratorOptions)).Get<TelegratorOptions>();
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();
@@ -103,11 +100,11 @@ public static class HostBuilderExtensions
services.AddSingleton(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>();
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));
}
@@ -132,6 +129,18 @@ public static class HostBuilderExtensions
/// </summary>
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>
/// Registers <see cref="IStateStorage"/> service
/// </summary>
@@ -145,7 +154,7 @@ public static class ServicesCollectionExtensions
}
/// <summary>
/// Registers <see cref="TelegramBotHost"/> default services
/// Registers <see cref="Telegrator"/> default services
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
@@ -200,11 +209,14 @@ public static class TelegramBotHostExtensions
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.LogHandlers(handlers);
}
botHost.AddLoggingAdapter();
botHost.SetBotCommands();
return botHost;
}
@@ -219,7 +231,12 @@ public static class TelegramBotHostExtensions
IUpdateRouter router = botHost.Services.GetRequiredService<IUpdateRouter>();
IEnumerable<BotCommand> aliases = router.HandlersProvider.GetBotCommands();
client.SetMyCommands(aliases).Wait();
if (aliases.Any())
{
client.SetMyCommands(aliases)
.ConfigureAwait(false).GetAwaiter().GetResult();
}
return botHost;
}
@@ -255,7 +272,7 @@ public static class LoggerExtensions
StringBuilder logBuilder = new StringBuilder("Registered handlers : ");
if (!handlers.Keys.Any())
throw new Exception();
throw new Exception("No update types were registered");
foreach (UpdateType updateType in handlers.Keys)
{
@@ -56,7 +56,7 @@ public abstract class UpdateHandlerBase(UpdateType handlingUpdateType) : IUpdate
.ExecutePre(this, container, cancellationToken)
.ConfigureAwait(false);
if (!preResult.Positive)
if (!preResult.InterruptRouter)
return preResult;
}
catch (NotImplementedException)
@@ -69,7 +69,7 @@ public abstract class UpdateHandlerBase(UpdateType handlingUpdateType) : IUpdate
{
// Executing handler
Result execResult = await ExecuteInternal(container, cancellationToken).ConfigureAwait(false);
if (!execResult.Positive)
if (!execResult.InterruptRouter)
return execResult;
}
catch (NotImplementedException)
@@ -86,7 +86,7 @@ public abstract class UpdateHandlerBase(UpdateType handlingUpdateType) : IUpdate
.ExecutePost(this, container, cancellationToken)
.ConfigureAwait(false);
if (!postResult.Positive)
if (!postResult.InterruptRouter)
return postResult;
}
}
+17 -28
View File
@@ -19,27 +19,22 @@ namespace Telegrator.Mediation;
/// </summary>
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;
/// <inheritdoc/>
public IHandlersProvider HandlersProvider => _handlersProvider;
public TelegratorOptions Options { get; }
/// <inheritdoc/>
public IAwaitingProvider AwaitingProvider => _awaitingProvider;
public IHandlersProvider HandlersProvider { get; }
/// <inheritdoc/>
public IStateStorage StateStorage => _stateStorage;
public IAwaitingProvider AwaitingProvider { get; }
/// <inheritdoc/>
public TelegratorOptions Options => _options;
public IStateStorage StateStorage { get; }
/// <inheritdoc/>
public IUpdateHandlersPool HandlersPool => _HandlersPool;
public IUpdateHandlersPool HandlersPool { get; }
/// <inheritdoc/>
public IRouterExceptionHandler? ExceptionHandler { get; set; }
@@ -57,11 +52,11 @@ public class UpdateRouter : IUpdateRouter
/// <param name="botInfo"></param>
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, IStateStorage stateStorage, TelegratorOptions options, ITelegramBotInfo botInfo)
{
_options = options;
_handlersProvider = handlersProvider;
_awaitingProvider = awaitingProvider;
_stateStorage = stateStorage;
_HandlersPool = new UpdateHandlersPool(this, _options, _options.GlobalCancellationToken);
Options = options;
HandlersProvider = handlersProvider;
AwaitingProvider = awaitingProvider;
StateStorage = stateStorage;
HandlersPool = new UpdateHandlersPool(this, Options, Options.GlobalCancellationToken);
_botInfo = botInfo;
}
@@ -116,7 +111,7 @@ public class UpdateRouter : IUpdateRouter
if (lastResult == null)
break; // Smth went horribly wrong, better to stop routing
if (lastResult != null && !lastResult.RouteNext)
if (lastResult.InterruptRouter)
break;
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)
break; // Smth went horribly wrong, better to stop routing
if (lastResult != null && !lastResult.RouteNext)
if (lastResult.InterruptRouter)
break;
TelegratorLogging.LogTrace("Handler '{0}' requested route continuation (Update {1})", handlerInfo.DisplayString, handlerInfo.HandlingUpdate.Id);
@@ -249,16 +244,12 @@ public class UpdateRouter : IUpdateRouter
FiltersFallbackReport report = new FiltersFallbackReport(descriptor, filterContext);
Result filtersResult = descriptor.Filters.Validate(filterContext, descriptor.FormReport, ref report);
if (filtersResult.RouteNext)
{
Result fallbackResult = handlerInstance.FiltersFallback(report, client, cancellationToken).Result;
breakRouting = !fallbackResult.RouteNext;
if (filtersResult.InterruptRouter)
return null;
}
else if (!filtersResult.Positive)
{
return null;
}
Result fallbackResult = handlerInstance.FiltersFallback(report, client, cancellationToken).Result;
breakRouting = fallbackResult.InterruptRouter;
return null;
}
return new DescribedHandlerDescriptor(descriptor, this, AwaitingProvider, StateStorage, client, handlerInstance, filterContext, descriptor.DisplayString);
@@ -307,6 +298,4 @@ public class UpdateRouter : IUpdateRouter
TelegratorLogging.LogTrace(sb.ToString());
}
private class BreakDescribingException : Exception { }
}
+8 -14
View File
@@ -10,29 +10,23 @@ namespace Telegrator;
/// </summary>
public sealed class Result
{
private static readonly Result ok = new Result(true, false, null);
private static readonly Result fault = new Result(false, false, null);
private static readonly Result next = new Result(true, true, null);
private static readonly Result ok = new Result(true, null);
private static readonly Result fault = new Result(true, null);
private static readonly Result next = new Result(false, null);
/// <summary>
/// Is result positive
/// Tell router to stop describing
/// </summary>
public bool Positive { get; }
/// <summary>
/// Should router search for next matching handler
/// </summary>
public bool RouteNext { get; }
public bool InterruptRouter { get; }
/// <summary>
/// Exact type that router should search
/// </summary>
public Type? NextType { get; }
internal Result(bool positive, bool routeNext, Type? nextType)
internal Result(bool interruptRouter, Type? nextType)
{
Positive = positive;
RouteNext = routeNext;
InterruptRouter = interruptRouter;
NextType = nextType;
}
@@ -79,5 +73,5 @@ public sealed class Result
/// <typeparam name="T"></typeparam>
/// <returns></returns>
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);
}
/// <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>
/// Enumerates objects in a <paramref name="source"/> and executes an <paramref name="action"/> on each one
/// </summary>
@@ -188,7 +203,7 @@ public static partial class ReflectionExtensions
/// </summary>
/// <param name="type"></param>
/// <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);
/// <summary>
@@ -260,6 +275,7 @@ public static partial class StringExtensions
{
char[] chars = target.ToCharArray();
int index = chars.IndexOf(char.IsLetter);
chars[index] = char.ToUpper(chars[index]);
return new string(chars);
}
@@ -273,6 +289,7 @@ public static partial class StringExtensions
{
char[] chars = target.ToCharArray();
int index = chars.IndexOf(char.IsLetter);
chars[index] = char.ToLower(chars[index]);
return new string(chars);
}
+5 -8
View File
@@ -29,7 +29,7 @@ public class TelegratorClient : TelegramBotClient, ITelegratorBot, ICollectingPr
public ITelegramBotInfo BotInfo { get; private set; }
/// <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>
/// 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.
/// </summary>
/// <param name="receiverOptions">Optional receiver options for configuring update polling.</param>
/// <param name="cancellationToken">The cancellation token to stop receiving updates.</param>
public void StartReceiving(ReceiverOptions? receiverOptions = null, CancellationToken cancellationToken = default)
/// <param name="globalCancellationToken">The cancellation token to stop receiving updates.</param>
public void StartReceiving(ReceiverOptions? receiverOptions = null, CancellationToken globalCancellationToken = default)
{
if (Options.GlobalCancellationToken == CancellationToken.None)
Options.GlobalCancellationToken = cancellationToken;
Options.GlobalCancellationToken = globalCancellationToken;
HandlersProvider handlerProvider = new HandlersProvider(Handlers, Options);
AwaitingProvider awaitingProvider = new AwaitingProvider(Options);
@@ -82,8 +82,7 @@ public class TelegratorClient : TelegramBotClient, ITelegratorBot, ICollectingPr
// Log startup
TelegratorLogging.LogInformation($"Telegrator bot starting up - BotId: {BotInfo.User.Id}, Username: {BotInfo.User.Username}, MaxParallelHandlers: {Options.MaximumParallelWorkingHandlers ?? -1}");
StartReceivingInternal(receiverOptions, cancellationToken);
StartReceivingInternal(receiverOptions, globalCancellationToken);
}
/// <summary>
@@ -115,6 +114,4 @@ public class TelegratorClient : TelegramBotClient, ITelegratorBot, ICollectingPr
TelegratorLogging.LogInformation("Telegrator bot stopped (cancelled)");
}
}
}
+22 -2
View File
@@ -412,6 +412,26 @@ public static partial class HandlersCollectionExtensions
"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>
/// Collects all public handlers from the current app domain.
/// Scans for types that implement handlers and adds them to the collection.
@@ -438,7 +458,7 @@ public static partial class HandlersCollectionExtensions
{
(collectingTarget ?? Assembly.GetCallingAssembly())
.GetExportedTypes()
.Where(type => type.GetCustomAttribute<DontCollectAttribute>() == null && type.IsHandlerRealization())
.Where(type => type.GetCustomAttribute<DontCollectAttribute>() == null && type.IsHandlerImplementation())
.ForEach(type => handlers.AddHandler(type));
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>
public static IHandlersCollection AddHandler(this IHandlersCollection handlers, Type handlerType)
{
if (!handlerType.IsHandlerRealization())
if (!handlerType.IsHandlerImplementation())
throw new Exception();
if (handlerType.IsCustomDescriptorsProvider())