mcdiscordbot/MinecraftDiscordBot/Services/RootCommandService.cs

76 lines
3.3 KiB
C#
Raw Normal View History

using Discord.WebSocket;
using Fleck;
using MinecraftDiscordBot.Commands;
using MinecraftDiscordBot.Models;
using Newtonsoft.Json;
namespace MinecraftDiscordBot.Services;
public delegate Task<TResponse> HandleCommandDelegate<TResponse>(SocketUserMessage message, string[] parameters, CancellationToken ct);
public delegate Task HandleCommandDelegate(SocketUserMessage message, string[] parameters, CancellationToken ct);
public class RootCommandService : CommandRouter, ITaskWaitSource {
protected readonly IWebSocketConnection _socket;
public override string HelpTextPrefix => "!";
public RootCommandService(IWebSocketConnection socket, IUserRoleManager roleManager) : base() {
socket.OnMessage = OnMessage;
_socket = socket;
_rs = new RefinedStorageService(this, roleManager);
}
private void OnMessage(string message) {
if (JsonConvert.DeserializeObject<ReplyMessage>(message) is not ReplyMessage msg) return;
IChunkWaiter? waiter;
lock (_syncRoot) if (!_waits.TryGetValue(msg.AnswerId, out waiter)) {
Program.LogWarningAsync("Socket", $"Invalid wait id '{msg.AnswerId}'!");
return;
}
waiter.SetResultState(msg.State);
waiter.AddChunk(msg.Chunk, msg.Total, msg.Result);
if (waiter.Finished || waiter.IsCancellationRequested)
lock (_syncRoot)
_waits.Remove(waiter.ID);
}
public Task Send(string message) => _socket.Send(message);
public Task Send(Message message) => Send(JsonConvert.SerializeObject(message));
private readonly object _syncRoot = new();
private readonly Dictionary<int, IChunkWaiter> _waits = new();
private readonly Random _rnd = new();
public IWebSocketConnectionInfo ConnectionInfo => _socket.ConnectionInfo;
private int GetFreeId() {
var attempts = 0;
while (true) {
var id = _rnd.Next();
if (!_waits.ContainsKey(id))
return id;
Program.LogWarningAsync(Program.WebSocketSource, $"Could not get a free ID after {++attempts} attempts!");
}
}
public ChunkWaiter<T> GetWaiter<T>(Func<string, T> resultParser, CancellationToken ct) {
ChunkWaiter<T> waiter;
lock (_syncRoot) {
waiter = new ChunkWaiter<T>(GetFreeId(), resultParser, ct);
_waits.Add(waiter.ID, waiter);
}
return waiter;
}
private readonly ICommandHandler<ResponseType> _rs;
[CommandHandler("rs", HelpText = "Provides some commands for interacting with the Refined Storage system.")]
public Task<ResponseType> RefinedStorageHandler(SocketUserMessage message, string[] parameters, CancellationToken ct)
=> _rs.HandleCommand(message, parameters, ct);
public static Func<string, T> Deserialize<T>() => msg
=> JsonConvert.DeserializeObject<T>(msg) ?? throw new InvalidProgramException("Empty response!");
public override Task<ResponseType> FallbackHandler(SocketUserMessage message, string method, string[] parameters, CancellationToken ct)
=> throw new ReplyException($"What the fuck do you mean by '{method}'?");
}
public interface ITaskWaitSource {
ChunkWaiter<T> GetWaiter<T>(Func<string, T> resultParser, CancellationToken ct);
Task Send(Message requestMessage);
}