complete lab4
This commit is contained in:
parent
c88b6ac234
commit
ac702ce8f3
12
Lab1/ChatRoom/Properties/launchSettings.json
Normal file
12
Lab1/ChatRoom/Properties/launchSettings.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"ChatRoom": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:65375;http://localhost:65376"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Lab2-grpc/ChatRoom/Properties/launchSettings.json
Normal file
12
Lab2-grpc/ChatRoom/Properties/launchSettings.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"ChatRoom": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:56849;http://localhost:56850"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,13 +16,13 @@
|
|||||||
|
|
||||||
<!-- This will only work if server is running. It needs access to http://localhost:5000/swagger/v1/swagger.json . -->
|
<!-- This will only work if server is running. It needs access to http://localhost:5000/swagger/v1/swagger.json . -->
|
||||||
<Target Name="NSwag" BeforeTargets="BeforeBuild">
|
<Target Name="NSwag" BeforeTargets="BeforeBuild">
|
||||||
<Exec
|
<Exec
|
||||||
Command="$(NSwagExe_Net60) run nswag.json /variables:Configuration=$(Configuration),OutDir=$(OutDir)"
|
Command="$(NSwagExe_Net60) run nswag.json /variables:Configuration=$(Configuration),OutDir=$(OutDir)"
|
||||||
IgnoreExitCode="true"
|
IgnoreExitCode="true"
|
||||||
/>
|
/>
|
||||||
<!-- See 'https://stackoverflow.com/questions/44818730/is-there-a-net-core-cli-pre-before-build-task' as to why the following is necessary-->
|
<!-- See 'https://stackoverflow.com/questions/44818730/is-there-a-net-core-cli-pre-before-build-task' as to why the following is necessary-->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile
|
<Compile
|
||||||
Include="**/*$(DefaultLanguageSourceExtension)"
|
Include="**/*$(DefaultLanguageSourceExtension)"
|
||||||
Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);$(BaseIntermediateOutputPath)**;$(BaseOutputPath)**;@(Compile)" />
|
Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);$(BaseIntermediateOutputPath)**;$(BaseOutputPath)**;@(Compile)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
104
Lab4/.vscode/launch.json
vendored
Normal file
104
Lab4/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "ChatRoom",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-ChatRoom",
|
||||||
|
"program": "${workspaceFolder}/ChatRoom/bin/Debug/net6.0/ChatRoom.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/ChatRoom",
|
||||||
|
"console": "externalTerminal",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ModeratorGrpc",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-ModeratorGrpc",
|
||||||
|
"program": "${workspaceFolder}/ModeratorGrpc/bin/Debug/net6.0/ModeratorGrpc.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/ModeratorGrpc",
|
||||||
|
"console": "externalTerminal",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ParticipantGrpc",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-ParticipantGrpc",
|
||||||
|
"program": "${workspaceFolder}/ParticipantGrpc/bin/Debug/net6.0/ParticipantGrpc.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/ParticipantGrpc",
|
||||||
|
"console": "externalTerminal",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ModeratorRest",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-ModeratorRest",
|
||||||
|
"program": "${workspaceFolder}/ModeratorRest/bin/Debug/net6.0/ModeratorRest.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/ModeratorRest",
|
||||||
|
"console": "externalTerminal",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ParticipantRest",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-ParticipantRest",
|
||||||
|
"program": "${workspaceFolder}/ParticipantRest/bin/Debug/net6.0/ParticipantRest.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/ParticipantRest",
|
||||||
|
"console": "externalTerminal",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ModeratorSimpleRPC",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-ModeratorSimpleRPC",
|
||||||
|
"program": "${workspaceFolder}/ModeratorSimpleRPC/bin/Debug/net6.0/ModeratorSimpleRPC.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/ModeratorSimpleRPC",
|
||||||
|
"console": "externalTerminal",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ParticipantSimpleRPC",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-ParticipantSimpleRPC",
|
||||||
|
"program": "${workspaceFolder}/ParticipantSimpleRPC/bin/Debug/net6.0/ParticipantSimpleRPC.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/ParticipantSimpleRPC",
|
||||||
|
"console": "externalTerminal",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ModeratorRabbitMQ",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-ModeratorRabbitMQ",
|
||||||
|
"program": "${workspaceFolder}/ModeratorRabbitMQ/bin/Debug/net6.0/ModeratorRabbitMQ.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/ModeratorRabbitMQ",
|
||||||
|
"console": "externalTerminal",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ParticipantRabbitMQ",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-ParticipantRabbitMQ",
|
||||||
|
"program": "${workspaceFolder}/ParticipantRabbitMQ/bin/Debug/net6.0/ParticipantRabbitMQ.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/ParticipantRabbitMQ",
|
||||||
|
"console": "externalTerminal",
|
||||||
|
"stopAtEntry": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
113
Lab4/.vscode/tasks.json
vendored
Normal file
113
Lab4/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build-ChatRoom",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/ChatRoom/ChatRoom.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-ModeratorGrpc",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/ModeratorRest/ModeratorRest.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-ParticipantRest",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/ParticipantRest/ParticipantRest.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-ModeratorGrpc",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/ModeratorGrpc/ModeratorGrpc.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-ParticipantGrpc",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/ParticipantGrpc/ParticipantGrpc.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-ModeratorSimpleRPC",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/ModeratorSimpleRPC/ModeratorSimpleRPC.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-ParticipantSimpleRPC",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/ParticipantSimpleRPC/ParticipantSimpleRPC.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-ModeratorRabbitMQ",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/ModeratorRabbitMQ/ModeratorRabbitMQ.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-ParticipantRabbitMQ",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/ParticipantRabbitMQ/ParticipantRabbitMQ.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
22
Lab4/ChatRoom/ChatRoom.csproj
Normal file
22
Lab4/ChatRoom/ChatRoom.csproj
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Grpc.AspNetCore" Version="2.67.0" />
|
||||||
|
<PackageReference Include="NLog" Version="5.3.4" />
|
||||||
|
<PackageReference Include="SimpleRpc" Version="1.0.0-beta1" />
|
||||||
|
<PackageReference Include="SimpleRpc.Serialization.Hyperion" Version="1.0.0-beta1" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
113
Lab4/ChatRoom/ChatRoomGrpcService.cs
Normal file
113
Lab4/ChatRoom/ChatRoomGrpcService.cs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
using Grpc.Core;
|
||||||
|
using ChatRoomContract.Protocol;
|
||||||
|
|
||||||
|
namespace ChatRoom;
|
||||||
|
|
||||||
|
public class ChatRoomGrpcService : ChatRoomContract.Protocol.ChatRoom.ChatRoomBase
|
||||||
|
{
|
||||||
|
private readonly ChatRoomLogic logic;
|
||||||
|
public ChatRoomGrpcService(ChatRoomLogic logic)
|
||||||
|
{
|
||||||
|
this.logic = logic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register client with a name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Name of client.</param>
|
||||||
|
/// <param name="context">Call context.</param>
|
||||||
|
/// <returns>Client ID</returns>
|
||||||
|
public override Task<ClientId> RegisterClient(RegisterClientRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var result = new ClientId { Id = logic.RegisterClient(request.Name) };
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get number of strikes a participant has
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Client ID</param>
|
||||||
|
/// <param name="context">Call context.</param>
|
||||||
|
/// <returns>Number of strikes</returns>
|
||||||
|
public override Task<Srikes> GetStrikes(ClientId request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var result = new Srikes { Strikes = logic.GetStrikes(request.Id) };
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get timestamp until when the client is blocked
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Client ID</param>
|
||||||
|
/// <param name="context">Call context.</param>
|
||||||
|
/// <returns>Optional datetime object</returns>
|
||||||
|
public override Task<BlockedUntil> GetBlockedUntil(ClientId request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var timestamp = logic.GetBlockedUntil(request.Id);
|
||||||
|
var result = new BlockedUntil { HasTimestamp = false };
|
||||||
|
if (timestamp != null)
|
||||||
|
{
|
||||||
|
result.HasTimestamp = true;
|
||||||
|
result.Timestamp = Timestamp.FromDateTime(timestamp.Value);
|
||||||
|
}
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Send a message, will be given to a moderator to be approved
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Message details</param>
|
||||||
|
/// <param name="context">Call context.</param>
|
||||||
|
/// <returns>Was sending successful, can fail if user is blocked</returns>
|
||||||
|
public override Task<BoolResponse> SendMessage(UserMessageRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var success = logic.SendMessage(request.ClientId, request.Contents, request.NeedsToBeCensored);
|
||||||
|
var result = new BoolResponse { Success = success };
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next message which hasn't been approved or rejected
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Empty</param>
|
||||||
|
/// <param name="context">Call context.</param>
|
||||||
|
/// <returns>Message object. Returns null if there is no message</returns>
|
||||||
|
public override Task<NewUserMessage> GetNewMessage(Empty request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var message = logic.GetNewMessage();
|
||||||
|
var result = new NewUserMessage { HasMessage = false, Message = new UserMessage() };
|
||||||
|
if (message != null)
|
||||||
|
{
|
||||||
|
result.HasMessage = true;
|
||||||
|
result.Message.Id = message.id;
|
||||||
|
result.Message.Contents = message.contents;
|
||||||
|
result.Message.NeedsToBeCensored = message.needsToBeCensored;
|
||||||
|
}
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Message ID</param>
|
||||||
|
/// <param name="context">Call context.</param>
|
||||||
|
/// <returns>Empty</returns>
|
||||||
|
public override Task<Empty> ApproveMessage(MessageId request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
logic.ApproveMessage(request.Id);
|
||||||
|
return Task.FromResult(new Empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Message ID</param>
|
||||||
|
/// <param name="context">Call context.</param>
|
||||||
|
/// <returns>Empty</returns>
|
||||||
|
public override Task<Empty> RejectMessage(MessageId request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
logic.RejectMessage(request.Id);
|
||||||
|
return Task.FromResult(new Empty());
|
||||||
|
}
|
||||||
|
}
|
296
Lab4/ChatRoom/ChatRoomLogic.cs
Normal file
296
Lab4/ChatRoom/ChatRoomLogic.cs
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace ChatRoom;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chat room service logic
|
||||||
|
/// </summary>
|
||||||
|
public class ChatRoomLogic
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Background thread of deleting approved or rejected messages
|
||||||
|
/// </summary>
|
||||||
|
private Thread thread;
|
||||||
|
/// <summary>
|
||||||
|
/// Chat Room state
|
||||||
|
/// </summary>
|
||||||
|
private ChatRoomState state = new ChatRoomState();
|
||||||
|
/// <summary>
|
||||||
|
/// Logger
|
||||||
|
/// </summary>
|
||||||
|
private Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chat room logic constructor
|
||||||
|
/// </summary>
|
||||||
|
public ChatRoomLogic()
|
||||||
|
{
|
||||||
|
thread = new Thread(BackgroundTask);
|
||||||
|
thread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate the next incrementing ID
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Unique ID</returns>
|
||||||
|
int NextId()
|
||||||
|
{
|
||||||
|
int id = state.lastUniqueId;
|
||||||
|
state.lastUniqueId++;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a client
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Client name</param>
|
||||||
|
/// <returns>Client ID</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
foreach (var client in state.clients)
|
||||||
|
{
|
||||||
|
if (client.id == clientId)
|
||||||
|
{
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
foreach (var message in state.messages)
|
||||||
|
{
|
||||||
|
if (message.id == messageId)
|
||||||
|
{
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (client.blockedUntil != null && DateTime.UtcNow >= client.blockedUntil)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
sentAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
state.messages.Add(message);
|
||||||
|
log.Info($"Client '{client.name}' ({client.id}) sent message '{contents}' ({message.id}). Needs to censored: {needsToBeCensored}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get next message which isin't approved or rejected
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Optional message object</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
lock (state.accessLock)
|
||||||
|
{
|
||||||
|
var client = FindClientById(clientId);
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.strikes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
lock (state.accessLock)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(10 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
159
Lab4/ChatRoom/ChatRoomRabbitMQService.cs
Normal file
159
Lab4/ChatRoom/ChatRoomRabbitMQService.cs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using RabbitMQ.Client.Events;
|
||||||
|
using ChatRoomContract;
|
||||||
|
using System.Text;
|
||||||
|
using MessagePack;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using NLog.LayoutRenderers.Wrappers;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace ChatRoom;
|
||||||
|
|
||||||
|
internal class ChatRoomRabbitMQService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Logger for this class.
|
||||||
|
/// </summary>
|
||||||
|
private Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Communications channel to RabbitMQ message broker.
|
||||||
|
/// </summary>
|
||||||
|
private IModel rmqChannel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service logic.
|
||||||
|
/// </summary>
|
||||||
|
private ChatRoomLogic logic;
|
||||||
|
|
||||||
|
public ChatRoomRabbitMQService(ChatRoomLogic logic, IConnection connection, string exchangeName, string serverQueueName)
|
||||||
|
{
|
||||||
|
this.logic = logic;
|
||||||
|
|
||||||
|
//get channel, configure exchanges and request queue
|
||||||
|
rmqChannel = connection.CreateModel();
|
||||||
|
|
||||||
|
rmqChannel.ExchangeDeclare(exchange: exchangeName, type: ExchangeType.Direct);
|
||||||
|
rmqChannel.QueueDeclare(queue: serverQueueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
|
||||||
|
rmqChannel.QueueBind(queue: serverQueueName, exchange: exchangeName, routingKey: serverQueueName, arguments: null);
|
||||||
|
|
||||||
|
//connect to the queue as consumer
|
||||||
|
//XXX: see https://www.rabbitmq.com/dotnet-api-guide.html#concurrency for threading issues
|
||||||
|
var rmqConsumer = new EventingBasicConsumer(rmqChannel);
|
||||||
|
rmqConsumer.Received += (consumer, delivery) => OnMessageReceived(((EventingBasicConsumer)consumer).Model, delivery);
|
||||||
|
rmqChannel.BasicConsume(queue: serverQueueName, autoAck: true, consumer: rmqConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is invoked to process messages received.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="channel">Related communications channel.</param>
|
||||||
|
/// <param name="msgIn">Message deliver data.</param>
|
||||||
|
private void OnMessageReceived(IModel channel, BasicDeliverEventArgs msgIn)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var msg = MessagePackSerializer.Deserialize<RPCMessage>(msgIn.Body);
|
||||||
|
Debug.Assert(msg != null);
|
||||||
|
|
||||||
|
if (msg.isResponse == true)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasResponse = false;
|
||||||
|
byte[]? response = null;
|
||||||
|
|
||||||
|
switch (msg.method)
|
||||||
|
{
|
||||||
|
case nameof(IChatRoomService.RegisterClient):
|
||||||
|
{
|
||||||
|
var name = MessagePackSerializer.Deserialize<string>(msg.args);
|
||||||
|
var clientId = logic.RegisterClient(name);
|
||||||
|
response = MessagePackSerializer.Serialize(clientId);
|
||||||
|
hasResponse = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case nameof(IChatRoomService.GetStrikes):
|
||||||
|
{
|
||||||
|
var clientId = MessagePackSerializer.Deserialize<int>(msg.args);
|
||||||
|
var strikes = logic.GetStrikes(clientId);
|
||||||
|
response = MessagePackSerializer.Serialize(strikes);
|
||||||
|
hasResponse = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case nameof(IChatRoomService.GetBlockedUntil):
|
||||||
|
{
|
||||||
|
var clientId = MessagePackSerializer.Deserialize<int>(msg.args);
|
||||||
|
var blockedUntil = logic.GetBlockedUntil(clientId);
|
||||||
|
response = MessagePackSerializer.Serialize(blockedUntil);
|
||||||
|
hasResponse = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case nameof(IChatRoomService.SendMessage):
|
||||||
|
{
|
||||||
|
var args = MessagePackSerializer.Deserialize<SendMessageArgs>(msg.args);
|
||||||
|
var success = logic.SendMessage(args.clientId, args.contents, args.needsToBeCensored);
|
||||||
|
response = MessagePackSerializer.Serialize(success);
|
||||||
|
hasResponse = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case nameof(IChatRoomService.GetNewMessage):
|
||||||
|
{
|
||||||
|
var newMessage = logic.GetNewMessage();
|
||||||
|
response = MessagePackSerializer.Serialize(newMessage);
|
||||||
|
hasResponse = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case nameof(IChatRoomService.RejectMessage):
|
||||||
|
{
|
||||||
|
var messageId = MessagePackSerializer.Deserialize<int>(msg.args);
|
||||||
|
logic.ApproveMessage(messageId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case nameof(IChatRoomService.ApproveMessage):
|
||||||
|
{
|
||||||
|
var messageId = MessagePackSerializer.Deserialize<int>(msg.args);
|
||||||
|
logic.ApproveMessage(messageId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new Exception("Unknown RPC method");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasResponse)
|
||||||
|
{
|
||||||
|
var responseMsg = new RPCMessage
|
||||||
|
{
|
||||||
|
isResponse = true,
|
||||||
|
method = msg.method,
|
||||||
|
args = response
|
||||||
|
};
|
||||||
|
|
||||||
|
var properties = channel.CreateBasicProperties();
|
||||||
|
properties.CorrelationId = msgIn.BasicProperties.CorrelationId;
|
||||||
|
|
||||||
|
channel.BasicPublish(
|
||||||
|
exchange: msgIn.Exchange,
|
||||||
|
routingKey: msgIn.BasicProperties.ReplyTo,
|
||||||
|
basicProperties: properties,
|
||||||
|
body: MessagePackSerializer.Serialize(responseMsg)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.Error(e, "Unhandled exception caught when processing a message. The message is now lost.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
103
Lab4/ChatRoom/ChatRoomRestController.cs
Normal file
103
Lab4/ChatRoom/ChatRoomRestController.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace ChatRoom;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service. Class must be marked public, otherwise ASP.NET core runtime will not find it.
|
||||||
|
///
|
||||||
|
/// Look into FromXXX attributes if you need to map inputs to custom parts of HTTP request.
|
||||||
|
/// </summary>
|
||||||
|
[Route("chatRoom")]
|
||||||
|
[ApiController]
|
||||||
|
public class ChatRoomRestController : ControllerBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Service logic. This is created in Server.StartServer() and received through DI in constructor.
|
||||||
|
/// </summary>
|
||||||
|
private readonly ChatRoomLogic logic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logic">Logic to use. This will get passed through DI.</param>
|
||||||
|
public ChatRoomRestController(ChatRoomLogic logic)
|
||||||
|
{
|
||||||
|
this.logic = logic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register client with a name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of client, can be duplicate between clients</param>
|
||||||
|
/// <returns>Client ID</returns>
|
||||||
|
[HttpPost("/registerClient")]
|
||||||
|
public ActionResult<int> RegisterClient(string name)
|
||||||
|
{
|
||||||
|
return logic.RegisterClient(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get number of strikes a participant has
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Number of strikes</returns>
|
||||||
|
[HttpGet("/getStrikes")]
|
||||||
|
public ActionResult<int> GetStrikes(int clientId)
|
||||||
|
{
|
||||||
|
return logic.GetStrikes(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get timestamp until when the client is blocked
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Optional datetime object</returns>
|
||||||
|
[HttpGet("/getBlockedUntil")]
|
||||||
|
public ActionResult<DateTime?> GetBlockedUntil(int clientId)
|
||||||
|
{
|
||||||
|
return logic.GetBlockedUntil(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>
|
||||||
|
[HttpPost("/sendMessage")]
|
||||||
|
public ActionResult<bool> SendMessage(int clientId, string contents, bool needsToBeCensored)
|
||||||
|
{
|
||||||
|
return logic.SendMessage(clientId, contents, 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>
|
||||||
|
[HttpGet("/getNewMessage")]
|
||||||
|
public ActionResult<ChatRoomContract.Message?> GetNewMessage()
|
||||||
|
{
|
||||||
|
return logic.GetNewMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
[HttpPost("/rejectMessage")]
|
||||||
|
public void RejectMessage(int messageId)
|
||||||
|
{
|
||||||
|
logic.RejectMessage(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
[HttpPost("/approveMessage")]
|
||||||
|
public void ApproveMessage(int messageId)
|
||||||
|
{
|
||||||
|
logic.ApproveMessage(messageId);
|
||||||
|
}
|
||||||
|
}
|
46
Lab4/ChatRoom/ChatRoomSimpleRPCService.cs
Normal file
46
Lab4/ChatRoom/ChatRoomSimpleRPCService.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
namespace ChatRoom;
|
||||||
|
|
||||||
|
public class ChatRoomSimpleRPCService : ChatRoomContract.IChatRoomService
|
||||||
|
{
|
||||||
|
private readonly ChatRoomLogic logic;
|
||||||
|
|
||||||
|
public ChatRoomSimpleRPCService(ChatRoomLogic logic)
|
||||||
|
{
|
||||||
|
this.logic = logic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RegisterClient(string name)
|
||||||
|
{
|
||||||
|
return logic.RegisterClient(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApproveMessage(int messageId)
|
||||||
|
{
|
||||||
|
logic.ApproveMessage(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChatRoomContract.Message? GetNewMessage()
|
||||||
|
{
|
||||||
|
return logic.GetNewMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetStrikes(int clientId)
|
||||||
|
{
|
||||||
|
return logic.GetStrikes(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RejectMessage(int messageId)
|
||||||
|
{
|
||||||
|
logic.RejectMessage(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SendMessage(int clientId, string contents, bool needsToBeCensored)
|
||||||
|
{
|
||||||
|
return logic.SendMessage(clientId, contents, needsToBeCensored);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime? GetBlockedUntil(int clientId)
|
||||||
|
{
|
||||||
|
return logic.GetBlockedUntil(clientId);
|
||||||
|
}
|
||||||
|
}
|
90
Lab4/ChatRoom/ChatRoomState.cs
Normal file
90
Lab4/ChatRoom/ChatRoomState.cs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
namespace ChatRoom;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Client information
|
||||||
|
/// </summary>
|
||||||
|
public class Client
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client ID
|
||||||
|
/// </summary>
|
||||||
|
public int id;
|
||||||
|
/// <summary>
|
||||||
|
/// Client name, can be the same between multiple clients
|
||||||
|
/// </summary>
|
||||||
|
public string name;
|
||||||
|
/// <summary>
|
||||||
|
/// Number of strikes
|
||||||
|
/// </summary>
|
||||||
|
public int strikes = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// Until when is this client blocked from sending messages
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? blockedUntil = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes the messages status/stage
|
||||||
|
/// </summary>
|
||||||
|
public enum MessageStatus
|
||||||
|
{
|
||||||
|
WaitingForModerator,
|
||||||
|
GivenToModerator,
|
||||||
|
Approved,
|
||||||
|
Rejected
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Message information
|
||||||
|
/// </summary>
|
||||||
|
public class Message
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Message ID
|
||||||
|
/// </summary>
|
||||||
|
public int id;
|
||||||
|
/// <summary>
|
||||||
|
/// Client ID
|
||||||
|
/// </summary>
|
||||||
|
public int clientId;
|
||||||
|
/// <summary>
|
||||||
|
/// Message contents
|
||||||
|
/// </summary>
|
||||||
|
public string contents;
|
||||||
|
/// <summary>
|
||||||
|
/// Does this message need to be censored?
|
||||||
|
/// </summary>
|
||||||
|
public bool needsToBeCensored;
|
||||||
|
/// <summary>
|
||||||
|
/// Message status/stage
|
||||||
|
/// </summary>
|
||||||
|
public MessageStatus status = MessageStatus.WaitingForModerator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When was this message sent
|
||||||
|
/// </summary>
|
||||||
|
public DateTime sentAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChatRoomState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Access lock.
|
||||||
|
/// </summary>
|
||||||
|
public readonly object accessLock = new object();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Last unique ID value generated.
|
||||||
|
/// </summary>
|
||||||
|
public int lastUniqueId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of all registered clients
|
||||||
|
/// </summary>
|
||||||
|
public List<Client> clients = new List<Client>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of messages
|
||||||
|
/// </summary>
|
||||||
|
public List<Message> messages = new List<Message>();
|
||||||
|
}
|
12
Lab4/ChatRoom/Properties/launchSettings.json
Normal file
12
Lab4/ChatRoom/Properties/launchSettings.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"ChatRoom": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:65375;http://localhost:65376"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
197
Lab4/ChatRoom/Server.cs
Normal file
197
Lab4/ChatRoom/Server.cs
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
using System.Net;
|
||||||
|
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
using SimpleRpc.Transports;
|
||||||
|
using SimpleRpc.Transports.Http.Server;
|
||||||
|
using SimpleRpc.Serialization.Hyperion;
|
||||||
|
using ChatRoomContract;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
|
|
||||||
|
namespace ChatRoom;
|
||||||
|
|
||||||
|
public class Server
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Logger for this class.
|
||||||
|
/// </summary>
|
||||||
|
Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configure loggin subsystem.
|
||||||
|
/// </summary>
|
||||||
|
private void ConfigureLogging()
|
||||||
|
{
|
||||||
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
|
||||||
|
var console =
|
||||||
|
new NLog.Targets.ConsoleTarget("console")
|
||||||
|
{
|
||||||
|
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
|
||||||
|
};
|
||||||
|
config.AddTarget(console);
|
||||||
|
config.AddRuleForAllLevels(console);
|
||||||
|
|
||||||
|
LogManager.Configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Program entry point.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">Command line arguments.</param>
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var self = new Server();
|
||||||
|
self.Run(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Program body.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">Command line arguments.</param>
|
||||||
|
private void Run(string[] args)
|
||||||
|
{
|
||||||
|
//configure logging
|
||||||
|
ConfigureLogging();
|
||||||
|
|
||||||
|
//indicate server is about to start
|
||||||
|
log.Info("Server is about to start");
|
||||||
|
|
||||||
|
//start the server
|
||||||
|
StartServer(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts integrated server.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">Command line arguments.</param>
|
||||||
|
private void StartServer(string[] args)
|
||||||
|
{
|
||||||
|
Console.Title = "Chat Room";
|
||||||
|
|
||||||
|
var logic = new ChatRoomLogic();
|
||||||
|
|
||||||
|
// Simple RPC
|
||||||
|
WebApplication simpleRPCWebApplication;
|
||||||
|
{
|
||||||
|
///create web app builder
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
//configure integrated server
|
||||||
|
builder.WebHost.ConfigureKestrel(opts => {
|
||||||
|
opts.Listen(IPAddress.Loopback, 5001);
|
||||||
|
});
|
||||||
|
|
||||||
|
//add SimpleRPC services
|
||||||
|
builder.Services
|
||||||
|
.AddSimpleRpcServer(new HttpServerTransportOptions { Path = "/simplerpc" })
|
||||||
|
.AddSimpleRpcHyperionSerializer();
|
||||||
|
|
||||||
|
//add our custom services
|
||||||
|
builder.Services.AddSingleton<IChatRoomService>(new ChatRoomSimpleRPCService(logic));
|
||||||
|
|
||||||
|
//build the server
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
//add SimpleRPC middleware
|
||||||
|
app.UseSimpleRpcServer();
|
||||||
|
|
||||||
|
simpleRPCWebApplication = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rest, GRPC
|
||||||
|
WebApplication restGrpcWebApplication;
|
||||||
|
{
|
||||||
|
///create web app builder
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
//configure integrated server
|
||||||
|
builder.WebHost.ConfigureKestrel(opts => {
|
||||||
|
opts.Listen(IPAddress.Loopback, 5000, opts =>
|
||||||
|
{
|
||||||
|
opts.Protocols = HttpProtocols.Http2;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//add and configure swagger documentation generator (http://127.0.0.1:5000/swagger/)
|
||||||
|
builder.Services.AddSwaggerGen(opts =>
|
||||||
|
{
|
||||||
|
//include code comments in swagger documentation
|
||||||
|
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
||||||
|
opts.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
|
||||||
|
});
|
||||||
|
|
||||||
|
//turn on support for web api controllers
|
||||||
|
builder.Services
|
||||||
|
.AddControllers()
|
||||||
|
.AddJsonOptions(opts =>
|
||||||
|
{
|
||||||
|
//this makes enumeration values to be strings instead of integers in opeanapi doc
|
||||||
|
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||||
|
});
|
||||||
|
|
||||||
|
//add CORS policies
|
||||||
|
builder.Services.AddCors(cr =>
|
||||||
|
{
|
||||||
|
//allow everything from everywhere
|
||||||
|
cr.AddPolicy("allowAll", cp =>
|
||||||
|
{
|
||||||
|
cp.AllowAnyOrigin();
|
||||||
|
cp.AllowAnyMethod();
|
||||||
|
cp.AllowAnyHeader();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//publish the background logic as (an internal) service through dependency injection,
|
||||||
|
//otherwise it will not start until the first client calls into controller
|
||||||
|
builder.Services.AddSingleton(logic);
|
||||||
|
|
||||||
|
//add support for GRPC services
|
||||||
|
builder.Services.AddGrpc();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton(new ChatRoomGrpcService(logic));
|
||||||
|
|
||||||
|
//build the server
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
//turn CORS policy on
|
||||||
|
app.UseCors("allowAll");
|
||||||
|
|
||||||
|
//turn on support for swagger doc web page
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
|
||||||
|
app.MapGrpcService<ChatRoomGrpcService>();
|
||||||
|
|
||||||
|
////turn on request routing
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
////configure routes
|
||||||
|
app.MapControllerRoute(
|
||||||
|
name: "default",
|
||||||
|
pattern: "{controller}/{action=Index}/{id?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
restGrpcWebApplication = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RabbitMQ
|
||||||
|
{
|
||||||
|
var service = new ChatRoomRabbitMQService(
|
||||||
|
logic,
|
||||||
|
RabbitMQConfig.CreateConnection(),
|
||||||
|
RabbitMQConfig.ExchangeName,
|
||||||
|
RabbitMQConfig.ServerQueueName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var restGrpcTask = restGrpcWebApplication.RunAsync();
|
||||||
|
var simpleRPCTask = simpleRPCWebApplication.RunAsync();
|
||||||
|
|
||||||
|
restGrpcTask.Wait();
|
||||||
|
simpleRPCTask.Wait();
|
||||||
|
}
|
||||||
|
}
|
11
Lab4/ChatRoom/appsettings.json
Normal file
11
Lab4/ChatRoom/appsettings.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information",
|
||||||
|
"Microsoft.AspNetCore.Hosting.Diagnostics": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
42
Lab4/ChatRoomContract/ChatRoomContract.csproj
Normal file
42
Lab4/ChatRoomContract/ChatRoomContract.csproj
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Protos\service.proto" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Grpc.AspNetCore" Version="2.67.0" />
|
||||||
|
<PackageReference Include="Grpc.Net.Client" Version="2.67.0" />
|
||||||
|
<PackageReference Include="MessagePack" Version="3.0.300" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="NSwag.MSBuild" Version="14.2.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Protobuf Include="Protos\service.proto" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- This will only work if server is running. It needs access to http://localhost:5000/swagger/v1/swagger.json . -->
|
||||||
|
<Target Name="NSwag" BeforeTargets="BeforeBuild">
|
||||||
|
<Exec
|
||||||
|
Command="$(NSwagExe_Net60) run nswag.json /variables:Configuration=$(Configuration),OutDir=$(OutDir)"
|
||||||
|
IgnoreExitCode="true"
|
||||||
|
/>
|
||||||
|
<!-- See 'https://stackoverflow.com/questions/44818730/is-there-a-net-core-cli-pre-before-build-task' as to why the following is necessary-->
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile
|
||||||
|
Include="**/*$(DefaultLanguageSourceExtension)"
|
||||||
|
Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);$(BaseIntermediateOutputPath)**;$(BaseOutputPath)**;@(Compile)" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
</Project>
|
116
Lab4/ChatRoomContract/ChatRoomGrpcClient.cs
Normal file
116
Lab4/ChatRoomContract/ChatRoomGrpcClient.cs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
|
||||||
|
using ChatRoomContract.Protocol;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
using Grpc.Net.Client;
|
||||||
|
|
||||||
|
namespace ChatRoomContract;
|
||||||
|
|
||||||
|
public class ChatRoomGrpcClient : IChatRoomService
|
||||||
|
{
|
||||||
|
GrpcChannel channel;
|
||||||
|
ChatRoom.ChatRoomClient client;
|
||||||
|
|
||||||
|
public ChatRoomGrpcClient(string address)
|
||||||
|
{
|
||||||
|
channel = GrpcChannel.ForAddress(address);
|
||||||
|
client = new ChatRoom.ChatRoomClient(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
public void ApproveMessage(int messageId)
|
||||||
|
{
|
||||||
|
client.ApproveMessage(new MessageId { Id = messageId });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
var result = client.GetBlockedUntil(new ClientId { Id = clientId });
|
||||||
|
if (result.HasTimestamp)
|
||||||
|
{
|
||||||
|
return result.Timestamp.ToDateTime();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next message which hasn't been approved or rejected
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Message object. Returns null if there is no message</returns>
|
||||||
|
public Message? GetNewMessage()
|
||||||
|
{
|
||||||
|
var result = client.GetNewMessage(new Empty());
|
||||||
|
if (result.HasMessage)
|
||||||
|
{
|
||||||
|
return new Message
|
||||||
|
{
|
||||||
|
id = result.Message.Id,
|
||||||
|
contents = result.Message.Contents,
|
||||||
|
needsToBeCensored = result.Message.NeedsToBeCensored
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get number of strikes a participant has
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Number of strikes</returns>
|
||||||
|
public int GetStrikes(int clientId)
|
||||||
|
{
|
||||||
|
var result = client.GetStrikes(new ClientId { Id = clientId });
|
||||||
|
return result.Strikes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register client with a name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of client, can be duplicate between clients</param>
|
||||||
|
/// <returns>Client ID</returns>
|
||||||
|
public int RegisterClient(string name)
|
||||||
|
{
|
||||||
|
var result = client.RegisterClient(new RegisterClientRequest { Name = name });
|
||||||
|
return result.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
public void RejectMessage(int messageId)
|
||||||
|
{
|
||||||
|
client.RejectMessage(new MessageId { Id = messageId });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
public bool SendMessage(int clientId, string contents, bool needsToBeCensored)
|
||||||
|
{
|
||||||
|
var result = client.SendMessage(new UserMessageRequest
|
||||||
|
{
|
||||||
|
ClientId = clientId,
|
||||||
|
Contents = contents,
|
||||||
|
NeedsToBeCensored = needsToBeCensored
|
||||||
|
});
|
||||||
|
return result.Success;
|
||||||
|
}
|
||||||
|
}
|
243
Lab4/ChatRoomContract/ChatRoomRabbitMQClient.cs
Normal file
243
Lab4/ChatRoomContract/ChatRoomRabbitMQClient.cs
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
using MessagePack;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
using RabbitMQ.Client.Events;
|
||||||
|
|
||||||
|
namespace ChatRoomContract;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RabbitMQ chat room client
|
||||||
|
/// </summary>
|
||||||
|
public class ChatRoomRabbitMQClient : IChatRoomService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Exchange name
|
||||||
|
/// </summary>
|
||||||
|
private string exchangeName;
|
||||||
|
/// <summary>
|
||||||
|
/// Client queue name
|
||||||
|
/// </summary>
|
||||||
|
private string clientQueueName;
|
||||||
|
/// <summary>
|
||||||
|
/// Server queue name
|
||||||
|
/// </summary>
|
||||||
|
private string serverQueueName;
|
||||||
|
/// <summary>
|
||||||
|
/// RabbitMQ channel
|
||||||
|
/// </summary>
|
||||||
|
private IModel rmqChannel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chat Room client constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection">RabbitMQ connection</param>
|
||||||
|
/// <param name="exchangeName">Exchange name</param>
|
||||||
|
/// <param name="clientQueueName">Client queue name</param>
|
||||||
|
/// <param name="serverQueueName">Server queue name</param>
|
||||||
|
public ChatRoomRabbitMQClient(IConnection connection, string exchangeName, string clientQueueName, string serverQueueName)
|
||||||
|
{
|
||||||
|
this.exchangeName = exchangeName;
|
||||||
|
this.clientQueueName = clientQueueName;
|
||||||
|
this.serverQueueName = serverQueueName;
|
||||||
|
rmqChannel = connection.CreateModel();
|
||||||
|
|
||||||
|
rmqChannel.ExchangeDeclare(exchange: exchangeName, type: ExchangeType.Direct);
|
||||||
|
rmqChannel.QueueDeclare(queue: clientQueueName, durable: false, exclusive: true, autoDelete: false, arguments: null);
|
||||||
|
rmqChannel.QueueBind(queue: clientQueueName, exchange: exchangeName, routingKey: clientQueueName, arguments: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Send rpc message without waiting for a response
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">Method name</param>
|
||||||
|
/// <param name="args">Serialized arguments</param>
|
||||||
|
/// <param name="correlationId">Optional correlation ID</param>
|
||||||
|
private void CallVoid(string method, byte[] args, string? correlationId = null)
|
||||||
|
{
|
||||||
|
correlationId ??= Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
var requestProps = rmqChannel.CreateBasicProperties();
|
||||||
|
requestProps.CorrelationId = correlationId;
|
||||||
|
requestProps.ReplyTo = clientQueueName;
|
||||||
|
|
||||||
|
var msg = new RPCMessage
|
||||||
|
{
|
||||||
|
isResponse = false,
|
||||||
|
method = method,
|
||||||
|
args = args
|
||||||
|
};
|
||||||
|
|
||||||
|
rmqChannel.BasicPublish(
|
||||||
|
exchange: exchangeName,
|
||||||
|
routingKey: serverQueueName,
|
||||||
|
basicProperties: requestProps,
|
||||||
|
body: MessagePackSerializer.Serialize(msg)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Send a rpc message and wait for a response
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="ResultType">Result type</typeparam>
|
||||||
|
/// <param name="method">Method name</param>
|
||||||
|
/// <param name="args">Serialized arguments</param>
|
||||||
|
/// <returns>Result</returns>
|
||||||
|
private ResultType Call<ResultType>(string method, byte[]? args)
|
||||||
|
{
|
||||||
|
var correlationId = Guid.NewGuid().ToString();
|
||||||
|
var isResultReady = false;
|
||||||
|
var resultReadySignal = new AutoResetEvent(false);
|
||||||
|
|
||||||
|
ResultType result = default;
|
||||||
|
|
||||||
|
//ensure contents of variables set in main thread, are loadable by receiver thread
|
||||||
|
Thread.MemoryBarrier();
|
||||||
|
|
||||||
|
//create response message consumer
|
||||||
|
var consumer = new EventingBasicConsumer(rmqChannel);
|
||||||
|
consumer.Received +=
|
||||||
|
(channel, delivery) => {
|
||||||
|
//ensure contents of variables set in main thread are loaded into this thread
|
||||||
|
Thread.MemoryBarrier();
|
||||||
|
|
||||||
|
if (isResultReady)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delivery.BasicProperties.CorrelationId != correlationId)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var msg = MessagePackSerializer.Deserialize<RPCMessage>(delivery.Body);
|
||||||
|
if (msg.isResponse && msg.method == method)
|
||||||
|
{
|
||||||
|
if (msg.args != null)
|
||||||
|
{
|
||||||
|
result = MessagePackSerializer.Deserialize<ResultType>(msg.args);
|
||||||
|
}
|
||||||
|
|
||||||
|
//indicate result has been received, ensure it is loadable by main thread
|
||||||
|
isResultReady = true;
|
||||||
|
Thread.MemoryBarrier();
|
||||||
|
|
||||||
|
//signal main thread that result has been received
|
||||||
|
resultReadySignal.Set();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//attach message consumer to the response queue
|
||||||
|
var consumerTag = rmqChannel.BasicConsume(clientQueueName, true, consumer);
|
||||||
|
|
||||||
|
CallVoid(method, args, correlationId);
|
||||||
|
|
||||||
|
//wait for the result to be ready
|
||||||
|
resultReadySignal.WaitOne();
|
||||||
|
|
||||||
|
//ensure contents of variables set by the receiver are loaded into this thread
|
||||||
|
Thread.MemoryBarrier();
|
||||||
|
|
||||||
|
//detach message consumer from the response queue
|
||||||
|
rmqChannel.BasicCancel(consumerTag);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
public void ApproveMessage(int messageId)
|
||||||
|
{
|
||||||
|
CallVoid(
|
||||||
|
nameof(IChatRoomService.ApproveMessage),
|
||||||
|
MessagePackSerializer.Serialize(messageId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
return Call<DateTime?>(
|
||||||
|
nameof(IChatRoomService.GetBlockedUntil),
|
||||||
|
MessagePackSerializer.Serialize(clientId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next message which hasn't been approved or rejected
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Message object. Returns null if there is no message</returns>
|
||||||
|
public Message? GetNewMessage()
|
||||||
|
{
|
||||||
|
return Call<Message?>(
|
||||||
|
nameof(IChatRoomService.GetNewMessage),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get number of strikes a participant has
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Number of strikes</returns>
|
||||||
|
public int GetStrikes(int clientId)
|
||||||
|
{
|
||||||
|
return Call<int>(
|
||||||
|
nameof(IChatRoomService.GetStrikes),
|
||||||
|
MessagePackSerializer.Serialize(clientId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register client with a name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of client, can be duplicate between clients</param>
|
||||||
|
/// <returns>Client ID</returns>
|
||||||
|
public int RegisterClient(string name)
|
||||||
|
{
|
||||||
|
return Call<int>(
|
||||||
|
nameof(IChatRoomService.RegisterClient),
|
||||||
|
MessagePackSerializer.Serialize(name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
public void RejectMessage(int messageId)
|
||||||
|
{
|
||||||
|
CallVoid(
|
||||||
|
nameof(IChatRoomService.RejectMessage),
|
||||||
|
MessagePackSerializer.Serialize(messageId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
public bool SendMessage(int clientId, string contents, bool needsToBeCensored)
|
||||||
|
{
|
||||||
|
var args = new SendMessageArgs
|
||||||
|
{
|
||||||
|
clientId = clientId,
|
||||||
|
contents = contents,
|
||||||
|
needsToBeCensored = needsToBeCensored
|
||||||
|
};
|
||||||
|
|
||||||
|
return Call<bool>(
|
||||||
|
nameof(IChatRoomService.SendMessage),
|
||||||
|
MessagePackSerializer.Serialize(args)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
124
Lab4/ChatRoomContract/ChatRoomRestClient.cs
Normal file
124
Lab4/ChatRoomContract/ChatRoomRestClient.cs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
|
||||||
|
using NswagGenerated;
|
||||||
|
|
||||||
|
namespace ChatRoomContract;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wrapper around generated NSwag client
|
||||||
|
/// </summary>
|
||||||
|
public class ChatRoomRestClient : IChatRoomService
|
||||||
|
{
|
||||||
|
NswagGenerated.NswagChatRoomClient nswag;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chat room client constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseUrl">Server base url</param>
|
||||||
|
/// <param name="httpClient">HTTP Client</param>
|
||||||
|
public ChatRoomRestClient(string baseUrl, HttpClient httpClient)
|
||||||
|
{
|
||||||
|
nswag = new NswagGenerated.NswagChatRoomClient(baseUrl, httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
public void ApproveMessage(int messageId)
|
||||||
|
{
|
||||||
|
nswag.ApproveMessage(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var offset = nswag.GetBlockedUntil(clientId);
|
||||||
|
return offset.DateTime;
|
||||||
|
}
|
||||||
|
catch (ApiException e)
|
||||||
|
{
|
||||||
|
if (e.StatusCode == 204)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next message which hasn't been approved or rejected
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Message object. Returns null if there is no message</returns>
|
||||||
|
public Message? GetNewMessage()
|
||||||
|
{
|
||||||
|
NswagGenerated.Message message;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
message = nswag.GetNewMessage();
|
||||||
|
}
|
||||||
|
catch (ApiException e)
|
||||||
|
{
|
||||||
|
if (e.StatusCode == 204)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Message
|
||||||
|
{
|
||||||
|
id = message.Id,
|
||||||
|
contents = message.Contents,
|
||||||
|
needsToBeCensored = message.NeedsToBeCensored
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get number of strikes a participant has
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Number of strikes</returns>
|
||||||
|
public int GetStrikes(int clientId)
|
||||||
|
{
|
||||||
|
return nswag.GetStrikes(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register client with a name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of client, can be duplicate between clients</param>
|
||||||
|
/// <returns>Client ID</returns>
|
||||||
|
public int RegisterClient(string name)
|
||||||
|
{
|
||||||
|
return nswag.RegisterClient(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
public void RejectMessage(int messageId)
|
||||||
|
{
|
||||||
|
nswag.RejectMessage(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
public bool SendMessage(int clientId, string contents, bool needsToBeCensored)
|
||||||
|
{
|
||||||
|
return nswag.SendMessage(clientId, contents, needsToBeCensored);
|
||||||
|
}
|
||||||
|
}
|
79
Lab4/ChatRoomContract/IChatRoomContract.cs
Normal file
79
Lab4/ChatRoomContract/IChatRoomContract.cs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
using MessagePack;
|
||||||
|
namespace ChatRoomContract;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minimal message description
|
||||||
|
/// </summary>
|
||||||
|
[MessagePackObject]
|
||||||
|
public class Message
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Message ID
|
||||||
|
/// </summary>
|
||||||
|
[Key(0)]
|
||||||
|
public int id;
|
||||||
|
/// <summary>
|
||||||
|
/// Message contents
|
||||||
|
/// </summary>
|
||||||
|
[Key(1)]
|
||||||
|
public string contents;
|
||||||
|
/// <summary>
|
||||||
|
/// Does this message need to be censored?
|
||||||
|
/// </summary>
|
||||||
|
[Key(2)]
|
||||||
|
public bool needsToBeCensored;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chat room service contract
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get number of strikes a participant has
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Number of strikes</returns>
|
||||||
|
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);
|
||||||
|
|
||||||
|
/// <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();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
void RejectMessage(int messageId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
void ApproveMessage(int messageId);
|
||||||
|
}
|
966
Lab4/ChatRoomContract/NswagChatRoomClient.cs
Normal file
966
Lab4/ChatRoomContract/NswagChatRoomClient.cs
Normal file
@ -0,0 +1,966 @@
|
|||||||
|
//----------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// Generated using the NSwag toolchain v14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)
|
||||||
|
// </auto-generated>
|
||||||
|
//----------------------
|
||||||
|
|
||||||
|
#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
|
||||||
|
#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword."
|
||||||
|
#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?'
|
||||||
|
#pragma warning disable 612 // Disable "CS0612 '...' is obsolete"
|
||||||
|
#pragma warning disable 649 // Disable "CS0649 Field is never assigned to, and will always have its default value null"
|
||||||
|
#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
|
||||||
|
#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
|
||||||
|
#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'"
|
||||||
|
#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant"
|
||||||
|
#pragma warning disable 8603 // Disable "CS8603 Possible null reference return"
|
||||||
|
#pragma warning disable 8604 // Disable "CS8604 Possible null reference argument for parameter"
|
||||||
|
#pragma warning disable 8625 // Disable "CS8625 Cannot convert null literal to non-nullable reference type"
|
||||||
|
#pragma warning disable 8765 // Disable "CS8765 Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes)."
|
||||||
|
|
||||||
|
namespace NswagGenerated
|
||||||
|
{
|
||||||
|
using System = global::System;
|
||||||
|
|
||||||
|
[System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||||
|
public partial class NswagChatRoomClient
|
||||||
|
{
|
||||||
|
#pragma warning disable 8618
|
||||||
|
private string _baseUrl;
|
||||||
|
#pragma warning restore 8618
|
||||||
|
|
||||||
|
private System.Net.Http.HttpClient _httpClient;
|
||||||
|
private static System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings, true);
|
||||||
|
private Newtonsoft.Json.JsonSerializerSettings _instanceSettings;
|
||||||
|
|
||||||
|
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||||
|
public NswagChatRoomClient(string baseUrl, System.Net.Http.HttpClient httpClient)
|
||||||
|
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||||
|
{
|
||||||
|
BaseUrl = baseUrl;
|
||||||
|
_httpClient = httpClient;
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
|
||||||
|
{
|
||||||
|
var settings = new Newtonsoft.Json.JsonSerializerSettings();
|
||||||
|
UpdateJsonSerializerSettings(settings);
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string BaseUrl
|
||||||
|
{
|
||||||
|
get { return _baseUrl; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_baseUrl = value;
|
||||||
|
if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/"))
|
||||||
|
_baseUrl += '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } }
|
||||||
|
|
||||||
|
static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
|
||||||
|
|
||||||
|
partial void Initialize();
|
||||||
|
|
||||||
|
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url);
|
||||||
|
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
|
||||||
|
partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register client with a name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of client, can be duplicate between clients</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual System.Threading.Tasks.Task<int> RegisterClientAsync(string name)
|
||||||
|
{
|
||||||
|
return RegisterClientAsync(name, System.Threading.CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register client with a name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of client, can be duplicate between clients</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual int RegisterClient(string name)
|
||||||
|
{
|
||||||
|
return System.Threading.Tasks.Task.Run(async () => await RegisterClientAsync(name, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||||
|
/// <summary>
|
||||||
|
/// Register client with a name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of client, can be duplicate between clients</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual async System.Threading.Tasks.Task<int> RegisterClientAsync(string name, System.Threading.CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var client_ = _httpClient;
|
||||||
|
var disposeClient_ = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||||
|
{
|
||||||
|
request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "text/plain");
|
||||||
|
request_.Method = new System.Net.Http.HttpMethod("POST");
|
||||||
|
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
|
||||||
|
|
||||||
|
var urlBuilder_ = new System.Text.StringBuilder();
|
||||||
|
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
|
||||||
|
// Operation Path: "registerClient"
|
||||||
|
urlBuilder_.Append("registerClient");
|
||||||
|
urlBuilder_.Append('?');
|
||||||
|
if (name != null)
|
||||||
|
{
|
||||||
|
urlBuilder_.Append(System.Uri.EscapeDataString("name")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(name, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
|
||||||
|
}
|
||||||
|
urlBuilder_.Length--;
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, urlBuilder_);
|
||||||
|
|
||||||
|
var url_ = urlBuilder_.ToString();
|
||||||
|
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, url_);
|
||||||
|
|
||||||
|
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||||
|
var disposeResponse_ = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
|
||||||
|
foreach (var item_ in response_.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
if (response_.Content != null && response_.Content.Headers != null)
|
||||||
|
{
|
||||||
|
foreach (var item_ in response_.Content.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessResponse(client_, response_);
|
||||||
|
|
||||||
|
var status_ = (int)response_.StatusCode;
|
||||||
|
if (status_ == 200)
|
||||||
|
{
|
||||||
|
var objectResponse_ = await ReadObjectResponseAsync<int>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (objectResponse_.Object == null)
|
||||||
|
{
|
||||||
|
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||||
|
}
|
||||||
|
return objectResponse_.Object;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeResponse_)
|
||||||
|
response_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeClient_)
|
||||||
|
client_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get number of strikes a participant has
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual System.Threading.Tasks.Task<int> GetStrikesAsync(int? clientId)
|
||||||
|
{
|
||||||
|
return GetStrikesAsync(clientId, System.Threading.CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get number of strikes a participant has
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual int GetStrikes(int? clientId)
|
||||||
|
{
|
||||||
|
return System.Threading.Tasks.Task.Run(async () => await GetStrikesAsync(clientId, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||||
|
/// <summary>
|
||||||
|
/// Get number of strikes a participant has
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual async System.Threading.Tasks.Task<int> GetStrikesAsync(int? clientId, System.Threading.CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var client_ = _httpClient;
|
||||||
|
var disposeClient_ = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||||
|
{
|
||||||
|
request_.Method = new System.Net.Http.HttpMethod("GET");
|
||||||
|
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
|
||||||
|
|
||||||
|
var urlBuilder_ = new System.Text.StringBuilder();
|
||||||
|
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
|
||||||
|
// Operation Path: "getStrikes"
|
||||||
|
urlBuilder_.Append("getStrikes");
|
||||||
|
urlBuilder_.Append('?');
|
||||||
|
if (clientId != null)
|
||||||
|
{
|
||||||
|
urlBuilder_.Append(System.Uri.EscapeDataString("clientId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(clientId, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
|
||||||
|
}
|
||||||
|
urlBuilder_.Length--;
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, urlBuilder_);
|
||||||
|
|
||||||
|
var url_ = urlBuilder_.ToString();
|
||||||
|
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, url_);
|
||||||
|
|
||||||
|
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||||
|
var disposeResponse_ = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
|
||||||
|
foreach (var item_ in response_.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
if (response_.Content != null && response_.Content.Headers != null)
|
||||||
|
{
|
||||||
|
foreach (var item_ in response_.Content.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessResponse(client_, response_);
|
||||||
|
|
||||||
|
var status_ = (int)response_.StatusCode;
|
||||||
|
if (status_ == 200)
|
||||||
|
{
|
||||||
|
var objectResponse_ = await ReadObjectResponseAsync<int>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (objectResponse_.Object == null)
|
||||||
|
{
|
||||||
|
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||||
|
}
|
||||||
|
return objectResponse_.Object;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeResponse_)
|
||||||
|
response_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeClient_)
|
||||||
|
client_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get timestamp until when the client is blocked
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual System.Threading.Tasks.Task<System.DateTimeOffset> GetBlockedUntilAsync(int? clientId)
|
||||||
|
{
|
||||||
|
return GetBlockedUntilAsync(clientId, System.Threading.CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get timestamp until when the client is blocked
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual System.DateTimeOffset GetBlockedUntil(int? clientId)
|
||||||
|
{
|
||||||
|
return System.Threading.Tasks.Task.Run(async () => await GetBlockedUntilAsync(clientId, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||||
|
/// <summary>
|
||||||
|
/// Get timestamp until when the client is blocked
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">Client ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual async System.Threading.Tasks.Task<System.DateTimeOffset> GetBlockedUntilAsync(int? clientId, System.Threading.CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var client_ = _httpClient;
|
||||||
|
var disposeClient_ = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||||
|
{
|
||||||
|
request_.Method = new System.Net.Http.HttpMethod("GET");
|
||||||
|
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
|
||||||
|
|
||||||
|
var urlBuilder_ = new System.Text.StringBuilder();
|
||||||
|
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
|
||||||
|
// Operation Path: "getBlockedUntil"
|
||||||
|
urlBuilder_.Append("getBlockedUntil");
|
||||||
|
urlBuilder_.Append('?');
|
||||||
|
if (clientId != null)
|
||||||
|
{
|
||||||
|
urlBuilder_.Append(System.Uri.EscapeDataString("clientId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(clientId, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
|
||||||
|
}
|
||||||
|
urlBuilder_.Length--;
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, urlBuilder_);
|
||||||
|
|
||||||
|
var url_ = urlBuilder_.ToString();
|
||||||
|
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, url_);
|
||||||
|
|
||||||
|
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||||
|
var disposeResponse_ = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
|
||||||
|
foreach (var item_ in response_.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
if (response_.Content != null && response_.Content.Headers != null)
|
||||||
|
{
|
||||||
|
foreach (var item_ in response_.Content.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessResponse(client_, response_);
|
||||||
|
|
||||||
|
var status_ = (int)response_.StatusCode;
|
||||||
|
if (status_ == 200)
|
||||||
|
{
|
||||||
|
var objectResponse_ = await ReadObjectResponseAsync<System.DateTimeOffset>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (objectResponse_.Object == null)
|
||||||
|
{
|
||||||
|
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||||
|
}
|
||||||
|
return objectResponse_.Object;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeResponse_)
|
||||||
|
response_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeClient_)
|
||||||
|
client_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual System.Threading.Tasks.Task<bool> SendMessageAsync(int? clientId, string contents, bool? needsToBeCensored)
|
||||||
|
{
|
||||||
|
return SendMessageAsync(clientId, contents, needsToBeCensored, System.Threading.CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual bool SendMessage(int? clientId, string contents, bool? needsToBeCensored)
|
||||||
|
{
|
||||||
|
return System.Threading.Tasks.Task.Run(async () => await SendMessageAsync(clientId, contents, needsToBeCensored, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||||
|
/// <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>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual async System.Threading.Tasks.Task<bool> SendMessageAsync(int? clientId, string contents, bool? needsToBeCensored, System.Threading.CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var client_ = _httpClient;
|
||||||
|
var disposeClient_ = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||||
|
{
|
||||||
|
request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "text/plain");
|
||||||
|
request_.Method = new System.Net.Http.HttpMethod("POST");
|
||||||
|
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
|
||||||
|
|
||||||
|
var urlBuilder_ = new System.Text.StringBuilder();
|
||||||
|
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
|
||||||
|
// Operation Path: "sendMessage"
|
||||||
|
urlBuilder_.Append("sendMessage");
|
||||||
|
urlBuilder_.Append('?');
|
||||||
|
if (clientId != null)
|
||||||
|
{
|
||||||
|
urlBuilder_.Append(System.Uri.EscapeDataString("clientId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(clientId, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
|
||||||
|
}
|
||||||
|
if (contents != null)
|
||||||
|
{
|
||||||
|
urlBuilder_.Append(System.Uri.EscapeDataString("contents")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(contents, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
|
||||||
|
}
|
||||||
|
if (needsToBeCensored != null)
|
||||||
|
{
|
||||||
|
urlBuilder_.Append(System.Uri.EscapeDataString("needsToBeCensored")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(needsToBeCensored, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
|
||||||
|
}
|
||||||
|
urlBuilder_.Length--;
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, urlBuilder_);
|
||||||
|
|
||||||
|
var url_ = urlBuilder_.ToString();
|
||||||
|
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, url_);
|
||||||
|
|
||||||
|
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||||
|
var disposeResponse_ = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
|
||||||
|
foreach (var item_ in response_.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
if (response_.Content != null && response_.Content.Headers != null)
|
||||||
|
{
|
||||||
|
foreach (var item_ in response_.Content.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessResponse(client_, response_);
|
||||||
|
|
||||||
|
var status_ = (int)response_.StatusCode;
|
||||||
|
if (status_ == 200)
|
||||||
|
{
|
||||||
|
var objectResponse_ = await ReadObjectResponseAsync<bool>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (objectResponse_.Object == null)
|
||||||
|
{
|
||||||
|
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||||
|
}
|
||||||
|
return objectResponse_.Object;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeResponse_)
|
||||||
|
response_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeClient_)
|
||||||
|
client_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next message which hasn't been approved or rejected
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual System.Threading.Tasks.Task<Message> GetNewMessageAsync()
|
||||||
|
{
|
||||||
|
return GetNewMessageAsync(System.Threading.CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next message which hasn't been approved or rejected
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual Message GetNewMessage()
|
||||||
|
{
|
||||||
|
return System.Threading.Tasks.Task.Run(async () => await GetNewMessageAsync(System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next message which hasn't been approved or rejected
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual async System.Threading.Tasks.Task<Message> GetNewMessageAsync(System.Threading.CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var client_ = _httpClient;
|
||||||
|
var disposeClient_ = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||||
|
{
|
||||||
|
request_.Method = new System.Net.Http.HttpMethod("GET");
|
||||||
|
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
|
||||||
|
|
||||||
|
var urlBuilder_ = new System.Text.StringBuilder();
|
||||||
|
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
|
||||||
|
// Operation Path: "getNewMessage"
|
||||||
|
urlBuilder_.Append("getNewMessage");
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, urlBuilder_);
|
||||||
|
|
||||||
|
var url_ = urlBuilder_.ToString();
|
||||||
|
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, url_);
|
||||||
|
|
||||||
|
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||||
|
var disposeResponse_ = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
|
||||||
|
foreach (var item_ in response_.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
if (response_.Content != null && response_.Content.Headers != null)
|
||||||
|
{
|
||||||
|
foreach (var item_ in response_.Content.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessResponse(client_, response_);
|
||||||
|
|
||||||
|
var status_ = (int)response_.StatusCode;
|
||||||
|
if (status_ == 200)
|
||||||
|
{
|
||||||
|
var objectResponse_ = await ReadObjectResponseAsync<Message>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (objectResponse_.Object == null)
|
||||||
|
{
|
||||||
|
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||||
|
}
|
||||||
|
return objectResponse_.Object;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeResponse_)
|
||||||
|
response_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeClient_)
|
||||||
|
client_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual System.Threading.Tasks.Task RejectMessageAsync(int? messageId)
|
||||||
|
{
|
||||||
|
return RejectMessageAsync(messageId, System.Threading.CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual void RejectMessage(int? messageId)
|
||||||
|
{
|
||||||
|
System.Threading.Tasks.Task.Run(async () => await RejectMessageAsync(messageId, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||||
|
/// <summary>
|
||||||
|
/// Reject a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual async System.Threading.Tasks.Task RejectMessageAsync(int? messageId, System.Threading.CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var client_ = _httpClient;
|
||||||
|
var disposeClient_ = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||||
|
{
|
||||||
|
request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json");
|
||||||
|
request_.Method = new System.Net.Http.HttpMethod("POST");
|
||||||
|
|
||||||
|
var urlBuilder_ = new System.Text.StringBuilder();
|
||||||
|
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
|
||||||
|
// Operation Path: "rejectMessage"
|
||||||
|
urlBuilder_.Append("rejectMessage");
|
||||||
|
urlBuilder_.Append('?');
|
||||||
|
if (messageId != null)
|
||||||
|
{
|
||||||
|
urlBuilder_.Append(System.Uri.EscapeDataString("messageId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(messageId, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
|
||||||
|
}
|
||||||
|
urlBuilder_.Length--;
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, urlBuilder_);
|
||||||
|
|
||||||
|
var url_ = urlBuilder_.ToString();
|
||||||
|
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, url_);
|
||||||
|
|
||||||
|
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||||
|
var disposeResponse_ = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
|
||||||
|
foreach (var item_ in response_.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
if (response_.Content != null && response_.Content.Headers != null)
|
||||||
|
{
|
||||||
|
foreach (var item_ in response_.Content.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessResponse(client_, response_);
|
||||||
|
|
||||||
|
var status_ = (int)response_.StatusCode;
|
||||||
|
if (status_ == 200)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeResponse_)
|
||||||
|
response_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeClient_)
|
||||||
|
client_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual System.Threading.Tasks.Task ApproveMessageAsync(int? messageId)
|
||||||
|
{
|
||||||
|
return ApproveMessageAsync(messageId, System.Threading.CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual void ApproveMessage(int? messageId)
|
||||||
|
{
|
||||||
|
System.Threading.Tasks.Task.Run(async () => await ApproveMessageAsync(messageId, System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||||
|
/// <summary>
|
||||||
|
/// Approve a message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">Message ID</param>
|
||||||
|
/// <returns>Success</returns>
|
||||||
|
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||||
|
public virtual async System.Threading.Tasks.Task ApproveMessageAsync(int? messageId, System.Threading.CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var client_ = _httpClient;
|
||||||
|
var disposeClient_ = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||||
|
{
|
||||||
|
request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json");
|
||||||
|
request_.Method = new System.Net.Http.HttpMethod("POST");
|
||||||
|
|
||||||
|
var urlBuilder_ = new System.Text.StringBuilder();
|
||||||
|
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
|
||||||
|
// Operation Path: "approveMessage"
|
||||||
|
urlBuilder_.Append("approveMessage");
|
||||||
|
urlBuilder_.Append('?');
|
||||||
|
if (messageId != null)
|
||||||
|
{
|
||||||
|
urlBuilder_.Append(System.Uri.EscapeDataString("messageId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(messageId, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
|
||||||
|
}
|
||||||
|
urlBuilder_.Length--;
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, urlBuilder_);
|
||||||
|
|
||||||
|
var url_ = urlBuilder_.ToString();
|
||||||
|
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||||
|
|
||||||
|
PrepareRequest(client_, request_, url_);
|
||||||
|
|
||||||
|
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||||
|
var disposeResponse_ = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
|
||||||
|
foreach (var item_ in response_.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
if (response_.Content != null && response_.Content.Headers != null)
|
||||||
|
{
|
||||||
|
foreach (var item_ in response_.Content.Headers)
|
||||||
|
headers_[item_.Key] = item_.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessResponse(client_, response_);
|
||||||
|
|
||||||
|
var status_ = (int)response_.StatusCode;
|
||||||
|
if (status_ == 200)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeResponse_)
|
||||||
|
response_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (disposeClient_)
|
||||||
|
client_.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected struct ObjectResponseResult<T>
|
||||||
|
{
|
||||||
|
public ObjectResponseResult(T responseObject, string responseText)
|
||||||
|
{
|
||||||
|
this.Object = responseObject;
|
||||||
|
this.Text = responseText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Object { get; }
|
||||||
|
|
||||||
|
public string Text { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ReadResponseAsString { get; set; }
|
||||||
|
|
||||||
|
protected virtual async System.Threading.Tasks.Task<ObjectResponseResult<T>> ReadObjectResponseAsync<T>(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Threading.CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (response == null || response.Content == null)
|
||||||
|
{
|
||||||
|
return new ObjectResponseResult<T>(default(T), string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReadResponseAsString)
|
||||||
|
{
|
||||||
|
var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(responseText, JsonSerializerSettings);
|
||||||
|
return new ObjectResponseResult<T>(typedBody, responseText);
|
||||||
|
}
|
||||||
|
catch (Newtonsoft.Json.JsonException exception)
|
||||||
|
{
|
||||||
|
var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
|
||||||
|
throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
|
||||||
|
using (var streamReader = new System.IO.StreamReader(responseStream))
|
||||||
|
using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader))
|
||||||
|
{
|
||||||
|
var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings);
|
||||||
|
var typedBody = serializer.Deserialize<T>(jsonTextReader);
|
||||||
|
return new ObjectResponseResult<T>(typedBody, string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Newtonsoft.Json.JsonException exception)
|
||||||
|
{
|
||||||
|
var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
|
||||||
|
throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is System.Enum)
|
||||||
|
{
|
||||||
|
var name = System.Enum.GetName(value.GetType(), value);
|
||||||
|
if (name != null)
|
||||||
|
{
|
||||||
|
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
|
||||||
|
if (field != null)
|
||||||
|
{
|
||||||
|
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
|
||||||
|
as System.Runtime.Serialization.EnumMemberAttribute;
|
||||||
|
if (attribute != null)
|
||||||
|
{
|
||||||
|
return attribute.Value != null ? attribute.Value : name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
|
||||||
|
return converted == null ? string.Empty : converted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (value is bool)
|
||||||
|
{
|
||||||
|
return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
|
||||||
|
}
|
||||||
|
else if (value is byte[])
|
||||||
|
{
|
||||||
|
return System.Convert.ToBase64String((byte[])value);
|
||||||
|
}
|
||||||
|
else if (value is string[])
|
||||||
|
{
|
||||||
|
return string.Join(",", (string[])value);
|
||||||
|
}
|
||||||
|
else if (value.GetType().IsArray)
|
||||||
|
{
|
||||||
|
var valueArray = (System.Array)value;
|
||||||
|
var valueTextArray = new string[valueArray.Length];
|
||||||
|
for (var i = 0; i < valueArray.Length; i++)
|
||||||
|
{
|
||||||
|
valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo);
|
||||||
|
}
|
||||||
|
return string.Join(",", valueTextArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = System.Convert.ToString(value, cultureInfo);
|
||||||
|
return result == null ? "" : result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||||
|
public partial class Message
|
||||||
|
{
|
||||||
|
[Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Newtonsoft.Json.JsonProperty("contents", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
|
||||||
|
public string Contents { get; set; }
|
||||||
|
|
||||||
|
[Newtonsoft.Json.JsonProperty("needsToBeCensored", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
|
||||||
|
public bool NeedsToBeCensored { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||||
|
public partial class ApiException : System.Exception
|
||||||
|
{
|
||||||
|
public int StatusCode { get; private set; }
|
||||||
|
|
||||||
|
public string Response { get; private set; }
|
||||||
|
|
||||||
|
public System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> Headers { get; private set; }
|
||||||
|
|
||||||
|
public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Exception innerException)
|
||||||
|
: base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException)
|
||||||
|
{
|
||||||
|
StatusCode = statusCode;
|
||||||
|
Response = response;
|
||||||
|
Headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
|
||||||
|
public partial class ApiException<TResult> : ApiException
|
||||||
|
{
|
||||||
|
public TResult Result { get; private set; }
|
||||||
|
|
||||||
|
public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, TResult result, System.Exception innerException)
|
||||||
|
: base(message, statusCode, response, headers, innerException)
|
||||||
|
{
|
||||||
|
Result = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning restore 108
|
||||||
|
#pragma warning restore 114
|
||||||
|
#pragma warning restore 472
|
||||||
|
#pragma warning restore 612
|
||||||
|
#pragma warning restore 1573
|
||||||
|
#pragma warning restore 1591
|
||||||
|
#pragma warning restore 8073
|
||||||
|
#pragma warning restore 3016
|
||||||
|
#pragma warning restore 8603
|
||||||
|
#pragma warning restore 8604
|
||||||
|
#pragma warning restore 8625
|
61
Lab4/ChatRoomContract/Protos/service.proto
Normal file
61
Lab4/ChatRoomContract/Protos/service.proto
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
//set the language version
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
//this will translate into C# namespace
|
||||||
|
package ChatRoomContract.Protocol;
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
service ChatRoom {
|
||||||
|
rpc RegisterClient(RegisterClientRequest) returns (ClientId);
|
||||||
|
rpc GetStrikes(ClientId) returns (Srikes);
|
||||||
|
rpc GetBlockedUntil(ClientId) returns (BlockedUntil);
|
||||||
|
rpc SendMessage(UserMessageRequest) returns (BoolResponse);
|
||||||
|
rpc GetNewMessage(google.protobuf.Empty) returns (NewUserMessage);
|
||||||
|
rpc RejectMessage(MessageId) returns (google.protobuf.Empty);
|
||||||
|
rpc ApproveMessage(MessageId) returns (google.protobuf.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
message BoolResponse {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RegisterClientRequest {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Srikes {
|
||||||
|
int32 strikes = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ClientId {
|
||||||
|
int32 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageId {
|
||||||
|
int32 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BlockedUntil {
|
||||||
|
bool hasTimestamp = 1;
|
||||||
|
google.protobuf.Timestamp timestamp = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UserMessageRequest {
|
||||||
|
int32 clientId = 1;
|
||||||
|
string contents = 2;
|
||||||
|
bool needsToBeCensored = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
message UserMessage {
|
||||||
|
int32 id = 1;
|
||||||
|
string contents = 2;
|
||||||
|
bool needsToBeCensored = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NewUserMessage {
|
||||||
|
bool hasMessage = 1;
|
||||||
|
UserMessage message = 2;
|
||||||
|
}
|
49
Lab4/ChatRoomContract/RPCMessage.cs
Normal file
49
Lab4/ChatRoomContract/RPCMessage.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using MessagePack;
|
||||||
|
|
||||||
|
namespace ChatRoomContract;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RabbitMQ message
|
||||||
|
/// </summary>
|
||||||
|
[MessagePackObject]
|
||||||
|
public class RPCMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Is this message a response
|
||||||
|
/// </summary>
|
||||||
|
[Key(0)]
|
||||||
|
public bool isResponse;
|
||||||
|
/// <summary>
|
||||||
|
/// Method name
|
||||||
|
/// </summary>
|
||||||
|
[Key(1)]
|
||||||
|
public string method;
|
||||||
|
/// <summary>
|
||||||
|
/// Optional arguments
|
||||||
|
/// </summary>
|
||||||
|
[Key(2)]
|
||||||
|
public byte[]? args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IChatRoomService.SendMessage arguments
|
||||||
|
/// </summary>
|
||||||
|
[MessagePackObject]
|
||||||
|
public class SendMessageArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client ID
|
||||||
|
/// </summary>
|
||||||
|
[Key(0)]
|
||||||
|
public int clientId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Message contents
|
||||||
|
/// </summary>
|
||||||
|
[Key(1)]
|
||||||
|
public string contents { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Does this message need to be censored?
|
||||||
|
/// </summary>
|
||||||
|
[Key(2)]
|
||||||
|
public bool needsToBeCensored { get; set; }
|
||||||
|
}
|
30
Lab4/ChatRoomContract/RabbitMQConfig.cs
Normal file
30
Lab4/ChatRoomContract/RabbitMQConfig.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace ChatRoomContract;
|
||||||
|
|
||||||
|
public static class RabbitMQConfig
|
||||||
|
{
|
||||||
|
public static string ExchangeName = "T120B180.ChatRoom.Exchange";
|
||||||
|
public static string ServerQueueName = "T120B180.ChatRoom.Server";
|
||||||
|
public static string ClientQueueNamePrefix = "T120B180.ChatRoom.Client_";
|
||||||
|
public static string HostName = "localhost";
|
||||||
|
public static string Username = "guest";
|
||||||
|
public static string Password = "guest";
|
||||||
|
|
||||||
|
public static string CreateClientQueueName()
|
||||||
|
{
|
||||||
|
var ClientId = Guid.NewGuid().ToString();
|
||||||
|
return ClientQueueNamePrefix + ClientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IConnection CreateConnection()
|
||||||
|
{
|
||||||
|
var rmqConnectionFactory = new ConnectionFactory
|
||||||
|
{
|
||||||
|
HostName = HostName,
|
||||||
|
UserName = Username,
|
||||||
|
Password = Password
|
||||||
|
};
|
||||||
|
return rmqConnectionFactory.CreateConnection();
|
||||||
|
}
|
||||||
|
}
|
19
Lab4/ChatRoomContract/nswag.json
Normal file
19
Lab4/ChatRoomContract/nswag.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"runtime": "Net60",
|
||||||
|
"documentGenerator": {
|
||||||
|
"fromDocument": {
|
||||||
|
"url": "http://localhost:5000/swagger/v1/swagger.json",
|
||||||
|
"output": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"codeGenerators": {
|
||||||
|
"openApiToCSharpClient": {
|
||||||
|
"classname" : "NswagChatRoomClient",
|
||||||
|
"namespace" : "NswagGenerated",
|
||||||
|
"output" : "NswagChatRoomClient.cs",
|
||||||
|
|
||||||
|
"generateSyncMethods": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
91
Lab4/Lab4.sln
Normal file
91
Lab4/Lab4.sln
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.11.35208.52
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatRoom", "ChatRoom\ChatRoom.csproj", "{0F2C1DA4-5EC0-4287-A27D-5DA3C2FF7F62}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatRoomContract", "ChatRoomContract\ChatRoomContract.csproj", "{301234B6-13D6-4947-BCEC-1287E7CFCA6F}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModeratorSimpleRPC", "ModeratorSimpleRPC\ModeratorSimpleRPC.csproj", "{1258DF7D-83C6-49AF-8A47-4A06F816846F}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParticipantSimpleRPC", "ParticipantSimpleRPC\ParticipantSimpleRPC.csproj", "{1228253D-45D1-477F-93F3-99C2741CF827}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModeratorRest", "ModeratorRest\ModeratorRest.csproj", "{64B1DFC1-B9AC-46D5-951E-7B66EFC47DA6}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParticipantRest", "ParticipantRest\ParticipantRest.csproj", "{8C97E0F7-5438-4B30-B3F7-DB1757E784C8}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moderator", "Moderator\Moderator.csproj", "{6DFD9D6C-B52B-44C4-8246-6ACC970A55F8}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Participant", "Participant\Participant.csproj", "{B307B77D-47D1-4603-AF04-35F9D5F2BDE0}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModeratorGrpc", "ModeratorGrpc\ModeratorGrpc.csproj", "{906F9E24-D852-44DD-8CB6-9A7EF70A09D7}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParticipantGrpc", "ParticipantGrpc\ParticipantGrpc.csproj", "{C14A7CA4-96DA-498A-B8A9-291AE54A5BD5}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModeratorRabbitMQ", "ModeratorRabbitMQ\ModeratorRabbitMQ.csproj", "{065DF942-2AFF-44A0-AA7A-44CCC5446848}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParticipantRabbitMQ", "ParticipantRabbitMQ\ParticipantRabbitMQ.csproj", "{9434774F-B80A-490F-A57A-52031CC2F5C6}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{0F2C1DA4-5EC0-4287-A27D-5DA3C2FF7F62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0F2C1DA4-5EC0-4287-A27D-5DA3C2FF7F62}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0F2C1DA4-5EC0-4287-A27D-5DA3C2FF7F62}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0F2C1DA4-5EC0-4287-A27D-5DA3C2FF7F62}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{301234B6-13D6-4947-BCEC-1287E7CFCA6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{301234B6-13D6-4947-BCEC-1287E7CFCA6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{301234B6-13D6-4947-BCEC-1287E7CFCA6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{301234B6-13D6-4947-BCEC-1287E7CFCA6F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1258DF7D-83C6-49AF-8A47-4A06F816846F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1258DF7D-83C6-49AF-8A47-4A06F816846F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1258DF7D-83C6-49AF-8A47-4A06F816846F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1258DF7D-83C6-49AF-8A47-4A06F816846F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1228253D-45D1-477F-93F3-99C2741CF827}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1228253D-45D1-477F-93F3-99C2741CF827}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1228253D-45D1-477F-93F3-99C2741CF827}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1228253D-45D1-477F-93F3-99C2741CF827}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{64B1DFC1-B9AC-46D5-951E-7B66EFC47DA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{64B1DFC1-B9AC-46D5-951E-7B66EFC47DA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{64B1DFC1-B9AC-46D5-951E-7B66EFC47DA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{64B1DFC1-B9AC-46D5-951E-7B66EFC47DA6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8C97E0F7-5438-4B30-B3F7-DB1757E784C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8C97E0F7-5438-4B30-B3F7-DB1757E784C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8C97E0F7-5438-4B30-B3F7-DB1757E784C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8C97E0F7-5438-4B30-B3F7-DB1757E784C8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6DFD9D6C-B52B-44C4-8246-6ACC970A55F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6DFD9D6C-B52B-44C4-8246-6ACC970A55F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6DFD9D6C-B52B-44C4-8246-6ACC970A55F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6DFD9D6C-B52B-44C4-8246-6ACC970A55F8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B307B77D-47D1-4603-AF04-35F9D5F2BDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B307B77D-47D1-4603-AF04-35F9D5F2BDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B307B77D-47D1-4603-AF04-35F9D5F2BDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B307B77D-47D1-4603-AF04-35F9D5F2BDE0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{906F9E24-D852-44DD-8CB6-9A7EF70A09D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{906F9E24-D852-44DD-8CB6-9A7EF70A09D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{906F9E24-D852-44DD-8CB6-9A7EF70A09D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{906F9E24-D852-44DD-8CB6-9A7EF70A09D7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C14A7CA4-96DA-498A-B8A9-291AE54A5BD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C14A7CA4-96DA-498A-B8A9-291AE54A5BD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C14A7CA4-96DA-498A-B8A9-291AE54A5BD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C14A7CA4-96DA-498A-B8A9-291AE54A5BD5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{065DF942-2AFF-44A0-AA7A-44CCC5446848}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{065DF942-2AFF-44A0-AA7A-44CCC5446848}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{065DF942-2AFF-44A0-AA7A-44CCC5446848}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{065DF942-2AFF-44A0-AA7A-44CCC5446848}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9434774F-B80A-490F-A57A-52031CC2F5C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9434774F-B80A-490F-A57A-52031CC2F5C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9434774F-B80A-490F-A57A-52031CC2F5C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9434774F-B80A-490F-A57A-52031CC2F5C6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {2D31C66F-9644-49FC-B5F8-3FA3EE49FAC8}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
18
Lab4/Moderator/Moderator.csproj
Normal file
18
Lab4/Moderator/Moderator.csproj
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Bogus" Version="35.6.1" />
|
||||||
|
<PackageReference Include="NLog" Version="5.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
49
Lab4/Moderator/ModeratorLogic.cs
Normal file
49
Lab4/Moderator/ModeratorLogic.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using ChatRoomContract;
|
||||||
|
using NLog;
|
||||||
|
using Bogus;
|
||||||
|
|
||||||
|
namespace Moderator;
|
||||||
|
|
||||||
|
public class ModeratorLogic
|
||||||
|
{
|
||||||
|
private IChatRoomService chatRoom;
|
||||||
|
|
||||||
|
public ModeratorLogic(IChatRoomService chatRoom)
|
||||||
|
{
|
||||||
|
this.chatRoom = chatRoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
var faker = new Faker("en");
|
||||||
|
|
||||||
|
var name = faker.Name.FullName();
|
||||||
|
int clientId = chatRoom.RegisterClient(name);
|
||||||
|
log.Info($"Registered with client id {clientId}");
|
||||||
|
|
||||||
|
Console.Title = $"Moderator | {name} | {clientId}";
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
Lab4/ModeratorGrpc/Moderator.cs
Normal file
52
Lab4/ModeratorGrpc/Moderator.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using ChatRoomContract;
|
||||||
|
using NLog;
|
||||||
|
using Moderator;
|
||||||
|
|
||||||
|
namespace ModeratorGrpc;
|
||||||
|
|
||||||
|
internal class Moderator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures logging subsystem.
|
||||||
|
/// </summary>
|
||||||
|
private static void ConfigureLogging()
|
||||||
|
{
|
||||||
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
|
||||||
|
var console =
|
||||||
|
new NLog.Targets.ConsoleTarget("console")
|
||||||
|
{
|
||||||
|
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
|
||||||
|
};
|
||||||
|
config.AddTarget(console);
|
||||||
|
config.AddRuleForAllLevels(console);
|
||||||
|
|
||||||
|
LogManager.Configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
ConfigureLogging();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var client = new ChatRoomGrpcClient("http://127.0.0.1:5000");
|
||||||
|
var moderator = new ModeratorLogic(client);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
moderator.Run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//log whatever exception to console
|
||||||
|
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
|
||||||
|
|
||||||
|
//prevent console spamming
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Lab4/ModeratorGrpc/ModeratorGrpc.csproj
Normal file
19
Lab4/ModeratorGrpc/ModeratorGrpc.csproj
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="NLog" Version="5.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
|
||||||
|
<ProjectReference Include="..\Moderator\Moderator.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
61
Lab4/ModeratorRabbitMQ/Moderator.cs
Normal file
61
Lab4/ModeratorRabbitMQ/Moderator.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using ChatRoomContract;
|
||||||
|
using NLog;
|
||||||
|
using Bogus;
|
||||||
|
using Moderator;
|
||||||
|
|
||||||
|
namespace ModeratorRabbitMQ;
|
||||||
|
|
||||||
|
internal class Moderator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures logging subsystem.
|
||||||
|
/// </summary>
|
||||||
|
private static void ConfigureLogging()
|
||||||
|
{
|
||||||
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
|
||||||
|
var console =
|
||||||
|
new NLog.Targets.ConsoleTarget("console")
|
||||||
|
{
|
||||||
|
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
|
||||||
|
};
|
||||||
|
config.AddTarget(console);
|
||||||
|
config.AddRuleForAllLevels(console);
|
||||||
|
|
||||||
|
LogManager.Configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entry point
|
||||||
|
/// </summary>
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
ConfigureLogging();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var chatRoom = new ChatRoomRabbitMQClient(
|
||||||
|
RabbitMQConfig.CreateConnection(),
|
||||||
|
RabbitMQConfig.ExchangeName,
|
||||||
|
RabbitMQConfig.CreateClientQueueName(),
|
||||||
|
RabbitMQConfig.ServerQueueName
|
||||||
|
);
|
||||||
|
var moderator = new ModeratorLogic(chatRoom);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
moderator.Run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//log whatever exception to console
|
||||||
|
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
|
||||||
|
|
||||||
|
//prevent console spamming
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Lab4/ModeratorRabbitMQ/ModeratorRabbitMQ.csproj
Normal file
19
Lab4/ModeratorRabbitMQ/ModeratorRabbitMQ.csproj
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="NLog" Version="5.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
|
||||||
|
<ProjectReference Include="..\Moderator\Moderator.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
52
Lab4/ModeratorRest/Moderator.cs
Normal file
52
Lab4/ModeratorRest/Moderator.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using ChatRoomContract;
|
||||||
|
using NLog;
|
||||||
|
using Moderator;
|
||||||
|
|
||||||
|
namespace ModeratorRest;
|
||||||
|
|
||||||
|
internal class Moderator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures logging subsystem.
|
||||||
|
/// </summary>
|
||||||
|
private static void ConfigureLogging()
|
||||||
|
{
|
||||||
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
|
||||||
|
var console =
|
||||||
|
new NLog.Targets.ConsoleTarget("console")
|
||||||
|
{
|
||||||
|
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
|
||||||
|
};
|
||||||
|
config.AddTarget(console);
|
||||||
|
config.AddRuleForAllLevels(console);
|
||||||
|
|
||||||
|
LogManager.Configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
ConfigureLogging();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var client = new ChatRoomRestClient("http://127.0.0.1:5000", new HttpClient());
|
||||||
|
var moderator = new ModeratorLogic(client);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
moderator.Run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//log whatever exception to console
|
||||||
|
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
|
||||||
|
|
||||||
|
//prevent console spamming
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Lab4/ModeratorRest/ModeratorRest.csproj
Normal file
19
Lab4/ModeratorRest/ModeratorRest.csproj
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="NLog" Version="5.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
|
||||||
|
<ProjectReference Include="..\Moderator\Moderator.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
81
Lab4/ModeratorSimpleRPC/Moderator.cs
Normal file
81
Lab4/ModeratorSimpleRPC/Moderator.cs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
using ChatRoomContract;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NLog;
|
||||||
|
using SimpleRpc.Transports.Http.Client;
|
||||||
|
using SimpleRpc.Serialization.Hyperion;
|
||||||
|
using SimpleRpc.Transports;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Moderator;
|
||||||
|
|
||||||
|
namespace ModeratorSimpleRPC;
|
||||||
|
|
||||||
|
internal class Moderator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures logging subsystem.
|
||||||
|
/// </summary>
|
||||||
|
private static void ConfigureLogging()
|
||||||
|
{
|
||||||
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
|
||||||
|
var console =
|
||||||
|
new NLog.Targets.ConsoleTarget("console")
|
||||||
|
{
|
||||||
|
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
|
||||||
|
};
|
||||||
|
config.AddTarget(console);
|
||||||
|
config.AddRuleForAllLevels(console);
|
||||||
|
|
||||||
|
LogManager.Configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
//configure logging
|
||||||
|
ConfigureLogging();
|
||||||
|
|
||||||
|
//initialize random number generator
|
||||||
|
var rnd = new Random();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
//connect to the server, get service client proxy
|
||||||
|
var sc = new ServiceCollection();
|
||||||
|
sc
|
||||||
|
.AddSimpleRpcClient(
|
||||||
|
"chatRoomService", //must be same as on line 86
|
||||||
|
new HttpClientTransportOptions
|
||||||
|
{
|
||||||
|
Url = "http://127.0.0.1:5001/simplerpc",
|
||||||
|
Serializer = "HyperionMessageSerializer"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.AddSimpleRpcHyperionSerializer();
|
||||||
|
|
||||||
|
sc.AddSimpleRpcProxy<IChatRoomService>("chatRoomService"); //must be same as on line 77
|
||||||
|
|
||||||
|
var sp = sc.BuildServiceProvider();
|
||||||
|
|
||||||
|
var chatRoom = sp.GetService<IChatRoomService>();
|
||||||
|
Debug.Assert(chatRoom != null);
|
||||||
|
|
||||||
|
var moderator = new ModeratorLogic(chatRoom);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
moderator.Run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//log whatever exception to console
|
||||||
|
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
|
||||||
|
|
||||||
|
//prevent console spamming
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
22
Lab4/ModeratorSimpleRPC/ModeratorSimpleRPC.csproj
Normal file
22
Lab4/ModeratorSimpleRPC/ModeratorSimpleRPC.csproj
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
|
<PackageReference Include="NLog" Version="5.3.4" />
|
||||||
|
<PackageReference Include="SimpleRpc" Version="1.0.0-beta1" />
|
||||||
|
<PackageReference Include="SimpleRpc.Serialization.Hyperion" Version="1.0.0-beta1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
|
||||||
|
<ProjectReference Include="..\Moderator\Moderator.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
18
Lab4/Participant/Participant.csproj
Normal file
18
Lab4/Participant/Participant.csproj
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Bogus" Version="35.6.1" />
|
||||||
|
<PackageReference Include="NLog" Version="5.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
54
Lab4/Participant/ParticipantLogic.cs
Normal file
54
Lab4/Participant/ParticipantLogic.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
using ChatRoomContract;
|
||||||
|
using Bogus;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace Participant;
|
||||||
|
|
||||||
|
public class ParticipantLogic
|
||||||
|
{
|
||||||
|
private IChatRoomService chatRoom;
|
||||||
|
|
||||||
|
public ParticipantLogic(IChatRoomService chatRoom)
|
||||||
|
{
|
||||||
|
this.chatRoom = chatRoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
var faker = new Faker("en");
|
||||||
|
var rnd = new Random();
|
||||||
|
|
||||||
|
var name = faker.Name.FullName();
|
||||||
|
int clientId = chatRoom.RegisterClient(name);
|
||||||
|
log.Info($"Registered with client id {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");
|
||||||
|
var blockedUntil = chatRoom.GetBlockedUntil(clientId);
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
if (blockedUntil != null && blockedUntil > now)
|
||||||
|
{
|
||||||
|
var delta = blockedUntil.Value - now;
|
||||||
|
log.Info($"Waiting {delta.TotalSeconds:F3}s until block expires");
|
||||||
|
Thread.Sleep((int)delta.TotalMilliseconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(2 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
Lab4/ParticipantGrpc/Participant.cs
Normal file
53
Lab4/ParticipantGrpc/Participant.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
using ChatRoomContract;
|
||||||
|
using NLog;
|
||||||
|
using Bogus;
|
||||||
|
using Participant;
|
||||||
|
|
||||||
|
namespace ParticipantGrpc;
|
||||||
|
|
||||||
|
internal class Participant
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures logging subsystem.
|
||||||
|
/// </summary>
|
||||||
|
private static void ConfigureLogging()
|
||||||
|
{
|
||||||
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
|
||||||
|
var console =
|
||||||
|
new NLog.Targets.ConsoleTarget("console")
|
||||||
|
{
|
||||||
|
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
|
||||||
|
};
|
||||||
|
config.AddTarget(console);
|
||||||
|
config.AddRuleForAllLevels(console);
|
||||||
|
|
||||||
|
LogManager.Configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
ConfigureLogging();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var client = new ChatRoomGrpcClient("http://127.0.0.1:5000");
|
||||||
|
var participant = new ParticipantLogic(client);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
participant.Run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//log whatever exception to console
|
||||||
|
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
|
||||||
|
|
||||||
|
//prevent console spamming
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Lab4/ParticipantGrpc/ParticipantGrpc.csproj
Normal file
19
Lab4/ParticipantGrpc/ParticipantGrpc.csproj
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="NLog" Version="5.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
|
||||||
|
<ProjectReference Include="..\Participant\Participant.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
60
Lab4/ParticipantRabbitMQ/Participant.cs
Normal file
60
Lab4/ParticipantRabbitMQ/Participant.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using ChatRoomContract;
|
||||||
|
using NLog;
|
||||||
|
using Participant;
|
||||||
|
|
||||||
|
namespace ParticipantRabbitMQ;
|
||||||
|
|
||||||
|
internal class Participant
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures logging subsystem.
|
||||||
|
/// </summary>
|
||||||
|
private static void ConfigureLogging()
|
||||||
|
{
|
||||||
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
|
||||||
|
var console =
|
||||||
|
new NLog.Targets.ConsoleTarget("console")
|
||||||
|
{
|
||||||
|
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
|
||||||
|
};
|
||||||
|
config.AddTarget(console);
|
||||||
|
config.AddRuleForAllLevels(console);
|
||||||
|
|
||||||
|
LogManager.Configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entry point
|
||||||
|
/// </summary>
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
ConfigureLogging();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var chatRoom = new ChatRoomRabbitMQClient(
|
||||||
|
RabbitMQConfig.CreateConnection(),
|
||||||
|
RabbitMQConfig.ExchangeName,
|
||||||
|
RabbitMQConfig.CreateClientQueueName(),
|
||||||
|
RabbitMQConfig.ServerQueueName
|
||||||
|
);
|
||||||
|
var participant = new ParticipantLogic(chatRoom);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
participant.Run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//log whatever exception to console
|
||||||
|
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
|
||||||
|
|
||||||
|
//prevent console spamming
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Lab4/ParticipantRabbitMQ/ParticipantRabbitMQ.csproj
Normal file
19
Lab4/ParticipantRabbitMQ/ParticipantRabbitMQ.csproj
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="NLog" Version="5.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
|
||||||
|
<ProjectReference Include="..\Participant\Participant.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
52
Lab4/ParticipantRest/Participant.cs
Normal file
52
Lab4/ParticipantRest/Participant.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using ChatRoomContract;
|
||||||
|
using NLog;
|
||||||
|
using Participant;
|
||||||
|
|
||||||
|
namespace ParticipantRest;
|
||||||
|
|
||||||
|
internal class Participant
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures logging subsystem.
|
||||||
|
/// </summary>
|
||||||
|
private static void ConfigureLogging()
|
||||||
|
{
|
||||||
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
|
||||||
|
var console =
|
||||||
|
new NLog.Targets.ConsoleTarget("console")
|
||||||
|
{
|
||||||
|
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
|
||||||
|
};
|
||||||
|
config.AddTarget(console);
|
||||||
|
config.AddRuleForAllLevels(console);
|
||||||
|
|
||||||
|
LogManager.Configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
ConfigureLogging();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var client = new ChatRoomRestClient("http://127.0.0.1:5000", new HttpClient());
|
||||||
|
var participant = new ParticipantLogic(client);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
participant.Run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//log whatever exception to console
|
||||||
|
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
|
||||||
|
|
||||||
|
//prevent console spamming
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Lab4/ParticipantRest/ParticipantRest.csproj
Normal file
19
Lab4/ParticipantRest/ParticipantRest.csproj
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="NLog" Version="5.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
|
||||||
|
<ProjectReference Include="..\Participant\Participant.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
81
Lab4/ParticipantSimpleRPC/Participant.cs
Normal file
81
Lab4/ParticipantSimpleRPC/Participant.cs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
using ChatRoomContract;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NLog;
|
||||||
|
using SimpleRpc.Transports.Http.Client;
|
||||||
|
using SimpleRpc.Serialization.Hyperion;
|
||||||
|
using SimpleRpc.Transports;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Participant;
|
||||||
|
|
||||||
|
namespace ParticipantSimpleRPC;
|
||||||
|
|
||||||
|
internal class Participant
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configures logging subsystem.
|
||||||
|
/// </summary>
|
||||||
|
private static void ConfigureLogging()
|
||||||
|
{
|
||||||
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
|
||||||
|
var console =
|
||||||
|
new NLog.Targets.ConsoleTarget("console")
|
||||||
|
{
|
||||||
|
Layout = @"${date:format=HH\:mm\:ss}|${level}| ${message} ${exception}"
|
||||||
|
};
|
||||||
|
config.AddTarget(console);
|
||||||
|
config.AddRuleForAllLevels(console);
|
||||||
|
|
||||||
|
LogManager.Configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
//configure logging
|
||||||
|
ConfigureLogging();
|
||||||
|
|
||||||
|
//initialize random number generator
|
||||||
|
var rnd = new Random();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
//connect to the server, get service client proxy
|
||||||
|
var sc = new ServiceCollection();
|
||||||
|
sc
|
||||||
|
.AddSimpleRpcClient(
|
||||||
|
"chatRoomService", //must be same as on line 86
|
||||||
|
new HttpClientTransportOptions
|
||||||
|
{
|
||||||
|
Url = "http://127.0.0.1:5001/simplerpc",
|
||||||
|
Serializer = "HyperionMessageSerializer"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.AddSimpleRpcHyperionSerializer();
|
||||||
|
|
||||||
|
sc.AddSimpleRpcProxy<IChatRoomService>("chatRoomService"); //must be same as on line 77
|
||||||
|
|
||||||
|
var sp = sc.BuildServiceProvider();
|
||||||
|
|
||||||
|
var chatRoom = sp.GetService<IChatRoomService>();
|
||||||
|
Debug.Assert(chatRoom != null);
|
||||||
|
|
||||||
|
var participant = new ParticipantLogic(chatRoom);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
participant.Run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//log whatever exception to console
|
||||||
|
log.Warn(e, "Unhandled exception caught. Will restart main loop.");
|
||||||
|
|
||||||
|
//prevent console spamming
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
22
Lab4/ParticipantSimpleRPC/ParticipantSimpleRPC.csproj
Normal file
22
Lab4/ParticipantSimpleRPC/ParticipantSimpleRPC.csproj
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
|
<PackageReference Include="NLog" Version="5.3.4" />
|
||||||
|
<PackageReference Include="SimpleRpc" Version="1.0.0-beta1" />
|
||||||
|
<PackageReference Include="SimpleRpc.Serialization.Hyperion" Version="1.0.0-beta1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatRoomContract\ChatRoomContract.csproj" />
|
||||||
|
<ProjectReference Include="..\Participant\Participant.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
Loading…
Reference in New Issue
Block a user