using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using DeukBot4.MessageHandlers.CommandHandler.RequestStructure; using DeukBot4.MessageHandlers.Permissions; using DeukBot4.Utilities; using Discord; using Discord.WebSocket; namespace DeukBot4.MessageHandlers.CommandHandler { public static class CommandHandler { public static Dictionary Commands { get; } = new Dictionary(); public const char CommandTrigger = '!'; public static void Build() { var commandContainers = typeof(CommandHandler).Assembly.GetTypes() .Where(x => typeof(CommandContainerBase).IsAssignableFrom(x) && !x.IsAbstract); foreach (var commandContainer in commandContainers) { if (!(Activator.CreateInstance(commandContainer) is CommandContainerBase obj)) continue; var commands = obj.GetCommands(); foreach (var command in commands) { Commands.Add(command.Name.ToLowerInvariant(), command); foreach (var commandAlternative in command.Alternatives) { Commands.Add(commandAlternative.ToLowerInvariant(), command); } } Logger.Main.Log( $"Loaded following commands for container {obj.Name}: {commands.Select(x => x.Name).Join(", ")}"); } } public static async Task HandleMessage(SocketMessage message) { if (string.IsNullOrWhiteSpace(message.Content)) return; if (message.Content[0] != CommandTrigger && message.MentionedUsers.All(x => x.Id != Program.BotId)) return; if (message.Content[1] == '!') return; var req = await CommandRequest.Create(message); var resultCode = req.Response; switch (resultCode) { case CommandRequest.RequestCode.Invalid: await Logger.Main.LogError("Invalid content: " + message.Content); return; case CommandRequest.RequestCode.InvalidParameters: await Logger.Main.LogError("Invalid parameters: " + message.Content); break; case CommandRequest.RequestCode.Forbidden: await Logger.Main.Log( $"Unauthorized user tried to run command: {message.Author.Username} -> {message.Content}"); break; case CommandRequest.RequestCode.OK: if (!(message.Channel is IGuildChannel) && req.Request.Command.ForbidInPm) { await Logger.Main.Log( $"User is trying to use blocked command in PM: {message.Author.Username}"); return; } try { await req.Request.Command.Invoke(req.Request); } catch (Exception e) { await Logger.Main.Log("An error occured: \n" + e); } break; case CommandRequest.RequestCode.UnknownCommand: var permission = await PermissionValidator.GetUserPermissionLevel(message); var similar = await GetSimilarCommand(req.CommandName, permission); await message.Channel.SendMessageAsync( $"Unknown command: ``{req.CommandName}``. Did you mean: ``{similar}``? " + $"Alternatively, use ``{CommandTrigger}help`` for a list of all commands"); break; default: throw new ArgumentOutOfRangeException(); } } private static async Task GetSimilarCommand(string command, PermissionLevel permission) { var closestString = ""; var similarity = int.MaxValue; foreach (var cmd in Commands) { if (cmd.Value.Permission > permission) continue; var distance = Lehvenstein.LevenshteinDistance(command, cmd.Key); if (distance >= similarity) continue; similarity = distance; closestString = cmd.Key; } return closestString; } public static Command GetCommand(string name) { return Commands.TryGetValue(name.ToLowerInvariant(), out var com) ? com : null; } } }