* 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:
@@ -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
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user