diff --git a/Govor.Console/Commands/AcceptFriendRequestCommand.cs b/Govor.Console/Commands/AcceptFriendRequestCommand.cs new file mode 100644 index 0000000..5a54b82 --- /dev/null +++ b/Govor.Console/Commands/AcceptFriendRequestCommand.cs @@ -0,0 +1,41 @@ +using System; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class AcceptFriendRequestCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn() || !EnsureHubConnection()) return; + + Guid friendshipId; + if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out friendshipId)) + { + Console.Write("Введите ID заявки, которую хотите принять: "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out friendshipId)) + { + Console.WriteLine("[Ошибка] Неверный или пустой ID заявки."); + return; + } + } + + try + { + // API uses Hub for this: AcceptFriendRequest(Guid friendshipId) + await HubConnection.InvokeAsync("AcceptFriendRequest", friendshipId); + Console.WriteLine("Заявка в друзья принята."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка принятия заявки] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/accept [ID_заявки] - Принять входящую заявку в друзья. Если ID не указан, запросит ввод."; + } + } +} diff --git a/Govor.Console/Commands/AdminListAllFriendshipsCommand.cs b/Govor.Console/Commands/AdminListAllFriendshipsCommand.cs new file mode 100644 index 0000000..24aef5a --- /dev/null +++ b/Govor.Console/Commands/AdminListAllFriendshipsCommand.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading.Tasks; +using Govor.Contracts.DTOs; // Required for FriendshipDto + +namespace Govor.ConsoleClient.Commands +{ + public class AdminListAllFriendshipsCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn()) return; + // Consider adding an admin role check here if possible, + // though the API itself is protected by [Authorize(Roles = "Admin")] + + Console.WriteLine("Получение всех дружеских связей (только для администраторов)..."); + try + { + var response = await HttpClientService.GetAsync("api/admin/Friendships"); + if (response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + Console.WriteLine("[Ошибка] Доступ запрещен. Эта команда только для администраторов."); + return; + } + response.EnsureSuccessStatusCode(); + + var friendships = await response.Content.ReadFromJsonAsync>(); + + if (friendships != null && friendships.Any()) + { + Console.WriteLine("Все дружеские связи в системе:"); + foreach (var f in friendships) + { + Console.WriteLine($"- ID: {f.Id}, Пользователь1: {f.RequesterId}, Пользователь2: {f.AddresseeId}, Статус: {f.Status}"); + } + } + else + { + Console.WriteLine("Дружеские связи не найдены в системе."); + } + } + catch (HttpRequestException httpEx) when (httpEx.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + Console.WriteLine("[Ошибка] Доступ запрещен. Эта команда только для администраторов."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/adminlistallfs - (Админ) Показать все дружеские связи в системе."; + } + } +} diff --git a/Govor.Console/Commands/AdminListUserFriendshipsCommand.cs b/Govor.Console/Commands/AdminListUserFriendshipsCommand.cs new file mode 100644 index 0000000..3b81970 --- /dev/null +++ b/Govor.Console/Commands/AdminListUserFriendshipsCommand.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading.Tasks; +using Govor.Contracts.DTOs; // Required for FriendshipDto + +namespace Govor.ConsoleClient.Commands +{ + public class AdminListUserFriendshipsCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn()) return; + + Guid userId; + if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out userId)) + { + Console.Write("Введите ID пользователя для просмотра его связей: "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out userId)) + { + Console.WriteLine("[Ошибка] Неверный или пустой ID пользователя."); + return; + } + } + + Console.WriteLine($"Получение всех связей для пользователя {userId} (только для администраторов)..."); + try + { + var response = await HttpClientService.GetAsync($"api/admin/Friendships/{userId}"); + if (response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + Console.WriteLine("[Ошибка] Доступ запрещен. Эта команда только для администраторов."); + return; + } + response.EnsureSuccessStatusCode(); + + var friendships = await response.Content.ReadFromJsonAsync>(); + + if (friendships != null && friendships.Any()) + { + Console.WriteLine($"Дружеские связи для пользователя {userId}:"); + foreach (var f in friendships) + { + Console.WriteLine($"- ID: {f.Id}, Пользователь1: {f.RequesterId}, Пользователь2: {f.AddresseeId}, Статус: {f.Status}"); + } + } + else + { + Console.WriteLine($"Дружеские связи для пользователя {userId} не найдены."); + } + } + catch (HttpRequestException httpEx) when (httpEx.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + Console.WriteLine("[Ошибка] Доступ запрещен. Эта команда только для администраторов."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/adminlistuserfs [ID_пользователя] - (Админ) Показать все дружеские связи для указанного пользователя. Если ID не указан, запросит ввод."; + } + } +} diff --git a/Govor.Console/Commands/AdminRemoveFriendshipCommand.cs b/Govor.Console/Commands/AdminRemoveFriendshipCommand.cs new file mode 100644 index 0000000..07fdea2 --- /dev/null +++ b/Govor.Console/Commands/AdminRemoveFriendshipCommand.cs @@ -0,0 +1,64 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class AdminRemoveFriendshipCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn()) return; + + Guid friendshipId; + if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out friendshipId)) + { + Console.Write("Введите ID дружеской связи для удаления: "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out friendshipId)) + { + Console.WriteLine("[Ошибка] Неверный или пустой ID связи."); + return; + } + } + + Console.WriteLine($"Удаление дружеской связи {friendshipId} (только для администраторов)..."); + try + { + // The API endpoint is: [HttpPost] public async Task RemoveFriendship(Guid Id) + // It expects the Id in the query string, like: api/admin/Friendships?Id=GUID + // Or it might be a POST with x-www-form-urlencoded data or JSON body, need to check API implementation. + // Based on `[HttpPost] public async Task RemoveFriendship(Guid Id)`, it's likely a query parameter or form data. + // Let's assume query parameter for now as it's simpler for HttpPost. + + var response = await HttpClientService.PostAsync($"api/admin/Friendships?Id={friendshipId}", null); + + if (response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + Console.WriteLine("[Ошибка] Доступ запрещен. Эта команда только для администраторов."); + return; + } + response.EnsureSuccessStatusCode(); + + Console.WriteLine($"Дружеская связь {friendshipId} успешно удалена."); + } + catch (HttpRequestException httpEx) when (httpEx.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + Console.WriteLine("[Ошибка] Доступ запрещен. Эта команда только для администраторов."); + } + catch (HttpRequestException httpEx) when (httpEx.StatusCode == System.Net.HttpStatusCode.NotFound) + { + Console.WriteLine($"[Ошибка] Дружеская связь с ID {friendshipId} не найдена."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/adminremovefs [ID_связи] - (Админ) Удалить дружескую связь по ее ID. Если ID не указан, запросит ввод."; + } + } +} diff --git a/Govor.Console/Commands/BaseCommand.cs b/Govor.Console/Commands/BaseCommand.cs new file mode 100644 index 0000000..358b48d --- /dev/null +++ b/Govor.Console/Commands/BaseCommand.cs @@ -0,0 +1,65 @@ +using System; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public abstract class BaseCommand : ICommand + { + protected static FriendsClient? FriendsClient { get; private set; } + protected static HttpClientService HttpClientService { get; private set; } + protected static Func GetAuthToken { get; private set; } + protected static Action SetAuthToken { get; private set; } + protected static Func InitializeHubConnectionAsync { get; private set; } + protected static Microsoft.AspNetCore.SignalR.Client.HubConnection? HubConnection => _hubConnection?.Invoke(); + private static Func _hubConnection; + + + public static void InitializeServices( + FriendsClient? friendsClient, + HttpClientService httpClientService, + Func getAuthToken, + Action setAuthToken, + Func initializeHubConnectionAsync, + Func hubConnection) + { + FriendsClient = friendsClient; + HttpClientService = httpClientService; + GetAuthToken = getAuthToken; + SetAuthToken = setAuthToken; + InitializeHubConnectionAsync = initializeHubConnectionAsync; + _hubConnection = hubConnection; + } + + public abstract Task ExecuteAsync(string? argument); + public abstract string GetHelp(); + + protected boolEnsureLoggedIn() + { + if (GetAuthToken() == null) + { + System.Console.WriteLine("[Ошибка] Сначала войдите в систему. Используйте /login или /reg."); + return false; + } + if (FriendsClient == null && !(this is LoginCommand || this is RegisterCommand)) // FriendsClient might not be needed for auth commands + { + // Attempt to re-initialize FriendsClient if token exists but client is null + // This can happen if the app was closed and token was persisted, but client was not re-instantiated. + // However, the current setup in Program.cs initializes FriendsClient after login/reg. + // This check is more of a safeguard. + System.Console.WriteLine("[Ошибка] Клиент для работы с друзьями не инициализирован. Попробуйте войти снова."); + return false; + } + return true; + } + + protected boolEnsureHubConnection() + { + if (HubConnection == null || HubConnection.State != Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected) + { + System.Console.WriteLine("[Ошибка] SignalR соединение не установлено. Попробуйте войти снова или проверьте соединение."); + return false; + } + return true; + } + } +} diff --git a/Govor.Console/Commands/BlockUserCommand.cs b/Govor.Console/Commands/BlockUserCommand.cs new file mode 100644 index 0000000..766bef5 --- /dev/null +++ b/Govor.Console/Commands/BlockUserCommand.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class BlockUserCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn() || !EnsureHubConnection()) return; + + Guid targetUserId; + if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out targetUserId)) + { + Console.Write("Введите ID пользователя, которого хотите заблокировать: "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out targetUserId)) + { + Console.WriteLine("[Ошибка] Неверный или пустой ID пользователя."); + return; + } + } + + try + { + // API uses Hub for this: BlockUser(Guid targetUserId) + // Located in Govor.API/Hubs/FriendsHub.cs + await HubConnection.InvokeAsync("BlockUser", targetUserId); + Console.WriteLine($"Пользователь {targetUserId} заблокирован."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка блокировки пользователя] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/block [ID_пользователя] - Заблокировать пользователя. Если ID не указан, запросит ввод."; + } + } +} diff --git a/Govor.Console/Commands/HelpCommand.cs b/Govor.Console/Commands/HelpCommand.cs new file mode 100644 index 0000000..c331a83 --- /dev/null +++ b/Govor.Console/Commands/HelpCommand.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class HelpCommand : BaseCommand + { + private readonly Dictionary _availableCommands; + + // The HelpCommand needs access to the list of all commands to display their help messages. + // This will be injected from Program.cs where the commands are registered. + public HelpCommand(Dictionary availableCommands) + { + _availableCommands = availableCommands; + } + + public override Task ExecuteAsync(string? argument) + { + Console.WriteLine("Доступные команды:"); + if (!string.IsNullOrWhiteSpace(argument)) + { + string commandKey = argument.StartsWith("/") ? argument : "/" + argument; + if (_availableCommands.TryGetValue(commandKey.ToLower(), out var specificCommand)) + { + Console.WriteLine(specificCommand.GetHelp()); + } + else + { + Console.WriteLine($"Команда '{argument}' не найдена. Введите /help для списка всех команд."); + } + } + else + { + foreach (var commandEntry in _availableCommands.OrderBy(c => c.Key)) + { + Console.WriteLine(commandEntry.Value.GetHelp()); + } + } + return Task.CompletedTask; + } + + public override string GetHelp() + { + return "/help [команда] - Показать список всех команд или помощь по конкретной команде."; + } + } +} diff --git a/Govor.Console/Commands/ICommand.cs b/Govor.Console/Commands/ICommand.cs new file mode 100644 index 0000000..0e3c8ae --- /dev/null +++ b/Govor.Console/Commands/ICommand.cs @@ -0,0 +1,8 @@ +namespace Govor.ConsoleClient.Commands +{ + public interface ICommand + { + Task ExecuteAsync(string? argument); + string GetHelp(); + } +} diff --git a/Govor.Console/Commands/ListFriendsCommand.cs b/Govor.Console/Commands/ListFriendsCommand.cs new file mode 100644 index 0000000..1d4cf92 --- /dev/null +++ b/Govor.Console/Commands/ListFriendsCommand.cs @@ -0,0 +1,40 @@ +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class ListFriendsCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn()) return; + + try + { + var friends = await FriendsClient.GetFriendsAsync(); + if (friends.Any()) + { + Console.WriteLine("Ваши друзья:"); + foreach (var f in friends) + { + Console.WriteLine($"- {f.Username} | был онлайн: {f.WasOnline} [{f.Id}]"); + } + } + else + { + Console.WriteLine("У вас пока нет друзей."); + } + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/friends - Показать список ваших друзей."; + } + } +} diff --git a/Govor.Console/Commands/ListIncomingRequestsCommand.cs b/Govor.Console/Commands/ListIncomingRequestsCommand.cs new file mode 100644 index 0000000..3bfa6a7 --- /dev/null +++ b/Govor.Console/Commands/ListIncomingRequestsCommand.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class ListIncomingRequestsCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn()) return; + + try + { + // This still uses REST client as per existing FriendsClient + var requests = await FriendsClient.GetIncomingRequestsAsync(); + if (requests.Any()) + { + Console.WriteLine("Входящие заявки в друзья:"); + foreach (var r in requests) + { + // Assuming you want to show who sent the request. + // The FriendshipDto contains RequesterId and AddresseeId. + // If current user is AddresseeId, then RequesterId is the one who sent it. + Console.WriteLine($"- Запрос ID: {r.Id}. От пользователя ID: {r.RequesterId}. Статус: {r.Status}. (принять через /accept {r.Id})"); + } + } + else + { + Console.WriteLine("У вас нет входящих заявок в друзья."); + } + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/incoming - Показать список входящих заявок в друзья."; + } + } +} diff --git a/Govor.Console/Commands/ListOutgoingRequestsCommand.cs b/Govor.Console/Commands/ListOutgoingRequestsCommand.cs new file mode 100644 index 0000000..992dd60 --- /dev/null +++ b/Govor.Console/Commands/ListOutgoingRequestsCommand.cs @@ -0,0 +1,50 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading.Tasks; +using Govor.Contracts.DTOs; // Required for FriendshipDto + +namespace Govor.ConsoleClient.Commands +{ + public class ListOutgoingRequestsCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn()) return; + + try + { + // This functionality maps to GET api/friends/responses in FriendsRequestQueryController + // FriendsClient doesn't have a dedicated method, so we use HttpClientService or direct HttpClient + var response = await HttpClientService.GetAsync("api/friends/responses"); + response.EnsureSuccessStatusCode(); + + var requests = await response.Content.ReadFromJsonAsync>(); + + if (requests != null && requests.Any()) + { + Console.WriteLine("Отправленные вами заявки в друзья (ожидают ответа):"); + foreach (var r in requests) + { + // If current user is RequesterId, then AddresseeId is the one they sent to. + Console.WriteLine($"- Запрос ID: {r.Id}. Пользователю ID: {r.AddresseeId}. Статус: {r.Status}."); + } + } + else + { + Console.WriteLine("У вас нет отправленных заявок, ожидающих ответа."); + } + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/outgoing - Показать список отправленных вами заявок в друзья, ожидающих ответа."; + } + } +} diff --git a/Govor.Console/Commands/LoginCommand.cs b/Govor.Console/Commands/LoginCommand.cs new file mode 100644 index 0000000..94b8665 --- /dev/null +++ b/Govor.Console/Commands/LoginCommand.cs @@ -0,0 +1,50 @@ +using System; +using System.Net.Http.Headers; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class LoginCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + Console.Write("username: "); + var loginUsername = Console.ReadLine(); + + Console.Write("password: "); + var loginPassword = Console.ReadLine(); + + if (string.IsNullOrWhiteSpace(loginUsername) || string.IsNullOrWhiteSpace(loginPassword)) + { + Console.WriteLine("[Ошибка] Имя пользователя и пароль не могут быть пустыми."); + return; + } + + try + { + var authToken = await HttpClientService.LoginAsync(loginUsername, loginPassword); + SetAuthToken(authToken); + + // Initialize FriendsClient after successful login + var sharedClient = new HttpClient { BaseAddress = new Uri(HttpClientService.GetBaseUrl()) }; + sharedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken); + var friendsClient = new FriendsClient(sharedClient); + + // Re-initialize services in BaseCommand with the new FriendsClient + InitializeServices(friendsClient, HttpClientService, GetAuthToken, SetAuthToken, InitializeHubConnectionAsync, HubConnection); + + await InitializeHubConnectionAsync(); + Console.WriteLine("[Успех] Вход выполнен. Токен сохранен."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/login - Войти в существующий аккаунт."; + } + } +} diff --git a/Govor.Console/Commands/RegisterCommand.cs b/Govor.Console/Commands/RegisterCommand.cs new file mode 100644 index 0000000..c1bcfc0 --- /dev/null +++ b/Govor.Console/Commands/RegisterCommand.cs @@ -0,0 +1,53 @@ +using System; +using System.Net.Http.Headers; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class RegisterCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + Console.Write("username: "); + var regUsername = Console.ReadLine(); + + Console.Write("password: "); + var regPassword = Console.ReadLine(); + + Console.Write("invitation code: "); + var inviteCode = Console.ReadLine(); + + if (string.IsNullOrWhiteSpace(regUsername) || string.IsNullOrWhiteSpace(regPassword) || string.IsNullOrWhiteSpace(inviteCode)) + { + Console.WriteLine("[Ошибка] Имя пользователя, пароль и код приглашения не могут быть пустыми."); + return; + } + + try + { + var authToken = await HttpClientService.RegisterAsync(regUsername, regPassword, inviteCode); + SetAuthToken(authToken); + + // Initialize FriendsClient after successful registration + var sharedClient = new HttpClient { BaseAddress = new Uri(HttpClientService.GetBaseUrl()) }; + sharedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken); + var friendsClient = new FriendsClient(sharedClient); + + // Re-initialize services in BaseCommand with the new FriendsClient + InitializeServices(friendsClient, HttpClientService, GetAuthToken, SetAuthToken, InitializeHubConnectionAsync, HubConnection); + + await InitializeHubConnectionAsync(); + Console.WriteLine("[Успех] Регистрация завершена. Токен сохранен."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/reg - Зарегистрировать новый аккаунт с помощью кода приглашения."; + } + } +} diff --git a/Govor.Console/Commands/RejectFriendRequestCommand.cs b/Govor.Console/Commands/RejectFriendRequestCommand.cs new file mode 100644 index 0000000..f2a0f83 --- /dev/null +++ b/Govor.Console/Commands/RejectFriendRequestCommand.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class RejectFriendRequestCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn() || !EnsureHubConnection()) return; + + Guid friendshipId; + if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out friendshipId)) + { + Console.Write("Введите ID заявки, которую хотите отклонить: "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out friendshipId)) + { + Console.WriteLine("[Ошибка] Неверный или пустой ID заявки."); + return; + } + } + + try + { + // API uses Hub for this: RejectFriendRequest(Guid friendshipId) + // Located in Govor.API/Hubs/FriendsHub.cs + await HubConnection.InvokeAsync("RejectFriendRequest", friendshipId); + Console.WriteLine("Заявка в друзья отклонена."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка отклонения заявки] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/reject [ID_заявки] - Отклонить входящую заявку в друзья. Если ID не указан, запросит ввод."; + } + } +} diff --git a/Govor.Console/Commands/SearchUserCommand.cs b/Govor.Console/Commands/SearchUserCommand.cs new file mode 100644 index 0000000..1c00d31 --- /dev/null +++ b/Govor.Console/Commands/SearchUserCommand.cs @@ -0,0 +1,51 @@ +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class SearchUserCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn()) return; + + if (string.IsNullOrWhiteSpace(argument)) + { + Console.Write("Введите имя пользователя для поиска: "); + argument = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(argument)) + { + Console.WriteLine("[Ошибка] Поисковый запрос не может быть пустым."); + return; + } + } + + try + { + var foundUsers = await FriendsClient.SearchAsync(argument); + if (foundUsers.Any()) + { + Console.WriteLine("Найденные пользователи:"); + foreach (var user in foundUsers) + { + Console.WriteLine($"- {user.Username} [{user.Id}]"); + } + } + else + { + Console.WriteLine("Пользователи не найдены."); + } + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/search [имя_пользователя] - Найти пользователей по имени. Если имя не указано, запросит ввод."; + } + } +} diff --git a/Govor.Console/Commands/SendFriendRequestCommand.cs b/Govor.Console/Commands/SendFriendRequestCommand.cs new file mode 100644 index 0000000..62aae31 --- /dev/null +++ b/Govor.Console/Commands/SendFriendRequestCommand.cs @@ -0,0 +1,41 @@ +using System; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class SendFriendRequestCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn() || !EnsureHubConnection()) return; + + Guid targetUserId; + if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out targetUserId)) + { + Console.Write("Введите ID пользователя, которому хотите отправить запрос: "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out targetUserId)) + { + Console.WriteLine("[Ошибка] Неверный или пустой ID пользователя."); + return; + } + } + + try + { + // API uses Hub for this: SendFriendRequest(Guid targetUserId) + await HubConnection.InvokeAsync("SendFriendRequest", targetUserId); + Console.WriteLine("Запрос в друзья отправлен."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка отправки запроса] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/friend [ID_пользователя] - Отправить запрос в друзья пользователю с указанным ID. Если ID не указан, запросит ввод."; + } + } +} diff --git a/Govor.Console/Commands/UnblockUserCommand.cs b/Govor.Console/Commands/UnblockUserCommand.cs new file mode 100644 index 0000000..bc2dae4 --- /dev/null +++ b/Govor.Console/Commands/UnblockUserCommand.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading.Tasks; + +namespace Govor.ConsoleClient.Commands +{ + public class UnblockUserCommand : BaseCommand + { + public override async Task ExecuteAsync(string? argument) + { + if (!EnsureLoggedIn() || !EnsureHubConnection()) return; + + Guid targetUserId; + if (string.IsNullOrWhiteSpace(argument) || !Guid.TryParse(argument, out targetUserId)) + { + Console.Write("Введите ID пользователя, которого хотите разблокировать: "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || !Guid.TryParse(input, out targetUserId)) + { + Console.WriteLine("[Ошибка] Неверный или пустой ID пользователя."); + return; + } + } + + try + { + // API uses Hub for this: UnblockUser(Guid targetUserId) + // Located in Govor.API/Hubs/FriendsHub.cs + await HubConnection.InvokeAsync("UnblockUser", targetUserId); + Console.WriteLine($"Пользователь {targetUserId} разблокирован."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка разблокировки пользователя] {ex.Message}"); + } + } + + public override string GetHelp() + { + return "/unblock [ID_пользователя] - Разблокировать пользователя. Если ID не указан, запросит ввод."; + } + } +} diff --git a/Govor.Console/FriendsClient.cs b/Govor.Console/FriendsClient.cs index 21593f7..0af8398 100644 --- a/Govor.Console/FriendsClient.cs +++ b/Govor.Console/FriendsClient.cs @@ -1,5 +1,8 @@ using System.Net.Http.Json; using Govor.Contracts.DTOs; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; // Added for Task namespace Govor.ConsoleClient { @@ -20,11 +23,12 @@ namespace Govor.ConsoleClient return await response.Content.ReadFromJsonAsync>() ?? new List(); } - public async Task SendFriendRequestAsync(Guid targetUserId) - { - var response = await _client.PostAsync($"api/friends/request?targetUserId={targetUserId}", null); - response.EnsureSuccessStatusCode(); - } + // SendFriendRequestAsync was removed as the SendFriendRequestCommand now uses SignalR directly. + // public async Task SendFriendRequestAsync(Guid targetUserId) + // { + // var response = await _client.PostAsync($"api/friends/request?targetUserId={targetUserId}", null); + // response.EnsureSuccessStatusCode(); + // } public async Task> GetIncomingRequestsAsync() { @@ -34,11 +38,12 @@ namespace Govor.ConsoleClient return await response.Content.ReadFromJsonAsync>() ?? new List(); } - public async Task AcceptFriendRequestAsync(Guid requesterId) - { - var response = await _client.PostAsync($"/api/friends/accept?friendshipId={requesterId}", null); - response.EnsureSuccessStatusCode(); - } + // AcceptFriendRequestAsync was removed as the AcceptFriendRequestCommand now uses SignalR directly. + // public async Task AcceptFriendRequestAsync(Guid friendshipId) // Parameter was requesterId, should be friendshipId + // { + // var response = await _client.PostAsync($"/api/friends/accept?friendshipId={friendshipId}", null); + // response.EnsureSuccessStatusCode(); + // } public async Task> GetFriendsAsync() { @@ -49,4 +54,4 @@ namespace Govor.ConsoleClient } } -} +} \ No newline at end of file diff --git a/Govor.Console/Program.cs b/Govor.Console/Program.cs index 7bfbf4a..1c8760c 100644 --- a/Govor.Console/Program.cs +++ b/Govor.Console/Program.cs @@ -1,13 +1,14 @@ using Microsoft.AspNetCore.SignalR.Client; +using System; +using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Net.Http; using System.Net.Http.Headers; -using System.Net.Http.Json; -using System.Text.Json; -using Govor.Contracts.Requests; +using System.Threading.Tasks; +using Govor.ConsoleClient.Commands; using Govor.Contracts.Requests.SignalR; using Govor.Contracts.Responses.SignalR; -using Govor.Core.Models; - /*==================================== *Личные сообщения| Егор @@ -29,14 +30,26 @@ namespace Govor.ConsoleClient static string baseUrl = "https://localhost:7155"; static string? AuthToken = null; static HttpClientService HttpService = new(baseUrl); - private static FriendsClient? friendsClient; + private static FriendsClient? _friendsClient; // Renamed to avoid conflict with BaseCommand static HubConnection? _hubConnection; static Dictionary> ChatHistory = new(); static string? CurrentChatUser = null; static Guid CurrentChatUserId = Guid.Empty; + private static Dictionary _commands = new(); + static async Task Main() { + InitializeCommands(); + BaseCommand.InitializeServices( + _friendsClient, // Initially null, will be set by Login/Register commands + HttpService, + () => AuthToken, + (token) => AuthToken = token, + InitializeHubConnection, + () => _hubConnection + ); + LoginWindow(); while (true) @@ -47,170 +60,121 @@ namespace Govor.ConsoleClient if (string.IsNullOrWhiteSpace(input)) continue; if (input.StartsWith("/")) - HandleCommand(input); + await HandleCommandAsync(input); else if (CurrentChatUser != null) - SendMessage(input); + await SendMessageAsync(input); else - Console.WriteLine("[Система] Введите команду, например /friends"); + Console.WriteLine("[Система] Введите команду, например /help или /friends"); } } - static async void HandleCommand(string input) + static void InitializeCommands() { - var args = input.Split(' ', 2); - var command = args[0].ToLower(); - var argument = args.Length > 1 ? args[1] : null; - - switch (command) + _commands = new Dictionary { - case "/login": - Console.Write("username: "); - var loginUsername = Console.ReadLine(); - - Console.Write("password: "); - var loginPassword = Console.ReadLine(); - - try - { - AuthToken = await HttpService.LoginAsync(loginUsername, loginPassword); - HttpClient sharedClient = new(); - sharedClient.BaseAddress = new Uri(baseUrl); - sharedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken); - - friendsClient = new FriendsClient(sharedClient); - await InitializeHubConnection(); - Console.WriteLine("[Успех] Вход выполнен. Токен сохранен."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка] {ex.Message}"); - } - break; - case "/reg": - Console.Write("username: "); - var regUsername = Console.ReadLine(); - - Console.Write("password: "); - var regPassword = Console.ReadLine(); - - Console.Write("invitation: "); - var inviteCode = Console.ReadLine(); - - try - { - AuthToken = await HttpService.RegisterAsync(regUsername, regPassword, inviteCode); - HttpClient sharedClient = new(); - sharedClient.BaseAddress = new Uri(baseUrl); - sharedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken); - - friendsClient = new FriendsClient(sharedClient); - await InitializeHubConnection(); - Console.WriteLine("[Успех] Регистрация завершена. Токен сохранен."); - } - catch (Exception ex) - { - Console.WriteLine($"[Ошибка] {ex.Message}"); - } - break; - case "/search": - if (friendsClient == null) - { - Console.WriteLine("[Ошибка] Сначала войдите в систему."); - break; - } - Console.Write("Введите имя друга: "); - var q = Console.ReadLine(); - var foundUsers = await friendsClient.SearchAsync(q); - foreach (var user in foundUsers) - { - Console.WriteLine($"{user.Username} [{user.Id}]"); - } - break; + { "/login", new LoginCommand() }, + { "/reg", new RegisterCommand() }, + { "/search", new SearchUserCommand() }, + { "/friends", new ListFriendsCommand() }, + { "/friend", new SendFriendRequestCommand() }, // Alias for SendFriendRequest + { "/sendrequest", new SendFriendRequestCommand() }, + { "/accept", new AcceptFriendRequestCommand() }, + { "/reject", new RejectFriendRequestCommand() }, + { "/incoming", new ListIncomingRequestsCommand() }, + { "/outgoing", new ListOutgoingRequestsCommand() }, + { "/block", new BlockUserCommand() }, + { "/unblock", new UnblockUserCommand() }, - case "/exit": - Console.WriteLine("[Система] Выход..."); - if (_hubConnection != null) - { - await _hubConnection.StopAsync(); - await _hubConnection.DisposeAsync(); - } - Environment.Exit(0); - break; + { "/adminlistallfs", new AdminListAllFriendshipsCommand() }, + { "/adminlistuserfs", new AdminListUserFriendshipsCommand() }, + { "/adminremovefs", new AdminRemoveFriendshipCommand() }, + // Help command is special as it needs the list of commands + // It will be added after other commands are initialized + }; + _commands.Add("/help", new HelpCommand(_commands)); + _commands.Add("-h", new HelpCommand(_commands)); // Alias for help - case "/friends": - if (friendsClient == null) - { - Console.WriteLine("[Ошибка] Сначала войдите в систему."); - break; - } - var friends = await friendsClient.GetFriendsAsync(); - foreach (var f in friends) - { - Console.WriteLine($"{f.Username} | был онлайн: {f.WasOnline} [{f.Id}]"); - } - break; - case "/friend": - if (friendsClient == null) - { - Console.WriteLine("[Ошибка] Сначала войдите в систему."); - break; - } - Console.Write("Введите ID пользователя, которому хотите отправить запрос: "); - var targetId = Guid.Parse(Console.ReadLine()); - await friendsClient.SendFriendRequestAsync(targetId); - Console.WriteLine("Запрос отправлен"); - break; + // Add other general commands not refactored yet if any, or create classes for them + // For example, /exit, /chat, /back will be handled separately for now or made into commands + } - case "/accept": - if (friendsClient == null) - { - Console.WriteLine("[Ошибка] Сначала войдите в систему."); - break; - } - Console.Write("Введите ID заявки, которую хотите принять принимаете: "); - var acceptId = Guid.Parse(Console.ReadLine()); - await friendsClient.AcceptFriendRequestAsync(acceptId); - Console.WriteLine("Принято"); - break; - case "/incoming": - if (friendsClient == null) - { - Console.WriteLine("[Ошибка] Сначала войдите в систему."); - break; - } - var requests = await friendsClient.GetIncomingRequestsAsync(); - foreach (var r in requests) - { - Console.WriteLine($"Запрос от: {r.RequesterId} (добавьте через /accept {r.Id})"); - } - break; - case "/chat": - if (string.IsNullOrEmpty(argument)) - { - Console.WriteLine("[Ошибка] Укажите имя друга и ID через пробел (например /chat Egor GUID)"); - break; - } + // Public static method to allow commands (like Login/Register) to update the FriendsClient + public static void UpdateFriendsClient(FriendsClient newFriendsClient) + { + _friendsClient = newFriendsClient; + // Re-initialize services in BaseCommand with the new FriendsClient + BaseCommand.InitializeServices( + _friendsClient, + HttpService, + () => AuthToken, + (token) => AuthToken = token, + InitializeHubConnection, + () => _hubConnection + ); + } - var chatArgs = argument.Split(' ', 2); - if (chatArgs.Length < 2 || !Guid.TryParse(chatArgs[1], out var chatUserId)) - { - Console.WriteLine("[Ошибка] Неверный формат. Укажите имя друга и ID (например /chat Egor GUID)"); + + static async Task HandleCommandAsync(string input) + { + var parts = input.Split(new[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries); + var commandKey = parts[0].ToLower(); + var argument = parts.Length > 1 ? parts[1] : null; + + if (_commands.TryGetValue(commandKey, out var commandInstance)) + { + try + { + await commandInstance.ExecuteAsync(argument); + } + catch (Exception ex) + { + Console.WriteLine($"[Критическая ошибка выполнения команды {commandKey}]: {ex.Message}"); + // Log detailed exception: Console.WriteLine(ex.ToString()); + } + } + else + { + // Handle non-refactored commands for now + switch (commandKey) + { + case "/exit": + Console.WriteLine("[Система] Выход..."); + if (_hubConnection != null) + { + await _hubConnection.StopAsync(); + await _hubConnection.DisposeAsync(); + } + Environment.Exit(0); break; - } + case "/chat": + if (string.IsNullOrEmpty(argument)) + { + Console.WriteLine("[Ошибка] Укажите имя друга и ID через пробел (например /chat Egor GUID)"); + break; + } - CurrentChatUser = chatArgs[0]; - CurrentChatUserId = chatUserId; - ShowChat(CurrentChatUser); - break; + var chatArgs = argument.Split(' ', 2); + if (chatArgs.Length < 2 || !Guid.TryParse(chatArgs[1], out var chatUserId)) + { + Console.WriteLine("[Ошибка] Неверный формат. Укажите имя друга и ID (например /chat Egor GUID)"); + break; + } - case "/back": - CurrentChatUser = null; - CurrentChatUserId = Guid.Empty; - break; + CurrentChatUser = chatArgs[0]; + CurrentChatUserId = chatUserId; + ShowChat(CurrentChatUser); + break; - default: - Console.WriteLine("[Ошибка] Неизвестная команда"); - break; + case "/back": + CurrentChatUser = null; + CurrentChatUserId = Guid.Empty; + Console.Clear(); // Optionally clear console when going back + LoginWindow(); // Or some other general view + break; + default: + Console.WriteLine($"[Ошибка] Неизвестная команда: {commandKey}. Введите /help для списка команд."); + break; + } } } @@ -218,10 +182,17 @@ namespace Govor.ConsoleClient { if (string.IsNullOrEmpty(AuthToken)) { + // This check might be redundant if commands calling this ensure login first Console.WriteLine("[Ошибка] Токен аутентификации отсутствует. Пожалуйста, войдите в систему."); return; } + if (_hubConnection != null && _hubConnection.State != HubConnectionState.Disconnected) + { + Console.WriteLine("[SignalR] Соединение уже установлено или устанавливается."); + return; + } + _hubConnection = new HubConnectionBuilder() .WithUrl($"{baseUrl}/api/chats", options => { @@ -231,40 +202,76 @@ namespace Govor.ConsoleClient _hubConnection.On("ReceiveMessage", (message) => { - var senderName = message.SenderId == GetMyUserId() ? "Ты" : CurrentChatUser ?? "Неизвестно"; // Simplified for now + var myId = GetMyUserId(); + var senderName = message.SenderId == myId ? "Ты" : CurrentChatUser ?? message.SenderUsername ?? "Неизвестно"; var displayMessage = $"[{message.SentAt:HH:mm}] {senderName} >> {message.EncryptedContent}"; - string chatKey = message.SenderId == GetMyUserId() ? message.RecipientId.ToString() : message.SenderId.ToString(); + // Determine chat key based on sender/recipient, ensuring consistency + string chatKey = message.SenderId == myId ? message.RecipientId.ToString() : message.SenderId.ToString(); + string activeChatDisplayUser = CurrentChatUser; // User whose chat window is open - if (CurrentChatUser != null && (message.SenderId == CurrentChatUserId || message.RecipientId == CurrentChatUserId)) + // If the message belongs to the currently active chat window + if (CurrentChatUser != null && (message.SenderId == CurrentChatUserId || (message.RecipientId == CurrentChatUserId && message.SenderId == myId))) { - if (!ChatHistory.ContainsKey(CurrentChatUser)) + if (!ChatHistory.ContainsKey(activeChatDisplayUser)) { - ChatHistory[CurrentChatUser] = new List(); + ChatHistory[activeChatDisplayUser] = new List(); } - ChatHistory[CurrentChatUser].Add(displayMessage); - Console.SetCursorPosition(0, Console.CursorTop); // Move cursor to beginning of line - Console.Write(new string(' ', Console.WindowWidth -1)); // Clear current line - Console.SetCursorPosition(0, Console.CursorTop); - Console.WriteLine($" * {displayMessage}"); // Display new message - Console.Write(">> "); // Re-display prompt + ChatHistory[activeChatDisplayUser].Add(displayMessage); + + // Smartly update console without disrupting user input too much + int originalCursorTop = Console.CursorTop; + int originalCursorLeft = Console.CursorLeft; + Console.MoveBufferArea(0, originalCursorTop, Console.BufferWidth, 1, 0, originalCursorTop +1); // scroll current line down + Console.SetCursorPosition(0, originalCursorTop); + Console.WriteLine($" * {displayMessage}"); + Console.SetCursorPosition(originalCursorLeft, originalCursorTop +1); // +1 because writeline added a line + Console.Write(">> "); // Re-display prompt part } - else + else // Notification for message not in the current chat { - // Handle notification for messages not in the current chat - TBD - Console.WriteLine($"\n[Новое сообщение от {message.SenderId}]: {message.EncryptedContent}"); - Console.Write(CurrentChatUser == null ? "/> " : ">> "); + Console.WriteLine($"\n[Новое сообщение от {message.SenderUsername ?? message.SenderId.ToString()}]: {message.EncryptedContent}"); + Console.Write(CurrentChatUser == null ? "/> " : ">> "); // Re-display prompt } }); _hubConnection.On("MessageSent", (message) => { // Optional: Handle confirmation that message was sent successfully by the server - // This could be useful for updating UI to show message status (e.g., "sent", "delivered") - // For console, just logging or ignoring might be fine. - // Console.WriteLine($"[Система] Сообщение {message.MessageId} доставлено на сервер."); + // For console, maybe a subtle notification or log if verbose mode is on. + // Example: Console.WriteLine($"[Система] Сообщение для {message.RecipientUsername} доставлено на сервер."); }); + _hubConnection.On("FriendRequestReceived", (message) => // Assuming message is a simple string notification + { + Console.WriteLine($"\n[Система] Новый запрос в друзья: {message}"); + Console.Write(CurrentChatUser == null ? "/> " : ">> "); + }); + + _hubConnection.On("FriendRequestAccepted", (message) => + { + Console.WriteLine($"\n[Система] Ваш запрос в друзья принят: {message}"); + Console.Write(CurrentChatUser == null ? "/> " : ">> "); + }); + + _hubConnection.On("FriendRemoved", (message) => + { + Console.WriteLine($"\n[Система] Пользователь удалил вас из друзей или вы удалили его: {message}"); + Console.Write(CurrentChatUser == null ? "/> " : ">> "); + }); + + _hubConnection.On("UserBlocked", (message) => + { + Console.WriteLine($"\n[Система] Пользователь заблокирован: {message}"); + Console.Write(CurrentChatUser == null ? "/> " : ">> "); + }); + _hubConnection.On("UserUnblocked", (message) => + { + Console.WriteLine($"\n[Система] Пользователь разблокирован: {message}"); + Console.Write(CurrentChatUser == null ? "/> " : ">> "); + }); + + try { await _hubConnection.StartAsync(); @@ -280,8 +287,9 @@ namespace Govor.ConsoleClient { if (AuthToken == null) return Guid.Empty; var handler = new JwtSecurityTokenHandler(); + if (!handler.CanReadToken(AuthToken)) return Guid.Empty; var jwtToken = handler.ReadJwtToken(AuthToken); - var userIdClaim = jwtToken.Claims.FirstOrDefault(claim => claim.Type == "userId"); + var userIdClaim = jwtToken.Claims.FirstOrDefault(claim => claim.Type == "userId" || claim.Type == System.Security.Claims.ClaimTypes.NameIdentifier); return userIdClaim != null && Guid.TryParse(userIdClaim.Value, out var userId) ? userId : Guid.Empty; } @@ -291,22 +299,24 @@ namespace Govor.ConsoleClient Console.Clear(); Console.WriteLine($"/*===================================="); Console.WriteLine($" * Личные сообщения | {user} ({CurrentChatUserId})"); + Console.WriteLine($" * (/back для выхода из чата)"); Console.WriteLine($" *===================================="); if (!ChatHistory.ContainsKey(user)) { ChatHistory[user] = new List(); - // Example initial messages - consider fetching history if implementing that - // ChatHistory[user].Add($"[Система] Начало чата с {user}"); + // Potentially load chat history here via an API call if desired + // ChatHistory[user].Add($"[Система] Начало чата с {user}. Введите сообщение или /back."); + } + else + { + foreach (var msg in ChatHistory[user]) + Console.WriteLine(" * " + msg); } - - foreach (var msg in ChatHistory[user]) - Console.WriteLine(" * " + msg); - Console.WriteLine(" *------------------------------------"); } - static async void SendMessage(string messageContent) + static async Task SendMessageAsync(string messageContent) { if (string.IsNullOrWhiteSpace(messageContent) || _hubConnection == null || CurrentChatUserId == Guid.Empty) return; @@ -320,24 +330,23 @@ namespace Govor.ConsoleClient var messageRequest = new MessageRequest { RecipientId = CurrentChatUserId, - EncryptedContent = messageContent, // Assuming content is not actually encrypted for console client simplicity for now + EncryptedContent = messageContent, RecipientType = RecipientType.User, - // MediaAttachments and ReplyToMessageId can be added if needed }; try { await _hubConnection.InvokeAsync("Send", messageRequest); - // Add to local history immediately for responsiveness. - // Server will send "ReceiveMessage" to self as well if needed, or "MessageSent" for confirmation. var displayMessage = $"[{DateTime.Now:HH:mm}] Ты >> {messageContent}"; if (!ChatHistory.ContainsKey(CurrentChatUser!)) { ChatHistory[CurrentChatUser!] = new List(); } ChatHistory[CurrentChatUser!].Add(displayMessage); - // Console.WriteLine($"Ты >> {messageContent}"); // Already handled by input echo + ReceiveMessage from self + // No Console.WriteLine here, message will be echoed by "ReceiveMessage" if server sends it to self, + // or we rely on user seeing their own typed message. + // The ReceiveMessage handler should ideally handle self-messages for consistent display. } catch (Exception ex) { @@ -352,12 +361,14 @@ namespace Govor.ConsoleClient Console.WriteLine($" *===================================="); Console.WriteLine(" * /reg - создать аккаунт "); Console.WriteLine(" * /login - войти"); + Console.WriteLine(" * /help - список команд"); } } } - -public class LoginResponse -{ - public string token { get; set; } -} \ No newline at end of file +// LoginResponse class was here, can be removed if not used elsewhere or moved to a Contracts project. +// Assuming it's implicitly handled by HttpClientService or was specific to the old Program.cs structure. +// public class LoginResponse +// { +// public string token { get; set; } +// } \ No newline at end of file