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