using System; using System.Collections.Generic; using System.Linq; namespace SharpChat.SockChat { public class SockChatConnectionsContext { public static readonly TimeSpan TimeOut = TimeSpan.FromMinutes(5); private readonly HashSet Connections = new(); private readonly HashSet AuthedConnections = new(); private readonly Dictionary> UserConnections = new(); public SockChatConnectionInfo[] All => Connections.ToArray(); public SockChatConnectionInfo[] Authed => AuthedConnections.ToArray(); public void WithAll(Action body) { foreach(SockChatConnectionInfo conn in Connections) body(conn); } public void WithAuthed(Action body) { foreach(SockChatConnectionInfo conn in AuthedConnections) body(conn); } public SockChatConnectionInfo[] GetTimedOut() { List conns = new(); foreach(SockChatConnectionInfo conn in Connections) if(DateTimeOffset.UtcNow - conn.LastPing > TimeOut) conns.Add(conn); return conns.ToArray(); } public int GetCountForUser(long userId) { return UserConnections.ContainsKey(userId) ? UserConnections.Count : 0; } public int GetCountForUser(UserInfo userInfo) { return GetCountForUser(userInfo.UserId); } public bool HasUser(long userId) { return GetCountForUser(userId) > 0; } public bool HasUser(UserInfo userInfo) { return HasUser(userInfo.UserId); } public SockChatConnectionInfo[] GetUser(long userId) { if(!UserConnections.ContainsKey(userId)) return Array.Empty(); return UserConnections[userId].ToArray(); } public SockChatConnectionInfo[] GetUser(UserInfo userInfo) { return GetUser(userInfo.UserId); } public void WithUser(long userId, Action body) { if(!UserConnections.ContainsKey(userId)) return; foreach(SockChatConnectionInfo conn in UserConnections[userId]) body(conn); } public void WithUser(UserInfo userInfo, Action body) { WithUser(userInfo.UserId, body); } public string[] GetAllRemoteAddresses() { HashSet addrs = new(); foreach(SockChatConnectionInfo conn in Connections) addrs.Add(conn.RemoteAddress); return addrs.ToArray(); } public string[] GetAuthedRemoteAddresses() { HashSet addrs = new(); foreach(SockChatConnectionInfo conn in AuthedConnections) addrs.Add(conn.RemoteAddress); return addrs.ToArray(); } public string[] GetUserRemoteAddresses(long userId) { if(!UserConnections.ContainsKey(userId)) return Array.Empty(); HashSet addrs = new(); foreach(SockChatConnectionInfo conn in UserConnections[userId]) addrs.Add(conn.RemoteAddress); return addrs.ToArray(); } public string[] GetUserRemoteAddresses(UserInfo userInfo) { return GetUserRemoteAddresses(userInfo.UserId); } public void Add(SockChatConnectionInfo conn) { if(Connections.Contains(conn)) return; Connections.Add(conn); if(conn.UserId > 0) { AuthedConnections.Add(conn); if(UserConnections.ContainsKey(conn.UserId)) UserConnections[conn.UserId].Add(conn); else UserConnections.Add(conn.UserId, new() { conn }); } } public void Remove(SockChatConnectionInfo conn) { if(Connections.Contains(conn)) Connections.Remove(conn); if(AuthedConnections.Contains(conn)) AuthedConnections.Remove(conn); if(conn.UserId > 0 && UserConnections.ContainsKey(conn.UserId)) { UserConnections[conn.UserId].Remove(conn); if(UserConnections[conn.UserId].Count < 1) UserConnections.Remove(conn.UserId); } } public void SetUser(SockChatConnectionInfo conn, long userId) { if(!Connections.Contains(conn)) return; // so yeah this is implemented but SetUserId will throw an exception if we're trying to re-auth a connection // will just leave this here but i'm not sure how to go forward with this if(conn.UserId > 0) { if(UserConnections[conn.UserId].Contains(conn)) UserConnections[conn.UserId].Remove(conn); if(UserConnections[conn.UserId].Count < 1) UserConnections.Remove(conn.UserId); } conn.SetUserId(userId); if(conn.UserId > 0) { if(UserConnections.ContainsKey(conn.UserId)) UserConnections[conn.UserId].Add(conn); else UserConnections.Add(conn.UserId, new() { conn }); } if(conn.UserId > 0) { if(!AuthedConnections.Contains(conn)) AuthedConnections.Add(conn); } else { if(AuthedConnections.Contains(conn)) AuthedConnections.Remove(conn); } } public void SetUser(SockChatConnectionInfo conn, UserInfo userInfo) { SetUser(conn, userInfo.UserId); } } }