Files
Govor/Govor.Console/Program.cs
T
Artemy 96587b491d Add AutoMapper and message response models
Introduced AutoMapper to the API project and registered it in the DI container. Added mapping profiles for core models to DTOs and response objects. Refactored controllers to use AutoMapper for mapping entities to response DTOs. Implemented new response models for messages, media attachments, reactions, and views. Updated MessagesLoader to load messages with related data. Added a migration to support ChatGroupId in messages. Updated dependencies and fixed minor issues in related files.
2025-07-12 16:39:06 +07:00

382 lines
18 KiB
C#

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;
/*====================================
*Личные сообщения| Егор
*====================================
* [15:59] Вы добавили (Егор) в друзья
*
* [16:30] Егор >> Привет!
* [16:31] Ты >> Че как?
*
*------------------------------------
* >> "Пойдем завтра гулять?!"
*/
namespace Govor.ConsoleClient
{
class Program
{
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;
private static Dictionary<string, ICommand> _commands = new();
static async Task Main()
{
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
);
LoginWindow();
while (true)
{
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");
}
}
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; }
// }