mirror of
https://github.com/Govor-team/Govor.git
synced 2026-07-21 11:44:56 +00:00
the basis for group and private messages
This commit is contained in:
+119
-83
@@ -13,42 +13,138 @@ namespace Govor.API.Hubs;
|
||||
public class ChatsHub : Hub
|
||||
{
|
||||
private readonly ILogger<ChatsHub> _logger;
|
||||
private readonly IChatService _chatService;
|
||||
private readonly IGroupService _groupService;
|
||||
private readonly IMessageService _messageService;
|
||||
|
||||
public ChatsHub(ILogger<ChatsHub> logger,
|
||||
IChatService chatService
|
||||
)
|
||||
public ChatsHub(ILogger<ChatsHub> logger, IMessageService messageService)
|
||||
{
|
||||
_logger = logger;
|
||||
_chatService = chatService;
|
||||
_messageService = messageService;
|
||||
}
|
||||
|
||||
|
||||
public override async Task OnConnectedAsync()
|
||||
{
|
||||
var userId = GetUserId();
|
||||
|
||||
if (userId != Guid.Empty)
|
||||
if (userId == Guid.Empty)
|
||||
{
|
||||
// Binding ConnectionId to UserId
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, userId.ToString());
|
||||
_logger.LogInformation("User {UserId} connected with ConnectionId {ConnectionId}", userId, Context.ConnectionId);
|
||||
_logger.LogWarning("User connected with invalid UserID claim.");
|
||||
Context.Abort(); // Abort connection if userID is invalid
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Add user to their own group (for private messages and notifications)
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, userId.ToString());
|
||||
_logger.LogInformation("User {UserId} connected with ConnectionId {ConnectionId} and added to their group", userId, Context.ConnectionId);
|
||||
|
||||
// TODO: Add user to their chat groups - this might require fetching user's groups from a service
|
||||
// var userGroups = await _userService.GetUserGroupsAsync(userId);
|
||||
// foreach (var group in userGroups)
|
||||
// {
|
||||
// await Groups.AddToGroupAsync(Context.ConnectionId, $"group_{group.Id}");
|
||||
// }
|
||||
|
||||
await base.OnConnectedAsync();
|
||||
}
|
||||
|
||||
|
||||
public override async Task OnDisconnectedAsync(Exception? exception)
|
||||
{
|
||||
var userId = GetUserId();
|
||||
var userId = GetUserId(suppressException: true); // Suppress exception if userID is not found (e.g. connection aborted early)
|
||||
if (userId != Guid.Empty)
|
||||
{
|
||||
// Remove user from their own group
|
||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId, userId.ToString());
|
||||
_logger.LogInformation("User {UserId} disconnected", userId);
|
||||
_logger.LogInformation("User {UserId} disconnected with ConnectionId {ConnectionId} and removed from their group", userId, Context.ConnectionId);
|
||||
|
||||
// TODO: Remove user from their chat groups
|
||||
// var userGroups = await _userService.GetUserGroupsAsync(userId); // This might be problematic if the service relies on the user being connected
|
||||
// foreach (var group in userGroups)
|
||||
// {
|
||||
// await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"group_{group.Id}");
|
||||
// }
|
||||
}
|
||||
else if (exception != null)
|
||||
{
|
||||
_logger.LogWarning(exception, "User disconnected with an exception and invalid UserID claim. ConnectionId: {ConnectionId}", Context.ConnectionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("User disconnected with no exception and invalid UserID claim. ConnectionId: {ConnectionId}", Context.ConnectionId);
|
||||
}
|
||||
|
||||
await base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
|
||||
public async Task Send(MessageRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.EncryptedContent) && (request.MediaAttachments == null || !request.MediaAttachments.Any()))
|
||||
{
|
||||
_logger.LogWarning("Empty message (no content and no attachments) received from user {UserId}", GetUserId());
|
||||
throw new ArgumentException("Message cannot be empty (must have content or attachments).");
|
||||
}
|
||||
|
||||
var senderId = GetUserId();
|
||||
_logger.LogInformation("Message send initiated by {SenderId} to {RecipientId} of type {RecipientType}", senderId, request.RecipientId, request.RecipientType);
|
||||
|
||||
var sendMessageParams = new SendMessage(
|
||||
EncryptContent: request.EncryptedContent,
|
||||
ReplyToMessageId: request.ReplyToMessageId,
|
||||
FromUserId: senderId,
|
||||
RecipientId: request.RecipientId,
|
||||
RecipientType: request.RecipientType,
|
||||
SendAt: DateTime.UtcNow,
|
||||
Media: request.MediaAttachments?.Select(f => new SendMedia(
|
||||
f.MediaId, f.EncryptedKey, f.Type, f.MimeType)) ?? Array.Empty<SendMedia>()
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _messageService.SendMessageAsync(sendMessageParams);
|
||||
|
||||
if (!result.IsSuccess || result.MessageId == Guid.Empty)
|
||||
{
|
||||
_logger.LogError(result.Exception, "Failed to send message from {SenderId} to {RecipientId}. Error: {ErrorMessage}", senderId, request.RecipientId, result.Exception?.Message ?? "Unknown error");
|
||||
if (result.Exception != null) throw result.Exception;
|
||||
throw new HubException("Failed to send message due to an internal error.");
|
||||
}
|
||||
|
||||
var messageResponse = new UserMessageResponse // Assuming a response DTO
|
||||
{
|
||||
MessageId = result.MessageId,
|
||||
SenderId = senderId,
|
||||
RecipientId = request.RecipientId,
|
||||
RecipientType = request.RecipientType,
|
||||
EncryptedContent = request.EncryptedContent,
|
||||
SentAt = sendMessageParams.SendAt,
|
||||
IsEdited = false,
|
||||
MediaAttachments = request.MediaAttachments,
|
||||
ReplyToMessageId = request.ReplyToMessageId
|
||||
};
|
||||
|
||||
// Notify recipient (user or group)
|
||||
if (request.RecipientType == RecipientType.User)
|
||||
{
|
||||
// Send to the recipient's personal group
|
||||
await Clients.Group(request.RecipientId.ToString()).SendAsync("ReceiveMessage", messageResponse);
|
||||
}
|
||||
else if (request.RecipientType == RecipientType.Group)
|
||||
{
|
||||
// Send to all members of the group, including the sender if they are part of the group via a different connection
|
||||
await Clients.Group($"group_{request.RecipientId}").SendAsync("ReceiveMessage", messageResponse);
|
||||
}
|
||||
|
||||
// Notify sender (confirmation) on their connection
|
||||
await Clients.Caller.SendAsync("MessageSent", messageResponse); // Or use "ReceiveMessage" if the sender should also just get it like anyone else
|
||||
|
||||
_logger.LogInformation("Message {MessageId} sent successfully from {SenderId} to {RecipientId} ({RecipientType})", result.MessageId, senderId, request.RecipientId, request.RecipientType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error sending message from {SenderId} to {RecipientId}", senderId, request.RecipientId);
|
||||
// Consider sending a specific error message to the caller instead of a generic HubException or rethrowing.
|
||||
// For example: await Clients.Caller.SendAsync("SendMessageFailed", new { Error = ex.Message });
|
||||
throw new HubException("An error occurred while sending the message.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Remove(Guid recipientId, Guid messageId)
|
||||
{
|
||||
|
||||
@@ -59,79 +155,19 @@ public class ChatsHub : Hub
|
||||
|
||||
}
|
||||
|
||||
public async Task SendGroup(GroupMessageRequest request)
|
||||
{
|
||||
var senderId = GetUserId();
|
||||
|
||||
// Создание сообщения
|
||||
var message = new SendMessage(
|
||||
EncryptContent: request.EncryptedContent,
|
||||
ReplyToMessageId: request.ReplyToMessageId,
|
||||
FromUserId: senderId,
|
||||
RecipientId: request.GroupId,
|
||||
SendAt: DateTime.UtcNow,
|
||||
Media: request.MediaAttachments?.Select(f => new SendMedia(
|
||||
f.MediaId, f.EncryptedKey, f.Type, f.MimeType)) ?? Array.Empty<SendMedia>());
|
||||
|
||||
var result = await _groupService.SendMessageAsync(message);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
throw result.Exception!;
|
||||
|
||||
// Шлём всем участникам группы, кроме отправителя
|
||||
await Clients.GroupExcept($"group_{request.GroupId}", Context.ConnectionId)
|
||||
.SendAsync("ReceiveGroupMessage", message);
|
||||
|
||||
// Отправителю тоже
|
||||
await Clients.Caller.SendAsync("ReceiveGroupMessage", message);
|
||||
}
|
||||
|
||||
public async Task Send(MessageRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.EncryptedContent))
|
||||
{
|
||||
_logger.LogWarning("Empty message received from user {UserId}", GetUserId());
|
||||
throw new ArgumentException("Message cannot be empty", nameof(request.EncryptedContent));
|
||||
}
|
||||
|
||||
var senderId = GetUserId();
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Message sent from {SenderId} to {RecipientId} at {UtcNow}", senderId, request.RecipientId, DateTime.UtcNow);
|
||||
|
||||
var message = new SendMessage(
|
||||
EncryptContent: request.EncryptedContent,
|
||||
ReplyToMessageId: request.ReplyToMessageId,
|
||||
FromUserId: senderId,
|
||||
RecipientId: request.RecipientId,
|
||||
SendAt: DateTime.UtcNow,
|
||||
Media: request.MediaAttachments?.Select(f => new SendMedia(
|
||||
f.MediaId, f.EncryptedKey, f.Type, f.MimeType)) ?? Array.Empty<SendMedia>());
|
||||
|
||||
Result result = await _chatService.SendMessageAsync(message);
|
||||
if(result.IsSuccess == false)
|
||||
throw result.Exception;
|
||||
|
||||
// Sending a message to the sender and recipient
|
||||
await Clients.Group(message.RecipientId.ToString()).SendAsync("Receive", message);
|
||||
await Clients.Group(message.FromUserId.ToString()).SendAsync("Receive", message);
|
||||
// TODO: Send to Group
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error sending message from {SenderId} to {RecipientId}", senderId, request.RecipientId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private Guid GetUserId()
|
||||
private Guid GetUserId(bool suppressException = false)
|
||||
{
|
||||
var userIdClaim = Context.User?.FindFirst("userID")?.Value;
|
||||
if (string.IsNullOrEmpty(userIdClaim) || !Guid.TryParse(userIdClaim, out var userId))
|
||||
{
|
||||
_logger.LogError("Could not retrieve sender userId");
|
||||
throw new UnauthorizedAccessException("userID claim is missing or invalid");
|
||||
if (!suppressException)
|
||||
{
|
||||
_logger.LogError("Could not retrieve sender userId. Claim was: {UserIDClaim}", userIdClaim);
|
||||
throw new UnauthorizedAccessException("userID claim is missing or invalid.");
|
||||
}
|
||||
return Guid.Empty;
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user