213 lines
8.4 KiB
C#

using qtc_api.Services.RoomService;
using System.Text.Json;
namespace qtc_api.Hubs
{
public class ChatHub : Hub
{
private IUserService _userService;
private IRoomService _roomService;
private ILogger<ChatHub> _logger;
private static readonly Dictionary<string, List<User>> GroupUsers = [];
private static readonly Dictionary<string, List<string>> ConnectedUsers = [];
private static readonly List<User> OnlineUsers = [];
public ChatHub(IUserService userService, IRoomService roomService, ILogger<ChatHub> logger)
{
_userService = userService;
_roomService = roomService;
_logger = logger;
}
public async override Task OnConnectedAsync()
{
Log("Client Connected To Hub");
var uid = Context.UserIdentifier;
if (uid != null)
{
var user = await _userService.GetUserById(uid);
if (user != null && user.Success && user.Data != null)
{
var userId = user.Data.Id;
lock (ConnectedUsers)
{
if(!ConnectedUsers.TryGetValue(userId, out List<string>? value))
{
value = [];
ConnectedUsers[userId] = value;
}
value.Add(Context.ConnectionId);
}
if(!OnlineUsers.Any(e => e.Id == userId))
{
Log("Setting User Online...");
OnlineUsers.Add(user.Data);
if (user.Data.Status == 0) await UpdateStatusAsync(user.Data, 1);
}
}
}
}
public override async Task OnDisconnectedAsync(Exception? ex)
{
Log("Client Disconnected From Hub");
var uid = Context.UserIdentifier;
if (uid != null)
{
var user = await _userService.GetUserById(uid);
if (user != null && user.Success && user.Data != null)
{
var userId = user.Data.Id;
lock (ConnectedUsers)
{
if(ConnectedUsers.TryGetValue(userId, out List<string>? value))
{
value.Remove(Context.ConnectionId);
}
}
if (ConnectedUsers[userId].Count == 0)
{
ConnectedUsers.Remove(userId);
if (OnlineUsers.Any(e => e.Id == userId))
{
var onlineUser = OnlineUsers.FirstOrDefault(e => e.Id == userId);
if(onlineUser != null)
{
Log("User Has No More Connections. Setting Offline...");
OnlineUsers.Remove(onlineUser);
if (user.Data.Status >= 1) await UpdateStatusAsync(onlineUser, 0);
}
}
}
}
}
}
[HubMethodName("JoinRoomGuest")]
public async Task JoinRoomGuestAsync(string roomId, string username)
{
// here we can just add the client to the room group and call it a day since the user isn't authenticated
var room = await _roomService.GetRoom(roomId);
if(room != null && room.Success && room.Data != null)
{
await Groups.AddToGroupAsync(Context.ConnectionId, room.Data.Id);
await Clients.Group(room.Data.Id).SendAsync("GuestJoin", username);
}
}
[HubMethodName("UpdateStatus")]
[Authorize]
public async Task UpdateStatusAsync(User user, int status)
{
var statusDto = new UserStatusDto { Id = user.Id, Status = status };
Log($"Updating Status\n{JsonSerializer.Serialize(statusDto)}");
var res = await _userService.UpdateStatus(statusDto);
if (res != null && res.Success && res.Data != null)
{
await Clients.All.SendAsync("RefreshUserLists");
Log($"Status Was Set To {res.Data.Status} On User {user.Username}");
}
else
Log($"Something Went Wrong Setting The Status On User {user.Username}");
}
[HubMethodName("JoinLobby")]
[Authorize]
public async Task JoinLobbyAsync(User user)
{
await Groups.AddToGroupAsync(Context.ConnectionId, "LOBBY");
await Clients.Group("LOBBY").SendAsync("RoomMessage", $"[SERVER] User {user.Username} Has Joined The Lobby");
if (!GroupUsers.TryGetValue("LOBBY", out _)) { GroupUsers.Add("LOBBY", new List<User>()); }
GroupUsers["LOBBY"].Add(user);
await Clients.Groups("LOBBY").SendAsync("RoomUserList", GroupUsers["LOBBY"]);
Log($"User {user.Username} Has Joined The Lobby");
}
[HubMethodName("LeaveLobby")]
[Authorize]
public async Task LeaveLobbyAsync(User user)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "LOBBY");
await Clients.Group("LOBBY").SendAsync("RoomMessage", $"[SERVER] User {user.Username} Has Left The Lobby");
if (GroupUsers.TryGetValue("LOBBY", out _)) GroupUsers["LOBBY"].Remove(GroupUsers["LOBBY"].FirstOrDefault(e => e.Id == user.Id)!);
await Clients.Client("LOBBY").SendAsync("RoomUserList", GroupUsers["LOBBY"]);
Log($"User {user.Username} Has Left The Lobby");
}
[HubMethodName("JoinRoom")]
[Authorize]
public async Task JoinRoomAsync(User user, Room room)
{
await Groups.AddToGroupAsync(Context.ConnectionId, room.Id);
await Clients.Group(room.Id).SendAsync("RoomMessage", $"[SERVER] User {user.Username} Has Joined {room.Name}");
if (!GroupUsers.TryGetValue(room.Id, out _)) { GroupUsers.Add(room.Id, new List<User>()); }
GroupUsers[room.Id].Add(user);
await Clients.Group(room.Id).SendAsync("RoomUserList", GroupUsers[room.Id]);
Log($"User {user.Username} Has Joined {room.Name}");
}
[HubMethodName("LeaveRoom")]
[Authorize]
public async Task LeaveRoomAsync(User user, Room room)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, room.Id);
await Clients.Group(room.Id).SendAsync("RoomMessage", $"[SERVER] User {user.Username} Has Left {room.Name}");
if (GroupUsers.TryGetValue(room.Id, out _)) GroupUsers[room.Id].Remove(GroupUsers[room.Id].FirstOrDefault(e => e.Id == user.Id)!);
await Clients.Group(room.Id).SendAsync("RoomUserList", GroupUsers[room.Id]);
Log($"User {user.Username} Has Left {room.Name}");
}
[HubMethodName("SendMessage")]
[Authorize]
public async Task SendMessageAsync(User user, Message message, bool IsLobbyMsg, Room room = null!)
{
if(IsLobbyMsg == true) { await Clients.Group("LOBBY").SendAsync("RoomMessage", $"[{user.Username}] {message.Content}"); return; }
await Clients.Group(room.Id).SendAsync("RoomMessage", $"[{user.Username}] {message.Content}");
}
[HubMethodName("SendDirectMessage")]
[Authorize]
public async Task SendDirectMessageAsync(User user, UserInformationDto userToMsg, Message message)
{
// send direct message directly to connected user
if(ConnectedUsers.TryGetValue(userToMsg.Id, out List<string>? value))
{
var connection = value.FirstOrDefault();
if(connection != null)
{
UserInformationDto userInformationDto = new UserInformationDto { Id = user.Id, Username = user.Username, Bio = user.Bio, Role = user.Role, Status = user.Status, CreatedAt = user.CreatedAt, DateOfBirth = user.DateOfBirth, ProfilePicture = user.ProfilePicture };
await Clients.Client(connection).SendAsync("ReceiveDirectMessage", message, userInformationDto);
return;
}
}
}
private void Log(string message) => _logger.LogInformation(message);
}
}