diff --git a/.idea/.idea.JOBot/.idea/.gitignore b/.idea/.idea.JOBot/.idea/.gitignore new file mode 100644 index 0000000..d1bb458 --- /dev/null +++ b/.idea/.idea.JOBot/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/projectSettingsUpdater.xml +/modules.xml +/contentModel.xml +/.idea.JOBot.iml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/JOBot.Backend/DAL/Models/User.cs b/JOBot.Backend/DAL/Models/User.cs index 4767238..429a47c 100644 --- a/JOBot.Backend/DAL/Models/User.cs +++ b/JOBot.Backend/DAL/Models/User.cs @@ -10,6 +10,7 @@ public class User [Key] public required long TelegramId { get; set; } + [MaxLength(50)] public string? Username { get; set; } - public DateTime CreatedAt { get; set; } = DateTime.Now; + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; } \ No newline at end of file diff --git a/JOBot.Backend/Data/Migrations/20250501164641_InitialCreate.Designer.cs b/JOBot.Backend/Data/Migrations/20250501164641_InitialCreate.Designer.cs new file mode 100644 index 0000000..7263472 --- /dev/null +++ b/JOBot.Backend/Data/Migrations/20250501164641_InitialCreate.Designer.cs @@ -0,0 +1,52 @@ +// +using System; +using JOBot.Backend.DAL.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace JOBot.Backend.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20250501164641_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("JOBot.Backend.DAL.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TelegramId") + .HasColumnType("bigint"); + + b.Property("Username") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasAlternateKey("TelegramId"); + + b.ToTable("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/JOBot.Backend/Data/Migrations/20250501164641_InitialCreate.cs b/JOBot.Backend/Data/Migrations/20250501164641_InitialCreate.cs new file mode 100644 index 0000000..ed3cae4 --- /dev/null +++ b/JOBot.Backend/Data/Migrations/20250501164641_InitialCreate.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace JOBot.Backend.Data.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + TelegramId = table.Column(type: "bigint", nullable: false), + Username = table.Column(type: "text", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + table.UniqueConstraint("AK_Users_TelegramId", x => x.TelegramId); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Users"); + } + } +} diff --git a/JOBot.Backend/Data/Migrations/20250501165245_UpdateUser.Designer.cs b/JOBot.Backend/Data/Migrations/20250501165245_UpdateUser.Designer.cs new file mode 100644 index 0000000..fe93466 --- /dev/null +++ b/JOBot.Backend/Data/Migrations/20250501165245_UpdateUser.Designer.cs @@ -0,0 +1,50 @@ +// +using System; +using JOBot.Backend.DAL.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace JOBot.Backend.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20250501165245_UpdateUser")] + partial class UpdateUser + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("JOBot.Backend.DAL.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("TelegramId") + .HasColumnType("bigint"); + + b.Property("Username") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasAlternateKey("TelegramId"); + + b.ToTable("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/JOBot.Backend/Data/Migrations/20250501165245_UpdateUser.cs b/JOBot.Backend/Data/Migrations/20250501165245_UpdateUser.cs new file mode 100644 index 0000000..22073d8 --- /dev/null +++ b/JOBot.Backend/Data/Migrations/20250501165245_UpdateUser.cs @@ -0,0 +1,50 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace JOBot.Backend.Data.Migrations +{ + /// + public partial class UpdateUser : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "Users"); + + migrationBuilder.AlterColumn( + name: "Username", + table: "Users", + type: "character varying(50)", + maxLength: 50, + nullable: true, + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Username", + table: "Users", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(50)", + oldMaxLength: 50, + oldNullable: true); + + migrationBuilder.AddColumn( + name: "CreatedAt", + table: "Users", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + } +} diff --git a/JOBot.Backend/Data/Migrations/20250501165633_AddTimeStamp.Designer.cs b/JOBot.Backend/Data/Migrations/20250501165633_AddTimeStamp.Designer.cs new file mode 100644 index 0000000..ee9e7b9 --- /dev/null +++ b/JOBot.Backend/Data/Migrations/20250501165633_AddTimeStamp.Designer.cs @@ -0,0 +1,53 @@ +// +using System; +using JOBot.Backend.DAL.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace JOBot.Backend.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20250501165633_AddTimeStamp")] + partial class AddTimeStamp + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("JOBot.Backend.DAL.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TelegramId") + .HasColumnType("bigint"); + + b.Property("Username") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasAlternateKey("TelegramId"); + + b.ToTable("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/JOBot.Backend/Data/Migrations/20250501165633_AddTimeStamp.cs b/JOBot.Backend/Data/Migrations/20250501165633_AddTimeStamp.cs new file mode 100644 index 0000000..35dd638 --- /dev/null +++ b/JOBot.Backend/Data/Migrations/20250501165633_AddTimeStamp.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace JOBot.Backend.Data.Migrations +{ + /// + public partial class AddTimeStamp : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CreatedAt", + table: "Users", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "Users"); + } + } +} diff --git a/JOBot.Backend/Data/Migrations/AppDbContextModelSnapshot.cs b/JOBot.Backend/Data/Migrations/AppDbContextModelSnapshot.cs new file mode 100644 index 0000000..b4aae05 --- /dev/null +++ b/JOBot.Backend/Data/Migrations/AppDbContextModelSnapshot.cs @@ -0,0 +1,50 @@ +// +using System; +using JOBot.Backend.DAL.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace JOBot.Backend.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + partial class AppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("JOBot.Backend.DAL.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TelegramId") + .HasColumnType("bigint"); + + b.Property("Username") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasAlternateKey("TelegramId"); + + b.ToTable("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/JOBot.Backend/Program.cs b/JOBot.Backend/Program.cs index a91fc1d..cc05177 100644 --- a/JOBot.Backend/Program.cs +++ b/JOBot.Backend/Program.cs @@ -1,6 +1,9 @@ +using Microsoft.AspNetCore.Server.Kestrel.Core; + var builder = WebApplication.CreateBuilder(args); var startup = new Startup(builder.Configuration); + startup.ConfigureServices(builder.Services); var app = builder.Build(); diff --git a/JOBot.Backend/Properties/launchSettings.json b/JOBot.Backend/Properties/launchSettings.json index 81c6804..8d54c34 100644 --- a/JOBot.Backend/Properties/launchSettings.json +++ b/JOBot.Backend/Properties/launchSettings.json @@ -4,7 +4,7 @@ "http": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, + "launchBrowser": false, "applicationUrl": "http://localhost:5253", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" @@ -13,7 +13,7 @@ "https": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, + "launchBrowser": false, "applicationUrl": "https://localhost:7176;http://localhost:5253", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/JOBot.Backend/Services/gRPC/UserService.cs b/JOBot.Backend/Services/gRPC/UserService.cs index 243882c..369ff25 100644 --- a/JOBot.Backend/Services/gRPC/UserService.cs +++ b/JOBot.Backend/Services/gRPC/UserService.cs @@ -12,15 +12,16 @@ public class UserService(AppDbContext dbContext) : User.UserBase RegisterRequest request, ServerCallContext context) { - if(!dbContext.Users.Where(x => x.TelegramId == request.UserId) - .Any()) + if(!dbContext.Users + .Any(x => x.TelegramId == request.UserId)) { - dbContext.Users.Add(new Models.User() + dbContext.Users.Add(new Models.User { TelegramId = request.UserId, Username = !string.IsNullOrEmpty(request.Username) ? request.Username : null }); - + + dbContext.SaveChanges(); return Task.FromResult(new RegisterResponse { Success = true diff --git a/JOBot.Backend/appsettings.json b/JOBot.Backend/appsettings.json index 12d83db..01f5b1d 100644 --- a/JOBot.Backend/appsettings.json +++ b/JOBot.Backend/appsettings.json @@ -1,12 +1,20 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, "AllowedHosts": "*", "ConnectionStrings": { "PostgreSQL": "Host=localhost;Port=5432;Database=jobot;Username=postgres;Password=LocalDbPass" + }, + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://localhost:5001", + "Protocols": "Http2" + } + } } } \ No newline at end of file diff --git a/JOBot.TClient/JOBot.TClient.csproj b/JOBot.TClient/JOBot.TClient.csproj index 603f3c2..998eb6a 100644 --- a/JOBot.TClient/JOBot.TClient.csproj +++ b/JOBot.TClient/JOBot.TClient.csproj @@ -1,19 +1,35 @@  - - Exe - net9.0 - enable - enable - + + Exe + net9.0 + enable + enable + - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + Always + + + Always + + diff --git a/JOBot.TClient/Program.cs b/JOBot.TClient/Program.cs index 3751555..9e89a24 100644 --- a/JOBot.TClient/Program.cs +++ b/JOBot.TClient/Program.cs @@ -1,2 +1,44 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); +using Grpc.Net.Client; +using Proto = JOBot.Proto; +using Telegram.Bot; +using Telegram.Bot.Types; +using Microsoft.Extensions.Configuration; + +var config = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .AddJsonFile("appsettings.Development.json", true) + .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 diff --git a/JOBot.TClient/appsettings.Development.json b/JOBot.TClient/appsettings.Development.json new file mode 100644 index 0000000..2377161 --- /dev/null +++ b/JOBot.TClient/appsettings.Development.json @@ -0,0 +1,4 @@ +{ + "TelegramToken": "7372524570:AAGLW5dXhW5Jd78i9Zguyga-5gfeSF9KU4I", + "BackendHost": "http://localhost:5001" +} \ No newline at end of file diff --git a/JOBot.TClient/appsettings.json b/JOBot.TClient/appsettings.json new file mode 100644 index 0000000..0e0dcd2 --- /dev/null +++ b/JOBot.TClient/appsettings.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/Proto/user.proto b/Proto/user.proto index 8fa07db..815c5c1 100644 --- a/Proto/user.proto +++ b/Proto/user.proto @@ -5,9 +5,11 @@ service User { rpc Register (RegisterRequest) returns (RegisterResponse); } +import "google/protobuf/wrappers.proto"; + message RegisterRequest{ int64 user_id = 1; - string username = 2; + google.protobuf.StringValue username = 2; } message RegisterResponse{