mirror of
https://github.com/yawaflua/Telegram.Net.git
synced 2025-12-08 19:49:30 +02:00
Add logger
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Telegram.Net.Models;
|
||||
using Telegram.Net.Services;
|
||||
|
||||
@@ -8,7 +10,8 @@ public static class ServiceBindings
|
||||
{
|
||||
public static IServiceCollection ConnectTelegram(this IServiceCollection isc, TelegramBotConfig config)
|
||||
{
|
||||
isc.AddHostedService<TelegramHostedService>(k => new(config, isc));
|
||||
var logger = isc.BuildServiceProvider().GetRequiredService<ILogger<TelegramHostedService>>();
|
||||
isc.AddHostedService<TelegramHostedService>(k => new(config, isc, logger));
|
||||
return isc;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices.JavaScript;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Payments;
|
||||
@@ -22,169 +24,241 @@ public class TelegramHostedService : IHostedService
|
||||
internal Dictionary<string, Func<ITelegramBotClient, InlineQuery ,CancellationToken, Task>> InlineHandler { get; } = new();
|
||||
internal Func<ITelegramBotClient, PreCheckoutQuery,CancellationToken, Task>? PreCheckoutHandler { get; set; }
|
||||
internal List<Func<ITelegramBotClient, Update, CancellationToken, Task>> DefaultUpdateHandler { get; } = new();
|
||||
|
||||
public TelegramHostedService(ITelegramBotConfig config, IServiceCollection isc)
|
||||
internal static ILogger<TelegramHostedService> _logger;
|
||||
|
||||
public TelegramHostedService(ITelegramBotConfig config, IServiceCollection isc, ILogger<TelegramHostedService> logger)
|
||||
{
|
||||
Client = new TelegramBotClient(config.Token);
|
||||
Config = config;
|
||||
this.isc = isc;
|
||||
try
|
||||
{
|
||||
_logger = logger;
|
||||
Client = new TelegramBotClient(config.Token);
|
||||
Config = config;
|
||||
this.isc = isc;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Critical, new EventId(), ex, "Catched exception when creating TelegramHostedService: ");
|
||||
}
|
||||
}
|
||||
internal static bool IsValidHandlerMethod(MethodInfo method, Type parameterType)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
return method.ReturnType == typeof(Task) &&
|
||||
parameters.Length == 3 &&
|
||||
parameters[0].ParameterType == typeof(ITelegramBotClient) &&
|
||||
parameters[1].ParameterType == parameterType &&
|
||||
parameters[2].ParameterType == typeof(CancellationToken);
|
||||
try
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
return method.ReturnType == typeof(Task) &&
|
||||
parameters.Length == 3 &&
|
||||
parameters[0].ParameterType == typeof(ITelegramBotClient) &&
|
||||
parameters[1].ParameterType == parameterType &&
|
||||
parameters[2].ParameterType == typeof(CancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Catched exception in parsing and checking params.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static Func<ITelegramBotClient, T, CancellationToken, Task> CreateDelegate<T>(MethodInfo method)
|
||||
{
|
||||
var delegateType = typeof(Func<ITelegramBotClient, T, CancellationToken, Task>);
|
||||
return (Delegate.CreateDelegate(delegateType, null, method) as Func<ITelegramBotClient, T, CancellationToken, Task>)!;
|
||||
try
|
||||
{
|
||||
var delegateType = typeof(Func<ITelegramBotClient, T, CancellationToken, Task>);
|
||||
return (Delegate.CreateDelegate(delegateType, null, method) as
|
||||
Func<ITelegramBotClient, T, CancellationToken, Task>)!;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Critical, new EventId(), ex, "Catched exception in CreateDelegate function: ");
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal async Task AddAttributes(CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
var implementations = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes())
|
||||
.Where(t => typeof(IUpdatePollingService).IsAssignableFrom(t) && !t.IsInterface);
|
||||
|
||||
foreach (var implementation in implementations)
|
||||
try
|
||||
{
|
||||
isc.AddSingleton(implementation);
|
||||
}
|
||||
|
||||
var methods = implementations
|
||||
.SelectMany(t => t.GetMethods(
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Public | BindingFlags.NonPublic |
|
||||
BindingFlags.DeclaredOnly))
|
||||
.Where(m =>
|
||||
m.GetCustomAttribute<CommandAttribute>() != null ||
|
||||
m.GetCustomAttribute<CallbackAttribute>() != null ||
|
||||
m.GetCustomAttribute<EditMessageAttribute>() != null ||
|
||||
m.GetCustomAttribute<InlineAttribute>() != null ||
|
||||
m.GetCustomAttribute<PreCheckoutAttribute>() != null ||
|
||||
m.GetCustomAttribute<UpdateAttribute>() != null);
|
||||
var implementations = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes())
|
||||
.Where(t => typeof(IUpdatePollingService).IsAssignableFrom(t) && !t.IsInterface);
|
||||
|
||||
foreach (var method in methods)
|
||||
{
|
||||
foreach (var implementation in implementations)
|
||||
{
|
||||
isc.AddScoped(implementation);
|
||||
}
|
||||
|
||||
var methods = implementations
|
||||
.SelectMany(t => t.GetMethods(
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Public | BindingFlags.NonPublic |
|
||||
BindingFlags.DeclaredOnly))
|
||||
.Where(m =>
|
||||
m.GetCustomAttribute<CommandAttribute>() != null ||
|
||||
m.GetCustomAttribute<CallbackAttribute>() != null ||
|
||||
m.GetCustomAttribute<EditMessageAttribute>() != null ||
|
||||
m.GetCustomAttribute<InlineAttribute>() != null ||
|
||||
m.GetCustomAttribute<PreCheckoutAttribute>() != null ||
|
||||
m.GetCustomAttribute<UpdateAttribute>() != null);
|
||||
|
||||
if (methods.Count() == 0)
|
||||
{
|
||||
_logger.LogWarning("Not founded methods with attributes.");
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
|
||||
var commandAttr = method.GetCustomAttribute<CommandAttribute>();
|
||||
if (commandAttr != null)
|
||||
foreach (var method in methods)
|
||||
{
|
||||
if (IsValidHandlerMethod(method, typeof(Message)))
|
||||
var commandAttr = method.GetCustomAttribute<CommandAttribute>();
|
||||
if (commandAttr != null)
|
||||
{
|
||||
var handler = CreateDelegate<Message>(method);
|
||||
CommandHandler.TryAdd(commandAttr.Command, handler);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (IsValidHandlerMethod(method, typeof(Message)))
|
||||
{
|
||||
var handler = CreateDelegate<Message>(method);
|
||||
if (!CommandHandler.TryAdd(commandAttr.Command, handler))
|
||||
throw new Exception($"Failed to add in commandHandler: {commandAttr.Command}");
|
||||
}
|
||||
|
||||
var callbackAttr = method.GetCustomAttribute<CallbackAttribute>();
|
||||
if (callbackAttr != null)
|
||||
{
|
||||
if (IsValidHandlerMethod(method, typeof(CallbackQuery)))
|
||||
{
|
||||
var handler = CreateDelegate<CallbackQuery>(method);
|
||||
CallbackQueryHandler.TryAdd(callbackAttr.QueryId, handler);
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var editMessageAttr = method.GetCustomAttribute<EditMessageAttribute>();
|
||||
if (editMessageAttr != null)
|
||||
{
|
||||
if (IsValidHandlerMethod(method, typeof(Message)))
|
||||
var callbackAttr = method.GetCustomAttribute<CallbackAttribute>();
|
||||
if (callbackAttr != null)
|
||||
{
|
||||
var handler = CreateDelegate<Message>(method);
|
||||
EditedMessageHandler.Add(handler);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (IsValidHandlerMethod(method, typeof(CallbackQuery)))
|
||||
{
|
||||
var handler = CreateDelegate<CallbackQuery>(method);
|
||||
if (!CallbackQueryHandler.TryAdd(callbackAttr.QueryId, handler))
|
||||
throw new Exception($"Failed to add in callbacKQuery: {callbackAttr.QueryId}");;
|
||||
}
|
||||
|
||||
var inlineAttr = method.GetCustomAttribute<InlineAttribute>();
|
||||
if (inlineAttr != null)
|
||||
{
|
||||
if (IsValidHandlerMethod(method, typeof(InlineQuery)))
|
||||
{
|
||||
var handler = CreateDelegate<InlineQuery>(method);
|
||||
InlineHandler.TryAdd(inlineAttr.InlineId, handler);
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var preCheckoutAttr = method.GetCustomAttribute<PreCheckoutAttribute>();
|
||||
if (preCheckoutAttr != null)
|
||||
{
|
||||
if (IsValidHandlerMethod(method, typeof(PreCheckoutQuery)))
|
||||
var editMessageAttr = method.GetCustomAttribute<EditMessageAttribute>();
|
||||
if (editMessageAttr != null)
|
||||
{
|
||||
var handler = CreateDelegate<PreCheckoutQuery>(method);
|
||||
PreCheckoutHandler = handler;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (IsValidHandlerMethod(method, typeof(Message)))
|
||||
{
|
||||
var handler = CreateDelegate<Message>(method);
|
||||
EditedMessageHandler.Add(handler);
|
||||
}
|
||||
|
||||
var updateAttr = method.GetCustomAttribute<UpdateAttribute>();
|
||||
if (updateAttr != null)
|
||||
{
|
||||
if (IsValidHandlerMethod(method, typeof(Update)))
|
||||
{
|
||||
var handler = CreateDelegate<Update>(method);
|
||||
DefaultUpdateHandler.Add(handler);
|
||||
continue;
|
||||
}
|
||||
|
||||
var inlineAttr = method.GetCustomAttribute<InlineAttribute>();
|
||||
if (inlineAttr != null)
|
||||
{
|
||||
if (IsValidHandlerMethod(method, typeof(InlineQuery)))
|
||||
{
|
||||
var handler = CreateDelegate<InlineQuery>(method);
|
||||
if (!InlineHandler.TryAdd(inlineAttr.InlineId, handler))
|
||||
throw new Exception($"Failed to add in inlineHandler: {inlineAttr.InlineId}");;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var preCheckoutAttr = method.GetCustomAttribute<PreCheckoutAttribute>();
|
||||
if (preCheckoutAttr != null)
|
||||
{
|
||||
if (IsValidHandlerMethod(method, typeof(PreCheckoutQuery)))
|
||||
{
|
||||
var handler = CreateDelegate<PreCheckoutQuery>(method);
|
||||
PreCheckoutHandler = handler;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var updateAttr = method.GetCustomAttribute<UpdateAttribute>();
|
||||
if (updateAttr != null)
|
||||
{
|
||||
if (IsValidHandlerMethod(method, typeof(Update)))
|
||||
{
|
||||
var handler = CreateDelegate<Update>(method);
|
||||
DefaultUpdateHandler.Add(handler);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Critical, new EventId(), ex, "Catched new exception when added methods: ");
|
||||
}
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
internal async Task UpdateHandler(ITelegramBotClient client, Update update, CancellationToken ctx)
|
||||
{
|
||||
switch (update)
|
||||
try
|
||||
{
|
||||
case { Message: { } message }:
|
||||
await CommandHandler.FirstOrDefault(k => message.Text!.StartsWith(k.Key)).Value(client, message, ctx);
|
||||
break;
|
||||
case { EditedMessage: { } message }:
|
||||
EditedMessageHandler.ForEach(async k => await k(client, message, ctx));
|
||||
break;
|
||||
case { CallbackQuery: { } callbackQuery }:
|
||||
await CallbackQueryHandler.FirstOrDefault(k => callbackQuery.Data!.StartsWith(k.Key)).Value(client, callbackQuery, ctx);
|
||||
break;
|
||||
case { InlineQuery: { } inlineQuery }:
|
||||
await InlineHandler.FirstOrDefault(k => inlineQuery.Id.StartsWith(k.Key)).Value(client, inlineQuery, ctx);
|
||||
break;
|
||||
case { PreCheckoutQuery: { } preCheckoutQuery }:
|
||||
if (PreCheckoutHandler != null) await PreCheckoutHandler(client, preCheckoutQuery, ctx);
|
||||
break;
|
||||
default:
|
||||
DefaultUpdateHandler.ForEach(async k => await k(client, update, ctx));
|
||||
break;
|
||||
switch (update)
|
||||
{
|
||||
case { Message: { } message }:
|
||||
await CommandHandler.FirstOrDefault(k => message.Text!.StartsWith(k.Key))
|
||||
.Value(client, message, ctx);
|
||||
break;
|
||||
case { EditedMessage: { } message }:
|
||||
EditedMessageHandler.ForEach(async k => await k(client, message, ctx));
|
||||
break;
|
||||
case { CallbackQuery: { } callbackQuery }:
|
||||
await CallbackQueryHandler.FirstOrDefault(k => callbackQuery.Data!.StartsWith(k.Key))
|
||||
.Value(client, callbackQuery, ctx);
|
||||
break;
|
||||
case { InlineQuery: { } inlineQuery }:
|
||||
await InlineHandler.FirstOrDefault(k => inlineQuery.Id.StartsWith(k.Key))
|
||||
.Value(client, inlineQuery, ctx);
|
||||
break;
|
||||
case { PreCheckoutQuery: { } preCheckoutQuery }:
|
||||
if (PreCheckoutHandler != null) await PreCheckoutHandler(client, preCheckoutQuery, ctx);
|
||||
break;
|
||||
default:
|
||||
DefaultUpdateHandler.ForEach(async k => await k(client, update, ctx));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, new EventId(), ex, "Catched exception in UpdateHandler: ");
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "AsyncVoidLambda")]
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await AddAttributes(cancellationToken);
|
||||
try
|
||||
{
|
||||
await AddAttributes(cancellationToken);
|
||||
|
||||
|
||||
|
||||
Client.StartReceiving(
|
||||
UpdateHandler,
|
||||
Config.errorHandler ?? ((_, _, _) => Task.CompletedTask),
|
||||
Config.ReceiverOptions,
|
||||
cancellationToken);
|
||||
Client.StartReceiving(
|
||||
UpdateHandler,
|
||||
Config.errorHandler ?? ((_, _, _) => Task.CompletedTask),
|
||||
Config.ReceiverOptions,
|
||||
cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Critical, new EventId(), ex, "Failed to start. Catched exception: ");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await Client.DropPendingUpdates(cancellationToken);
|
||||
try
|
||||
{
|
||||
await Client.DropPendingUpdates(cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogCritical(ex, "Failed to stop. Exception: ");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ using Telegram.Net.Services;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
@@ -22,6 +23,7 @@ namespace Telegram.Tests
|
||||
{
|
||||
private TelegramBotConfig _configMock;
|
||||
private ServiceCollection _services;
|
||||
private ILogger<TelegramHostedService> logger;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
@@ -31,7 +33,8 @@ namespace Telegram.Tests
|
||||
.AddJsonFile("appsettings.json", false)
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
var loggerFactory = new LoggerFactory();
|
||||
logger = loggerFactory.CreateLogger<TelegramHostedService>();
|
||||
_configMock = new TelegramBotConfig(conf.GetValue<string>("telegram_test_token") ?? throw new Exception("Provide telegram token first"));
|
||||
_services = new ServiceCollection();
|
||||
}
|
||||
@@ -67,7 +70,7 @@ namespace Telegram.Tests
|
||||
public void AddAttributes_RegistersCommandHandlersCorrectly()
|
||||
{
|
||||
_services.AddSingleton<TestHandler>();
|
||||
var service = new TelegramHostedService(_configMock, _services);
|
||||
var service = new TelegramHostedService(_configMock, _services, logger);
|
||||
|
||||
service.AddAttributes(CancellationToken.None).Wait();
|
||||
|
||||
@@ -132,8 +135,8 @@ namespace Telegram.Tests
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<TelegramHostedServiceTests.TestHandler>();
|
||||
_botClientMock = new Mock<ITelegramBotClient>();
|
||||
|
||||
_hostedService = new TelegramHostedService(_configMock.Object, services);
|
||||
var logger = new LoggerFactory().CreateLogger<TelegramHostedService>();
|
||||
_hostedService = new TelegramHostedService(_configMock.Object, services, logger);
|
||||
_hostedService.AddAttributes(CancellationToken.None).Wait();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user