Reorganized and renamed ModTools to ModCommands
This commit is contained in:
parent
06583a1472
commit
bfb699d62f
8 changed files with 76 additions and 66 deletions
|
@ -6,30 +6,35 @@ using System;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Noikoio.RegexBot.Module.ModTools
|
||||
namespace Noikoio.RegexBot.Module.ModCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// ModTools module.
|
||||
/// This class manages reading configuration and creating instances based on it.
|
||||
/// It processes input and looks for messages that intend to invoke commands defined in configuration.
|
||||
/// </summary>
|
||||
class ModTools : BotModule
|
||||
/// <remarks>
|
||||
/// Discord.Net has its own recommended way of implementing commands, but it's not exactly
|
||||
/// done in a way that would easily allow for flexibility and modifications during runtime.
|
||||
/// Thus, reinventing the wheel right here.
|
||||
/// </remarks>
|
||||
class CommandListener : BotModule
|
||||
{
|
||||
public override string Name => "ModTools";
|
||||
public override string Name => "ModCommands";
|
||||
|
||||
public ModTools(DiscordSocketClient client) : base(client)
|
||||
public CommandListener(DiscordSocketClient client) : base(client)
|
||||
{
|
||||
client.MessageReceived += Client_MessageReceived;
|
||||
}
|
||||
|
||||
private async Task Client_MessageReceived(SocketMessage arg)
|
||||
{
|
||||
// Always ignore bots
|
||||
if (arg.Author.IsBot) return;
|
||||
// Always ignore these
|
||||
if (arg.Author.IsBot || arg.Author.IsWebhook) return;
|
||||
|
||||
if (arg.Channel is IGuildChannel) await CommandCheckInvoke(arg);
|
||||
}
|
||||
|
||||
[ConfigSection("ModTools")]
|
||||
[ConfigSection("ModCommands")]
|
||||
public override async Task<object> ProcessConfiguration(JToken configSection)
|
||||
{
|
||||
// Constructor throws exception on config errors
|
||||
|
@ -69,12 +74,13 @@ namespace Noikoio.RegexBot.Module.ModTools
|
|||
{
|
||||
try
|
||||
{
|
||||
await Log($"'{c.Label}' invoked by {arg.Author.ToString()} in {g.Name}/#{arg.Channel.Name}");
|
||||
await c.Invoke(g, arg);
|
||||
// TODO Custom invocation log messages? Not by the user, but by the command.
|
||||
await Log($"{g.Name}/#{arg.Channel.Name}: {arg.Author} invoked {arg.Content}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Log($"Encountered an error for the command '{c.Label}'. Details follow:");
|
||||
await Log($"Encountered an unhandled exception while processing '{c.Label}'. Details follow:");
|
||||
await Log(ex.ToString());
|
||||
}
|
||||
}
|
|
@ -7,9 +7,9 @@ using System.Linq;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Noikoio.RegexBot.Module.ModTools.Commands
|
||||
namespace Noikoio.RegexBot.Module.ModCommands.Commands
|
||||
{
|
||||
class BanKick : CommandBase
|
||||
class BanKick : Command
|
||||
{
|
||||
// Ban and kick commands are highly similar in implementation, and thus are handled in a single class.
|
||||
protected enum CommandMode { Ban, Kick }
|
||||
|
@ -28,7 +28,7 @@ namespace Noikoio.RegexBot.Module.ModTools.Commands
|
|||
// "notifymsg" - Message to send to the target user being acted upon. Default message is used
|
||||
// if the value is not specified. If a blank value is given, the feature is disabled.
|
||||
// Takes the special values $s for server name and $r for reason text.
|
||||
protected BanKick(ModTools l, string label, JObject conf, CommandMode mode) : base(l, label, conf)
|
||||
protected BanKick(CommandListener l, string label, JObject conf, CommandMode mode) : base(l, label, conf)
|
||||
{
|
||||
_mode = mode;
|
||||
_forceReason = conf["forcereason"]?.Value<bool>() ?? false;
|
||||
|
@ -190,7 +190,7 @@ namespace Noikoio.RegexBot.Module.ModTools.Commands
|
|||
|
||||
private async Task SendUsageMessage(SocketMessage m, string message)
|
||||
{
|
||||
string desc = $"{this.Command} [user or user ID] " + (_forceReason ? "[reason]" : "*[reason]*") + "\n";
|
||||
string desc = $"{this.Trigger} [user or user ID] " + (_forceReason ? "[reason]" : "*[reason]*") + "\n";
|
||||
desc += "Removes the given user from this server and prevents the user from rejoining. ";
|
||||
desc += (_forceReason ? "L" : "Optionally l") + "ogs the reason for the ban to the Audit Log.";
|
||||
if (_purgeDays > 0)
|
||||
|
@ -217,13 +217,13 @@ namespace Noikoio.RegexBot.Module.ModTools.Commands
|
|||
|
||||
class Ban : BanKick
|
||||
{
|
||||
public Ban(ModTools l, string label, JObject conf)
|
||||
public Ban(CommandListener l, string label, JObject conf)
|
||||
: base(l, label, conf, CommandMode.Ban) { }
|
||||
}
|
||||
|
||||
class Kick : BanKick
|
||||
{
|
||||
public Kick(ModTools l, string label, JObject conf)
|
||||
public Kick(CommandListener l, string label, JObject conf)
|
||||
: base(l, label, conf, CommandMode.Kick) { }
|
||||
}
|
||||
}
|
|
@ -2,12 +2,12 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Noikoio.RegexBot.Module.ModTools.Commands
|
||||
namespace Noikoio.RegexBot.Module.ModCommands.Commands
|
||||
{
|
||||
class ConfReload : CommandBase
|
||||
class ConfReload : Command
|
||||
{
|
||||
// No configuration.
|
||||
public ConfReload(ModTools l, string label, JObject conf) : base(l, label, conf) { }
|
||||
public ConfReload(CommandListener l, string label, JObject conf) : base(l, label, conf) { }
|
||||
|
||||
// Usage: (command)
|
||||
public override async Task Invoke(SocketGuild g, SocketMessage msg)
|
|
@ -4,13 +4,13 @@ using Newtonsoft.Json.Linq;
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Noikoio.RegexBot.Module.ModTools.Commands
|
||||
namespace Noikoio.RegexBot.Module.ModCommands.Commands
|
||||
{
|
||||
class Say : CommandBase
|
||||
class Say : Command
|
||||
{
|
||||
// No configuration at the moment.
|
||||
// TODO: Whitelist/blacklist - to limit which channels it can "say" into
|
||||
public Say(ModTools l, string label, JObject conf) : base(l, label, conf) { }
|
||||
public Say(CommandListener l, string label, JObject conf) : base(l, label, conf) { }
|
||||
|
||||
public override async Task Invoke(SocketGuild g, SocketMessage msg)
|
||||
{
|
||||
|
@ -33,7 +33,7 @@ namespace Noikoio.RegexBot.Module.ModTools.Commands
|
|||
|
||||
private async Task SendUsageMessage(SocketMessage m, string message)
|
||||
{
|
||||
string desc = $"{this.Command} [channel] [message]\n";
|
||||
string desc = $"{this.Trigger} [channel] [message]\n";
|
||||
desc += "Displays the given message exactly as specified to the given channel.";
|
||||
|
||||
var usageEmbed = new EmbedBuilder()
|
|
@ -6,14 +6,14 @@ using System.Linq;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Noikoio.RegexBot.Module.ModTools.Commands
|
||||
namespace Noikoio.RegexBot.Module.ModCommands.Commands
|
||||
{
|
||||
class Unban : CommandBase
|
||||
class Unban : Command
|
||||
{
|
||||
// No configuration.
|
||||
// TODO bring in some options from BanKick. Particularly custom success msg.
|
||||
// TODO when ModLogs fully implemented, add a reason?
|
||||
public Unban(ModTools l, string label, JObject conf) : base(l, label, conf) { }
|
||||
public Unban(CommandListener l, string label, JObject conf) : base(l, label, conf) { }
|
||||
|
||||
#region Strings
|
||||
const string FailPrefix = ":x: **Unable to unban:** ";
|
||||
|
@ -93,7 +93,7 @@ namespace Noikoio.RegexBot.Module.ModTools.Commands
|
|||
|
||||
private async Task SendUsageMessage(SocketMessage m, string message)
|
||||
{
|
||||
string desc = $"{this.Command} [user or user ID]\n";
|
||||
string desc = $"{this.Trigger} [user or user ID]\n";
|
||||
desc += "Unbans the given user, allowing them to rejoin the server.";
|
||||
|
||||
var usageEmbed = new EmbedBuilder()
|
|
@ -9,24 +9,25 @@ using System.Reflection;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Noikoio.RegexBot.Module.ModTools
|
||||
namespace Noikoio.RegexBot.Module.ModCommands.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for ModTools command.
|
||||
/// We are not using Discord.Net's Commands extension, as it does not allow for changes during runtime.
|
||||
/// Base class for a command within the module.
|
||||
/// After implementing, don't forget to add a reference to
|
||||
/// <see cref="CreateInstance(CommandListener, JProperty)"/>.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Label}-type command")]
|
||||
abstract class CommandBase
|
||||
[DebuggerDisplay("Command def: {Label}")]
|
||||
abstract class Command
|
||||
{
|
||||
private readonly ModTools _modtools;
|
||||
private readonly CommandListener _modtools;
|
||||
private readonly string _label;
|
||||
private readonly string _command;
|
||||
|
||||
protected ModTools Mt => _modtools;
|
||||
protected CommandListener Module => _modtools;
|
||||
public string Label => _label;
|
||||
public string Command => _command;
|
||||
public string Trigger => _command;
|
||||
|
||||
protected CommandBase(ModTools l, string label, JObject conf)
|
||||
public Command(CommandListener l, string label, JObject conf)
|
||||
{
|
||||
_modtools = l;
|
||||
_label = label;
|
||||
|
@ -46,14 +47,14 @@ namespace Noikoio.RegexBot.Module.ModTools
|
|||
new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
// Define all command types and their corresponding Types here
|
||||
{ "ban", typeof(Commands.Ban) },
|
||||
{ "confreload", typeof(Commands.ConfReload) },
|
||||
{ "kick", typeof(Commands.Kick) },
|
||||
{ "say", typeof(Commands.Say) },
|
||||
{ "unban", typeof(Commands.Unban) }
|
||||
{ "ban", typeof(Ban) },
|
||||
{ "confreload", typeof(ConfReload) },
|
||||
{ "kick", typeof(Kick) },
|
||||
{ "say", typeof(Say) },
|
||||
{ "unban", typeof(Unban) }
|
||||
});
|
||||
|
||||
public static CommandBase CreateInstance(ModTools root, JProperty def)
|
||||
public static Command CreateInstance(CommandListener root, JProperty def)
|
||||
{
|
||||
string label = def.Name;
|
||||
if (string.IsNullOrWhiteSpace(label)) throw new RuleImportException("Label cannot be blank.");
|
||||
|
@ -68,13 +69,11 @@ namespace Noikoio.RegexBot.Module.ModTools
|
|||
string ctypestr = definition["type"]?.Value<string>();
|
||||
if (string.IsNullOrWhiteSpace(ctypestr))
|
||||
throw new RuleImportException($"Value 'type' must be specified in definition for '{label}'.");
|
||||
Type ctype;
|
||||
if (!_commands.TryGetValue(ctypestr, out ctype))
|
||||
throw new RuleImportException($"The given 'type' value is invalid in definition for '{label}'.");
|
||||
|
||||
if (_commands.TryGetValue(ctypestr, out Type ctype))
|
||||
{
|
||||
try
|
||||
{
|
||||
return (CommandBase)Activator.CreateInstance(ctype, root, label, definition);
|
||||
return (Command)Activator.CreateInstance(ctype, root, label, definition);
|
||||
}
|
||||
catch (TargetInvocationException ex)
|
||||
{
|
||||
|
@ -83,9 +82,14 @@ namespace Noikoio.RegexBot.Module.ModTools
|
|||
else throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuleImportException($"The given 'type' value is invalid in definition for '{label}'.");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Helper methods and values
|
||||
#region Helper methods and common values
|
||||
protected static readonly Regex UserMention = new Regex(@"<@!?(?<snowflake>\d+)>", RegexOptions.Compiled);
|
||||
protected static readonly Regex RoleMention = new Regex(@"<@&(?<snowflake>\d+)>", RegexOptions.Compiled);
|
||||
protected static readonly Regex ChannelMention = new Regex(@"<#(?<snowflake>\d+)>", RegexOptions.Compiled);
|
|
@ -1,21 +1,22 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using Noikoio.RegexBot.ConfigItem;
|
||||
using Noikoio.RegexBot.Module.ModCommands.Commands;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Noikoio.RegexBot.Module.ModTools
|
||||
namespace Noikoio.RegexBot.Module.ModCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents ModTools configuration within one server.
|
||||
/// </summary>
|
||||
class ConfigItem
|
||||
{
|
||||
private readonly ReadOnlyDictionary<string, CommandBase> _cmdInstances;
|
||||
private readonly ReadOnlyDictionary<string, Command> _cmdInstances;
|
||||
|
||||
public ReadOnlyDictionary<string, CommandBase> Commands => _cmdInstances;
|
||||
public ReadOnlyDictionary<string, Command> Commands => _cmdInstances;
|
||||
|
||||
public ConfigItem(ModTools instance, JToken inconf)
|
||||
public ConfigItem(CommandListener instance, JToken inconf)
|
||||
{
|
||||
if (inconf.Type != JTokenType.Object)
|
||||
{
|
||||
|
@ -23,9 +24,8 @@ namespace Noikoio.RegexBot.Module.ModTools
|
|||
}
|
||||
var config = (JObject)inconf;
|
||||
|
||||
|
||||
// Command instances
|
||||
var commands = new Dictionary<string, CommandBase>(StringComparer.OrdinalIgnoreCase);
|
||||
// Command instance creation
|
||||
var commands = new Dictionary<string, Command>(StringComparer.OrdinalIgnoreCase);
|
||||
var commandconf = config["Commands"];
|
||||
if (commandconf != null)
|
||||
{
|
||||
|
@ -37,16 +37,16 @@ namespace Noikoio.RegexBot.Module.ModTools
|
|||
foreach (var def in commandconf.Children<JProperty>())
|
||||
{
|
||||
string label = def.Name;
|
||||
var cmd = CommandBase.CreateInstance(instance, def);
|
||||
if (commands.ContainsKey(cmd.Command))
|
||||
var cmd = Command.CreateInstance(instance, def);
|
||||
if (commands.ContainsKey(cmd.Trigger))
|
||||
throw new RuleImportException(
|
||||
$"{label}: 'command' value must not be equal to that of another definition. " +
|
||||
$"Given value is being used for {commands[cmd.Command].Label}.");
|
||||
$"Given value is being used for \"{commands[cmd.Trigger].Label}\".");
|
||||
|
||||
commands.Add(cmd.Command, cmd);
|
||||
commands.Add(cmd.Trigger, cmd);
|
||||
}
|
||||
}
|
||||
_cmdInstances = new ReadOnlyDictionary<string, CommandBase>(commands);
|
||||
_cmdInstances = new ReadOnlyDictionary<string, Command>(commands);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -56,7 +56,7 @@ namespace Noikoio.RegexBot
|
|||
{
|
||||
new Module.DMLogger.DMLogger(_client),
|
||||
new Module.AutoMod.AutoMod(_client),
|
||||
new Module.ModTools.ModTools(_client),
|
||||
new Module.ModCommands.CommandListener(_client),
|
||||
new Module.AutoRespond.AutoRespond(_client),
|
||||
new EntityCache.Module(_client) // EntityCache goes before anything else that uses its data
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue