using Discord.WebSocket; using MinecraftDiscordBot.Models; using MinecraftDiscordBot.Services; using System.Reflection; using System.Text; namespace MinecraftDiscordBot.Commands; public abstract class CommandRouter : ICommandHandler { public record struct HandlerStruct(HandleCommandDelegate Delegate, CommandHandlerAttribute Attribute); private readonly Dictionary _handlers = new(); public abstract string HelpTextPrefix { get; } public CommandRouter() { foreach (var method in GetType().GetMethods()) if (GetHandlerAttribute(method) is CommandHandlerAttribute attribute) try { _handlers.Add(attribute.CommandName, new(method.CreateDelegate>(this), attribute)); } catch (Exception) { Program.LogWarning("CommandRouter", $"Could not add delegate for method {attribute.CommandName} in function {method.ReturnType} {method.Name}(...)!"); throw; } } [CommandHandler("help", HelpText = "Show this help information!")] public virtual Task GetHelpText(SocketUserMessage message, string[] parameters, CancellationToken ct) => Task.FromResult(ResponseType.AsString(GenerateHelp())); private static CommandHandlerAttribute? GetHandlerAttribute(MethodInfo method) => method.GetCustomAttributes(typeof(CommandHandlerAttribute), true).OfType().FirstOrDefault(); public virtual Task RootAnswer(SocketUserMessage message, CancellationToken ct) => GetHelpText(message, Array.Empty(), ct); public abstract Task FallbackHandler(SocketUserMessage message, string method, string[] parameters, CancellationToken ct); public Task HandleCommand(SocketUserMessage message, string[] parameters, CancellationToken ct) => parameters is { Length: 0 } ? RootAnswer(message, ct) : _handlers.TryGetValue(parameters[0], out var handler) ? handler.Delegate(message, parameters[1..], ct) : FallbackHandler(message, parameters[0], parameters[1..], ct); private string GenerateHelp() { var sb = new StringBuilder(); sb.Append("Command usage:"); foreach (var (name, handler) in _handlers) { sb.Append($"\n{HelpTextPrefix}{name}"); if (handler.Attribute.HelpText is string help) sb.Append($": {help}"); } return sb.ToString(); } }