120 lines
4.7 KiB
C#
120 lines
4.7 KiB
C#
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<string, Command> Commands { get; } = new Dictionary<string, Command>();
|
|
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<string> 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;
|
|
}
|
|
}
|
|
} |