diff --git a/JOBot.Backend/Services/HeadHunterService.cs b/JOBot.Backend/Services/HeadHunterService.cs index 3395a22..c155837 100644 --- a/JOBot.Backend/Services/HeadHunterService.cs +++ b/JOBot.Backend/Services/HeadHunterService.cs @@ -14,6 +14,7 @@ public class HeadHunterService( IChannel channel, ILogger logger, IOptions config, + IWebHostEnvironment env, AppDbContext dbContext) { public enum Status @@ -47,45 +48,54 @@ public class HeadHunterService( return Status.UserAuthRejectedError; } - using var client = new HttpClient(); - var form = new Dictionary + HeadHunterTokenResponseDto? responseDto; + if (!env.IsDevelopment()) //Production server { - { "client_id", _config.ClientId }, - { "client_secret", _config.Secret }, - { "code", authorizationCode }, - { "grant_type", "authorization_code" }, - { "redirect_uri", GetRedirectUrl(userId) } - }; - client.BaseAddress = new UriBuilder(_config.Links.HeadHunterApiDomain) - { - Port = -1, - Scheme = "https" - }.Uri; - client.DefaultRequestHeaders.UserAgent.ParseAdd("Jobot BackEnd Service"); - - using var res = await client.SendAsync( - new HttpRequestMessage( - HttpMethod.Post, - _config.Links.HeadHunterTokenRoute) + using var client = new HttpClient(); + var form = new Dictionary { - Content = new FormUrlEncodedContent(form) - }); + { "client_id", _config.ClientId }, + { "client_secret", _config.Secret }, + { "code", authorizationCode }, + { "grant_type", "authorization_code" }, + { "redirect_uri", GetRedirectUrl(userId) } + }; + client.BaseAddress = new UriBuilder(_config.Links.HeadHunterApiDomain) + { + Port = -1, + Scheme = "https" + }.Uri; + client.DefaultRequestHeaders.UserAgent.ParseAdd("Jobot BackEnd Service"); - if (!res.IsSuccessStatusCode) - { - logger.LogWarning($"Response of HttpRequest {_config.Links.HeadHunterApiDomain}" + - $"{_config.Links.HeadHunterTokenRoute} has unsuccessful status code {res.StatusCode}"); - logger.LogWarning($"{res.Content.ReadAsStringAsync().Result}"); - return Status.HeadHunterAuthRejectedError; + using var res = await client.SendAsync( + new HttpRequestMessage( + HttpMethod.Post, + _config.Links.HeadHunterTokenRoute) + { + Content = new FormUrlEncodedContent(form) + }); + + if (!res.IsSuccessStatusCode) + { + logger.LogWarning($"Response of HttpRequest {_config.Links.HeadHunterApiDomain}" + + $"{_config.Links.HeadHunterTokenRoute} has unsuccessful status code {res.StatusCode}"); + logger.LogWarning($"{res.Content.ReadAsStringAsync().Result}"); + return Status.HeadHunterAuthRejectedError; + } + + responseDto = + JsonSerializer.Deserialize(await res.Content.ReadAsStringAsync()); + + if (responseDto == null) + { + logger.LogWarning($"User {userId} auth completed with error " + + $"{nameof(Status.HeadHunterResponseDeserializationFailedError)}"); + return Status.HeadHunterResponseDeserializationFailedError; + } } - - var responseDto = JsonSerializer.Deserialize(await res.Content.ReadAsStringAsync()); - - if (responseDto == null) + else { - logger.LogWarning($"User {userId} auth completed with error " + - $"{nameof(Status.HeadHunterResponseDeserializationFailedError)}"); - return Status.HeadHunterResponseDeserializationFailedError; + responseDto = new HeadHunterTokenResponseDto("testtoken", 0, "testtoken", "--"); } var user = await dbContext.Users.FirstOrDefaultAsync(x => x.UserId == userId); diff --git a/JOBot.Backend/Startup.cs b/JOBot.Backend/Startup.cs index 957d184..9cb2137 100644 --- a/JOBot.Backend/Startup.cs +++ b/JOBot.Backend/Startup.cs @@ -19,9 +19,9 @@ public class Startup(IConfiguration configuration) services.AddControllers(); services.AddLogging(); - await using var rabbitMqConnection = await new ConnectionFactory + var rabbitMqConnection = await new ConnectionFactory { - HostName = "jobot-rabbitmq" + HostName = "rabbitmq" }.CreateConnectionAsync(); var channel = await rabbitMqConnection.CreateChannelAsync(); await channel.QueueDeclareAsync( diff --git a/JOBot.TClient/Commands/Commands/InfoCommand.cs b/JOBot.TClient/Commands/Commands/InfoCommand.cs index cc2a653..2375337 100644 --- a/JOBot.TClient/Commands/Commands/InfoCommand.cs +++ b/JOBot.TClient/Commands/Commands/InfoCommand.cs @@ -1,3 +1,4 @@ +using JOBot.TClient.Infrastructure.Extensions; using Telegram.Bot; using Telegram.Bot.Types; @@ -9,6 +10,6 @@ public class InfoCommand(ITelegramBotClient bot) : ITelegramCommand { ArgumentNullException.ThrowIfNull(update.Message?.From); - await bot.SendMessage(update.Message.From.Id, TextResource.Info, cancellationToken: ct); + await bot.SendMessageRemK(update.Message.From.Id, TextResource.Info, cancellationToken: ct); } } \ No newline at end of file diff --git a/JOBot.TClient/Core/HostedServices/BotBackgroundService.cs b/JOBot.TClient/Core/HostedServices/BotBackgroundService.cs index 3006f7e..a063b8f 100644 --- a/JOBot.TClient/Core/HostedServices/BotBackgroundService.cs +++ b/JOBot.TClient/Core/HostedServices/BotBackgroundService.cs @@ -77,7 +77,7 @@ public sealed class BotBackgroundService( return; } - await bot.SendMessage(update.Message.From.Id, TextResource.CommandNotFound, cancellationToken: ct); + await bot.SendMessageRemK(update.Message.From.Id, TextResource.CommandNotFound, cancellationToken: ct); } } diff --git a/JOBot.TClient/Infrastructure/Exceptions/FallbackException.cs b/JOBot.TClient/Infrastructure/Exceptions/FallbackException.cs index afe8459..1a765e9 100644 --- a/JOBot.TClient/Infrastructure/Exceptions/FallbackException.cs +++ b/JOBot.TClient/Infrastructure/Exceptions/FallbackException.cs @@ -1,3 +1,4 @@ +using JOBot.TClient.Infrastructure.Extensions; using Telegram.Bot; using Telegram.Bot.Types; @@ -11,7 +12,7 @@ public class FallbackException : Exception { public FallbackException(string message, Message botMessage, ITelegramBotClient botClient) : base(message) { - botClient.SendMessage(botMessage.Chat.Id, + botClient.SendMessageRemK(botMessage.Chat.Id, TextResource.FallbackMessage); } } \ No newline at end of file diff --git a/JOBot.TClient/Infrastructure/Extensions/TelegramBotClientExtensions.cs b/JOBot.TClient/Infrastructure/Extensions/TelegramBotClientExtensions.cs new file mode 100644 index 0000000..b713e49 --- /dev/null +++ b/JOBot.TClient/Infrastructure/Extensions/TelegramBotClientExtensions.cs @@ -0,0 +1,52 @@ +using Telegram.Bot; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.ReplyMarkups; + +namespace JOBot.TClient.Infrastructure.Extensions; + +public static class TelegramBotClientExtensions +{ + /// + /// Extension method for auto-remove of reply keyboard if is null + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static Task SendMessageRemK( + this ITelegramBotClient bot, + ChatId chatId, + string text, + ParseMode parseMode = default, + ReplyParameters? replyParameters = null, + ReplyMarkup? replyMarkup = null, + LinkPreviewOptions? linkPreviewOptions = null, + int? messageThreadId = null, + IEnumerable? entities = null, + bool disableNotification = false, + bool protectContent = false, + string? messageEffectId = null, + string? businessConnectionId = null, + bool allowPaidBroadcast = false, + CancellationToken cancellationToken = default) + { + replyMarkup ??= new ReplyKeyboardRemove(); + + return bot.SendMessage(chatId, text, parseMode, replyParameters, replyMarkup, linkPreviewOptions, messageThreadId, + entities, disableNotification, protectContent, messageEffectId, businessConnectionId, allowPaidBroadcast, + cancellationToken); + } +} \ No newline at end of file diff --git a/JOBot.TClient/Queues/AuthQueue.cs b/JOBot.TClient/Queues/AuthQueue.cs index e5b9498..d3e1fc1 100644 --- a/JOBot.TClient/Queues/AuthQueue.cs +++ b/JOBot.TClient/Queues/AuthQueue.cs @@ -1,26 +1,24 @@ using System.Text; using JOBot.Infrastructure.Config; using JOBot.TClient.Services; +using Microsoft.Extensions.Hosting; using RabbitMQ.Client; using RabbitMQ.Client.Events; namespace JOBot.TClient.Queues; -public class AuthQueue +public class AuthQueue(IChannel channel, PrepareUserService prepareUserService) : BackgroundService { - private readonly PrepareUserService _prepareUserService; - public AuthQueue( - IChannel channel, PrepareUserService prepareUserService) - { - _prepareUserService = prepareUserService; - - var consumer = new AsyncEventingBasicConsumer(channel); - consumer.ReceivedAsync += OnDataReceivedAsync; - channel.BasicConsumeAsync(RabbitQueues.AuthQueue, autoAck: true, consumer: consumer); - } private async Task OnDataReceivedAsync(object sender, BasicDeliverEventArgs eventArgs) { - await _prepareUserService.SelectCv(Convert.ToInt64(Encoding.UTF8.GetString(eventArgs.Body.ToArray()))); + await prepareUserService.AuthHookReceived(Convert.ToInt64(Encoding.UTF8.GetString(eventArgs.Body.ToArray()))); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + var consumer = new AsyncEventingBasicConsumer(channel); + consumer.ReceivedAsync += OnDataReceivedAsync; + await channel.BasicConsumeAsync(RabbitQueues.AuthQueue, autoAck: true, consumer: consumer, cancellationToken: stoppingToken); } } \ No newline at end of file diff --git a/JOBot.TClient/Services/MenuService.cs b/JOBot.TClient/Services/MenuService.cs index 4f2e4d1..bdfd891 100644 --- a/JOBot.TClient/Services/MenuService.cs +++ b/JOBot.TClient/Services/MenuService.cs @@ -1,3 +1,4 @@ +using JOBot.TClient.Infrastructure.Extensions; using Telegram.Bot; using Telegram.Bot.Types; @@ -9,6 +10,6 @@ public class MenuService(ITelegramBotClient bot) { ArgumentNullException.ThrowIfNull(update.Message?.From); - await bot.SendMessage(update.Message.From.Id, "PrepareUser stage is done.", cancellationToken: ct); + await bot.SendMessageRemK(update.Message.From.Id, "PrepareUser stage is done.", cancellationToken: ct); } } \ No newline at end of file diff --git a/JOBot.TClient/Services/PrepareUserService.cs b/JOBot.TClient/Services/PrepareUserService.cs index df61817..55898cd 100644 --- a/JOBot.TClient/Services/PrepareUserService.cs +++ b/JOBot.TClient/Services/PrepareUserService.cs @@ -1,5 +1,6 @@ using JOBot.Proto; using JOBot.TClient.Infrastructure.Exceptions; +using JOBot.TClient.Infrastructure.Extensions; using Telegram.Bot; using Telegram.Bot.Types; using User = JOBot.Proto.User; @@ -31,7 +32,7 @@ public class PrepareUserService(ITelegramBotClient bot, User.UserClient userClie if (!result.Success) { - await _bot.SendMessage(update.Message.Chat.Id, + await _bot.SendMessageRemK(update.Message.Chat.Id, TextResource.FallbackMessage, cancellationToken: ct); @@ -59,7 +60,7 @@ public class PrepareUserService(ITelegramBotClient bot, User.UserClient userClie { ArgumentNullException.ThrowIfNull(update.Message?.From); - await _bot.SendMessage( + await _bot.SendMessageRemK( update.Message.From.Id, TextResource.EULA, replyMarkup: new[] { ButtonResource.EULAAgrement }, cancellationToken: ct); @@ -93,7 +94,7 @@ public class PrepareUserService(ITelegramBotClient bot, User.UserClient userClie UserId = update.Message.From.Id }); - await _bot.SendMessage( + await _bot.SendMessageRemK( update.Message.From.Id, string.Format(TextResource.AskForAuth, [url.RegistrationUrl]), cancellationToken: ct); @@ -101,7 +102,7 @@ public class PrepareUserService(ITelegramBotClient bot, User.UserClient userClie public async Task AuthHookReceived(long userId, CancellationToken ct = default) { - await _bot.SendMessage(userId, "Авторизация завершена успешно!", cancellationToken: ct); + await _bot.SendMessageRemK(userId, "✅ Авторизация завершена успешно!", cancellationToken: ct); await SelectCv(userId, ct); } @@ -114,7 +115,7 @@ public class PrepareUserService(ITelegramBotClient bot, User.UserClient userClie public async Task SelectCv(long userId, CancellationToken ct = default) { - await _bot.SendMessage(userId, "Давайте выберем одно из доступных резюме:", + await _bot.SendMessageRemK(userId, "Давайте выберем одно из доступных резюме:", cancellationToken: ct); //TODO: https://git.lisoveliy.su/Lisoveliy/JOBot/issues/9 } } \ No newline at end of file diff --git a/JOBot.TClient/Startup.cs b/JOBot.TClient/Startup.cs index 23630fd..fa3bdaa 100644 --- a/JOBot.TClient/Startup.cs +++ b/JOBot.TClient/Startup.cs @@ -23,17 +23,17 @@ public static class Startup { services.AddSingleton(config); - services.AddScoped(_ => GrpcChannel.ForAddress(config.GetValue("BackendHost") - ?? throw new MissingFieldException("Host is not defined"))); + services.AddSingleton(_ => GrpcChannel.ForAddress(config.GetValue("BackendHost") + ?? throw new MissingFieldException("Host is not defined"))); #region Commands - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); //buttons - services.AddScoped(); + services.AddSingleton(); #endregion @@ -46,21 +46,21 @@ public static class Startup #region Services services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); #endregion #region States - services.AddScoped(); + services.AddSingleton(); #endregion #region RabbitMQ Clients - var factory = new ConnectionFactory { HostName = "localhost" }; - using var connection = factory.CreateConnectionAsync().Result; + var factory = new ConnectionFactory { HostName = "rabbitmq" }; + var connection = factory.CreateConnectionAsync().Result; var channel = connection.CreateChannelAsync().Result; channel.QueueDeclareAsync( @@ -69,8 +69,9 @@ public static class Startup false, false, arguments: null).Wait(); - - services.AddSingleton(); + + services.AddSingleton(channel); + services.AddHostedService(); #endregion diff --git a/compose.dev.yml b/compose.dev.yml index d21dad1..daa1ca1 100644 --- a/compose.dev.yml +++ b/compose.dev.yml @@ -9,6 +9,8 @@ services: - "15672:15672" volumes: - rabbitmq_data:/var/lib/rabbitmq/ + networks: + - jobot postgres: image: postgres:15 @@ -28,6 +30,7 @@ services: dockerfile: JOBot.Backend/Dockerfile depends_on: - postgres + - rabbitmq ports: - "5000:5000" - "5001:5001" diff --git a/compose.yml b/compose.yml index dbb502e..d737d73 100644 --- a/compose.yml +++ b/compose.yml @@ -6,6 +6,8 @@ services: image: rabbitmq:4 volumes: - rabbitmq_data:/var/lib/rabbitmq/ + networks: + - jobot postgres: image: postgres:15 @@ -23,7 +25,7 @@ services: dockerfile: JOBot.Backend/Dockerfile depends_on: - postgres - - image + - rabbitmq ports: - "5000:5000" networks: