234 lines
9.2 KiB
C#
234 lines
9.2 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 DataContext _dataContext;
|
|
private ServerConfig _serverConfig;
|
|
|
|
public static readonly Dictionary<string, List<User>> GroupUsers = [];
|
|
public static readonly Dictionary<string, List<string>> ConnectedUsers = [];
|
|
public static readonly List<User> OnlineUsers = [];
|
|
|
|
private static User ServerUser = new()
|
|
{
|
|
Id = "0",
|
|
Username = "Server",
|
|
};
|
|
|
|
public ChatHub(IUserService userService, IRoomService roomService, ILogger<ChatHub> logger, DataContext dataContext, ServerConfig serverConfig)
|
|
{
|
|
_userService = userService;
|
|
_roomService = roomService;
|
|
_logger = logger;
|
|
_dataContext = dataContext;
|
|
_serverConfig = serverConfig;
|
|
}
|
|
|
|
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);
|
|
|
|
await Clients.Client(Context.ConnectionId).SendAsync("RefreshRoomList");
|
|
await Clients.Client(Context.ConnectionId).SendAsync("ReceiveServerConfig", _serverConfig);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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))
|
|
{
|
|
// set user offline if there aren't any more connections
|
|
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);
|
|
}
|
|
|
|
// also remove the user from any rooms
|
|
var room = await _roomService.GetRoom(user.Data.CurrentRoomId);
|
|
if (room.Data != null)
|
|
await LeaveRoomAsync(user.Data, room.Data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[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)
|
|
{
|
|
// send UserUpdate to all clients
|
|
UserInformationDto userInfoDto = new();
|
|
userInfoDto.MapToUser(user);
|
|
|
|
await Clients.All.SendAsync("UserUpdate", userInfoDto);
|
|
|
|
Log($"Status Was Set To {res.Data.Status} On User {user.Username}");
|
|
await Clients.Client(Context.ConnectionId).SendAsync("UpdateCurrentUser");
|
|
}
|
|
else
|
|
Log($"Something Went Wrong Setting The Status On User {user.Username}");
|
|
}
|
|
|
|
[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", ServerUser, $"User {user.Username} Has Joined {room.Name}", room);
|
|
|
|
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", room, GroupUsers[room.Id]);
|
|
Log($"User {user.Username} Has Joined {room.Name}");
|
|
|
|
user.CurrentRoomId = room.Id;
|
|
|
|
ServiceResponse<Room> dbRoomRes = await _roomService.GetRoom(room.Id);
|
|
Room? dbRoom = dbRoomRes?.Data;
|
|
|
|
if (dbRoom != null) dbRoom.UserCount += 1;
|
|
|
|
await _dataContext.SaveChangesAsync();
|
|
await Clients.All.SendAsync("RefreshRoomList");
|
|
}
|
|
|
|
[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("RoomMessage", ServerUser, $"Guest User {username} Has Joined {room.Data.Name}", room);
|
|
|
|
room.Data.UserCount += 1;
|
|
await _dataContext.SaveChangesAsync();
|
|
|
|
await Clients.All.SendAsync("RefreshRoomList");
|
|
}
|
|
}
|
|
|
|
[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", ServerUser, $"User {user.Username} Has Left {room.Name}", room);
|
|
|
|
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", room, GroupUsers[room.Id]);
|
|
Log($"User {user.Username} Has Left {room.Name}");
|
|
|
|
user.CurrentRoomId = string.Empty;
|
|
|
|
ServiceResponse<Room> dbRoomRes = await _roomService.GetRoom(room.Id);
|
|
Room? dbRoom = dbRoomRes?.Data;
|
|
|
|
if (dbRoom != null) dbRoom.UserCount -= 1;
|
|
|
|
await _dataContext.SaveChangesAsync();
|
|
await Clients.All.SendAsync("RefreshRoomList");
|
|
}
|
|
|
|
[HubMethodName("SendMessage")]
|
|
[Authorize]
|
|
public async Task SendMessageAsync(User user, Message message, Room room)
|
|
{
|
|
await Clients.Group(room.Id).SendAsync("RoomMessage", user, $"{message.Content}", room);
|
|
}
|
|
|
|
[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, ProfileCosmeticId = user.ActiveProfileCosmetic };
|
|
await Clients.Client(connection).SendAsync("ReceiveDirectMessage", message, userInformationDto);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Log(string message) => _logger.LogInformation(message);
|
|
}
|
|
}
|