DeukBot4/DeukBot4/MessageHandlers/CommandHandler/CommandHandler.cs

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;
}
}
}