From e788f2f4ef3bbe15e5b2b9bf04c71df6dc92bd54 Mon Sep 17 00:00:00 2001 From: Azat Jalilov Date: Fri, 29 Nov 2024 13:22:34 +0100 Subject: [PATCH 1/5] Add polling job, add telegram logger --- Place4.AppHost/Program.cs | 4 +- Place4.ServiceDefaults/Extensions.cs | 4 +- Place4.TelegramBot/BotConfiguration.cs | 9 ++++ Place4.TelegramBot/Logging/TelegramLogger.cs | 26 ++++++++++++ .../Logging/TelegramLoggerProvider.cs | 35 ++++++++++++++++ Place4.TelegramBot/Program.cs | 22 +++++++++- Place4.TelegramBot/TelegramPollingJob.cs | 42 +++++++++++++++++++ Place4.TelegramBot/UpdateHandler.cs | 31 ++++++++++++++ .../UpdateHandlers/ITelegramUpdateHandler.cs | 10 +++++ .../UpdateHandlers/NewUserUpdateHandler.cs | 38 +++++++++++++++++ .../UpdateHandlers/ParrotUpdateHandler.cs | 26 ++++++++++++ Place4.TelegramBot/appsettings.json | 7 +++- 12 files changed, 249 insertions(+), 5 deletions(-) create mode 100644 Place4.TelegramBot/BotConfiguration.cs create mode 100644 Place4.TelegramBot/Logging/TelegramLogger.cs create mode 100644 Place4.TelegramBot/Logging/TelegramLoggerProvider.cs create mode 100644 Place4.TelegramBot/TelegramPollingJob.cs create mode 100644 Place4.TelegramBot/UpdateHandler.cs create mode 100644 Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs create mode 100644 Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs create mode 100644 Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs diff --git a/Place4.AppHost/Program.cs b/Place4.AppHost/Program.cs index ba89173..6063e9c 100644 --- a/Place4.AppHost/Program.cs +++ b/Place4.AppHost/Program.cs @@ -1,4 +1,6 @@ +using Projects; + var builder = DistributedApplication.CreateBuilder(args); -builder.AddProject("bot", launchProfileName: "https"); +builder.AddProject("bot", launchProfileName: "https"); builder.Build().Run(); \ No newline at end of file diff --git a/Place4.ServiceDefaults/Extensions.cs b/Place4.ServiceDefaults/Extensions.cs index 2b52e8e..f4e9ba7 100644 --- a/Place4.ServiceDefaults/Extensions.cs +++ b/Place4.ServiceDefaults/Extensions.cs @@ -1,3 +1,5 @@ +namespace Place4.ServiceDefaults; + using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.Extensions.DependencyInjection; @@ -8,8 +10,6 @@ using OpenTelemetry.Metrics; using OpenTelemetry.Trace; -namespace Place4.ServiceDefaults; - // Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. // This project should be referenced by each service project in your solution. // To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults diff --git a/Place4.TelegramBot/BotConfiguration.cs b/Place4.TelegramBot/BotConfiguration.cs new file mode 100644 index 0000000..3d3c798 --- /dev/null +++ b/Place4.TelegramBot/BotConfiguration.cs @@ -0,0 +1,9 @@ +namespace Place4.TelegramBot +{ + public class BotConfiguration + { + public required string BotToken { get; init; } + public required string LogChatId { get; init; } + public int LogChatMessageThreadId { get; init; } + } +} \ No newline at end of file diff --git a/Place4.TelegramBot/Logging/TelegramLogger.cs b/Place4.TelegramBot/Logging/TelegramLogger.cs new file mode 100644 index 0000000..eb55ceb --- /dev/null +++ b/Place4.TelegramBot/Logging/TelegramLogger.cs @@ -0,0 +1,26 @@ +namespace Place4.TelegramBot.Logging +{ + using Telegram.Bot; + + public class TelegramLogger(string name, ITelegramBotClient telegramBotClient, BotConfiguration configuration) : ILogger + { + public IDisposable? BeginScope(TState state) where TState: notnull => default; + + public bool IsEnabled(LogLevel logLevel) + { + return logLevel > LogLevel.Information; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + if (!this.IsEnabled(logLevel)) + { + return; + } + telegramBotClient.SendMessage( + configuration.LogChatId, + formatter(state, exception), + messageThreadId: configuration.LogChatMessageThreadId).GetAwaiter().GetResult(); + } + } +} \ No newline at end of file diff --git a/Place4.TelegramBot/Logging/TelegramLoggerProvider.cs b/Place4.TelegramBot/Logging/TelegramLoggerProvider.cs new file mode 100644 index 0000000..83d62f4 --- /dev/null +++ b/Place4.TelegramBot/Logging/TelegramLoggerProvider.cs @@ -0,0 +1,35 @@ +namespace Place4.TelegramBot.Logging +{ + using System.Collections.Concurrent; + using Microsoft.Extensions.Options; + using Telegram.Bot; + + public class TelegramLoggerProvider : ILoggerProvider + { + private readonly ITelegramBotClient _telegramBotClient; + private readonly IDisposable? _onChangeToken; + private BotConfiguration _currentConfig; + private readonly ConcurrentDictionary _loggers = + new(StringComparer.OrdinalIgnoreCase); + + public TelegramLoggerProvider( + IOptionsMonitor config, + ITelegramBotClient telegramBotClient) + { + this._telegramBotClient = telegramBotClient; + this._currentConfig = config.CurrentValue; + this._onChangeToken = config.OnChange(updatedConfig => this._currentConfig = updatedConfig); + } + + public ILogger CreateLogger(string categoryName) => + this._loggers.GetOrAdd(categoryName, name => new TelegramLogger(name, this._telegramBotClient, this.GetCurrentConfig())); + + private BotConfiguration GetCurrentConfig() => this._currentConfig; + + public void Dispose() + { + this._loggers.Clear(); + this._onChangeToken?.Dispose(); + } + } +} \ No newline at end of file diff --git a/Place4.TelegramBot/Program.cs b/Place4.TelegramBot/Program.cs index 7a9b670..0875ede 100644 --- a/Place4.TelegramBot/Program.cs +++ b/Place4.TelegramBot/Program.cs @@ -1,10 +1,30 @@ +using Microsoft.Extensions.Options; using Place4.TelegramBot; +using Place4.TelegramBot.Logging; +using Place4.TelegramBot.UpdateHandlers; +using Telegram.Bot; +using Telegram.Bot.Polling; var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); +builder.Services.AddHttpClient("telegram_bot_client").RemoveAllLoggers() + .AddTypedClient((httpClient, sp) => + { + var botConfiguration = sp.GetService>()?.Value; + ArgumentNullException.ThrowIfNull(botConfiguration); + TelegramBotClientOptions options = new(botConfiguration.BotToken); + return new TelegramBotClient(options, httpClient); + }); +builder.Services.AddHostedService(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); + +builder.Services.Configure(builder.Configuration.GetSection("BotConfiguration")); +builder.Services.AddSingleton(); var app = builder.Build(); @@ -41,6 +61,6 @@ namespace Place4.TelegramBot { record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + public int TemperatureF => 32 + (int)(this.TemperatureC / 0.5556); } } diff --git a/Place4.TelegramBot/TelegramPollingJob.cs b/Place4.TelegramBot/TelegramPollingJob.cs new file mode 100644 index 0000000..d01e55f --- /dev/null +++ b/Place4.TelegramBot/TelegramPollingJob.cs @@ -0,0 +1,42 @@ +namespace Place4.TelegramBot +{ + using Telegram.Bot; + using Telegram.Bot.Polling; + using Telegram.Bot.Types.Enums; + + public class TelegramPollingJob( + ITelegramBotClient telegramBotClient, + IUpdateHandler updateHandler, + ILogger logger) : + BackgroundService + { + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + logger.LogInformation("Starting polling service"); + await this.DoWork(stoppingToken); + } + + private async Task DoWork(CancellationToken stoppingToken) + { + var receiverOptions = new ReceiverOptions + { + AllowedUpdates = [UpdateType.Message, UpdateType.ChatMember], + DropPendingUpdates = true + }; + // Make sure we receive updates until Cancellation Requested + while (!stoppingToken.IsCancellationRequested) + { + try + { + await telegramBotClient.ReceiveAsync(updateHandler, receiverOptions, stoppingToken); + } + catch (Exception ex) + { + logger.LogError("Polling failed with exception: {Exception}", ex); + // Cooldown if something goes wrong + await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); + } + } + } + } +} \ No newline at end of file diff --git a/Place4.TelegramBot/UpdateHandler.cs b/Place4.TelegramBot/UpdateHandler.cs new file mode 100644 index 0000000..4f3960e --- /dev/null +++ b/Place4.TelegramBot/UpdateHandler.cs @@ -0,0 +1,31 @@ +namespace Place4.TelegramBot +{ + using Telegram.Bot; + using Telegram.Bot.Polling; + using Telegram.Bot.Types; + using UpdateHandlers; + + public class UpdateHandler(IEnumerable updateHandlers, ILogger logger) : IUpdateHandler + { + public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) + { + foreach (var updateHandler in updateHandlers) + { + try + { + await updateHandler.HandleUpdateAsync(botClient, update, cancellationToken); + } + catch (Exception ex) + { + await this.HandleErrorAsync(botClient, ex, HandleErrorSource.HandleUpdateError, cancellationToken); + } + } + } + + public Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, HandleErrorSource source, CancellationToken cancellationToken) + { + logger.LogError(exception, "Error occured in {Source}", source); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs b/Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs new file mode 100644 index 0000000..3ac1d62 --- /dev/null +++ b/Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs @@ -0,0 +1,10 @@ +namespace Place4.TelegramBot.UpdateHandlers +{ + using Telegram.Bot; + using Telegram.Bot.Types; + + public interface ITelegramUpdateHandler + { + Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs b/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs new file mode 100644 index 0000000..9e80ece --- /dev/null +++ b/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs @@ -0,0 +1,38 @@ +namespace Place4.TelegramBot.UpdateHandlers +{ + using Telegram.Bot; + using Telegram.Bot.Types; + using Telegram.Bot.Types.Enums; + + public class NewUserUpdateHandler(ILogger logger) : ITelegramUpdateHandler + { + public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) + { + var newUserUpdate = IsNewUserUpdate(update); + if (newUserUpdate.chat == null || newUserUpdate.user == null) + { + return; + } + + var chatId = newUserUpdate.chat.Id; + var message = $"Welcome, [{newUserUpdate.user.FirstName} {newUserUpdate.user.LastName}](tg://user?id={newUserUpdate.user.Id})"; + await botClient.SendMessage( + chatId, + message, + cancellationToken: cancellationToken, + parseMode: ParseMode.MarkdownV2); + } + + private static (Chat? chat, User? user) IsNewUserUpdate(Update update) + { + var isFakeNewMember = update is { Type: UpdateType.Message, Message.Text: "I am new" }; // for test only + var isNewMember = update is { Type: UpdateType.ChatMember, ChatMember.NewChatMember: not null }; + if (isNewMember) + { + return (update.ChatMember?.Chat, update.ChatMember?.NewChatMember?.User); + } + + return isFakeNewMember ? (update.Message?.Chat, update.Message?.From) : (null, null); + } + } +} \ No newline at end of file diff --git a/Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs b/Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs new file mode 100644 index 0000000..3566c58 --- /dev/null +++ b/Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs @@ -0,0 +1,26 @@ +namespace Place4.TelegramBot.UpdateHandlers +{ + using Telegram.Bot; + using Telegram.Bot.Types; + using Telegram.Bot.Types.Enums; + + public class ParrotUpdateHandler(ILogger logger) : ITelegramUpdateHandler + { + public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) + { + if (update.Type != UpdateType.Message || update.Message?.From == null) + { + return; + } + var chatId = update.Message.Chat.Id; + var message = $"Hi, [{update.Message.From.FirstName} {update.Message.From.LastName}](tg://user?id={update.Message.From.Id}). You said: {update.Message.Text}"; + await botClient.SendMessage( + chatId, + message, + cancellationToken: cancellationToken, + messageThreadId: update.Message.MessageThreadId, + parseMode: ParseMode.Markdown); + } + + } +} \ No newline at end of file diff --git a/Place4.TelegramBot/appsettings.json b/Place4.TelegramBot/appsettings.json index 10f68b8..45d64e9 100644 --- a/Place4.TelegramBot/appsettings.json +++ b/Place4.TelegramBot/appsettings.json @@ -5,5 +5,10 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "BotConfiguration": { + "BotToken": "your_token", + "LogChatId": "your_chat_id", + "LogChatMessageThreadId": 0 + } } From 2db960c165f723133500cbbb4973ac2e08873a13 Mon Sep 17 00:00:00 2001 From: Azat Jalilov Date: Fri, 29 Nov 2024 14:08:32 +0100 Subject: [PATCH 2/5] Add tests --- .../Place4.TelegramBot.Tests.csproj | 28 ++++++ .../NewUserUpdateHandlerTests.cs | 95 +++++++++++++++++++ .../ParrotUpdateHandlerTests.cs | 94 ++++++++++++++++++ .../UpdateHandlers/ITelegramUpdateHandler.cs | 2 +- .../UpdateHandlers/NewUserUpdateHandler.cs | 6 +- .../UpdateHandlers/ParrotUpdateHandler.cs | 4 +- Place4.sln | 6 ++ 7 files changed, 229 insertions(+), 6 deletions(-) create mode 100644 Place4.TelegramBot.Tests/Place4.TelegramBot.Tests.csproj create mode 100644 Place4.TelegramBot.Tests/UpdateHandlers/NewUserUpdateHandlerTests.cs create mode 100644 Place4.TelegramBot.Tests/UpdateHandlers/ParrotUpdateHandlerTests.cs diff --git a/Place4.TelegramBot.Tests/Place4.TelegramBot.Tests.csproj b/Place4.TelegramBot.Tests/Place4.TelegramBot.Tests.csproj new file mode 100644 index 0000000..bd0d2c1 --- /dev/null +++ b/Place4.TelegramBot.Tests/Place4.TelegramBot.Tests.csproj @@ -0,0 +1,28 @@ + + + + net9.0 + latest + enable + enable + false + + + + + + + + + + + + + + + + + + + + diff --git a/Place4.TelegramBot.Tests/UpdateHandlers/NewUserUpdateHandlerTests.cs b/Place4.TelegramBot.Tests/UpdateHandlers/NewUserUpdateHandlerTests.cs new file mode 100644 index 0000000..fc704f3 --- /dev/null +++ b/Place4.TelegramBot.Tests/UpdateHandlers/NewUserUpdateHandlerTests.cs @@ -0,0 +1,95 @@ +namespace Place4.TelegramBot.Tests.UpdateHandlers +{ + using FakeItEasy; + using Microsoft.Extensions.Logging; + using Telegram.Bot; + using Telegram.Bot.Requests; + using Telegram.Bot.Types; + using Telegram.Bot.Types.Enums; + using TelegramBot.UpdateHandlers; + + public class NewUserUpdateHandlerTests + { + [SetUp] + public void Setup() + { + } + + [Test] + public async Task WhenNewChatMemberIsReceived_SendsCorrectReply() + { + // Arrange + var logger = A.Fake>(); + var newUserUpdateHandler = new NewUserUpdateHandler(logger); + var telegramBotClient = A.Fake(); + var update = new Update + { + ChatMember = new ChatMemberUpdated + { + NewChatMember = new ChatMemberMember + { + User = new User + { + Id = 123, + FirstName = "John", + LastName = "Doe" + } + }, + Chat = new Chat + { + Id = 456 + } + } + }; + + // Act + await newUserUpdateHandler.HandleUpdateAsync(telegramBotClient, update, CancellationToken.None); + + // Assert + A.CallTo(() => telegramBotClient.SendRequest( + A.That.Matches( + x => x.ChatId == 456 + && x.Text == $"Welcome, [John Doe](tg://user?id={123})" + && x.ParseMode == ParseMode.Markdown + ), + A.Ignored + )).MustHaveHappenedOnceExactly(); + } + + + [Test] + public async Task WhenNonMessageUpdateIsReceived_DoesntDoAnything() + { + // Arrange + var logger = A.Fake>(); + var parrotUpdateHandler = new NewUserUpdateHandler(logger); + var telegramBotClient = A.Fake(); + var update = new Update + { + Message = new Message + { + Text = "Hi!", + From = new User + { + Id = 123, + FirstName = "John", + LastName = "Doe" + }, + Chat = new Chat + { + Id = 456 + } + } + }; + + // Act + await parrotUpdateHandler.HandleUpdateAsync(telegramBotClient, update, CancellationToken.None); + + // Assert + A.CallTo(() => telegramBotClient.SendRequest( + A.Ignored, + A.Ignored + )).MustNotHaveHappened(); + } + } +} \ No newline at end of file diff --git a/Place4.TelegramBot.Tests/UpdateHandlers/ParrotUpdateHandlerTests.cs b/Place4.TelegramBot.Tests/UpdateHandlers/ParrotUpdateHandlerTests.cs new file mode 100644 index 0000000..3ea7bed --- /dev/null +++ b/Place4.TelegramBot.Tests/UpdateHandlers/ParrotUpdateHandlerTests.cs @@ -0,0 +1,94 @@ +namespace Place4.TelegramBot.Tests.UpdateHandlers; + +using FakeItEasy; +using Microsoft.Extensions.Logging; +using Place4.TelegramBot.UpdateHandlers; +using Telegram.Bot; +using Telegram.Bot.Requests; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; + +public class ParrotUpdateHandlerTests +{ + [SetUp] + public void Setup() + { + } + + [Test] + public async Task WhenMessageUpdateIsReceived_SendsCorrectReply() + { + // Arrange + var logger = A.Fake>(); + var parrotUpdateHandler = new ParrotUpdateHandler(logger); + var telegramBotClient = A.Fake(); + var update = new Update + { + Message = new Message + { + Text = "Hi!", + From = new User + { + Id = 123, + FirstName = "John", + LastName = "Doe" + }, + Chat = new Chat + { + Id = 456 + } + } + }; + + // Act + await parrotUpdateHandler.HandleUpdateAsync(telegramBotClient, update, CancellationToken.None); + + // Assert + A.CallTo(() => telegramBotClient.SendRequest( + A.That.Matches( + x => x.ChatId == 456 + && x.Text == $"Hi, [John Doe](tg://user?id={123}). You said: Hi!" + && x.ParseMode == ParseMode.Markdown + ), + A.Ignored + )).MustHaveHappenedOnceExactly(); + } + + + [Test] + public async Task WhenNonMessageUpdateIsReceived_DoesntDoAnything() + { + // Arrange + var logger = A.Fake>(); + var parrotUpdateHandler = new ParrotUpdateHandler(logger); + var telegramBotClient = A.Fake(); + var update = new Update + { + ChatMember = new ChatMemberUpdated + { + NewChatMember = new ChatMemberMember + { + User = new() + { + Id = 123, + FirstName = "John", + LastName = "Doe" + } + }, + Chat = new Chat + { + Id = 456 + } + } + }; + + // Act + await parrotUpdateHandler.HandleUpdateAsync(telegramBotClient, update, CancellationToken.None); + + // Assert + A.CallTo(() => telegramBotClient.SendRequest( + A.Ignored, + A.Ignored + )).MustNotHaveHappened(); + } +} \ No newline at end of file diff --git a/Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs b/Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs index 3ac1d62..0091f1e 100644 --- a/Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs +++ b/Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs @@ -5,6 +5,6 @@ namespace Place4.TelegramBot.UpdateHandlers public interface ITelegramUpdateHandler { - Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken); + Task HandleUpdateAsync(ITelegramBotClient telegramBotClient, Update update, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs b/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs index 9e80ece..da6240d 100644 --- a/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs +++ b/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs @@ -6,7 +6,7 @@ namespace Place4.TelegramBot.UpdateHandlers public class NewUserUpdateHandler(ILogger logger) : ITelegramUpdateHandler { - public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) + public async Task HandleUpdateAsync(ITelegramBotClient telegramBotClient, Update update, CancellationToken cancellationToken) { var newUserUpdate = IsNewUserUpdate(update); if (newUserUpdate.chat == null || newUserUpdate.user == null) @@ -16,11 +16,11 @@ public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, var chatId = newUserUpdate.chat.Id; var message = $"Welcome, [{newUserUpdate.user.FirstName} {newUserUpdate.user.LastName}](tg://user?id={newUserUpdate.user.Id})"; - await botClient.SendMessage( + await telegramBotClient.SendMessage( chatId, message, cancellationToken: cancellationToken, - parseMode: ParseMode.MarkdownV2); + parseMode: ParseMode.Markdown); } private static (Chat? chat, User? user) IsNewUserUpdate(Update update) diff --git a/Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs b/Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs index 3566c58..56b04f6 100644 --- a/Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs +++ b/Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs @@ -6,7 +6,7 @@ namespace Place4.TelegramBot.UpdateHandlers public class ParrotUpdateHandler(ILogger logger) : ITelegramUpdateHandler { - public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) + public async Task HandleUpdateAsync(ITelegramBotClient telegramBotClient, Update update, CancellationToken cancellationToken) { if (update.Type != UpdateType.Message || update.Message?.From == null) { @@ -14,7 +14,7 @@ public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, } var chatId = update.Message.Chat.Id; var message = $"Hi, [{update.Message.From.FirstName} {update.Message.From.LastName}](tg://user?id={update.Message.From.Id}). You said: {update.Message.Text}"; - await botClient.SendMessage( + await telegramBotClient.SendMessage( chatId, message, cancellationToken: cancellationToken, diff --git a/Place4.sln b/Place4.sln index b32a5ff..998bd57 100644 --- a/Place4.sln +++ b/Place4.sln @@ -6,6 +6,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Place4.AppHost", "Place4.Ap EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Place4.ServiceDefaults", "Place4.ServiceDefaults\Place4.ServiceDefaults.csproj", "{EEF046CF-366A-4614-B456-5282CA07509C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Place4.TelegramBot.Tests", "Place4.TelegramBot.Tests\Place4.TelegramBot.Tests.csproj", "{E89209BA-9F0C-4AA8-8A07-A53972A8233E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -24,5 +26,9 @@ Global {EEF046CF-366A-4614-B456-5282CA07509C}.Debug|Any CPU.Build.0 = Debug|Any CPU {EEF046CF-366A-4614-B456-5282CA07509C}.Release|Any CPU.ActiveCfg = Release|Any CPU {EEF046CF-366A-4614-B456-5282CA07509C}.Release|Any CPU.Build.0 = Release|Any CPU + {E89209BA-9F0C-4AA8-8A07-A53972A8233E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E89209BA-9F0C-4AA8-8A07-A53972A8233E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E89209BA-9F0C-4AA8-8A07-A53972A8233E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E89209BA-9F0C-4AA8-8A07-A53972A8233E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From d244d6cb4ed9f184f7239975ab7cacf9b9b6ee47 Mon Sep 17 00:00:00 2001 From: Azat Jalilov Date: Fri, 29 Nov 2024 15:02:01 +0100 Subject: [PATCH 3/5] Added editorconfig, removed tests --- .../Place4.TelegramBot.Tests.csproj | 28 ------ .../NewUserUpdateHandlerTests.cs | 95 ------------------- .../ParrotUpdateHandlerTests.cs | 94 ------------------ Place4.TelegramBot/BotConfiguration.cs | 13 ++- Place4.TelegramBot/Logging/TelegramLogger.cs | 35 ++++--- .../Logging/TelegramLoggerProvider.cs | 53 +++++------ Place4.TelegramBot/Program.cs | 2 +- Place4.TelegramBot/TelegramPollingJob.cs | 61 ++++++------ Place4.TelegramBot/UpdateHandler.cs | 43 ++++----- .../UpdateHandlers/ITelegramUpdateHandler.cs | 15 ++- .../UpdateHandlers/NewUserUpdateHandler.cs | 57 ++++++----- .../UpdateHandlers/ParrotUpdateHandler.cs | 39 ++++---- Place4.sln | 6 -- Place4.sln.DotSettings | 28 ++++++ 14 files changed, 183 insertions(+), 386 deletions(-) delete mode 100644 Place4.TelegramBot.Tests/Place4.TelegramBot.Tests.csproj delete mode 100644 Place4.TelegramBot.Tests/UpdateHandlers/NewUserUpdateHandlerTests.cs delete mode 100644 Place4.TelegramBot.Tests/UpdateHandlers/ParrotUpdateHandlerTests.cs create mode 100644 Place4.sln.DotSettings diff --git a/Place4.TelegramBot.Tests/Place4.TelegramBot.Tests.csproj b/Place4.TelegramBot.Tests/Place4.TelegramBot.Tests.csproj deleted file mode 100644 index bd0d2c1..0000000 --- a/Place4.TelegramBot.Tests/Place4.TelegramBot.Tests.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - net9.0 - latest - enable - enable - false - - - - - - - - - - - - - - - - - - - - diff --git a/Place4.TelegramBot.Tests/UpdateHandlers/NewUserUpdateHandlerTests.cs b/Place4.TelegramBot.Tests/UpdateHandlers/NewUserUpdateHandlerTests.cs deleted file mode 100644 index fc704f3..0000000 --- a/Place4.TelegramBot.Tests/UpdateHandlers/NewUserUpdateHandlerTests.cs +++ /dev/null @@ -1,95 +0,0 @@ -namespace Place4.TelegramBot.Tests.UpdateHandlers -{ - using FakeItEasy; - using Microsoft.Extensions.Logging; - using Telegram.Bot; - using Telegram.Bot.Requests; - using Telegram.Bot.Types; - using Telegram.Bot.Types.Enums; - using TelegramBot.UpdateHandlers; - - public class NewUserUpdateHandlerTests - { - [SetUp] - public void Setup() - { - } - - [Test] - public async Task WhenNewChatMemberIsReceived_SendsCorrectReply() - { - // Arrange - var logger = A.Fake>(); - var newUserUpdateHandler = new NewUserUpdateHandler(logger); - var telegramBotClient = A.Fake(); - var update = new Update - { - ChatMember = new ChatMemberUpdated - { - NewChatMember = new ChatMemberMember - { - User = new User - { - Id = 123, - FirstName = "John", - LastName = "Doe" - } - }, - Chat = new Chat - { - Id = 456 - } - } - }; - - // Act - await newUserUpdateHandler.HandleUpdateAsync(telegramBotClient, update, CancellationToken.None); - - // Assert - A.CallTo(() => telegramBotClient.SendRequest( - A.That.Matches( - x => x.ChatId == 456 - && x.Text == $"Welcome, [John Doe](tg://user?id={123})" - && x.ParseMode == ParseMode.Markdown - ), - A.Ignored - )).MustHaveHappenedOnceExactly(); - } - - - [Test] - public async Task WhenNonMessageUpdateIsReceived_DoesntDoAnything() - { - // Arrange - var logger = A.Fake>(); - var parrotUpdateHandler = new NewUserUpdateHandler(logger); - var telegramBotClient = A.Fake(); - var update = new Update - { - Message = new Message - { - Text = "Hi!", - From = new User - { - Id = 123, - FirstName = "John", - LastName = "Doe" - }, - Chat = new Chat - { - Id = 456 - } - } - }; - - // Act - await parrotUpdateHandler.HandleUpdateAsync(telegramBotClient, update, CancellationToken.None); - - // Assert - A.CallTo(() => telegramBotClient.SendRequest( - A.Ignored, - A.Ignored - )).MustNotHaveHappened(); - } - } -} \ No newline at end of file diff --git a/Place4.TelegramBot.Tests/UpdateHandlers/ParrotUpdateHandlerTests.cs b/Place4.TelegramBot.Tests/UpdateHandlers/ParrotUpdateHandlerTests.cs deleted file mode 100644 index 3ea7bed..0000000 --- a/Place4.TelegramBot.Tests/UpdateHandlers/ParrotUpdateHandlerTests.cs +++ /dev/null @@ -1,94 +0,0 @@ -namespace Place4.TelegramBot.Tests.UpdateHandlers; - -using FakeItEasy; -using Microsoft.Extensions.Logging; -using Place4.TelegramBot.UpdateHandlers; -using Telegram.Bot; -using Telegram.Bot.Requests; -using Telegram.Bot.Types; -using Telegram.Bot.Types.Enums; - -public class ParrotUpdateHandlerTests -{ - [SetUp] - public void Setup() - { - } - - [Test] - public async Task WhenMessageUpdateIsReceived_SendsCorrectReply() - { - // Arrange - var logger = A.Fake>(); - var parrotUpdateHandler = new ParrotUpdateHandler(logger); - var telegramBotClient = A.Fake(); - var update = new Update - { - Message = new Message - { - Text = "Hi!", - From = new User - { - Id = 123, - FirstName = "John", - LastName = "Doe" - }, - Chat = new Chat - { - Id = 456 - } - } - }; - - // Act - await parrotUpdateHandler.HandleUpdateAsync(telegramBotClient, update, CancellationToken.None); - - // Assert - A.CallTo(() => telegramBotClient.SendRequest( - A.That.Matches( - x => x.ChatId == 456 - && x.Text == $"Hi, [John Doe](tg://user?id={123}). You said: Hi!" - && x.ParseMode == ParseMode.Markdown - ), - A.Ignored - )).MustHaveHappenedOnceExactly(); - } - - - [Test] - public async Task WhenNonMessageUpdateIsReceived_DoesntDoAnything() - { - // Arrange - var logger = A.Fake>(); - var parrotUpdateHandler = new ParrotUpdateHandler(logger); - var telegramBotClient = A.Fake(); - var update = new Update - { - ChatMember = new ChatMemberUpdated - { - NewChatMember = new ChatMemberMember - { - User = new() - { - Id = 123, - FirstName = "John", - LastName = "Doe" - } - }, - Chat = new Chat - { - Id = 456 - } - } - }; - - // Act - await parrotUpdateHandler.HandleUpdateAsync(telegramBotClient, update, CancellationToken.None); - - // Assert - A.CallTo(() => telegramBotClient.SendRequest( - A.Ignored, - A.Ignored - )).MustNotHaveHappened(); - } -} \ No newline at end of file diff --git a/Place4.TelegramBot/BotConfiguration.cs b/Place4.TelegramBot/BotConfiguration.cs index 3d3c798..eaf0efa 100644 --- a/Place4.TelegramBot/BotConfiguration.cs +++ b/Place4.TelegramBot/BotConfiguration.cs @@ -1,9 +1,8 @@ -namespace Place4.TelegramBot +namespace Place4.TelegramBot; + +public class BotConfiguration { - public class BotConfiguration - { - public required string BotToken { get; init; } - public required string LogChatId { get; init; } - public int LogChatMessageThreadId { get; init; } - } + public required string BotToken { get; init; } + public required string LogChatId { get; init; } + public int LogChatMessageThreadId { get; init; } } \ No newline at end of file diff --git a/Place4.TelegramBot/Logging/TelegramLogger.cs b/Place4.TelegramBot/Logging/TelegramLogger.cs index eb55ceb..ff45806 100644 --- a/Place4.TelegramBot/Logging/TelegramLogger.cs +++ b/Place4.TelegramBot/Logging/TelegramLogger.cs @@ -1,26 +1,25 @@ -namespace Place4.TelegramBot.Logging +namespace Place4.TelegramBot.Logging; + +using Telegram.Bot; + +public class TelegramLogger(string name, ITelegramBotClient telegramBotClient, BotConfiguration configuration) : ILogger { - using Telegram.Bot; + public IDisposable? BeginScope(TState state) where TState: notnull => default; - public class TelegramLogger(string name, ITelegramBotClient telegramBotClient, BotConfiguration configuration) : ILogger + public bool IsEnabled(LogLevel logLevel) { - public IDisposable? BeginScope(TState state) where TState: notnull => default; - - public bool IsEnabled(LogLevel logLevel) - { - return logLevel > LogLevel.Information; - } + return logLevel > LogLevel.Information; + } - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + if (!IsEnabled(logLevel)) { - if (!this.IsEnabled(logLevel)) - { - return; - } - telegramBotClient.SendMessage( - configuration.LogChatId, - formatter(state, exception), - messageThreadId: configuration.LogChatMessageThreadId).GetAwaiter().GetResult(); + return; } + telegramBotClient.SendMessage( + configuration.LogChatId, + formatter(state, exception), + messageThreadId: configuration.LogChatMessageThreadId).GetAwaiter().GetResult(); } } \ No newline at end of file diff --git a/Place4.TelegramBot/Logging/TelegramLoggerProvider.cs b/Place4.TelegramBot/Logging/TelegramLoggerProvider.cs index 83d62f4..76a3351 100644 --- a/Place4.TelegramBot/Logging/TelegramLoggerProvider.cs +++ b/Place4.TelegramBot/Logging/TelegramLoggerProvider.cs @@ -1,35 +1,34 @@ -namespace Place4.TelegramBot.Logging +namespace Place4.TelegramBot.Logging; + +using System.Collections.Concurrent; +using Microsoft.Extensions.Options; +using Telegram.Bot; + +public class TelegramLoggerProvider : ILoggerProvider { - using System.Collections.Concurrent; - using Microsoft.Extensions.Options; - using Telegram.Bot; + private readonly ITelegramBotClient _telegramBotClient; + private readonly IDisposable? _onChangeToken; + private BotConfiguration _currentConfig; + private readonly ConcurrentDictionary _loggers = + new(StringComparer.OrdinalIgnoreCase); - public class TelegramLoggerProvider : ILoggerProvider + public TelegramLoggerProvider( + IOptionsMonitor config, + ITelegramBotClient telegramBotClient) { - private readonly ITelegramBotClient _telegramBotClient; - private readonly IDisposable? _onChangeToken; - private BotConfiguration _currentConfig; - private readonly ConcurrentDictionary _loggers = - new(StringComparer.OrdinalIgnoreCase); - - public TelegramLoggerProvider( - IOptionsMonitor config, - ITelegramBotClient telegramBotClient) - { - this._telegramBotClient = telegramBotClient; - this._currentConfig = config.CurrentValue; - this._onChangeToken = config.OnChange(updatedConfig => this._currentConfig = updatedConfig); - } + _telegramBotClient = telegramBotClient; + _currentConfig = config.CurrentValue; + _onChangeToken = config.OnChange(updatedConfig => _currentConfig = updatedConfig); + } - public ILogger CreateLogger(string categoryName) => - this._loggers.GetOrAdd(categoryName, name => new TelegramLogger(name, this._telegramBotClient, this.GetCurrentConfig())); + public ILogger CreateLogger(string categoryName) => + _loggers.GetOrAdd(categoryName, name => new TelegramLogger(name, _telegramBotClient, GetCurrentConfig())); - private BotConfiguration GetCurrentConfig() => this._currentConfig; + private BotConfiguration GetCurrentConfig() => _currentConfig; - public void Dispose() - { - this._loggers.Clear(); - this._onChangeToken?.Dispose(); - } + public void Dispose() + { + _loggers.Clear(); + _onChangeToken?.Dispose(); } } \ No newline at end of file diff --git a/Place4.TelegramBot/Program.cs b/Place4.TelegramBot/Program.cs index 0875ede..9b7db73 100644 --- a/Place4.TelegramBot/Program.cs +++ b/Place4.TelegramBot/Program.cs @@ -61,6 +61,6 @@ namespace Place4.TelegramBot { record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { - public int TemperatureF => 32 + (int)(this.TemperatureC / 0.5556); + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } } diff --git a/Place4.TelegramBot/TelegramPollingJob.cs b/Place4.TelegramBot/TelegramPollingJob.cs index d01e55f..e81b47f 100644 --- a/Place4.TelegramBot/TelegramPollingJob.cs +++ b/Place4.TelegramBot/TelegramPollingJob.cs @@ -1,41 +1,40 @@ -namespace Place4.TelegramBot +namespace Place4.TelegramBot; + +using Telegram.Bot; +using Telegram.Bot.Polling; +using Telegram.Bot.Types.Enums; + +public class TelegramPollingJob( + ITelegramBotClient telegramBotClient, + IUpdateHandler updateHandler, + ILogger logger) : + BackgroundService { - using Telegram.Bot; - using Telegram.Bot.Polling; - using Telegram.Bot.Types.Enums; + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + logger.LogInformation("Starting polling service"); + await DoWork(stoppingToken); + } - public class TelegramPollingJob( - ITelegramBotClient telegramBotClient, - IUpdateHandler updateHandler, - ILogger logger) : - BackgroundService + private async Task DoWork(CancellationToken stoppingToken) { - protected override async Task ExecuteAsync(CancellationToken stoppingToken) + var receiverOptions = new ReceiverOptions { - logger.LogInformation("Starting polling service"); - await this.DoWork(stoppingToken); - } - - private async Task DoWork(CancellationToken stoppingToken) + AllowedUpdates = [UpdateType.Message, UpdateType.ChatMember], + DropPendingUpdates = true + }; + // Make sure we receive updates until Cancellation Requested + while (!stoppingToken.IsCancellationRequested) { - var receiverOptions = new ReceiverOptions + try { - AllowedUpdates = [UpdateType.Message, UpdateType.ChatMember], - DropPendingUpdates = true - }; - // Make sure we receive updates until Cancellation Requested - while (!stoppingToken.IsCancellationRequested) + await telegramBotClient.ReceiveAsync(updateHandler, receiverOptions, stoppingToken); + } + catch (Exception ex) { - try - { - await telegramBotClient.ReceiveAsync(updateHandler, receiverOptions, stoppingToken); - } - catch (Exception ex) - { - logger.LogError("Polling failed with exception: {Exception}", ex); - // Cooldown if something goes wrong - await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); - } + logger.LogError("Polling failed with exception: {Exception}", ex); + // Cooldown if something goes wrong + await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); } } } diff --git a/Place4.TelegramBot/UpdateHandler.cs b/Place4.TelegramBot/UpdateHandler.cs index 4f3960e..e0458ae 100644 --- a/Place4.TelegramBot/UpdateHandler.cs +++ b/Place4.TelegramBot/UpdateHandler.cs @@ -1,31 +1,30 @@ -namespace Place4.TelegramBot -{ - using Telegram.Bot; - using Telegram.Bot.Polling; - using Telegram.Bot.Types; - using UpdateHandlers; +namespace Place4.TelegramBot; + +using Telegram.Bot; +using Telegram.Bot.Polling; +using Telegram.Bot.Types; +using UpdateHandlers; - public class UpdateHandler(IEnumerable updateHandlers, ILogger logger) : IUpdateHandler +public class UpdateHandler(IEnumerable updateHandlers, ILogger logger) : IUpdateHandler +{ + public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) { - public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) + foreach (var updateHandler in updateHandlers) { - foreach (var updateHandler in updateHandlers) + try + { + await updateHandler.HandleUpdateAsync(botClient, update, cancellationToken); + } + catch (Exception ex) { - try - { - await updateHandler.HandleUpdateAsync(botClient, update, cancellationToken); - } - catch (Exception ex) - { - await this.HandleErrorAsync(botClient, ex, HandleErrorSource.HandleUpdateError, cancellationToken); - } + await HandleErrorAsync(botClient, ex, HandleErrorSource.HandleUpdateError, cancellationToken); } } + } - public Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, HandleErrorSource source, CancellationToken cancellationToken) - { - logger.LogError(exception, "Error occured in {Source}", source); - return Task.CompletedTask; - } + public Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, HandleErrorSource source, CancellationToken cancellationToken) + { + logger.LogError(exception, "Error occured in {Source}", source); + return Task.CompletedTask; } } \ No newline at end of file diff --git a/Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs b/Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs index 0091f1e..0f19c5f 100644 --- a/Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs +++ b/Place4.TelegramBot/UpdateHandlers/ITelegramUpdateHandler.cs @@ -1,10 +1,9 @@ -namespace Place4.TelegramBot.UpdateHandlers -{ - using Telegram.Bot; - using Telegram.Bot.Types; +namespace Place4.TelegramBot.UpdateHandlers; + +using Telegram.Bot; +using Telegram.Bot.Types; - public interface ITelegramUpdateHandler - { - Task HandleUpdateAsync(ITelegramBotClient telegramBotClient, Update update, CancellationToken cancellationToken); - } +public interface ITelegramUpdateHandler +{ + Task HandleUpdateAsync(ITelegramBotClient telegramBotClient, Update update, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs b/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs index da6240d..4bc8aab 100644 --- a/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs +++ b/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs @@ -1,38 +1,37 @@ -namespace Place4.TelegramBot.UpdateHandlers -{ - using Telegram.Bot; - using Telegram.Bot.Types; - using Telegram.Bot.Types.Enums; +namespace Place4.TelegramBot.UpdateHandlers; + +using Telegram.Bot; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; - public class NewUserUpdateHandler(ILogger logger) : ITelegramUpdateHandler +public class NewUserUpdateHandler(ILogger logger) : ITelegramUpdateHandler +{ + public async Task HandleUpdateAsync(ITelegramBotClient telegramBotClient, Update update, CancellationToken cancellationToken) { - public async Task HandleUpdateAsync(ITelegramBotClient telegramBotClient, Update update, CancellationToken cancellationToken) + var newUserUpdate = IsNewUserUpdate(update); + if (newUserUpdate.chat == null || newUserUpdate.user == null) { - var newUserUpdate = IsNewUserUpdate(update); - if (newUserUpdate.chat == null || newUserUpdate.user == null) - { - return; - } - - var chatId = newUserUpdate.chat.Id; - var message = $"Welcome, [{newUserUpdate.user.FirstName} {newUserUpdate.user.LastName}](tg://user?id={newUserUpdate.user.Id})"; - await telegramBotClient.SendMessage( - chatId, - message, - cancellationToken: cancellationToken, - parseMode: ParseMode.Markdown); + return; } + + var chatId = newUserUpdate.chat.Id; + var message = $"Welcome, [{newUserUpdate.user.FirstName} {newUserUpdate.user.LastName}](tg://user?id={newUserUpdate.user.Id})"; + await telegramBotClient.SendMessage( + chatId, + message, + cancellationToken: cancellationToken, + parseMode: ParseMode.Markdown); + } - private static (Chat? chat, User? user) IsNewUserUpdate(Update update) + private static (Chat? chat, User? user) IsNewUserUpdate(Update update) + { + var isFakeNewMember = update is { Type: UpdateType.Message, Message.Text: "I am new" }; // for test only + var isNewMember = update is { Type: UpdateType.ChatMember, ChatMember.NewChatMember: not null }; + if (isNewMember) { - var isFakeNewMember = update is { Type: UpdateType.Message, Message.Text: "I am new" }; // for test only - var isNewMember = update is { Type: UpdateType.ChatMember, ChatMember.NewChatMember: not null }; - if (isNewMember) - { - return (update.ChatMember?.Chat, update.ChatMember?.NewChatMember?.User); - } - - return isFakeNewMember ? (update.Message?.Chat, update.Message?.From) : (null, null); + return (update.ChatMember?.Chat, update.ChatMember?.NewChatMember?.User); } + + return isFakeNewMember ? (update.Message?.Chat, update.Message?.From) : (null, null); } } \ No newline at end of file diff --git a/Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs b/Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs index 56b04f6..e101538 100644 --- a/Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs +++ b/Place4.TelegramBot/UpdateHandlers/ParrotUpdateHandler.cs @@ -1,26 +1,25 @@ -namespace Place4.TelegramBot.UpdateHandlers -{ - using Telegram.Bot; - using Telegram.Bot.Types; - using Telegram.Bot.Types.Enums; +namespace Place4.TelegramBot.UpdateHandlers; + +using Telegram.Bot; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; - public class ParrotUpdateHandler(ILogger logger) : ITelegramUpdateHandler +public class ParrotUpdateHandler : ITelegramUpdateHandler +{ + public async Task HandleUpdateAsync(ITelegramBotClient telegramBotClient, Update update, CancellationToken cancellationToken) { - public async Task HandleUpdateAsync(ITelegramBotClient telegramBotClient, Update update, CancellationToken cancellationToken) + if (update.Type != UpdateType.Message || update.Message?.From == null) { - if (update.Type != UpdateType.Message || update.Message?.From == null) - { - return; - } - var chatId = update.Message.Chat.Id; - var message = $"Hi, [{update.Message.From.FirstName} {update.Message.From.LastName}](tg://user?id={update.Message.From.Id}). You said: {update.Message.Text}"; - await telegramBotClient.SendMessage( - chatId, - message, - cancellationToken: cancellationToken, - messageThreadId: update.Message.MessageThreadId, - parseMode: ParseMode.Markdown); + return; } - + var chatId = update.Message.Chat.Id; + var message = $"Hi, [{update.Message.From.FirstName} {update.Message.From.LastName}](tg://user?id={update.Message.From.Id}). You said: {update.Message.Text}"; + await telegramBotClient.SendMessage( + chatId, + message, + cancellationToken: cancellationToken, + messageThreadId: update.Message.MessageThreadId, + parseMode: ParseMode.Markdown); } + } \ No newline at end of file diff --git a/Place4.sln b/Place4.sln index 998bd57..b32a5ff 100644 --- a/Place4.sln +++ b/Place4.sln @@ -6,8 +6,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Place4.AppHost", "Place4.Ap EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Place4.ServiceDefaults", "Place4.ServiceDefaults\Place4.ServiceDefaults.csproj", "{EEF046CF-366A-4614-B456-5282CA07509C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Place4.TelegramBot.Tests", "Place4.TelegramBot.Tests\Place4.TelegramBot.Tests.csproj", "{E89209BA-9F0C-4AA8-8A07-A53972A8233E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -26,9 +24,5 @@ Global {EEF046CF-366A-4614-B456-5282CA07509C}.Debug|Any CPU.Build.0 = Debug|Any CPU {EEF046CF-366A-4614-B456-5282CA07509C}.Release|Any CPU.ActiveCfg = Release|Any CPU {EEF046CF-366A-4614-B456-5282CA07509C}.Release|Any CPU.Build.0 = Release|Any CPU - {E89209BA-9F0C-4AA8-8A07-A53972A8233E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E89209BA-9F0C-4AA8-8A07-A53972A8233E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E89209BA-9F0C-4AA8-8A07-A53972A8233E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E89209BA-9F0C-4AA8-8A07-A53972A8233E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Place4.sln.DotSettings b/Place4.sln.DotSettings new file mode 100644 index 0000000..e82190c --- /dev/null +++ b/Place4.sln.DotSettings @@ -0,0 +1,28 @@ + + WARNING + BlockBody + Required + NotRequired + Required + False + Implicit + public protected override internal file new private abstract virtual sealed required async extern unsafe volatile readonly static + FileScoped + Remove + None + True + WRAP_IF_LONG + 0 + 0 + ALWAYS + ALWAYS + NEVER + False + False + True + True + True + CHOP_IF_LONG + 235 + CHOP_ALWAYS + CHOP_IF_LONG \ No newline at end of file From 9e20aff3fc10e73a787f142e7a2e06e2acbf5855 Mon Sep 17 00:00:00 2001 From: Azat Jalilov Date: Fri, 29 Nov 2024 15:31:20 +0100 Subject: [PATCH 4/5] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 320fc6e..0cfc608 100644 --- a/.gitignore +++ b/.gitignore @@ -398,3 +398,4 @@ FodyWeavers.xsd *.sln.iml .idea/ +Place4.TelegramBot/appsettings.Development.json From 05e920de704230329af25935a76f56a543f6c2d7 Mon Sep 17 00:00:00 2001 From: Azat Jalilov Date: Fri, 29 Nov 2024 17:17:59 +0100 Subject: [PATCH 5/5] Add editorconfig --- .editorconfig | 27 ++++++++++++++++++ Place4.TelegramBot/Logging/TelegramLogger.cs | 4 +-- .../UpdateHandlers/NewUserUpdateHandler.cs | 10 +++---- Place4.sln.DotSettings | 28 ------------------- 4 files changed, 34 insertions(+), 35 deletions(-) create mode 100644 .editorconfig delete mode 100644 Place4.sln.DotSettings diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2aeccb6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,27 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +# Microsoft .NET properties +csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion +dotnet_style_qualification_for_event = false:warning +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_property = false:warning + +csharp_style_namespace_declarations = file_scoped:warning + +# ReSharper properties +resharper_namespace_body = file_scoped + +[*.cs] +charset = utf-8-bom +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false diff --git a/Place4.TelegramBot/Logging/TelegramLogger.cs b/Place4.TelegramBot/Logging/TelegramLogger.cs index ff45806..fd9d3f6 100644 --- a/Place4.TelegramBot/Logging/TelegramLogger.cs +++ b/Place4.TelegramBot/Logging/TelegramLogger.cs @@ -10,7 +10,7 @@ public bool IsEnabled(LogLevel logLevel) { return logLevel > LogLevel.Information; } - + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { if (!IsEnabled(logLevel)) @@ -22,4 +22,4 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except formatter(state, exception), messageThreadId: configuration.LogChatMessageThreadId).GetAwaiter().GetResult(); } -} \ No newline at end of file +} diff --git a/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs b/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs index 4bc8aab..f54e880 100644 --- a/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs +++ b/Place4.TelegramBot/UpdateHandlers/NewUserUpdateHandler.cs @@ -4,7 +4,7 @@ namespace Place4.TelegramBot.UpdateHandlers; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; -public class NewUserUpdateHandler(ILogger logger) : ITelegramUpdateHandler +public class NewUserUpdateHandler : ITelegramUpdateHandler { public async Task HandleUpdateAsync(ITelegramBotClient telegramBotClient, Update update, CancellationToken cancellationToken) { @@ -17,12 +17,12 @@ public async Task HandleUpdateAsync(ITelegramBotClient telegramBotClient, Update var chatId = newUserUpdate.chat.Id; var message = $"Welcome, [{newUserUpdate.user.FirstName} {newUserUpdate.user.LastName}](tg://user?id={newUserUpdate.user.Id})"; await telegramBotClient.SendMessage( - chatId, - message, + chatId, + message, cancellationToken: cancellationToken, parseMode: ParseMode.Markdown); } - + private static (Chat? chat, User? user) IsNewUserUpdate(Update update) { var isFakeNewMember = update is { Type: UpdateType.Message, Message.Text: "I am new" }; // for test only @@ -34,4 +34,4 @@ private static (Chat? chat, User? user) IsNewUserUpdate(Update update) return isFakeNewMember ? (update.Message?.Chat, update.Message?.From) : (null, null); } -} \ No newline at end of file +} diff --git a/Place4.sln.DotSettings b/Place4.sln.DotSettings deleted file mode 100644 index e82190c..0000000 --- a/Place4.sln.DotSettings +++ /dev/null @@ -1,28 +0,0 @@ - - WARNING - BlockBody - Required - NotRequired - Required - False - Implicit - public protected override internal file new private abstract virtual sealed required async extern unsafe volatile readonly static - FileScoped - Remove - None - True - WRAP_IF_LONG - 0 - 0 - ALWAYS - ALWAYS - NEVER - False - False - True - True - True - CHOP_IF_LONG - 235 - CHOP_ALWAYS - CHOP_IF_LONG \ No newline at end of file