diff --git a/JOBot.TClient/Commands/ITelegramCommand.cs b/JOBot.TClient/Commands/ITelegramCommand.cs new file mode 100644 index 0000000..fdb01d2 --- /dev/null +++ b/JOBot.TClient/Commands/ITelegramCommand.cs @@ -0,0 +1,8 @@ +using Telegram.Bot.Types; + +namespace JOBot.TClient.Commands; + +public interface ITelegramCommand +{ + public Task ExecuteAsync(Update update, CancellationToken ct); +} \ No newline at end of file diff --git a/JOBot.TClient/Commands/StartCommand.cs b/JOBot.TClient/Commands/StartCommand.cs new file mode 100644 index 0000000..f528be1 --- /dev/null +++ b/JOBot.TClient/Commands/StartCommand.cs @@ -0,0 +1,18 @@ +using JOBot.TClient.Core.Services; +using Telegram.Bot; +using Telegram.Bot.Types; + +namespace JOBot.TClient.Commands; + +public class StartCommand(ITelegramBotClient bot, UserService userService) : ITelegramCommand +{ + public async Task ExecuteAsync(Update update, CancellationToken ct) + { + var isRegistered = await userService.RegisterAsync( + update.Message?.Chat.Id ?? throw new Exception("Chat id is null"), + update.Message.Chat.Username); + + await bot.SendMessage(chatId: update.Message.Chat.Id, + isRegistered ? "You registered" : "Not registered", cancellationToken: ct); + } +} \ No newline at end of file diff --git a/JOBot.TClient/Core/HostedServices/BotBackgroundService.cs b/JOBot.TClient/Core/HostedServices/BotBackgroundService.cs new file mode 100644 index 0000000..c342862 --- /dev/null +++ b/JOBot.TClient/Core/HostedServices/BotBackgroundService.cs @@ -0,0 +1,45 @@ +using JOBot.TClient.Commands; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace JOBot.TClient.Core.HostedServices; + +using Telegram.Bot; +using Telegram.Bot.Types; +using Microsoft.Extensions.Hosting; + +public sealed class BotBackgroundService( + ITelegramBotClient botClient, + IServiceProvider services, + ILogger logger) + : BackgroundService +{ + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + botClient.StartReceiving( + updateHandler: HandleUpdateAsync, + errorHandler: HandleErrorAsync, + cancellationToken: stoppingToken); + + return Task.CompletedTask; + } + + private async Task HandleUpdateAsync(ITelegramBotClient bot, Update update, CancellationToken ct) + { + using var scope = services.CreateScope(); + var commands = new Dictionary + { + ["/start"] = scope.ServiceProvider.GetRequiredService() + }; + + if (update.Message?.Text is { } text && commands.TryGetValue(text, out var command)) + await command.ExecuteAsync(update, ct); + } + + private Task HandleErrorAsync(ITelegramBotClient bot, Exception ex, CancellationToken ct) + { + services.CreateScope(); + logger.LogError(ex, ex.Message); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/JOBot.TClient/Core/Services/UserService.cs b/JOBot.TClient/Core/Services/UserService.cs new file mode 100644 index 0000000..c59bcb8 --- /dev/null +++ b/JOBot.TClient/Core/Services/UserService.cs @@ -0,0 +1,18 @@ +using User = JOBot.Proto.User; + +namespace JOBot.TClient.Core.Services; + +public class UserService(User.UserClient client) +{ + public async Task RegisterAsync(long userId, string? username) + { + + var response = await client.RegisterAsync(new() + { + UserId = userId, + Username = username + }); + + return response.Success; + } +} \ No newline at end of file diff --git a/JOBot.TClient/DependencyInjection.cs b/JOBot.TClient/DependencyInjection.cs new file mode 100644 index 0000000..5341d5f --- /dev/null +++ b/JOBot.TClient/DependencyInjection.cs @@ -0,0 +1,38 @@ +using Grpc.Core; +using Grpc.Net.Client; +using JOBot.Proto; +using JOBot.TClient.Commands; +using JOBot.TClient.Core.Services; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Telegram.Bot; + +namespace JOBot.TClient; + +public static class DependencyInjection +{ + public static IServiceCollection ConfigureServices(this IServiceCollection services, IConfiguration config) + { + services.AddSingleton(config); + + services.AddScoped(_ => GrpcChannel.ForAddress(config.GetValue("BackendHost") + ?? throw new MissingFieldException("Host is not defined"))); + + //Commands + services.AddScoped(); + + services.AddSingleton(); + + //gRPC Clients + services.AddScoped(); + + // Telegram Bot + services.AddSingleton(_ => + new TelegramBotClient(config.GetValue("TelegramToken") + ?? throw new MissingFieldException("TelegramToken is not set"))); + + services.AddLogging(builder => builder.AddConsole()); + return services; + } +} \ No newline at end of file diff --git a/JOBot.TClient/JOBot.TClient.csproj b/JOBot.TClient/JOBot.TClient.csproj index 998eb6a..0beb30a 100644 --- a/JOBot.TClient/JOBot.TClient.csproj +++ b/JOBot.TClient/JOBot.TClient.csproj @@ -16,6 +16,8 @@ + + @@ -32,4 +34,8 @@ + + + + diff --git a/JOBot.TClient/Program.cs b/JOBot.TClient/Program.cs index 9e89a24..b3b4a8d 100644 --- a/JOBot.TClient/Program.cs +++ b/JOBot.TClient/Program.cs @@ -1,44 +1,17 @@ -using Grpc.Net.Client; -using Proto = JOBot.Proto; -using Telegram.Bot; -using Telegram.Bot.Types; -using Microsoft.Extensions.Configuration; +using JOBot.TClient; +using JOBot.TClient.Core.HostedServices; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; -var config = new ConfigurationBuilder() - .AddJsonFile("appsettings.json") - .AddJsonFile("appsettings.Development.json", true) +var host = Host.CreateDefaultBuilder(args) + .ConfigureServices((context, services) => + { + // Настройка DI + services.ConfigureServices(context.Configuration); + + // Фоновый сервис для бота + services.AddHostedService(); + }) .Build(); -var botClient = new TelegramBotClient(config.GetValue("TelegramToken") - ?? throw new MissingFieldException("TelegramToken is not set")); -botClient.StartReceiving(HandleUpdate, HandleError); - -Console.WriteLine("Бот запущен. Нажмите Enter для остановки."); -Console.ReadLine(); - -async Task HandleUpdate(ITelegramBotClient bot, Update update, CancellationToken ct) -{ - using var channel = GrpcChannel.ForAddress(config.GetValue("BackendHost") - ?? throw new MissingFieldException("Host is not defined")); - var userClient = new Proto.User.UserClient(channel); - - if (update.Message?.Text == "/start") - { - var reply = await userClient.RegisterAsync(new Proto.RegisterRequest - { - UserId = update.Message.Chat.Id, - Username = update.Message.Chat.Username ?? "" - }); - - await bot.SendMessage( - chatId: update.Message.Chat.Id, - text: reply.Success ? "✅ Вы зарегистрированы!" : "❌ Ошибка", - cancellationToken: ct); - } -} - -Task HandleError(ITelegramBotClient bot, Exception ex, CancellationToken ct) -{ - Console.WriteLine($"Ошибка: {ex.Message}"); - return Task.CompletedTask; -} \ No newline at end of file +await host.RunAsync(); \ No newline at end of file diff --git a/JOBot.TClient/appsettings.json b/JOBot.TClient/appsettings.json index 0e0dcd2..083047e 100644 --- a/JOBot.TClient/appsettings.json +++ b/JOBot.TClient/appsettings.json @@ -1,3 +1,4 @@ { - + "TelegramToken": "7372524570:AAGLW5dXhW5Jd78i9Zguyga-5gfeSF9KU4I", + "BackendHost": "http://localhost:5001" } \ No newline at end of file