1
0

Compare commits

...

4 Commits

Author SHA1 Message Date
02e35eac48 add rejection of messages 2024-09-15 15:33:04 +03:00
619e735fd5 add approving of messages 2024-09-15 15:09:33 +03:00
7f6283a815 add getting new messages 2024-09-15 14:59:08 +03:00
6c523af453 added sending of messages 2024-09-15 12:11:42 +03:00
9 changed files with 239 additions and 35 deletions

View File

@ -1,26 +1,35 @@
using NLog;
using Castle.DynamicProxy.Generators.Emitters.SimpleAST;
using NLog;
namespace ChatRoom;
public class ChatRoomLogic
{
private Thread thread;
private ChatRoomState state = new ChatRoomState();
private ChatRoomState state = new ChatRoomState();
private Logger log = LogManager.GetCurrentClassLogger();
public ChatRoomLogic()
{
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 = state.lastUniqueId;
state.clients.Add(new Client{
int clientId = NextId();
state.clients.Add(new Client
{
id = clientId,
name = name
});
@ -29,11 +38,170 @@ public class ChatRoomLogic
}
}
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);
}
}
}

View File

@ -1,8 +1,6 @@
namespace ChatRoom;
using ChatRoomContract;
public class ChatRoomService : IChatRoomService
public class ChatRoomService : ChatRoomContract.IChatRoomService
{
//NOTE: instance-per-request service would need logic to be static or injected from a singleton instance
private readonly ChatRoomLogic logic = new ChatRoomLogic();
@ -14,26 +12,26 @@ public class ChatRoomService : IChatRoomService
public void ApproveMessage(int messageId)
{
throw new NotImplementedException();
logic.ApproveMessage(messageId);
}
public Message? GetNewMessage()
public ChatRoomContract.Message? GetNewMessage()
{
throw new NotImplementedException();
return logic.GetNewMessage();
}
public int GetStrikes(int clientId)
{
throw new NotImplementedException();
return logic.GetStrikes(clientId);
}
public void RejectMessage(int messageId)
{
throw new NotImplementedException();
logic.RejectMessage(messageId);
}
public bool SendMessage(int clientId, string contents)
public bool SendMessage(int clientId, string contents, bool needsToBeCensored)
{
throw new NotImplementedException();
return logic.SendMessage(clientId, contents, needsToBeCensored);
}
}

View File

@ -5,6 +5,25 @@ public class Client
public int id;
public string name;
public int strikes = 0;
public DateTime? blockedUntil = null;
}
public enum MessageStatus
{
WaitingForModerator,
GivenToModerator,
Approved,
Rejected
}
public class Message
{
public int id;
public int clientId;
public string contents;
public bool needsToBeCensored;
public MessageStatus status = MessageStatus.WaitingForModerator;
}
public class ChatRoomState
@ -20,4 +39,6 @@ public class ChatRoomState
public int lastUniqueId;
public List<Client> clients = new List<Client>();
public List<Message> messages = new List<Message>();
}

View File

@ -66,6 +66,8 @@ public class Server
/// <param name="args">Command line arguments.</param>
private void StartServer(string[] args)
{
Console.Title = "Chat Room";
///create web app builder
var builder = WebApplication.CreateBuilder(args);

View File

@ -2,7 +2,7 @@
public class Message
{
int id;
public int id;
public string contents;
public bool needsToBeCensored;
}
@ -13,7 +13,7 @@ public interface IChatRoomService
int GetStrikes(int clientId);
bool SendMessage(int clientId, string contents);
bool SendMessage(int clientId, string contents, bool needsToBeCensored);
Message? GetNewMessage();

View File

@ -60,7 +60,23 @@ internal class Moderator
while (true)
{
var message = chatRoom.GetNewMessage();
if (message != null)
{
log.Info($"Checking message ({message.id}): {message.contents}");
Thread.Sleep(500);
if (message.needsToBeCensored)
{
chatRoom.RejectMessage(message.id);
}
else
{
chatRoom.ApproveMessage(message.id);
}
}
Thread.Sleep(1 * 1000);
}
}

View File

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="SimpleRpc" Version="1.0.0-beta1" />

View File

@ -5,25 +5,12 @@ using SimpleRpc.Transports.Http.Client;
using SimpleRpc.Serialization.Hyperion;
using SimpleRpc.Transports;
using System.Diagnostics;
using Bogus;
namespace Participant;
internal class Participant
{
private readonly List<string> FIRSTNAMES =
new List<string> {
"John", "Peter", "Jack", "Steve"
};
/// <summary>
/// A set of surnames to choose from.
/// </summary>
private readonly List<string> LASTNAMES =
new List<String> {
"Johnson", "Peterson", "Jackson", "Steveson"
};
/// <summary>
/// Logger for this class.
/// </summary>
@ -49,17 +36,27 @@ internal class Participant
private void RunConnection(IChatRoomService chatRoom)
{
var faker = new Faker("en");
var rnd = new Random();
var name = FIRSTNAMES[rnd.Next(FIRSTNAMES.Count)] + " " + LASTNAMES[rnd.Next(LASTNAMES.Count)];
var name = faker.Name.FullName();
int clientId = chatRoom.RegisterClient(name);
log.Info($"Registered with client id {clientId}");
Console.Title = $"Participant | {name} | {clientId}";
while (true)
{
int strikes = chatRoom.GetStrikes(clientId);
Console.Title = $"Participant | {name} | {clientId} | {strikes} Strikes";
var message = string.Join(" ", faker.Lorem.Words(5));
bool needsToBeCensored = rnd.Next(0, 100) > 50;
if (chatRoom.SendMessage(clientId, message, needsToBeCensored)) {
log.Info("Sent message");
} else {
log.Info("Failed to send message, blocked");
}
Thread.Sleep(2 * 1000);
}
}

View File

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="SimpleRpc" Version="1.0.0-beta1" />