diff --git a/JOBot.Backend/Controllers/HeadHunterHookController.cs b/JOBot.Backend/Controllers/HeadHunterHookController.cs index 1ba1e24..4b630ae 100644 --- a/JOBot.Backend/Controllers/HeadHunterHookController.cs +++ b/JOBot.Backend/Controllers/HeadHunterHookController.cs @@ -8,16 +8,17 @@ namespace JOBot.Backend.Controllers; public class HeadHunterHookController(HeadHunterService hhService) : ControllerBase { - [HttpGet] public async Task Get(int userId, string? error, string code) { var res = await hhService.AuthUser(userId, code, error); return res switch { - HeadHunterService.Status.Success => Ok("Авторизация завершена успешно. Вернитесь в Telegram для продолжения."), + HeadHunterService.Status.Success => Ok( + "Авторизация завершена успешно. Вернитесь в Telegram для продолжения."), HeadHunterService.Status.UserNotFoundError => NotFound("Пользователь не найден."), - _ => BadRequest("Авторизация завершена с ошибкой. Вернитесь в Telegram для продолжения.") //TODO: Add resource + _ => BadRequest( + "Авторизация завершена с ошибкой. Вернитесь в Telegram для продолжения.") //TODO: Add resource }; } } \ No newline at end of file diff --git a/JOBot.Backend/DAL/Context/AppDbContext.cs b/JOBot.Backend/DAL/Context/AppDbContext.cs index 9e91d4e..12ef6af 100644 --- a/JOBot.Backend/DAL/Context/AppDbContext.cs +++ b/JOBot.Backend/DAL/Context/AppDbContext.cs @@ -1,14 +1,14 @@ -namespace JOBot.Backend.DAL.Context; - -using Models; +using JOBot.Backend.DAL.Models; using Microsoft.EntityFrameworkCore; +namespace JOBot.Backend.DAL.Context; + public class AppDbContext(DbContextOptions options) : DbContext(options) { public DbSet Users { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) - { + { base.OnModelCreating(modelBuilder); modelBuilder.Entity() diff --git a/JOBot.Backend/DAL/Models/User.cs b/JOBot.Backend/DAL/Models/User.cs index 82ec05c..0009df7 100644 --- a/JOBot.Backend/DAL/Models/User.cs +++ b/JOBot.Backend/DAL/Models/User.cs @@ -9,12 +9,12 @@ public class User { public Guid Id { get; set; } - [Key] - public required long UserId { get; set; } - [MaxLength(255)] - public string? Username { get; set; } + [Key] public required long UserId { get; set; } + + [MaxLength(255)] public string? Username { get; set; } + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - + [MaxLength(255)] public string? AccessToken { get; set; } = null; [MaxLength(255)] public string? RefreshToken { get; set; } = null; diff --git a/JOBot.Backend/DTOs/HeadHunterHook/HeadHunterTokenResponseDto.cs b/JOBot.Backend/DTOs/HeadHunterHook/HeadHunterTokenResponseDto.cs index 179cfb3..a970955 100644 --- a/JOBot.Backend/DTOs/HeadHunterHook/HeadHunterTokenResponseDto.cs +++ b/JOBot.Backend/DTOs/HeadHunterHook/HeadHunterTokenResponseDto.cs @@ -3,12 +3,12 @@ using System.Text.Json.Serialization; namespace JOBot.Backend.DTOs.HeadHunterHook; public record HeadHunterTokenResponseDto( - [property:JsonPropertyName("access_token")] + [property: JsonPropertyName("access_token")] string AccessToken, - [property:JsonPropertyName("expires_in")] + [property: JsonPropertyName("expires_in")] int ExpiresIn, - [property:JsonPropertyName("refresh_token")] + [property: JsonPropertyName("refresh_token")] string RefreshToken, - [property:JsonPropertyName("token_type")] + [property: JsonPropertyName("token_type")] string TokenType - ); \ No newline at end of file +); \ No newline at end of file diff --git a/JOBot.Backend/JOBot.Backend.csproj b/JOBot.Backend/JOBot.Backend.csproj index d137fc6..6526b76 100644 --- a/JOBot.Backend/JOBot.Backend.csproj +++ b/JOBot.Backend/JOBot.Backend.csproj @@ -1,34 +1,34 @@  - - net9.0 - enable - enable - + + net9.0 + enable + enable + - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + - - - + + + - - - + + + \ No newline at end of file diff --git a/JOBot.Backend/Services/HeadHunterService.cs b/JOBot.Backend/Services/HeadHunterService.cs index 3e67879..8ff1787 100644 --- a/JOBot.Backend/Services/HeadHunterService.cs +++ b/JOBot.Backend/Services/HeadHunterService.cs @@ -10,28 +10,36 @@ using RabbitMQ.Client; namespace JOBot.Backend.Services; public class HeadHunterService( - IChannel channel, - ILogger logger, - IOptions config, + IChannel channel, + ILogger logger, + IOptions config, AppDbContext dbContext) { + public enum Status + { + UserAuthRejectedError, + HeadHunterAuthRejectedError, + UserNotFoundError, + HeadHunterResponseDeserializationFailedError, + Success + } + private readonly HeadHunterConfig _config = config.Value; /// - /// Generate HeadHunter oauth authorization link + /// Generate HeadHunter oauth authorization link /// /// Telegram UserId /// Link for auth public string GenerateAuthLink(long userId) { - return string.Format(_config.Links.AuthLink, [_config.ClientId, GetRedirectUrl(userId)]); } - public async Task AuthUser(int userId, string authorizationCode, string? error) + public async Task AuthUser(int userId, string authorizationCode, string? error) //TODO: Разбить этот метод { logger.LogInformation($"Authorization for user {userId} in process..."); - + if (!string.IsNullOrEmpty(error)) { logger.LogWarning($"User {userId} auth completed with error {error}"); @@ -69,7 +77,7 @@ public class HeadHunterService( logger.LogWarning($"{res.Content.ReadAsStringAsync().Result}"); return Status.HeadHunterAuthRejectedError; } - + var responseDto = JsonSerializer.Deserialize(await res.Content.ReadAsStringAsync()); if (responseDto == null) @@ -78,7 +86,7 @@ public class HeadHunterService( $"{nameof(Status.HeadHunterResponseDeserializationFailedError)}"); return Status.HeadHunterResponseDeserializationFailedError; } - + var user = await dbContext.Users.FirstOrDefaultAsync(x => x.UserId == userId); if (user == null) @@ -86,20 +94,20 @@ public class HeadHunterService( logger.LogWarning($"User {userId} search completed with error {nameof(Status.UserNotFoundError)}"); return Status.UserNotFoundError; } - + user.AccessToken = responseDto.AccessToken; user.RefreshToken = responseDto.RefreshToken; - + await dbContext.SaveChangesAsync(); - + logger.LogInformation($"User {userId} auth completed!"); - + await channel.BasicPublishAsync( - string.Empty, - RabbitQueues.AuthQueue, + string.Empty, + RabbitQueues.AuthQueue, Encoding.UTF8.GetBytes(userId.ToString())); - - logger.LogInformation($"RabbitMQ event was created"); + + logger.LogInformation("RabbitMQ event was created"); return Status.Success; } @@ -114,13 +122,4 @@ public class HeadHunterService( Query = $"?userId={userId}" }.ToString(); } - - public enum Status - { - UserAuthRejectedError, - HeadHunterAuthRejectedError, - UserNotFoundError, - HeadHunterResponseDeserializationFailedError, - Success - } } \ No newline at end of file diff --git a/JOBot.Backend/Services/gRPC/UserService.cs b/JOBot.Backend/Services/gRPC/UserService.cs index 4a1cfe6..fdbbbec 100644 --- a/JOBot.Backend/Services/gRPC/UserService.cs +++ b/JOBot.Backend/Services/gRPC/UserService.cs @@ -1,4 +1,3 @@ -using Google.Protobuf.WellKnownTypes; using Grpc.Core; using JOBot.Backend.DAL.Context; using JOBot.Backend.DAL.Models; @@ -11,7 +10,7 @@ namespace JOBot.Backend.Services.gRPC; public class UserService(AppDbContext dbContext, HeadHunterService hhService) : Proto.User.UserBase { /// - /// Create user + /// Create user /// /// /// @@ -32,7 +31,7 @@ public class UserService(AppDbContext dbContext, HeadHunterService hhService) : } /// - /// Get user for client + /// Get user for client /// /// /// @@ -46,7 +45,7 @@ public class UserService(AppDbContext dbContext, HeadHunterService hhService) : } /// - /// Accept EULA for user + /// Accept EULA for user /// /// /// @@ -64,7 +63,7 @@ public class UserService(AppDbContext dbContext, HeadHunterService hhService) : } public override Task GetHeadHunterAuthHook( - GetHeadHunterAuthHookRequest request, + GetHeadHunterAuthHookRequest request, ServerCallContext context) { return Task.Run(() => new GetHeadHunterAuthHookResponse @@ -74,7 +73,7 @@ public class UserService(AppDbContext dbContext, HeadHunterService hhService) : } /// - /// Throw RPCException if user not found + /// Throw RPCException if user not found /// /// /// diff --git a/JOBot.Backend/Startup.cs b/JOBot.Backend/Startup.cs index d08caed..6890d1e 100644 --- a/JOBot.Backend/Startup.cs +++ b/JOBot.Backend/Startup.cs @@ -13,7 +13,7 @@ public class Startup(IConfiguration configuration) public async Task ConfigureServices(IServiceCollection services) { - services.AddGrpc(); + services.AddGrpc(); services.AddGrpcReflection(); services.AddControllers(); services.AddLogging(); @@ -23,9 +23,9 @@ public class Startup(IConfiguration configuration) HostName = "jobot-rabbitmq" }.CreateConnectionAsync(); await using var channel = await rabbitMqConnection.CreateChannelAsync(); - await channel.QueueDeclareAsync(RabbitQueues.AuthQueue, false, false, autoDelete: false); + await channel.QueueDeclareAsync(RabbitQueues.AuthQueue, false, false, false); services.AddSingleton(channel); - + services.AddDbContext(options => options.UseNpgsql(Configuration.GetConnectionString("PostgreSQL"))); diff --git a/JOBot.Backend/appsettings.json b/JOBot.Backend/appsettings.json index ae7b3b2..0605775 100644 --- a/JOBot.Backend/appsettings.json +++ b/JOBot.Backend/appsettings.json @@ -1,35 +1,35 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", - "ConnectionStrings": { - "PostgreSQL": "Host=postgres;Port=5432;Database=jobot;Username=postgres;Password=LocalDbPass" - }, - "HeadHunter": { - "Links": { - "AuthLink": "https://hh.ru/oauth/authorize?response_type=code&client_id={0}&redirect_uri={1}", - "HookDomain": "jobot.lisoveliy.su", - "HookRoute": "/auth", - "HeadHunterApiDomain": "api.hh.ru", - "HeadHunterTokenRoute": "/token" - }, - "ClientId": "", - "Secret": "" - }, - "Kestrel": { - "Endpoints": { - "gRPC": { - "Url": "http://*:5001", - "Protocols": "Http2" - }, - "REST": { - "Url": "http://*:5000", - "Protocols": "Http1" - } - } + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "PostgreSQL": "Host=postgres;Port=5432;Database=jobot;Username=postgres;Password=LocalDbPass" + }, + "HeadHunter": { + "Links": { + "AuthLink": "https://hh.ru/oauth/authorize?response_type=code&client_id={0}&redirect_uri={1}", + "HookDomain": "jobot.lisoveliy.su", + "HookRoute": "/auth", + "HeadHunterApiDomain": "api.hh.ru", + "HeadHunterTokenRoute": "/token" + }, + "ClientId": "", + "Secret": "" + }, + "Kestrel": { + "Endpoints": { + "gRPC": { + "Url": "http://*:5001", + "Protocols": "Http2" + }, + "REST": { + "Url": "http://*:5000", + "Protocols": "Http1" + } + } + } } \ No newline at end of file diff --git a/JOBot.TClient/ButtonResource.resx b/JOBot.TClient/ButtonResource.resx index a799cfc..91aa97c 100644 --- a/JOBot.TClient/ButtonResource.resx +++ b/JOBot.TClient/ButtonResource.resx @@ -1,9 +1,10 @@ - + - + @@ -13,10 +14,14 @@ 1.3 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + Соглашаюсь с условиями использования ✅ diff --git a/JOBot.TClient/Commands/Buttons/EulaAgreementButtonCommand.cs b/JOBot.TClient/Commands/Buttons/EulaAgreementButtonCommand.cs index f28e9ab..b9ff97c 100644 --- a/JOBot.TClient/Commands/Buttons/EulaAgreementButtonCommand.cs +++ b/JOBot.TClient/Commands/Buttons/EulaAgreementButtonCommand.cs @@ -1,9 +1,7 @@ using JOBot.Proto; using JOBot.TClient.Infrastructure.Attributes.Authorization; -using JOBot.TClient.Services; using JOBot.TClient.Statements; using Telegram.Bot.Types; -using Telegram.Bot.Types.Enums; namespace JOBot.TClient.Commands.Buttons; diff --git a/JOBot.TClient/Commands/Commands/InfoCommand.cs b/JOBot.TClient/Commands/Commands/InfoCommand.cs index 45bb655..cc2a653 100644 --- a/JOBot.TClient/Commands/Commands/InfoCommand.cs +++ b/JOBot.TClient/Commands/Commands/InfoCommand.cs @@ -8,7 +8,7 @@ public class InfoCommand(ITelegramBotClient bot) : ITelegramCommand public async Task ExecuteAsync(Update update, CancellationToken ct) { ArgumentNullException.ThrowIfNull(update.Message?.From); - + await bot.SendMessage(update.Message.From.Id, TextResource.Info, cancellationToken: ct); } } \ No newline at end of file diff --git a/JOBot.TClient/Commands/IAuthorizedTelegramCommand.cs b/JOBot.TClient/Commands/IAuthorizedTelegramCommand.cs index 7d0d035..b91b18d 100644 --- a/JOBot.TClient/Commands/IAuthorizedTelegramCommand.cs +++ b/JOBot.TClient/Commands/IAuthorizedTelegramCommand.cs @@ -1,17 +1,18 @@ using JOBot.Proto; -using Telegram.Bot; using Telegram.Bot.Types; namespace JOBot.TClient.Commands; public interface IAuthorizedTelegramCommand : ITelegramCommand { - public Task ExecuteAsync(Update update, GetUserResponse user, CancellationToken ct); - - /// Throws if you try to use ITelegramCommand.ExecuteAsync - /// instead of IAuthorizedTelegramCommand.ExecuteAsync + /// + /// Throws if you try to use ITelegramCommand.ExecuteAsync + /// instead of IAuthorizedTelegramCommand.ExecuteAsync + /// Task ITelegramCommand.ExecuteAsync(Update update, CancellationToken ct) { throw new UnauthorizedAccessException("You do not have permission to access this command."); } + + public Task ExecuteAsync(Update update, GetUserResponse user, 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 87e63ff..3006f7e 100644 --- a/JOBot.TClient/Core/HostedServices/BotBackgroundService.cs +++ b/JOBot.TClient/Core/HostedServices/BotBackgroundService.cs @@ -5,10 +5,10 @@ using JOBot.TClient.Infrastructure.Attributes.Authorization; using JOBot.TClient.Infrastructure.Extensions; using JOBot.TClient.Services; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Telegram.Bot; using Telegram.Bot.Types; -using Microsoft.Extensions.Hosting; namespace JOBot.TClient.Core.HostedServices; @@ -22,8 +22,8 @@ public sealed class BotBackgroundService( protected override Task ExecuteAsync(CancellationToken stoppingToken) { botClient.StartReceiving( - updateHandler: HandleUpdateAsync, - errorHandler: HandleErrorAsync, + HandleUpdateAsync, + HandleErrorAsync, cancellationToken: stoppingToken); return Task.CompletedTask; @@ -40,7 +40,7 @@ public sealed class BotBackgroundService( ["/info"] = scope.ServiceProvider.GetRequiredService(), //Buttons - [ButtonResource.EULAAgrement] = scope.ServiceProvider.GetRequiredService(), + [ButtonResource.EULAAgrement] = scope.ServiceProvider.GetRequiredService() }; if (update.Message is { Text: { } text, From: not null }) @@ -69,7 +69,10 @@ public sealed class BotBackgroundService( await authorizedTelegramCommand.ExecuteAsync(update, user, ct); } - else await command.ExecuteAsync(update, ct); + else + { + await command.ExecuteAsync(update, ct); + } return; } diff --git a/JOBot.TClient/DependencyInjection.cs b/JOBot.TClient/DependencyInjection.cs index 153be1b..6594372 100644 --- a/JOBot.TClient/DependencyInjection.cs +++ b/JOBot.TClient/DependencyInjection.cs @@ -18,39 +18,47 @@ 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"))); - + + services.AddScoped(_ => GrpcChannel.ForAddress(config.GetValue("BackendHost") + ?? throw new MissingFieldException("Host is not defined"))); + #region Commands + services.AddScoped(); services.AddScoped(); services.AddScoped(); - + //buttons services.AddScoped(); + #endregion - + #region gRPC Clients + services.AddGrpcClient(o => o.Address = new Uri("http://backend:5001")); + #endregion - + #region Services + services.AddSingleton(); services.AddScoped(); services.AddScoped(); + #endregion - + #region States + services.AddScoped(); + #endregion - + // Bot service services.AddHostedService(); - services.AddSingleton(_ => - new TelegramBotClient(config.GetValue("TelegramToken") + services.AddSingleton(_ => + new TelegramBotClient(config.GetValue("TelegramToken") ?? throw new MissingFieldException("TelegramToken is not set"))); - + services.AddLogging(builder => builder.AddConsole()); return services; } diff --git a/JOBot.TClient/Infrastructure/Attributes/Authorization/AcceptNotPreparedAttribute.cs b/JOBot.TClient/Infrastructure/Attributes/Authorization/AcceptNotPreparedAttribute.cs index bb9bf0d..bac3b51 100644 --- a/JOBot.TClient/Infrastructure/Attributes/Authorization/AcceptNotPreparedAttribute.cs +++ b/JOBot.TClient/Infrastructure/Attributes/Authorization/AcceptNotPreparedAttribute.cs @@ -1,7 +1,7 @@ namespace JOBot.TClient.Infrastructure.Attributes.Authorization; /// -///Атрибут допуска к команде пользователя не завершившего стадию PrepareUser +/// Атрибут допуска к команде пользователя не завершившего стадию PrepareUser /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] public class AcceptNotPreparedAttribute : Attribute; \ No newline at end of file diff --git a/JOBot.TClient/Infrastructure/Exceptions/FallbackException.cs b/JOBot.TClient/Infrastructure/Exceptions/FallbackException.cs index e624813..afe8459 100644 --- a/JOBot.TClient/Infrastructure/Exceptions/FallbackException.cs +++ b/JOBot.TClient/Infrastructure/Exceptions/FallbackException.cs @@ -4,14 +4,14 @@ using Telegram.Bot.Types; namespace JOBot.TClient.Infrastructure.Exceptions; /// -/// Exception for fallback -/// WARNING: Don't create new exception without throwing it +/// Exception for fallback +/// WARNING: Don't create new exception without throwing it /// public class FallbackException : Exception { public FallbackException(string message, Message botMessage, ITelegramBotClient botClient) : base(message) { - botClient.SendMessage(chatId: botMessage.Chat.Id, + botClient.SendMessage(botMessage.Chat.Id, TextResource.FallbackMessage); } } \ No newline at end of file diff --git a/JOBot.TClient/Infrastructure/Extensions/GRpcModelsExtensions.cs b/JOBot.TClient/Infrastructure/Extensions/GRpcModelsExtensions.cs index ba10820..0a81265 100644 --- a/JOBot.TClient/Infrastructure/Extensions/GRpcModelsExtensions.cs +++ b/JOBot.TClient/Infrastructure/Extensions/GRpcModelsExtensions.cs @@ -4,6 +4,9 @@ namespace JOBot.TClient.Infrastructure.Extensions; public static class GRpcModelsExtensions { - public static bool IsPrepared(this GetUserResponse user) => user is { Eula: true, IsLogged: true } && - !string.IsNullOrEmpty(user.CVUrl); + public static bool IsPrepared(this GetUserResponse user) + { + return user is { Eula: true, IsLogged: true } && + !string.IsNullOrEmpty(user.CVUrl); + } } \ No newline at end of file diff --git a/JOBot.TClient/JOBot.TClient.csproj b/JOBot.TClient/JOBot.TClient.csproj index 6489bda..5bcfbc3 100644 --- a/JOBot.TClient/JOBot.TClient.csproj +++ b/JOBot.TClient/JOBot.TClient.csproj @@ -20,7 +20,7 @@ - + @@ -59,9 +59,9 @@ - - PreserveNewest - + + PreserveNewest + diff --git a/JOBot.TClient/Program.cs b/JOBot.TClient/Program.cs index 5c14977..563d136 100644 --- a/JOBot.TClient/Program.cs +++ b/JOBot.TClient/Program.cs @@ -6,7 +6,6 @@ var host = Host.CreateDefaultBuilder(args) { // Настройка DI services.ConfigureServices(context.Configuration); - }) .Build(); diff --git a/JOBot.TClient/Services/MenuService.cs b/JOBot.TClient/Services/MenuService.cs index e8ba02e..4f2e4d1 100644 --- a/JOBot.TClient/Services/MenuService.cs +++ b/JOBot.TClient/Services/MenuService.cs @@ -1,6 +1,5 @@ using Telegram.Bot; using Telegram.Bot.Types; -using User = JOBot.Proto.User; namespace JOBot.TClient.Services; @@ -9,7 +8,7 @@ public class MenuService(ITelegramBotClient bot) public async Task RenderMenu(Update update, CancellationToken ct = default) { ArgumentNullException.ThrowIfNull(update.Message?.From); - - await bot.SendMessage(update.Message.From.Id,"PrepareUser stage is done.", cancellationToken: ct); + + await bot.SendMessage(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 db0fba7..4c6a71b 100644 --- a/JOBot.TClient/Services/PrepareUserService.cs +++ b/JOBot.TClient/Services/PrepareUserService.cs @@ -12,7 +12,7 @@ public class PrepareUserService(ITelegramBotClient bot, User.UserClient userClie private readonly User.UserClient _userClient = userClient; /// - /// Get or register user on system + /// Get or register user on system /// /// Telegram Update object /// Cancellation Token @@ -26,21 +26,21 @@ public class PrepareUserService(ITelegramBotClient bot, User.UserClient userClie var result = await _userClient.RegisterAsync(new RegisterRequest { UserId = update.Message.From.Id, - Username = update.Message.From.Username, + Username = update.Message.From.Username }); if (!result.Success) { - await _bot.SendMessage(chatId: update.Message.Chat.Id, + await _bot.SendMessage(update.Message.Chat.Id, TextResource.FallbackMessage, cancellationToken: ct); throw new FallbackException(TextResource.FallbackMessage, update.Message, _bot); } - var user = await _userClient.GetUserAsync(new GetUserRequest() + var user = await _userClient.GetUserAsync(new GetUserRequest { - UserId = update.Message.From.Id, + UserId = update.Message.From.Id }); if (user == null) @@ -50,7 +50,7 @@ public class PrepareUserService(ITelegramBotClient bot, User.UserClient userClie } /// - /// Get Eula Agreement from user + /// Get Eula Agreement from user /// /// Telegram Update object /// Cancellation Token @@ -66,7 +66,7 @@ public class PrepareUserService(ITelegramBotClient bot, User.UserClient userClie } /// - /// Accept EULA for user + /// Accept EULA for user /// /// Telegram update object /// Cancellation Token @@ -74,10 +74,10 @@ public class PrepareUserService(ITelegramBotClient bot, User.UserClient userClie public async Task AcceptEula(Update update, CancellationToken ct = default) { ArgumentNullException.ThrowIfNull(update.Message?.From); - var result = await _userClient.AcceptEulaAsync(new() + var result = await _userClient.AcceptEulaAsync(new AcceptEulaRequest { EulaAccepted = true, - UserId = update.Message.From.Id, + UserId = update.Message.From.Id }); if (!result.Success) @@ -87,22 +87,23 @@ public class PrepareUserService(ITelegramBotClient bot, User.UserClient userClie public async Task Auth(Update update, CancellationToken ct = default) { ArgumentNullException.ThrowIfNull(update.Message?.From); - - var url = await _userClient.GetHeadHunterAuthHookAsync(new GetHeadHunterAuthHookRequest() + + var url = await _userClient.GetHeadHunterAuthHookAsync(new GetHeadHunterAuthHookRequest { UserId = update.Message.From.Id }); await _bot.SendMessage( - update.Message.From.Id, - string.Format(TextResource.AskForAuth, [url.RegistrationUrl]), + update.Message.From.Id, + string.Format(TextResource.AskForAuth, [url.RegistrationUrl]), cancellationToken: ct); } public async Task SelectCv(Update update, CancellationToken ct = default) { ArgumentNullException.ThrowIfNull(update.Message?.From); - - await _bot.SendMessage(update.Message.From.Id, "Давайте выберем одно из доступных резюме:", cancellationToken: ct); //TODO: https://git.lisoveliy.su/Lisoveliy/JOBot/issues/9 + + await _bot.SendMessage(update.Message.From.Id, "Давайте выберем одно из доступных резюме:", + cancellationToken: ct); //TODO: https://git.lisoveliy.su/Lisoveliy/JOBot/issues/9 } } \ No newline at end of file diff --git a/JOBot.TClient/Services/UserService.cs b/JOBot.TClient/Services/UserService.cs index 51f4c7b..f097bdd 100644 --- a/JOBot.TClient/Services/UserService.cs +++ b/JOBot.TClient/Services/UserService.cs @@ -10,7 +10,7 @@ namespace JOBot.TClient.Services; public class UserService(ITelegramBotClient bot, User.UserClient userClient) { /// - /// Get user + /// Get user /// /// Telegram Update object /// Cancellation Token @@ -26,7 +26,7 @@ public class UserService(ITelegramBotClient bot, User.UserClient userClient) { user = await userClient.GetUserAsync(new GetUserRequest() //Получаем пользователя { - UserId = update.Message.From.Id, + UserId = update.Message.From.Id }); } catch (RpcException e) //Пользователь не найден? @@ -36,6 +36,7 @@ public class UserService(ITelegramBotClient bot, User.UserClient userClient) return null; } + return user; } } \ No newline at end of file diff --git a/JOBot.TClient/Statements/PrepareUserState.cs b/JOBot.TClient/Statements/PrepareUserState.cs index 338c40b..a44efa0 100644 --- a/JOBot.TClient/Statements/PrepareUserState.cs +++ b/JOBot.TClient/Statements/PrepareUserState.cs @@ -7,7 +7,7 @@ namespace JOBot.TClient.Statements; public class PrepareUserState(PrepareUserService prepareUserService, MenuService menuService) { /// - /// Try to prepare user if is not registered + /// Try to prepare user if is not registered /// /// Update telegram object /// Cancellation token @@ -20,24 +20,24 @@ public class PrepareUserState(PrepareUserService prepareUserService, MenuService await prepareUserService.AskForEulaAgreement(update, ct); return; //interrupt while eula isn't accepted } - + await OnUserEulaValidStage(user, update, ct); } /// - /// Signal for accepted eula + /// Signal for accepted eula /// /// /// /// public async Task AcceptEula(GetUserResponse user, Update update, CancellationToken ct = default) { - await prepareUserService.AcceptEula(update, ct: ct); + await prepareUserService.AcceptEula(update, ct); await OnUserEulaValidStage(user, update, ct); } /// - /// Check user logged + /// Check user logged /// /// /// @@ -49,21 +49,19 @@ public class PrepareUserState(PrepareUserService prepareUserService, MenuService await prepareUserService.Auth(update, ct); return; } + await OnAuthStage(user, update, ct); } - + /// - /// Check user selected CV + /// Check user selected CV /// /// /// /// private async Task OnAuthStage(GetUserResponse user, Update update, CancellationToken ct = default) { - if (string.IsNullOrEmpty(user.CVUrl)) - { - await prepareUserService.SelectCv(update, ct); - } + if (string.IsNullOrEmpty(user.CVUrl)) await prepareUserService.SelectCv(update, ct); await menuService.RenderMenu(update, ct); //boilerplate } } \ No newline at end of file diff --git a/JOBot.TClient/TextResource.resx b/JOBot.TClient/TextResource.resx index 7bed097..f1fede4 100644 --- a/JOBot.TClient/TextResource.resx +++ b/JOBot.TClient/TextResource.resx @@ -1,9 +1,10 @@ - + - + @@ -13,10 +14,14 @@ 1.3 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + Продолжая, вы принимаете политику конфиденциальности и правила сервиса diff --git a/README.md b/README.md index 8ae4af9..e9e3cc2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Telegram-бот для автоматизации поиска работы че ## 🔥 Возможности - Привяжите аккаунт HeadHunter для доступа к резюме и вакансиям. +Привяжите аккаунт HeadHunter для доступа к резюме и вакансиям. - **Отклик по предпочтениям** @@ -16,7 +16,7 @@ Telegram-бот для автоматизации поиска работы че - **Проверка резюме** Правка резюме, под ваши приоритеты. -- **Аналитика** +- **Аналитика** Статистика по откликам и приглашениям. @@ -25,36 +25,42 @@ Telegram-бот для автоматизации поиска работы че - **Backend**: .NET 8 + gRPC - **Frontend** Telegram клиент через Telegram.Bot - **БД**: PostgreSQL -- **Авторизация**: OAuth 2.0 (HH API), Telegram API +- **Авторизация**: OAuth 2.0 (HH API), Telegram API - **Инфраструктура**: Docker / Docker compose ## 🚀 Запуск ### Требования + - .NET 8 SDK - Docker (для работы с БД) - Аккаунт разработчика на [dev.hh.ru](https://dev.hh.ru) ### 1. Настройка конфигурации + Создайте `appsettings.json` в `JOBot.TClient` на основе `appsettings.Example.json` ### 2. Запуск через Docker + ``` docker-compose up -d --build ``` ## 📌 Команды бота -|Команда|Описание| -|-------|--------| -|/start |Начало работы| -|/menu|Настройка откликов и резюме по предпочтениям| -|/info|Информация о боте| + +| Команда | Описание | +|---------|----------------------------------------------| +| /start | Начало работы | +| /menu | Настройка откликов и резюме по предпочтениям | +| /info | Информация о боте | 📄 Лицензия MIT License. Подробнее в файле LICENSE. ## Отказ от ответственности -Продолжая, вы принимаете [политику конфиденциальности](https://hh.ru/article/personal_data?backurl=%2F&role=applicant) и [правила сервиса](https://hh.ru/account/agreement?backurl=%2Faccount%2Fsignup%3Fbackurl%3D%252F%26role%3Dapplicant&role=applicant) hh.ru +Продолжая, вы принимаете [политику конфиденциальности](https://hh.ru/article/personal_data?backurl=%2F&role=applicant) +и [правила сервиса](https://hh.ru/account/agreement?backurl=%2Faccount%2Fsignup%3Fbackurl%3D%252F%26role%3Dapplicant&role=applicant) +hh.ru Этот сервис никак не связан с HeadHunter™ (далее hh.ru), не выдаёт себя за hh.ru и не является hh.ru \ No newline at end of file diff --git a/compose.dev.yml b/compose.dev.yml index 3ff879e..d21dad1 100644 --- a/compose.dev.yml +++ b/compose.dev.yml @@ -45,7 +45,7 @@ services: networks: jobot: - + volumes: postgres_data: rabbitmq_data: \ No newline at end of file diff --git a/compose.yml b/compose.yml index c6f70c9..dbb502e 100644 --- a/compose.yml +++ b/compose.yml @@ -11,7 +11,7 @@ services: image: postgres:15 environment: POSTGRES_PASSWORD: LocalDbPass - POSTGRES_DB: jobot + POSTGRES_DB: jobot volumes: - postgres_data:/var/lib/postgresql/data networks: @@ -40,7 +40,7 @@ services: networks: jobot: - + volumes: postgres_data: \ No newline at end of file