From f2b4864237f6b5e8fad92456a1d7fca63fdac247 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Wed, 20 Mar 2024 19:48:43 +0400 Subject: [PATCH] Add infrastructure for update handling --- Directory.Packages.props | 37 +++++---- .../Nocr.TelegramListener.AppServices.csproj | 4 +- .../ServiceCollectionExtensions.cs | 14 +++- .../Handlers/IMessageHandler.cs | 8 ++ .../Handlers/IUpdateHandler.cs | 8 ++ .../Handlers/MessageHandler.cs | 31 +++++++ .../UpdateListeners/Handlers/UpdateHandler.cs | 82 +++++++++++++++++++ .../ITelegramClientContainer.cs | 14 ++++ .../TelegramClientContainer.cs | 58 +++++++++++++ .../TelegramObjectExtensions.cs | 16 ++++ .../UpdateListeners/TelegramRegistry.cs | 34 ++++++++ .../UpdateListenerBackgroundService.cs | 70 ++++++++++++++++ .../UpdateListeners/WTelegramClientOptions.cs | 19 +++++ .../IRepeatableBackgroundServiceHandler.cs | 6 ++ .../RepeatableBackgroundService.cs | 42 ++++++++++ .../RepeatableServiceOptions.cs | 8 ++ .../Nocr.TelegramListener.Core.csproj | 9 +- .../Infrastructure/Startup.cs | 3 +- 18 files changed, 443 insertions(+), 20 deletions(-) create mode 100644 src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/IMessageHandler.cs create mode 100644 src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/IUpdateHandler.cs create mode 100644 src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/MessageHandler.cs create mode 100644 src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/UpdateHandler.cs create mode 100644 src/Nocr.TelegramListener.AppServices/UpdateListeners/ITelegramClientContainer.cs create mode 100644 src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramClientContainer.cs create mode 100644 src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramObjectExtensions.cs create mode 100644 src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramRegistry.cs create mode 100644 src/Nocr.TelegramListener.AppServices/UpdateListeners/UpdateListenerBackgroundService.cs create mode 100644 src/Nocr.TelegramListener.AppServices/UpdateListeners/WTelegramClientOptions.cs create mode 100644 src/Nocr.TelegramListener.Core/BackgroundServices/IRepeatableBackgroundServiceHandler.cs create mode 100644 src/Nocr.TelegramListener.Core/BackgroundServices/RepeatableBackgroundService.cs create mode 100644 src/Nocr.TelegramListener.Core/BackgroundServices/RepeatableServiceOptions.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 52b8793..dc25262 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,16 +1,23 @@ - - net8.0 - enable - enable - 8.0.0 - - - - - - - - - - + + net8.0 + enable + enable + 8.0.0 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Nocr.TelegramListener.AppServices/Nocr.TelegramListener.AppServices.csproj b/src/Nocr.TelegramListener.AppServices/Nocr.TelegramListener.AppServices.csproj index ae0057e..96f2b81 100644 --- a/src/Nocr.TelegramListener.AppServices/Nocr.TelegramListener.AppServices.csproj +++ b/src/Nocr.TelegramListener.AppServices/Nocr.TelegramListener.AppServices.csproj @@ -2,10 +2,12 @@ + + - + diff --git a/src/Nocr.TelegramListener.AppServices/ServiceCollectionExtensions.cs b/src/Nocr.TelegramListener.AppServices/ServiceCollectionExtensions.cs index e72941d..330e802 100644 --- a/src/Nocr.TelegramListener.AppServices/ServiceCollectionExtensions.cs +++ b/src/Nocr.TelegramListener.AppServices/ServiceCollectionExtensions.cs @@ -1,15 +1,25 @@ +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Nocr.TelegramListener.AppServices.UpdateListeners; +using Nocr.TelegramListener.AppServices.UpdateListeners.Handlers; namespace Nocr.TelegramListener.AppServices; public static class ServiceCollectionExtensions { - public static IServiceCollection AddAppServices(this IServiceCollection services) + public static IServiceCollection AddAppServices(this IServiceCollection services, IConfiguration configuration) { - if (services == null) + if (services == null) throw new ArgumentNullException(nameof(services)); // Add registrations here + services.Configure(configuration.GetSection(nameof(WTelegramClientOptions))); + services.AddHostedService(); + + services.AddScoped(); + services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); return services; } diff --git a/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/IMessageHandler.cs b/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/IMessageHandler.cs new file mode 100644 index 0000000..bad3987 --- /dev/null +++ b/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/IMessageHandler.cs @@ -0,0 +1,8 @@ +using TL; + +namespace Nocr.TelegramListener.AppServices.UpdateListeners.Handlers; + +public interface IMessageHandler +{ + Task HandleMessage(MessageBase messageBase, bool edit = false); +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/IUpdateHandler.cs b/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/IUpdateHandler.cs new file mode 100644 index 0000000..573ba85 --- /dev/null +++ b/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/IUpdateHandler.cs @@ -0,0 +1,8 @@ +using TL; + +namespace Nocr.TelegramListener.AppServices.UpdateListeners.Handlers; + +public interface IUpdateHandler +{ + Task HandleUpdate(UpdatesBase updates, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/MessageHandler.cs b/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/MessageHandler.cs new file mode 100644 index 0000000..7023b30 --- /dev/null +++ b/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/MessageHandler.cs @@ -0,0 +1,31 @@ +using TL; + +namespace Nocr.TelegramListener.AppServices.UpdateListeners.Handlers; + +public sealed class MessageHandler : IMessageHandler +{ + private readonly TelegramRegistry _telegramRegistry; + + public MessageHandler(TelegramRegistry telegramRegistry) + { + _telegramRegistry = telegramRegistry ?? throw new ArgumentNullException(nameof(telegramRegistry)); + } + + public Task HandleMessage(MessageBase messageBase, bool edit = false) + { + if (edit) Console.Write("(Edit): "); + switch (messageBase) + { + case Message m: + Console.WriteLine( + $"{m.from_id.Peer(_telegramRegistry.Users, _telegramRegistry.Chats) ?? m.post_author} in {m.peer_id.Peer(_telegramRegistry.Users, _telegramRegistry.Chats)}> {m.message}"); + break; + case MessageService ms: + Console.WriteLine( + $"{ms.from_id.Peer(_telegramRegistry.Users, _telegramRegistry.Chats)} in {ms.peer_id.Peer(_telegramRegistry.Users, _telegramRegistry.Chats)} [{ms.action.GetType().Name[13..]}]"); + break; + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/UpdateHandler.cs b/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/UpdateHandler.cs new file mode 100644 index 0000000..159eea2 --- /dev/null +++ b/src/Nocr.TelegramListener.AppServices/UpdateListeners/Handlers/UpdateHandler.cs @@ -0,0 +1,82 @@ +using TL; + +namespace Nocr.TelegramListener.AppServices.UpdateListeners.Handlers; + +public sealed class UpdateHandler : IUpdateHandler +{ + private readonly ITelegramClientContainer _clientContainer; + private readonly TelegramRegistry _telegramRegistry; + private readonly IMessageHandler _messageHandler; + + public UpdateHandler(ITelegramClientContainer clientContainer, TelegramRegistry telegramRegistry, + IMessageHandler messageHandler) + { + _clientContainer = clientContainer ?? throw new ArgumentNullException(nameof(clientContainer)); + _telegramRegistry = telegramRegistry ?? throw new ArgumentNullException(nameof(telegramRegistry)); + _messageHandler = messageHandler ?? throw new ArgumentNullException(nameof(messageHandler)); + } + + public async Task HandleUpdate(UpdatesBase updates, CancellationToken cancellationToken = default) + { + var (client, users, chats) = + (_clientContainer.Client, _telegramRegistry.Users, _telegramRegistry.Chats); + + updates.CollectUsersChats(users, chats); + if (updates is UpdateShortMessage usm && !users.ContainsKey(usm.user_id)) + (await client.Updates_GetDifference(usm.pts - usm.pts_count, usm.date, 0)).CollectUsersChats( + users, chats); + else if (updates is UpdateShortChatMessage uscm && + (!users.ContainsKey(uscm.from_id) || + !chats.ContainsKey(uscm.chat_id))) + (await client.Updates_GetDifference(uscm.pts - uscm.pts_count, uscm.date, 0)).CollectUsersChats( + users, + chats); + foreach (var update in updates.UpdateList) + switch (update) + { + case UpdateNewMessage unm: + await _messageHandler.HandleMessage(unm.message); + break; + case UpdateEditMessage uem: + await _messageHandler.HandleMessage(uem.message, true); + break; + // Note: UpdateNewChannelMessage and UpdateEditChannelMessage are also handled by above cases + case UpdateDeleteChannelMessages udcm: + Console.WriteLine( + $"{udcm.messages.Length} message(s) deleted in {chats.Chat(udcm.channel_id)}"); + break; + case UpdateDeleteMessages udm: + Console.WriteLine($"{udm.messages.Length} message(s) deleted"); + break; + case UpdateUserTyping uut: + Console.WriteLine($"{users.User(uut.user_id)} is {uut.action}"); + break; + case UpdateChatUserTyping ucut: + Console.WriteLine( + $"{ucut.from_id.Peer(users, chats)} is {ucut.action} in {chats.Chat(ucut.chat_id)}"); + break; + case UpdateChannelUserTyping ucut2: + Console.WriteLine( + $"{ucut2.from_id.Peer(users, chats)} is {ucut2.action} in {chats.Chat(ucut2.channel_id)}"); + break; + case UpdateChatParticipants { participants: ChatParticipants cp }: + Console.WriteLine( + $"{cp.participants.Length} participants in {chats.Chat(cp.chat_id)}"); + break; + case UpdateUserStatus uus: + Console.WriteLine( + $"{users.User(uus.user_id)} is now {uus.status.GetType().Name[10..]}"); + break; + case UpdateUserName uun: + Console.WriteLine( + $"{users.User(uun.user_id)} has changed profile name: {uun.first_name} {uun.last_name}"); + break; + case UpdateUser uu: + Console.WriteLine($"{users.User(uu.user_id)} has changed infos/photo"); + break; + default: + Console.WriteLine(update.GetType().Name); + break; // there are much more update types than the above example cases + } + } +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.AppServices/UpdateListeners/ITelegramClientContainer.cs b/src/Nocr.TelegramListener.AppServices/UpdateListeners/ITelegramClientContainer.cs new file mode 100644 index 0000000..5e61279 --- /dev/null +++ b/src/Nocr.TelegramListener.AppServices/UpdateListeners/ITelegramClientContainer.cs @@ -0,0 +1,14 @@ +using WTelegram; + +namespace Nocr.TelegramListener.AppServices.UpdateListeners; + +public interface ITelegramClientContainer +{ + Client Client { get; } + + public bool Initialized { get; } + + public void Initialize(); + + public void Reset(); +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramClientContainer.cs b/src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramClientContainer.cs new file mode 100644 index 0000000..fd8bca7 --- /dev/null +++ b/src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramClientContainer.cs @@ -0,0 +1,58 @@ +using Microsoft.Extensions.Options; +using WTelegram; + +namespace Nocr.TelegramListener.AppServices.UpdateListeners; + +public sealed class TelegramClientContainer : ITelegramClientContainer, IDisposable +{ + private Client? _client; + private readonly WTelegramClientOptions _options; + + public Client Client => _client ?? throw new InvalidOperationException("Client not initialized yet"); + + public bool Initialized { get; private set; } + + public TelegramClientContainer(IOptions options) + { + _options = options.Value ?? throw new ArgumentNullException(nameof(options)); + } + + public void Initialize() + { + if (Initialized) + return; + + _client = new Client(ConfigureWTelegramClient); + + Initialized = true; + } + + public void Reset() + { + Initialized = false; + Dispose(); + _client = null; + } + + private string ConfigureWTelegramClient(string what) + { + switch (what) + { + case "api_id": return _options.ApiId; + case "api_hash": return _options.ApiHash; + case "phone_number": return _options.PhoneNumber; + case "verification_code": + Console.Write("Code: "); + return Console.ReadLine(); + //case "first_name": return "Dmitry"; // if sign-up is required + //case "last_name": return "Charushnikov"; // if sign-up is required + //case "password": return ""; // if user has enabled 2FA + default: return null; // let WTelegramClient decide the default config + } + } + + public void Dispose() + { + _client?.Dispose(); + } +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramObjectExtensions.cs b/src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramObjectExtensions.cs new file mode 100644 index 0000000..659a0c1 --- /dev/null +++ b/src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramObjectExtensions.cs @@ -0,0 +1,16 @@ +using TL; + +namespace Nocr.TelegramListener.AppServices.UpdateListeners; + +public static class TelegramObjectExtensions +{ + public static string User(this IDictionary dictionary, long id) => + dictionary.TryGetValue(id, out var user) ? user.ToString() : $"User {id}"; + + public static string Chat(this IDictionary dictionary, long id) => + dictionary.TryGetValue(id, out var chat) ? chat.ToString() : $"Chat {id}"; + + public static string Peer(this Peer peer, IDictionary users, IDictionary chats) => peer is null ? null + : peer is PeerUser user ? users.User(user.user_id) + : peer is PeerChat or PeerChannel ? chats.Chat(peer.ID) : $"Peer {peer.ID}"; +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramRegistry.cs b/src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramRegistry.cs new file mode 100644 index 0000000..13d271e --- /dev/null +++ b/src/Nocr.TelegramListener.AppServices/UpdateListeners/TelegramRegistry.cs @@ -0,0 +1,34 @@ +using System.Collections.Concurrent; +using TL; + +namespace Nocr.TelegramListener.AppServices.UpdateListeners; + +public sealed class TelegramRegistry +{ + public User My { get; private set; } + public ConcurrentDictionary Users = new(); + public ConcurrentDictionary Chats = new(); + + public void SetMy(User my) + { + if (my == null) + { + throw new ArgumentNullException(nameof(my)); + } + + if (My == null) + { + My = my; + return; + } + + throw new InvalidOperationException("My already set"); + } + + public void Clear() + { + My = null; + Users.Clear(); + Chats.Clear(); + } +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.AppServices/UpdateListeners/UpdateListenerBackgroundService.cs b/src/Nocr.TelegramListener.AppServices/UpdateListeners/UpdateListenerBackgroundService.cs new file mode 100644 index 0000000..d1e3c2a --- /dev/null +++ b/src/Nocr.TelegramListener.AppServices/UpdateListeners/UpdateListenerBackgroundService.cs @@ -0,0 +1,70 @@ +using System.Collections.Concurrent; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Nocr.TelegramListener.AppServices.UpdateListeners.Handlers; +using TL; +using TL.Methods; +using WTelegram; + +namespace Nocr.TelegramListener.AppServices.UpdateListeners; + +public sealed class UpdateListenerBackgroundService : BackgroundService +{ + private readonly IServiceProvider _serviceProvider; + private readonly ITelegramClientContainer _telegramClientContainer; + private readonly TelegramRegistry _telegramRegistry; + private readonly WTelegramClientOptions _wTelegramClientOptions; + + public UpdateListenerBackgroundService(IServiceProvider serviceProvider, + IOptions wTelegramClientOptions, + ITelegramClientContainer telegramClientContainer, + TelegramRegistry telegramRegistry) + { + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + _telegramClientContainer = telegramClientContainer ?? throw new ArgumentNullException(nameof(telegramClientContainer)); + _telegramRegistry = telegramRegistry ?? throw new ArgumentNullException(nameof(telegramRegistry)); + _wTelegramClientOptions = wTelegramClientOptions.Value ?? + throw new ArgumentNullException(nameof(wTelegramClientOptions)); + } + + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + Console.WriteLine( + "The program will display updates received for the logged-in user. Press any key to terminate"); + WTelegram.Helpers.Log = (l, s) => System.Diagnostics.Debug.WriteLine(s); + Client? client = null; + while (!stoppingToken.IsCancellationRequested) + { + if (client == null) + { + _telegramRegistry.Clear(); + _telegramClientContainer.Reset(); + _telegramClientContainer.Initialize(); + + client = _telegramClientContainer.Client; + client.OnUpdate += HandleUpdates; + var my = await client.LoginUserIfNeeded(); + _telegramRegistry.SetMy(my); + _telegramRegistry.Users[my.id] = my; + + // Note: on login, Telegram may sends a bunch of updates/messages that happened in the past and were not acknowledged + Console.WriteLine( + $"We are logged-in as {_telegramRegistry.My.username ?? _telegramRegistry.My.first_name + " " + _telegramRegistry.My.last_name} (id {_telegramRegistry.My.id})"); + // We collect all infos about the users/chats so that updates can be printed with their names + var dialogs = await client.Messages_GetAllDialogs(); // dialogs = groups/channels/users + dialogs.CollectUsersChats(_telegramRegistry.Users, _telegramRegistry.Chats); + } + + await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken); + } + } + + private async Task HandleUpdates(UpdatesBase updates) + { + using var scope = _serviceProvider.CreateScope(); + var updateHandler = scope.ServiceProvider.GetRequiredService(); + await updateHandler.HandleUpdate(updates); + } +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.AppServices/UpdateListeners/WTelegramClientOptions.cs b/src/Nocr.TelegramListener.AppServices/UpdateListeners/WTelegramClientOptions.cs new file mode 100644 index 0000000..dad08a5 --- /dev/null +++ b/src/Nocr.TelegramListener.AppServices/UpdateListeners/WTelegramClientOptions.cs @@ -0,0 +1,19 @@ +namespace Nocr.TelegramListener.AppServices.UpdateListeners; + +/// +/// Options for WTelegramClient +/// +public sealed class WTelegramClientOptions +{ + public string ApiId { get; set; } + + public string ApiHash { get; set; } + + public string PhoneNumber { get; set; } + + public string? FirstName { get; set; } + + public string? LastName { get; set; } + + public string? Password { get; set; } +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.Core/BackgroundServices/IRepeatableBackgroundServiceHandler.cs b/src/Nocr.TelegramListener.Core/BackgroundServices/IRepeatableBackgroundServiceHandler.cs new file mode 100644 index 0000000..e66dd15 --- /dev/null +++ b/src/Nocr.TelegramListener.Core/BackgroundServices/IRepeatableBackgroundServiceHandler.cs @@ -0,0 +1,6 @@ +namespace Nocr.TelegramListener.Core.BackgroundServices; + +public interface IRepeatableBackgroundServiceHandler +{ + Task Handle(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.Core/BackgroundServices/RepeatableBackgroundService.cs b/src/Nocr.TelegramListener.Core/BackgroundServices/RepeatableBackgroundService.cs new file mode 100644 index 0000000..3210e7d --- /dev/null +++ b/src/Nocr.TelegramListener.Core/BackgroundServices/RepeatableBackgroundService.cs @@ -0,0 +1,42 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Nocr.TelegramListener.Core.BackgroundServices; + +public abstract class RepeatableBackgroundService : BackgroundService + where THandler : IRepeatableBackgroundServiceHandler + where TOptions : RepeatableServiceOptions + +{ + private readonly ILogger _logger; + private readonly TOptions _options; + private readonly IServiceProvider _serviceProvider; + + protected RepeatableBackgroundService(ILogger logger, TOptions options, IServiceProvider serviceProvider) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + try + { + using var scope = _serviceProvider.CreateScope(); + var handler = scope.ServiceProvider.GetRequiredService(); + await handler.Handle(stoppingToken); + + await Task.Delay(_options.Interval, stoppingToken); + } + catch (Exception ex) + { + _logger.LogCritical(ex, "Failed to process..."); + await Task.Delay(_options.ExceptionInterval, stoppingToken); + } + } + } +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.Core/BackgroundServices/RepeatableServiceOptions.cs b/src/Nocr.TelegramListener.Core/BackgroundServices/RepeatableServiceOptions.cs new file mode 100644 index 0000000..2b6fb38 --- /dev/null +++ b/src/Nocr.TelegramListener.Core/BackgroundServices/RepeatableServiceOptions.cs @@ -0,0 +1,8 @@ +namespace Nocr.TelegramListener.Core.BackgroundServices; + +public abstract class RepeatableServiceOptions +{ + public TimeSpan Interval { get; set; } = TimeSpan.FromSeconds(60); + + public TimeSpan ExceptionInterval { get; set; } = TimeSpan.FromSeconds(300); +} \ No newline at end of file diff --git a/src/Nocr.TelegramListener.Core/Nocr.TelegramListener.Core.csproj b/src/Nocr.TelegramListener.Core/Nocr.TelegramListener.Core.csproj index 2ef1a36..c621883 100644 --- a/src/Nocr.TelegramListener.Core/Nocr.TelegramListener.Core.csproj +++ b/src/Nocr.TelegramListener.Core/Nocr.TelegramListener.Core.csproj @@ -1 +1,8 @@ - + + + + + + + + diff --git a/src/Nocr.TelegramListener.Host/Infrastructure/Startup.cs b/src/Nocr.TelegramListener.Host/Infrastructure/Startup.cs index a945626..075211f 100644 --- a/src/Nocr.TelegramListener.Host/Infrastructure/Startup.cs +++ b/src/Nocr.TelegramListener.Host/Infrastructure/Startup.cs @@ -1,4 +1,5 @@ using Nocr.TelegramListener.AppServices; +using Nocr.TelegramListener.AppServices.UpdateListeners; using Nocr.TelegramListener.Core.Dates; namespace Nocr.TelegramListener.Host.Infrastructure; @@ -16,7 +17,7 @@ public class Startup { services.AddSingleton(); - services.AddAppServices(); + services.AddAppServices(Configuration); } public void Configure(IApplicationBuilder app)