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,4 +1,5 @@
using NLog; using Castle.DynamicProxy.Generators.Emitters.SimpleAST;
using NLog;
namespace ChatRoom; namespace ChatRoom;
@ -15,12 +16,20 @@ public class ChatRoomLogic
thread.Start(); thread.Start();
} }
int NextId()
{
int id = state.lastUniqueId;
state.lastUniqueId++;
return id;
}
public int RegisterClient(string name) public int RegisterClient(string name)
{ {
lock (state.accessLock) lock (state.accessLock)
{ {
int clientId = state.lastUniqueId; int clientId = NextId();
state.clients.Add(new Client{ state.clients.Add(new Client
{
id = clientId, id = clientId,
name = name 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() public void BackgroundTask()
{ {
while (true) 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; namespace ChatRoom;
using ChatRoomContract; public class ChatRoomService : ChatRoomContract.IChatRoomService
public class ChatRoomService : IChatRoomService
{ {
//NOTE: instance-per-request service would need logic to be static or injected from a singleton instance //NOTE: instance-per-request service would need logic to be static or injected from a singleton instance
private readonly ChatRoomLogic logic = new ChatRoomLogic(); private readonly ChatRoomLogic logic = new ChatRoomLogic();
@ -14,26 +12,26 @@ public class ChatRoomService : IChatRoomService
public void ApproveMessage(int messageId) 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) public int GetStrikes(int clientId)
{ {
throw new NotImplementedException(); return logic.GetStrikes(clientId);
} }
public void RejectMessage(int messageId) 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 int id;
public string name; public string name;
public int strikes = 0; 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 public class ChatRoomState
@ -20,4 +39,6 @@ public class ChatRoomState
public int lastUniqueId; public int lastUniqueId;
public List<Client> clients = new List<Client>(); 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> /// <param name="args">Command line arguments.</param>
private void StartServer(string[] args) private void StartServer(string[] args)
{ {
Console.Title = "Chat Room";
///create web app builder ///create web app builder
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);

View File

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

View File

@ -60,7 +60,23 @@ internal class Moderator
while (true) 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> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="NLog" Version="5.3.4" /> <PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="SimpleRpc" Version="1.0.0-beta1" /> <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.Serialization.Hyperion;
using SimpleRpc.Transports; using SimpleRpc.Transports;
using System.Diagnostics; using System.Diagnostics;
using Bogus;
namespace Participant; namespace Participant;
internal class 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> /// <summary>
/// Logger for this class. /// Logger for this class.
/// </summary> /// </summary>
@ -49,17 +36,27 @@ internal class Participant
private void RunConnection(IChatRoomService chatRoom) private void RunConnection(IChatRoomService chatRoom)
{ {
var faker = new Faker("en");
var rnd = new Random(); 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); int clientId = chatRoom.RegisterClient(name);
log.Info($"Registered with client id {clientId}"); log.Info($"Registered with client id {clientId}");
Console.Title = $"Participant | {name} | {clientId}";
while (true) 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> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="NLog" Version="5.3.4" /> <PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="SimpleRpc" Version="1.0.0-beta1" /> <PackageReference Include="SimpleRpc" Version="1.0.0-beta1" />