Refactor friend request and media services, remove console commands

Refactored the friend request command service and SignalR hub to return and broadcast FriendshipDto objects, improving real-time updates. Enhanced media upload and download logic to support file storage and retrieval, including database integration. Removed all command classes and related infrastructure from the Govor.Console project, streamlining the console client. Updated dependency injection and interfaces to reflect these changes.
This commit is contained in:
Artemy
2025-07-13 19:03:01 +07:00
parent 97b2ea14b2
commit 6063aafd9e
40 changed files with 1029 additions and 1381 deletions
@@ -1,42 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
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("AcceptRequest", friendshipId);
Console.WriteLine("Заявка в друзья принята.");
}
catch (Exception ex)
{
Console.WriteLine($"[Ошибка принятия заявки] {ex.Message}");
}
}
public override string GetHelp()
{
return "/accept [ID_заявки] - Принять входящую заявку в друзья. Если ID не указан, запросит ввод.";
}
}
}
@@ -1,58 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.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 json = await HttpClientService.GetAsync("api/admin/Friendships");
var friendships = JsonSerializer.Deserialize<List<FriendshipDto>>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
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 - (Админ) Показать все дружеские связи в системе.";
}
}
}
@@ -1,65 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.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}");
var friendships = JsonSerializer.Deserialize<List<FriendshipDto>>(response);
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 не указан, запросит ввод.";
}
}
}
@@ -1,57 +0,0 @@
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<IActionResult> 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<IActionResult> 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);
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 не указан, запросит ввод.";
}
}
}
-65
View File
@@ -1,65 +0,0 @@
using Microsoft.AspNetCore.SignalR.Client;
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; } = null!;
protected static Func<string?> GetAuthToken { get; private set; } = null!;
protected static Action<string> SetAuthToken { get; private set; } = null!;
protected static Func<Task> InitializeHubConnectionAsync { get; private set; } = null!;
protected static HubConnection HubConnection => _getHubConnection?.Invoke();
private static Func<HubConnection?> _getHubConnection = null!;
public static void InitializeServices(
FriendsClient? friendsClient,
HttpClientService httpClientService,
Func<string?> getAuthToken,
Action<string> setAuthToken,
Func<Task> initializeHubConnectionAsync,
Func<HubConnection?> getHubConnection)
{
FriendsClient = friendsClient;
HttpClientService = httpClientService;
GetAuthToken = getAuthToken;
SetAuthToken = setAuthToken;
InitializeHubConnectionAsync = initializeHubConnectionAsync;
_getHubConnection = getHubConnection;
}
public abstract Task ExecuteAsync(string? argument);
public abstract string GetHelp();
protected bool EnsureLoggedIn()
{
if (GetAuthToken() == null)
{
Console.WriteLine("[Ошибка] Сначала войдите в систему. Используйте /login или /reg.");
return false;
}
if (FriendsClient == null && !(this is LoginCommand || this is RegisterCommand))
{
Console.WriteLine("[Ошибка] Клиент для работы с друзьями не инициализирован. Попробуйте войти снова.");
return false;
}
return true;
}
protected bool EnsureHubConnection()
{
var hub = _getHubConnection?.Invoke();
if (hub == null || hub.State != HubConnectionState.Connected)
{
Console.WriteLine("[Ошибка] SignalR соединение не установлено. Попробуйте войти снова или проверьте соединение.");
return false;
}
return true;
}
}
}
@@ -1,43 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
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 не указан, запросит ввод.";
}
}
}
-49
View File
@@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Govor.ConsoleClient.Commands
{
public class HelpCommand : BaseCommand
{
private readonly Dictionary<string, ICommand> _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<string, ICommand> 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 [команда] - Показать список всех команд или помощь по конкретной команде.";
}
}
}
-8
View File
@@ -1,8 +0,0 @@
namespace Govor.ConsoleClient.Commands
{
public interface ICommand
{
Task ExecuteAsync(string? argument);
string GetHelp();
}
}
@@ -1,40 +0,0 @@
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 - Показать список ваших друзей.";
}
}
}
@@ -1,44 +0,0 @@
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 - Показать список входящих заявок в друзья.";
}
}
}
@@ -1,53 +0,0 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.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 json = await HttpClientService.GetAsync("api/friends/responses");
var requests = JsonSerializer.Deserialize<List<FriendshipDto>>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
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 - Показать список отправленных вами заявок в друзья, ожидающих ответа.";
}
}
}
-51
View File
@@ -1,51 +0,0 @@
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);
Program.UpdateFriendsClient(friendsClient); // <-- единственный нужный вызов
await InitializeHubConnectionAsync();
Console.WriteLine("[Успех] Вход выполнен. Токен сохранен.");
}
catch (Exception ex)
{
Console.WriteLine($"[Ошибка] {ex.Message}");
}
}
public override string GetHelp()
{
return "/login - Войти в существующий аккаунт.";
}
}
}
-53
View File
@@ -1,53 +0,0 @@
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
Program.UpdateFriendsClient(friendsClient); // <-- единственный нужный вызов
await InitializeHubConnectionAsync();
Console.WriteLine("[Успех] Регистрация завершена. Токен сохранен.");
}
catch (Exception ex)
{
Console.WriteLine($"[Ошибка] {ex.Message}");
}
}
public override string GetHelp()
{
return "/reg - Зарегистрировать новый аккаунт с помощью кода приглашения.";
}
}
}
@@ -1,43 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
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("RejectRequest", friendshipId);
Console.WriteLine("Заявка в друзья отклонена.");
}
catch (Exception ex)
{
Console.WriteLine($"[Ошибка отклонения заявки] {ex.Message}");
}
}
public override string GetHelp()
{
return "/reject [ID_заявки] - Отклонить входящую заявку в друзья. Если ID не указан, запросит ввод.";
}
}
}
@@ -1,51 +0,0 @@
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 [имя_пользователя] - Найти пользователей по имени. Если имя не указано, запросит ввод.";
}
}
}
@@ -1,42 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
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("SendRequest", targetUserId);
Console.WriteLine("Запрос в друзья отправлен.");
}
catch (Exception ex)
{
Console.WriteLine($"[Ошибка отправки запроса] {ex.Message}");
}
}
public override string GetHelp()
{
return "/friend [ID_пользователя] - Отправить запрос в друзья пользователю с указанным ID. Если ID не указан, запросит ввод.";
}
}
}
@@ -1,43 +0,0 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
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 не указан, запросит ввод.";
}
}
}
+42 -129
View File
@@ -1,157 +1,70 @@
using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Client;
using Govor.Contracts.Responses.SignalR;
class FriendsHubClient
namespace Govor.ConsoleClient;
public class FriendsHubClient
{
private readonly HubConnection _connection;
private readonly string _hubUrl;
private readonly string _jwtToken;
private HubConnection _connection;
public FriendsHubClient(string hubUrl, string jwtToken)
{
_hubUrl = hubUrl;
_jwtToken = jwtToken;
}
public async Task ConnectAsync()
{
_connection = new HubConnectionBuilder()
.WithUrl(hubUrl, options =>
.WithUrl($"{_hubUrl}/friends", options =>
{
options.AccessTokenProvider = () => Task.FromResult(jwtToken);
options.AccessTokenProvider = () => Task.FromResult(_jwtToken);
})
.WithAutomaticReconnect()
.Build();
// Подписка на события от сервера
_connection.On<Guid>("FriendRequestReceived", OnFriendRequestReceived);
_connection.On<Guid>("FriendRequestAccepted", OnFriendRequestAccepted);
_connection.On<Guid>("FriendRequestRejected", OnFriendRequestRejected);
}
// Событие: получен входящий запрос в друзья
_connection.On<string>("FriendRequestReceived", userId =>
{
Console.WriteLine($"📨 Friend request received from: {userId}");
});
private void OnFriendRequestReceived(Guid userId)
{
Console.WriteLine($"[Event] Получен запрос в друзья от пользователя: {userId}");
}
// Событие: запрос принят
_connection.On<string>("FriendRequestAccepted", friendshipId =>
{
Console.WriteLine($"✅ Friend request accepted! Friendship ID: {friendshipId}");
});
private void OnFriendRequestAccepted(Guid friendshipId)
{
Console.WriteLine($"[Event] Запрос в друзья принят. ID дружбы: {friendshipId}");
}
// Событие: запрос отклонён
_connection.On<string>("FriendRequestRejected", friendshipId =>
{
Console.WriteLine($"❌ Friend request rejected! Friendship ID: {friendshipId}");
});
private void OnFriendRequestRejected(Guid friendshipId)
{
Console.WriteLine($"[Event] Запрос в друзья отклонён. ID дружбы: {friendshipId}");
}
public async Task StartAsync()
{
await _connection.StartAsync();
Console.WriteLine("Подключено к FriendsHub");
Console.WriteLine("✅ Connected to FriendsHub");
}
public async Task StopAsync()
public async Task SendFriendRequestAsync(Guid targetUserId)
{
await _connection.StopAsync();
Console.WriteLine("Отключено от FriendsHub");
var result = await _connection.InvokeAsync<HubResult<object>>("SendRequest", targetUserId);
Console.WriteLine($"📤 Friend request sent. Status: {result.Status}");
}
public async Task SendRequest(Guid targetUserId)
public async Task AcceptFriendRequestAsync(Guid friendshipId)
{
try
{
await _connection.InvokeAsync("SendRequest", targetUserId);
Console.WriteLine($"Запрос на добавление в друзья отправлен пользователю {targetUserId}");
}
catch (HubException ex)
{
Console.WriteLine($"Ошибка при отправке запроса: {ex.Message}");
}
var result = await _connection.InvokeAsync<HubResult<object>>("AcceptRequest", friendshipId);
Console.WriteLine($"👍 Accept result: {result.Status}");
}
public async Task AcceptRequest(Guid friendshipId)
public async Task RejectFriendRequestAsync(Guid friendshipId)
{
try
{
await _connection.InvokeAsync("AcceptRequest", friendshipId);
Console.WriteLine($"Запрос в друзья с ID {friendshipId} принят");
}
catch (HubException ex)
{
Console.WriteLine($"Ошибка при принятии запроса: {ex.Message}");
}
}
public async Task RejectRequest(Guid friendshipId)
{
try
{
await _connection.InvokeAsync("RejectRequest", friendshipId);
Console.WriteLine($"Запрос в друзья с ID {friendshipId} отклонён");
}
catch (HubException ex)
{
Console.WriteLine($"Ошибка при отклонении запроса: {ex.Message}");
}
}
}
class Program
{
static async Task TestMain(string[] args)
{
// Адрес хаба SignalR
string hubUrl = "https://yourserver.com/api/friends";
// JWT токен для аутентификации (замени на настоящий)
string jwtToken = "your_jwt_token_here";
var client = new FriendsHubClient(hubUrl, jwtToken);
await client.StartAsync();
Console.WriteLine("Команды:");
Console.WriteLine("send <userId> - Отправить запрос в друзья");
Console.WriteLine("accept <friendshipId> - Принять запрос");
Console.WriteLine("reject <friendshipId> - Отклонить запрос");
Console.WriteLine("exit - Выйти");
while (true)
{
Console.Write("> ");
var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
continue;
var parts = input.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
var command = parts[0].ToLowerInvariant();
if (command == "exit")
break;
if (parts.Length < 2)
{
Console.WriteLine("Ошибка: не хватает аргументов");
continue;
}
if (!Guid.TryParse(parts[1], out var id))
{
Console.WriteLine("Ошибка: некорректный GUID");
continue;
}
switch (command)
{
case "send":
await client.SendRequest(id);
break;
case "accept":
await client.AcceptRequest(id);
break;
case "reject":
await client.RejectRequest(id);
break;
default:
Console.WriteLine("Неизвестная команда");
break;
}
}
await client.StopAsync();
var result = await _connection.InvokeAsync<HubResult<object>>("RejectRequest", friendshipId);
Console.WriteLine($"👎 Reject result: {result.Status}");
}
}
+1
View File
@@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.5" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" />
</ItemGroup>
+42 -371
View File
@@ -1,382 +1,53 @@
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.Threading.Tasks;
using Govor.ConsoleClient.Commands;
using Govor.Contracts.Requests.SignalR;
using Govor.Contracts.Responses.SignalR;
using Govor.Core.Models;
using Govor.Core.Models.Messages;
using Govor.ConsoleClient;
/*====================================
*Личные сообщения| Егор
*====================================
* [15:59] Вы добавили (Егор) в друзья
*
* [16:30] Егор >> Привет!
* [16:31] Ты >> Че как?
*
*------------------------------------
* >> "Пойдем завтра гулять?!"
*/
namespace Govor.ConsoleClient
internal class Program
{
class Program
private const string HubBaseUrl = "https://govor-team-govor-88b3.twc1.net/hubs";
private static string jwtToken = "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDRjNzkyMi1hNjdlLTRlMzctYWI0MC0wNThlZDE5NzM5NjMiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbiIsImV4cCI6MTc1MjQ4MzE0MH0.ECXLPwIljdeWgWUJtBiXXQXKMFC8Iw0x7_zdjbsUe1I"; // сюда подставьте валидный JWT
static async Task Main()
{
static string baseUrl = "https://govor-team-govor-88b3.twc1.net";
//static string baseUrl = "https://localhost:7155";
static string? AuthToken = null;
static HttpClientService HttpService = new(baseUrl);
private static FriendsClient? _friendsClient; // Renamed to avoid conflict with BaseCommand
static HubConnection? _hubConnection;
static Dictionary<string, List<string>> ChatHistory = new();
static string? CurrentChatUser = null;
static Guid CurrentChatUserId = Guid.Empty;
Console.Write("🔑 JWT Token: ");
jwtToken = Console.ReadLine();
private static Dictionary<string, ICommand> _commands = new();
var client = new FriendsHubClient(HubBaseUrl, jwtToken);
await client.ConnectAsync();
static async Task Main()
while (true)
{
FriendsHubClient client = new FriendsHubClient($"{baseUrl}/hubs/friends", "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDRjNzkyMi1hNjdlLTRlMzctYWI0MC0wNThlZDE5NzM5NjMiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbiIsImV4cCI6MTc1MjMzMjE2NX0.mGSjSCvguEXkdHqHcbZ3eUH0S8c82kz3XP89potEh1k");
await client.StartAsync();
await client.AcceptRequest(Guid.Parse("3ba77c0c-7522-47be-8e77-ea47bd6c6e69"));
return;
InitializeCommands();
BaseCommand.InitializeServices(
_friendsClient, // Initially null, will be set by Login/Register commands
HttpService,
() => AuthToken,
(token) => AuthToken = token,
InitializeHubConnection,
() => _hubConnection
);
Console.WriteLine("\n1. Send friend request");
Console.WriteLine("2. Accept request");
Console.WriteLine("3. Reject request");
Console.Write("➡ Choose option: ");
var option = Console.ReadLine();
LoginWindow();
while (true)
switch (option)
{
Console.Write(CurrentChatUser == null ? "/> " : ">> ");
var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input)) continue;
if (input.StartsWith("/"))
await HandleCommandAsync(input);
else if (CurrentChatUser != null)
await SendMessageAsync(input);
else
Console.WriteLine("[Система] Введите команду, например /help или /friends");
case "1":
Console.Write("Target UserId: ");
if (Guid.TryParse(Console.ReadLine(), out var targetId))
{
await client.SendFriendRequestAsync(targetId);
}
break;
case "2":
Console.Write("FriendshipId to accept: ");
if (Guid.TryParse(Console.ReadLine(), out var acceptId))
{
await client.AcceptFriendRequestAsync(acceptId);
}
break;
case "3":
Console.Write("FriendshipId to reject: ");
if (Guid.TryParse(Console.ReadLine(), out var rejectId))
{
await client.RejectFriendRequestAsync(rejectId);
}
break;
default:
Console.WriteLine("❗ Unknown option");
break;
}
}
static void InitializeCommands()
{
_commands = new Dictionary<string, ICommand>
{
{ "/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() },
{ "/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
// 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
}
// 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
);
}
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;
}
var chatArgs = argument.Split(' ', 2);
if (chatArgs.Length < 2 || !Guid.TryParse(chatArgs[1], out var chatUserId))
{
Console.WriteLine("[Ошибка] Неверный формат. Укажите имя друга и ID (например /chat Egor GUID)");
break;
}
CurrentChatUser = chatArgs[0];
CurrentChatUserId = chatUserId;
ShowChat(CurrentChatUser);
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;
}
}
}
static async Task InitializeHubConnection()
{
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}/hubs/chats", options =>
{
options.AccessTokenProvider = () => Task.FromResult(AuthToken);
})
.Build();
_hubConnection.On<UserMessageResponse>("ReceiveMessage", (message) =>
{
var myId = GetMyUserId();
var senderName = message.SenderId == myId ? "Ты" : CurrentChatUser ?? message.SenderId.ToString() ?? "Неизвестно";
var displayMessage = $"[{message.SentAt:HH:mm}] {senderName} >> {message.EncryptedContent}";
// 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 the message belongs to the currently active chat window
if (CurrentChatUser != null && (message.SenderId == CurrentChatUserId || (message.RecipientId == CurrentChatUserId && message.SenderId == myId)))
{
if (!ChatHistory.ContainsKey(activeChatDisplayUser))
{
ChatHistory[activeChatDisplayUser] = new List<string>();
}
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 // Notification for message not in the current chat
{
Console.WriteLine($"\n[Новое сообщение от {message.SenderId.ToString()}]: {message.EncryptedContent}");
Console.Write(CurrentChatUser == null ? "/> " : ">> "); // Re-display prompt
}
});
_hubConnection.On<UserMessageResponse>("MessageSent", (message) =>
{
// Optional: Handle confirmation that message was sent successfully by the server
// For console, maybe a subtle notification or log if verbose mode is on.
// Example: Console.WriteLine($"[Система] Сообщение для {message.RecipientUsername} доставлено на сервер.");
});
_hubConnection.On<string>("FriendRequestReceived", (message) => // Assuming message is a simple string notification
{
Console.WriteLine($"\n[Система] Новый запрос в друзья: {message}");
Console.Write(CurrentChatUser == null ? "/> " : ">> ");
});
_hubConnection.On<string>("FriendRequestAccepted", (message) =>
{
Console.WriteLine($"\n[Система] Ваш запрос в друзья принят: {message}");
Console.Write(CurrentChatUser == null ? "/> " : ">> ");
});
_hubConnection.On<string>("FriendRemoved", (message) =>
{
Console.WriteLine($"\n[Система] Пользователь удалил вас из друзей или вы удалили его: {message}");
Console.Write(CurrentChatUser == null ? "/> " : ">> ");
});
_hubConnection.On<string>("UserBlocked", (message) =>
{
Console.WriteLine($"\n[Система] Пользователь заблокирован: {message}");
Console.Write(CurrentChatUser == null ? "/> " : ">> ");
});
_hubConnection.On<string>("UserUnblocked", (message) =>
{
Console.WriteLine($"\n[Система] Пользователь разблокирован: {message}");
Console.Write(CurrentChatUser == null ? "/> " : ">> ");
});
try
{
await _hubConnection.StartAsync();
Console.WriteLine("[SignalR] Соединение установлено.");
}
catch (Exception ex)
{
Console.WriteLine($"[SignalR Ошибка] {ex.Message}");
}
}
static Guid GetMyUserId()
{
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" || claim.Type == System.Security.Claims.ClaimTypes.NameIdentifier);
return userIdClaim != null && Guid.TryParse(userIdClaim.Value, out var userId) ? userId : Guid.Empty;
}
static void ShowChat(string user)
{
Console.Clear();
Console.WriteLine($"/*====================================");
Console.WriteLine($" * Личные сообщения | {user} ({CurrentChatUserId})");
Console.WriteLine($" * (/back для выхода из чата)");
Console.WriteLine($" *====================================");
if (!ChatHistory.ContainsKey(user))
{
ChatHistory[user] = new List<string>();
// 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);
}
Console.WriteLine(" *------------------------------------");
}
static async Task SendMessageAsync(string messageContent)
{
if (string.IsNullOrWhiteSpace(messageContent) || _hubConnection == null || CurrentChatUserId == Guid.Empty) return;
var myUserId = GetMyUserId();
if (myUserId == Guid.Empty)
{
Console.WriteLine("[Ошибка] Не удалось определить ID пользователя. Попробуйте войти снова.");
return;
}
var messageRequest = new MessageRequest
{
RecipientId = CurrentChatUserId,
EncryptedContent = messageContent,
RecipientType = RecipientType.User
};
try
{
await _hubConnection.InvokeAsync("Send", messageRequest);
var displayMessage = $"[{DateTime.Now:HH:mm}] Ты >> {messageContent}";
if (!ChatHistory.ContainsKey(CurrentChatUser!))
{
ChatHistory[CurrentChatUser!] = new List<string>();
}
ChatHistory[CurrentChatUser!].Add(displayMessage);
// 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)
{
Console.WriteLine($"[Ошибка отправки] {ex.Message}");
}
}
static void LoginWindow()
{
Console.WriteLine($"/*====================================");
Console.WriteLine($" * Добро пожаловать в Govor!");
Console.WriteLine($" *====================================");
Console.WriteLine(" * /reg - создать аккаунт ");
Console.WriteLine(" * /login - войти");
Console.WriteLine(" * /help - список команд");
}
}
}
// 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; }
// }
}