using Castle.DynamicProxy.Generators.Emitters.SimpleAST; using NLog; namespace ChatRoom; public class ChatRoomLogic { private Thread thread; private ChatRoomState state = new ChatRoomState(); private Logger log = LogManager.GetCurrentClassLogger(); public ChatRoomLogic() { thread = new Thread(BackgroundTask); thread.Start(); } int NextId() { int id = state.lastUniqueId; state.lastUniqueId++; return id; } public int RegisterClient(string name) { lock (state.accessLock) { int clientId = NextId(); state.clients.Add(new Client { id = clientId, name = name }); log.Info($"Registered with client '{name}' with id {clientId}"); return clientId; } } Client? FindClientById(int clientId) { foreach (var client in state.clients) { if (client.id == clientId) { return client; } } return null; } Message? FindMessageById(int messageId) { foreach (var message in state.messages) { if (message.id == messageId) { return message; } } return null; } bool GetAndUpdateBlockedState(Client client) { if (client.blockedUntil != null && DateTime.UtcNow >= client.blockedUntil) { client.blockedUntil = null; } return client.blockedUntil != null; } public bool SendMessage(int clientId, string contents, bool needsToBeCensored) { lock (state.accessLock) { var client = FindClientById(clientId); if (client == null) { return false; } if (GetAndUpdateBlockedState(client)) { return false; } var message = new Message { id = NextId(), clientId = clientId, contents = contents, needsToBeCensored = needsToBeCensored, status = MessageStatus.WaitingForModerator }; state.messages.Add(message); log.Info($"Client '{client.name}' ({client.id}) sent message '{contents}' ({message.id}). Needs to censored: {needsToBeCensored}"); } return true; } public ChatRoomContract.Message? GetNewMessage() { lock (state.accessLock) { foreach (var message in state.messages) { if (message.status != MessageStatus.WaitingForModerator) continue; log.Info($"Message '{message.id}' given to moderator"); message.status = MessageStatus.GivenToModerator; return new ChatRoomContract.Message{ id = message.id, contents = message.contents, needsToBeCensored = message.needsToBeCensored }; } } return null; } public void ApproveMessage(int messageId) { lock (state.accessLock) { var message = FindMessageById(messageId); if (message == null) { return; } if (message.status != MessageStatus.GivenToModerator) { return; } message.status = MessageStatus.Approved; log.Info($"Message {message.id} was approved"); } } public void RejectMessage(int messageId) { lock (state.accessLock) { var message = FindMessageById(messageId); if (message == null) { return; } if (message.status != MessageStatus.GivenToModerator) { return; } message.status = MessageStatus.Rejected; log.Info($"Message {message.id} was rejected"); var client = FindClientById(message.clientId); if (client != null && !GetAndUpdateBlockedState(client)) { client.strikes++; var rnd = new Random(); if (client.strikes > rnd.Next(0, 10)) { log.Info($"Client '{client.name}' ({client.id}) was blocked for {client.strikes}s"); client.blockedUntil = DateTime.UtcNow.AddSeconds(client.strikes); client.strikes = 0; } } } } public int GetStrikes(int clientId) { lock (state.accessLock) { var client = FindClientById(clientId); if (client == null) { return 0; } return client.strikes; } } public void BackgroundTask() { while (true) { lock (state.accessLock) { int count = state.messages.RemoveAll(msg => msg.status == MessageStatus.Approved || msg.status == MessageStatus.Rejected); log.Info($"Running periodic cleanup, removed {count} messages"); } Thread.Sleep(10 * 1000); } } }