using Discord.WebSocket; using Fleck; using MinecraftDiscordBot.Commands; using MinecraftDiscordBot.Models; using Newtonsoft.Json; namespace MinecraftDiscordBot.Services; public delegate Task HandleCommandDelegate(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) { switch (Message.Deserialize(message)) { case ReplyMessage msg: 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); break; default: Program.LogInfo(Program.WebSocketSource, $"Received unhandled message: {message}!"); break; } } 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 _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 GetWaiter(Func resultParser, CancellationToken ct) { ChunkWaiter waiter; lock (_syncRoot) { waiter = new ChunkWaiter(GetFreeId(), resultParser, ct); _waits.Add(waiter.ID, waiter); } return waiter; } private readonly ICommandHandler _rs; [CommandHandler("rs", HelpText = "Provides some commands for interacting with the Refined Storage system.")] public Task RefinedStorageHandler(SocketUserMessage message, string[] parameters, CancellationToken ct) => _rs.HandleCommand(message, parameters, ct); public static Func Deserialize() => msg => JsonConvert.DeserializeObject(msg) ?? throw new InvalidProgramException("Empty response!"); public override Task FallbackHandler(SocketUserMessage message, string method, string[] parameters, CancellationToken ct) => throw new ReplyException($"What the fuck do you mean by '{method}'?"); } public interface ITaskWaitSource { ChunkWaiter GetWaiter(Func resultParser, CancellationToken ct); Task Send(Message requestMessage); }