add comments
This commit is contained in:
parent
bacde2dbbe
commit
5bf5699a51
@ -1,21 +1,38 @@
|
|||||||
using Castle.DynamicProxy.Generators.Emitters.SimpleAST;
|
using NLog;
|
||||||
using NLog;
|
|
||||||
|
|
||||||
namespace ChatRoom;
|
namespace ChatRoom;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chat room service logic
|
||||||
|
/// </summary>
|
||||||
public class ChatRoomLogic
|
public class ChatRoomLogic
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Background thread of deleting approved or rejected messages
|
||||||
|
/// </summary>
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
|
/// <summary>
|
||||||
|
/// Chat Room state
|
||||||
|
/// </summary>
|
||||||
private ChatRoomState state = new ChatRoomState();
|
private ChatRoomState state = new ChatRoomState();
|
||||||
|
/// <summary>
|
||||||
|
/// Logger
|
||||||
|
/// </summary>
|
||||||
private Logger log = LogManager.GetCurrentClassLogger();
|
private Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chat room logic constructor
|
||||||
|
/// </summary>
|
||||||
public ChatRoomLogic()
|
public ChatRoomLogic()
|
||||||
{
|
{
|
||||||
thread = new Thread(BackgroundTask);
|
thread = new Thread(BackgroundTask);
|
||||||
thread.Start();
|
thread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate the next incrementing ID
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Unique ID</returns>
|
||||||
int NextId()
|
int NextId()
|
||||||
{
|
{
|
||||||
int id = state.lastUniqueId;
|
int id = state.lastUniqueId;
|
||||||
@ -23,6 +40,11 @@ public class ChatRoomLogic
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a client
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Client name</param>
|
||||||
|
/// <returns>Client ID</returns>
|
||||||
public int RegisterClient(string name)
|
public int RegisterClient(string name)
|
||||||
{
|
{
|
||||||
lock (state.accessLock)
|
lock (state.accessLock)
|
||||||
@ -38,6 +60,11 @@ public class ChatRoomLogic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find a client by ID, can return NULL if not found
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Optional client object</returns>
|
||||||
Client? FindClientById(int clientId)
|
Client? FindClientById(int clientId)
|
||||||
{
|
{
|
||||||
foreach (var client in state.clients)
|
foreach (var client in state.clients)
|
||||||
@ -50,6 +77,11 @@ public class ChatRoomLogic
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find message by ID, can return NULL if not found
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
/// <returns>Optional message object</returns>
|
||||||
Message? FindMessageById(int messageId)
|
Message? FindMessageById(int messageId)
|
||||||
{
|
{
|
||||||
foreach (var message in state.messages)
|
foreach (var message in state.messages)
|
||||||
@ -62,6 +94,11 @@ public class ChatRoomLogic
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if client is still blocked, will clear `blockedUntil` if client became unblocked
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client">Client object</param>
|
||||||
|
/// <returns>Is client blocked?</returns>
|
||||||
bool GetAndUpdateBlockedState(Client client)
|
bool GetAndUpdateBlockedState(Client client)
|
||||||
{
|
{
|
||||||
if (client.blockedUntil != null && DateTime.UtcNow >= client.blockedUntil)
|
if (client.blockedUntil != null && DateTime.UtcNow >= client.blockedUntil)
|
||||||
@ -72,6 +109,13 @@ public class ChatRoomLogic
|
|||||||
return client.blockedUntil != null;
|
return client.blockedUntil != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Send a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <param name="contents">Message contents</param>
|
||||||
|
/// <param name="needsToBeCensored">Does this message need to be censored?</param>
|
||||||
|
/// <returns>Was sending the message successful, can fail if client is blocked</returns>
|
||||||
public bool SendMessage(int clientId, string contents, bool needsToBeCensored)
|
public bool SendMessage(int clientId, string contents, bool needsToBeCensored)
|
||||||
{
|
{
|
||||||
lock (state.accessLock)
|
lock (state.accessLock)
|
||||||
@ -93,7 +137,8 @@ public class ChatRoomLogic
|
|||||||
clientId = clientId,
|
clientId = clientId,
|
||||||
contents = contents,
|
contents = contents,
|
||||||
needsToBeCensored = needsToBeCensored,
|
needsToBeCensored = needsToBeCensored,
|
||||||
status = MessageStatus.WaitingForModerator
|
status = MessageStatus.WaitingForModerator,
|
||||||
|
sentAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
state.messages.Add(message);
|
state.messages.Add(message);
|
||||||
log.Info($"Client '{client.name}' ({client.id}) sent message '{contents}' ({message.id}). Needs to censored: {needsToBeCensored}");
|
log.Info($"Client '{client.name}' ({client.id}) sent message '{contents}' ({message.id}). Needs to censored: {needsToBeCensored}");
|
||||||
@ -102,6 +147,10 @@ public class ChatRoomLogic
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get next message which isin't approved or rejected
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Optional message object</returns>
|
||||||
public ChatRoomContract.Message? GetNewMessage()
|
public ChatRoomContract.Message? GetNewMessage()
|
||||||
{
|
{
|
||||||
lock (state.accessLock)
|
lock (state.accessLock)
|
||||||
@ -123,6 +172,10 @@ public class ChatRoomLogic
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
public void ApproveMessage(int messageId)
|
public void ApproveMessage(int messageId)
|
||||||
{
|
{
|
||||||
lock (state.accessLock)
|
lock (state.accessLock)
|
||||||
@ -143,6 +196,10 @@ public class ChatRoomLogic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
public void RejectMessage(int messageId)
|
public void RejectMessage(int messageId)
|
||||||
{
|
{
|
||||||
lock (state.accessLock)
|
lock (state.accessLock)
|
||||||
@ -177,6 +234,11 @@ public class ChatRoomLogic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get number of strikes a client has
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Number of strikes</returns>
|
||||||
public int GetStrikes(int clientId)
|
public int GetStrikes(int clientId)
|
||||||
{
|
{
|
||||||
lock (state.accessLock)
|
lock (state.accessLock)
|
||||||
@ -191,13 +253,39 @@ public class ChatRoomLogic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get timestamp until when the client is blocked
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Optional datetime object</returns>
|
||||||
|
public DateTime? GetBlockedUntil(int clientId)
|
||||||
|
{
|
||||||
|
lock (state.accessLock)
|
||||||
|
{
|
||||||
|
var client = FindClientById(clientId);
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.blockedUntil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Main loop for background thread. Used for deleting approved or rejected messages.
|
||||||
|
/// </summary>
|
||||||
public void BackgroundTask()
|
public void BackgroundTask()
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
lock (state.accessLock)
|
lock (state.accessLock)
|
||||||
{
|
{
|
||||||
int count = state.messages.RemoveAll(msg => msg.status == MessageStatus.Approved || msg.status == MessageStatus.Rejected);
|
var now = DateTime.UtcNow;
|
||||||
|
int count = state.messages.RemoveAll(msg =>
|
||||||
|
(now - msg.sentAt).TotalSeconds > 5 &&
|
||||||
|
(msg.status == MessageStatus.Approved || msg.status == MessageStatus.Rejected)
|
||||||
|
);
|
||||||
log.Info($"Running periodic cleanup, removed {count} messages");
|
log.Info($"Running periodic cleanup, removed {count} messages");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
namespace ChatRoom;
|
|
||||||
|
namespace ChatRoom;
|
||||||
|
|
||||||
public class ChatRoomService : ChatRoomContract.IChatRoomService
|
public class ChatRoomService : ChatRoomContract.IChatRoomService
|
||||||
{
|
{
|
||||||
@ -34,4 +35,9 @@ public class ChatRoomService : ChatRoomContract.IChatRoomService
|
|||||||
{
|
{
|
||||||
return logic.SendMessage(clientId, contents, needsToBeCensored);
|
return logic.SendMessage(clientId, contents, needsToBeCensored);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DateTime? GetBlockedUntil(int clientId)
|
||||||
|
{
|
||||||
|
return logic.GetBlockedUntil(clientId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,31 @@
|
|||||||
namespace ChatRoom;
|
namespace ChatRoom;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Client information
|
||||||
|
/// </summary>
|
||||||
public class Client
|
public class Client
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client ID
|
||||||
|
/// </summary>
|
||||||
public int id;
|
public int id;
|
||||||
|
/// <summary>
|
||||||
|
/// Client name, can be the same between multiple clients
|
||||||
|
/// </summary>
|
||||||
public string name;
|
public string name;
|
||||||
|
/// <summary>
|
||||||
|
/// Number of strikes
|
||||||
|
/// </summary>
|
||||||
public int strikes = 0;
|
public int strikes = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// Until when is this client blocked from sending messages
|
||||||
|
/// </summary>
|
||||||
public DateTime? blockedUntil = null;
|
public DateTime? blockedUntil = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes the messages status/stage
|
||||||
|
/// </summary>
|
||||||
public enum MessageStatus
|
public enum MessageStatus
|
||||||
{
|
{
|
||||||
WaitingForModerator,
|
WaitingForModerator,
|
||||||
@ -17,13 +34,36 @@ public enum MessageStatus
|
|||||||
Rejected
|
Rejected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Message information
|
||||||
|
/// </summary>
|
||||||
public class Message
|
public class Message
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Message ID
|
||||||
|
/// </summary>
|
||||||
public int id;
|
public int id;
|
||||||
|
/// <summary>
|
||||||
|
/// Client ID
|
||||||
|
/// </summary>
|
||||||
public int clientId;
|
public int clientId;
|
||||||
|
/// <summary>
|
||||||
|
/// Message contents
|
||||||
|
/// </summary>
|
||||||
public string contents;
|
public string contents;
|
||||||
|
/// <summary>
|
||||||
|
/// Does this message need to be censored?
|
||||||
|
/// </summary>
|
||||||
public bool needsToBeCensored;
|
public bool needsToBeCensored;
|
||||||
|
/// <summary>
|
||||||
|
/// Message status/stage
|
||||||
|
/// </summary>
|
||||||
public MessageStatus status = MessageStatus.WaitingForModerator;
|
public MessageStatus status = MessageStatus.WaitingForModerator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When was this message sent
|
||||||
|
/// </summary>
|
||||||
|
public DateTime sentAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ChatRoomState
|
public class ChatRoomState
|
||||||
@ -38,7 +78,13 @@ public class ChatRoomState
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int lastUniqueId;
|
public int lastUniqueId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of all registered clients
|
||||||
|
/// </summary>
|
||||||
public List<Client> clients = new List<Client>();
|
public List<Client> clients = new List<Client>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of messages
|
||||||
|
/// </summary>
|
||||||
public List<Message> messages = new List<Message>();
|
public List<Message> messages = new List<Message>();
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,74 @@
|
|||||||
namespace ChatRoomContract;
|
namespace ChatRoomContract;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minimal message description
|
||||||
|
/// </summary>
|
||||||
public class Message
|
public class Message
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Message ID
|
||||||
|
/// </summary>
|
||||||
public int id;
|
public int id;
|
||||||
|
/// <summary>
|
||||||
|
/// Message contents
|
||||||
|
/// </summary>
|
||||||
public string contents;
|
public string contents;
|
||||||
|
/// <summary>
|
||||||
|
/// Does this message need to be censored?
|
||||||
|
/// </summary>
|
||||||
public bool needsToBeCensored;
|
public bool needsToBeCensored;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chat room service contract
|
||||||
|
/// </summary>
|
||||||
public interface IChatRoomService
|
public interface IChatRoomService
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Register client with a name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of client, can be duplicate between clients</param>
|
||||||
|
/// <returns>Client ID</returns>
|
||||||
int RegisterClient(string name);
|
int RegisterClient(string name);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get number of strikes a participant has
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Number of strikes</returns>
|
||||||
int GetStrikes(int clientId);
|
int GetStrikes(int clientId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get timestamp until when the client is blocked
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Optional datetime object</returns>
|
||||||
|
DateTime? GetBlockedUntil(int clientId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Send a message, will be given to a moderator to be approved
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <param name="contents">Message contents</param>
|
||||||
|
/// <param name="needsToBeCensored">Does this message need to be censored?</param>
|
||||||
|
/// <returns>Was sending successful, can fail if user is blocked</returns>
|
||||||
bool SendMessage(int clientId, string contents, bool needsToBeCensored);
|
bool SendMessage(int clientId, string contents, bool needsToBeCensored);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next message which hasn't been approved or rejected
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Message object. Returns null if there is no message</returns>
|
||||||
Message? GetNewMessage();
|
Message? GetNewMessage();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
void RejectMessage(int messageId);
|
void RejectMessage(int messageId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
void ApproveMessage(int messageId);
|
void ApproveMessage(int messageId);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user