From 343ab4de7f11cd0c8b1d33b6c4685740b2958a28 Mon Sep 17 00:00:00 2001 From: Dmitri Shimanski Date: Thu, 20 Mar 2025 23:34:34 +0200 Subject: [PATCH] Add logger --- Telegram.Net/ServiceBindings.cs | 5 +- .../Services/TelegramHostedService.cs | 300 +++++++++++------- .../TestTelegramHostedService.cs | 11 +- 3 files changed, 198 insertions(+), 118 deletions(-) diff --git a/Telegram.Net/ServiceBindings.cs b/Telegram.Net/ServiceBindings.cs index 9a1d0a1..9dc798f 100644 --- a/Telegram.Net/ServiceBindings.cs +++ b/Telegram.Net/ServiceBindings.cs @@ -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(k => new(config, isc)); + var logger = isc.BuildServiceProvider().GetRequiredService>(); + isc.AddHostedService(k => new(config, isc, logger)); return isc; } } \ No newline at end of file diff --git a/Telegram.Net/Services/TelegramHostedService.cs b/Telegram.Net/Services/TelegramHostedService.cs index 3f05c4f..f54fb21 100644 --- a/Telegram.Net/Services/TelegramHostedService.cs +++ b/Telegram.Net/Services/TelegramHostedService.cs @@ -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> InlineHandler { get; } = new(); internal Func? PreCheckoutHandler { get; set; } internal List> DefaultUpdateHandler { get; } = new(); - - public TelegramHostedService(ITelegramBotConfig config, IServiceCollection isc) + internal static ILogger _logger; + + public TelegramHostedService(ITelegramBotConfig config, IServiceCollection isc, ILogger 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 CreateDelegate(MethodInfo method) { - var delegateType = typeof(Func); - return (Delegate.CreateDelegate(delegateType, null, method) as Func)!; + try + { + var delegateType = typeof(Func); + return (Delegate.CreateDelegate(delegateType, null, method) as + Func)!; + } + 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() != null || - m.GetCustomAttribute() != null || - m.GetCustomAttribute() != null || - m.GetCustomAttribute() != null || - m.GetCustomAttribute() != null || - m.GetCustomAttribute() != 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() != null || + m.GetCustomAttribute() != null || + m.GetCustomAttribute() != null || + m.GetCustomAttribute() != null || + m.GetCustomAttribute() != null || + m.GetCustomAttribute() != null); + + if (methods.Count() == 0) + { + _logger.LogWarning("Not founded methods with attributes."); + } + await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); - var commandAttr = method.GetCustomAttribute(); - if (commandAttr != null) + foreach (var method in methods) { - if (IsValidHandlerMethod(method, typeof(Message))) + var commandAttr = method.GetCustomAttribute(); + if (commandAttr != null) { - var handler = CreateDelegate(method); - CommandHandler.TryAdd(commandAttr.Command, handler); - } - continue; - } + if (IsValidHandlerMethod(method, typeof(Message))) + { + var handler = CreateDelegate(method); + if (!CommandHandler.TryAdd(commandAttr.Command, handler)) + throw new Exception($"Failed to add in commandHandler: {commandAttr.Command}"); + } - var callbackAttr = method.GetCustomAttribute(); - if (callbackAttr != null) - { - if (IsValidHandlerMethod(method, typeof(CallbackQuery))) - { - var handler = CreateDelegate(method); - CallbackQueryHandler.TryAdd(callbackAttr.QueryId, handler); + continue; } - continue; - } - var editMessageAttr = method.GetCustomAttribute(); - if (editMessageAttr != null) - { - if (IsValidHandlerMethod(method, typeof(Message))) + var callbackAttr = method.GetCustomAttribute(); + if (callbackAttr != null) { - var handler = CreateDelegate(method); - EditedMessageHandler.Add(handler); - } - continue; - } + if (IsValidHandlerMethod(method, typeof(CallbackQuery))) + { + var handler = CreateDelegate(method); + if (!CallbackQueryHandler.TryAdd(callbackAttr.QueryId, handler)) + throw new Exception($"Failed to add in callbacKQuery: {callbackAttr.QueryId}");; + } - var inlineAttr = method.GetCustomAttribute(); - if (inlineAttr != null) - { - if (IsValidHandlerMethod(method, typeof(InlineQuery))) - { - var handler = CreateDelegate(method); - InlineHandler.TryAdd(inlineAttr.InlineId, handler); + continue; } - continue; - } - var preCheckoutAttr = method.GetCustomAttribute(); - if (preCheckoutAttr != null) - { - if (IsValidHandlerMethod(method, typeof(PreCheckoutQuery))) + var editMessageAttr = method.GetCustomAttribute(); + if (editMessageAttr != null) { - var handler = CreateDelegate(method); - PreCheckoutHandler = handler; - } - continue; - } + if (IsValidHandlerMethod(method, typeof(Message))) + { + var handler = CreateDelegate(method); + EditedMessageHandler.Add(handler); + } - var updateAttr = method.GetCustomAttribute(); - if (updateAttr != null) - { - if (IsValidHandlerMethod(method, typeof(Update))) - { - var handler = CreateDelegate(method); - DefaultUpdateHandler.Add(handler); + continue; + } + + var inlineAttr = method.GetCustomAttribute(); + if (inlineAttr != null) + { + if (IsValidHandlerMethod(method, typeof(InlineQuery))) + { + var handler = CreateDelegate(method); + if (!InlineHandler.TryAdd(inlineAttr.InlineId, handler)) + throw new Exception($"Failed to add in inlineHandler: {inlineAttr.InlineId}");; + } + + continue; + } + + var preCheckoutAttr = method.GetCustomAttribute(); + if (preCheckoutAttr != null) + { + if (IsValidHandlerMethod(method, typeof(PreCheckoutQuery))) + { + var handler = CreateDelegate(method); + PreCheckoutHandler = handler; + } + + continue; + } + + var updateAttr = method.GetCustomAttribute(); + if (updateAttr != null) + { + if (IsValidHandlerMethod(method, typeof(Update))) + { + var handler = CreateDelegate(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: "); + } } } \ No newline at end of file diff --git a/tests/Telegram.Tests/TestTelegramHostedService.cs b/tests/Telegram.Tests/TestTelegramHostedService.cs index 656485f..f26117d 100644 --- a/tests/Telegram.Tests/TestTelegramHostedService.cs +++ b/tests/Telegram.Tests/TestTelegramHostedService.cs @@ -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 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(); _configMock = new TelegramBotConfig(conf.GetValue("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(); - 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(); _botClientMock = new Mock(); - - _hostedService = new TelegramHostedService(_configMock.Object, services); + var logger = new LoggerFactory().CreateLogger(); + _hostedService = new TelegramHostedService(_configMock.Object, services, logger); _hostedService.AddAttributes(CancellationToken.None).Wait(); }