2018-03-10 07:16:30 +00:00
|
|
|
|
using Discord;
|
|
|
|
|
using Discord.WebSocket;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
using Noikoio.RegexBot.ConfigItem;
|
2017-10-09 18:54:12 +00:00
|
|
|
|
using System;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
using System.Collections.Generic;
|
2017-10-09 18:54:12 +00:00
|
|
|
|
using System.Collections.ObjectModel;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
using System.Diagnostics;
|
2018-03-10 18:38:50 +00:00
|
|
|
|
using System.Linq;
|
2017-10-09 18:54:12 +00:00
|
|
|
|
using System.Reflection;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2018-03-10 06:17:55 +00:00
|
|
|
|
namespace Noikoio.RegexBot.Module.ModCommands.Commands
|
2017-08-06 20:05:44 +00:00
|
|
|
|
{
|
2017-12-03 06:14:00 +00:00
|
|
|
|
/// <summary>
|
2018-03-10 06:17:55 +00:00
|
|
|
|
/// Base class for a command within the module.
|
|
|
|
|
/// After implementing, don't forget to add a reference to
|
2018-03-22 06:27:19 +00:00
|
|
|
|
/// <see cref="CreateInstance(ModCommands, JProperty)"/>.
|
2017-12-03 06:14:00 +00:00
|
|
|
|
/// </summary>
|
2018-03-10 06:17:55 +00:00
|
|
|
|
[DebuggerDisplay("Command def: {Label}")]
|
|
|
|
|
abstract class Command
|
2017-08-06 20:05:44 +00:00
|
|
|
|
{
|
2018-03-22 06:27:19 +00:00
|
|
|
|
private readonly ModCommands _mod;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
private readonly string _label;
|
|
|
|
|
private readonly string _command;
|
2017-12-08 18:30:41 +00:00
|
|
|
|
|
2018-03-22 06:27:19 +00:00
|
|
|
|
protected ModCommands Module => _mod;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
public string Label => _label;
|
2018-03-10 06:17:55 +00:00
|
|
|
|
public string Trigger => _command;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
|
2018-03-22 06:27:19 +00:00
|
|
|
|
public Command(ModCommands l, string label, JObject conf)
|
2017-08-06 20:05:44 +00:00
|
|
|
|
{
|
2018-03-17 20:51:09 +00:00
|
|
|
|
_mod = l;
|
2017-10-09 18:54:12 +00:00
|
|
|
|
_label = label;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
_command = conf["command"].Value<string>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public abstract Task Invoke(SocketGuild g, SocketMessage msg);
|
|
|
|
|
|
|
|
|
|
protected Task Log(string text)
|
|
|
|
|
{
|
2018-03-17 20:51:09 +00:00
|
|
|
|
return _mod.Log($"{Label}: {text}");
|
2017-08-06 20:05:44 +00:00
|
|
|
|
}
|
2017-10-09 18:54:12 +00:00
|
|
|
|
|
|
|
|
|
#region Config loading
|
|
|
|
|
private static readonly ReadOnlyDictionary<string, Type> _commands =
|
|
|
|
|
new ReadOnlyDictionary<string, Type>(
|
|
|
|
|
new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
|
|
|
|
|
{
|
|
|
|
|
// Define all command types and their corresponding Types here
|
2018-03-10 06:17:55 +00:00
|
|
|
|
{ "ban", typeof(Ban) },
|
|
|
|
|
{ "confreload", typeof(ConfReload) },
|
|
|
|
|
{ "kick", typeof(Kick) },
|
|
|
|
|
{ "say", typeof(Say) },
|
2018-03-16 04:26:59 +00:00
|
|
|
|
{ "unban", typeof(Unban) },
|
|
|
|
|
{ "addrole", typeof(RoleAdd) },
|
2018-03-16 04:46:18 +00:00
|
|
|
|
{ "delrole", typeof(RoleDel) }
|
2017-10-09 18:54:12 +00:00
|
|
|
|
});
|
|
|
|
|
|
2018-03-22 06:27:19 +00:00
|
|
|
|
public static Command CreateInstance(ModCommands root, JProperty def)
|
2017-10-09 18:54:12 +00:00
|
|
|
|
{
|
|
|
|
|
string label = def.Name;
|
|
|
|
|
if (string.IsNullOrWhiteSpace(label)) throw new RuleImportException("Label cannot be blank.");
|
|
|
|
|
|
|
|
|
|
var definition = (JObject)def.Value;
|
2018-03-05 04:45:12 +00:00
|
|
|
|
string cmdinvoke = definition["command"]?.Value<string>();
|
2017-10-09 18:54:12 +00:00
|
|
|
|
if (string.IsNullOrWhiteSpace(cmdinvoke))
|
|
|
|
|
throw new RuleImportException($"{label}: 'command' value was not specified.");
|
|
|
|
|
if (cmdinvoke.Contains(" "))
|
|
|
|
|
throw new RuleImportException($"{label}: 'command' must not contain spaces.");
|
|
|
|
|
|
|
|
|
|
string ctypestr = definition["type"]?.Value<string>();
|
|
|
|
|
if (string.IsNullOrWhiteSpace(ctypestr))
|
|
|
|
|
throw new RuleImportException($"Value 'type' must be specified in definition for '{label}'.");
|
2018-03-10 06:17:55 +00:00
|
|
|
|
if (_commands.TryGetValue(ctypestr, out Type ctype))
|
2017-10-09 18:54:12 +00:00
|
|
|
|
{
|
2018-03-10 06:17:55 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return (Command)Activator.CreateInstance(ctype, root, label, definition);
|
|
|
|
|
}
|
|
|
|
|
catch (TargetInvocationException ex)
|
|
|
|
|
{
|
|
|
|
|
if (ex.InnerException is RuleImportException)
|
|
|
|
|
throw new RuleImportException($"Error in configuration for command '{label}': {ex.InnerException.Message}");
|
|
|
|
|
else throw;
|
|
|
|
|
}
|
2017-10-09 18:54:12 +00:00
|
|
|
|
}
|
2018-03-10 06:17:55 +00:00
|
|
|
|
else
|
2017-10-09 18:54:12 +00:00
|
|
|
|
{
|
2018-03-10 06:17:55 +00:00
|
|
|
|
throw new RuleImportException($"The given 'type' value is invalid in definition for '{label}'.");
|
2017-10-09 18:54:12 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
2018-03-10 06:17:55 +00:00
|
|
|
|
#region Helper methods and common values
|
2017-10-09 18:54:12 +00:00
|
|
|
|
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);
|
|
|
|
|
protected static readonly Regex EmojiMatch = new Regex(@"<:(?<name>[A-Za-z0-9_]{2,}):(?<ID>\d+)>", RegexOptions.Compiled);
|
2018-03-10 18:38:50 +00:00
|
|
|
|
protected const string Fail403 = "I do not have the required permissions to perform that action.";
|
|
|
|
|
protected const string FailDefault = "An unknown error occurred. Notify the bot operator.";
|
2018-03-10 07:16:30 +00:00
|
|
|
|
|
|
|
|
|
protected string DefaultUsageMsg { get; set; }
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sends out the default usage message (<see cref="DefaultUsageMsg"/>) within an embed.
|
|
|
|
|
/// An optional message can be included, for uses such as notifying users of incorrect usage.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="target">Target channel for sending the message.</param>
|
|
|
|
|
/// <param name="message">The message to send alongside the default usage message.</param>
|
|
|
|
|
protected async Task SendUsageMessageAsync(ISocketMessageChannel target, string message = null)
|
|
|
|
|
{
|
|
|
|
|
if (DefaultUsageMsg == null)
|
|
|
|
|
throw new InvalidOperationException("DefaultUsage was not defined.");
|
|
|
|
|
|
|
|
|
|
var usageEmbed = new EmbedBuilder()
|
|
|
|
|
{
|
|
|
|
|
Title = "Usage",
|
|
|
|
|
Description = DefaultUsageMsg
|
|
|
|
|
};
|
|
|
|
|
await target.SendMessageAsync(message ?? "", embed: usageEmbed);
|
|
|
|
|
}
|
2018-03-10 18:38:50 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Helper method for turning input into user data. Only returns the first cache result.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>
|
|
|
|
|
/// First value: 0 for no data, 1 for no data + exception.
|
|
|
|
|
/// May return a partial result: a valid ulong value but no CacheUser.
|
|
|
|
|
/// </returns>
|
|
|
|
|
protected async Task<(ulong, EntityCache.CacheUser)> GetUserDataFromString(ulong guild, string input)
|
|
|
|
|
{
|
2018-08-14 05:48:28 +00:00
|
|
|
|
ulong uid;
|
2018-03-10 18:38:50 +00:00
|
|
|
|
EntityCache.CacheUser cdata = null;
|
|
|
|
|
|
2018-08-14 05:48:28 +00:00
|
|
|
|
// If input is a mention, isolate the ID value
|
2018-03-10 18:38:50 +00:00
|
|
|
|
Match m = UserMention.Match(input);
|
2018-08-14 05:48:28 +00:00
|
|
|
|
if (m.Success) input = m.Groups["snowflake"].Value;
|
|
|
|
|
|
|
|
|
|
// Attempt to turn the input into a ulong
|
|
|
|
|
try { uid = ulong.Parse(input); }
|
|
|
|
|
catch (FormatException) { uid = 0; }
|
2018-03-10 18:38:50 +00:00
|
|
|
|
|
2018-08-14 05:48:28 +00:00
|
|
|
|
// EntityCache lookup
|
2018-03-10 18:38:50 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2018-04-05 00:25:13 +00:00
|
|
|
|
cdata = (await EntityCache.EntityCache.QueryUserAsync(guild, input))
|
2018-03-10 18:38:50 +00:00
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
if (cdata != null) uid = cdata.UserId;
|
|
|
|
|
}
|
|
|
|
|
catch (Npgsql.NpgsqlException ex)
|
|
|
|
|
{
|
|
|
|
|
await Log("A databasae error occurred during user lookup: " + ex.Message);
|
|
|
|
|
if (uid == 0) uid = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (uid, cdata);
|
|
|
|
|
}
|
2018-03-10 07:16:30 +00:00
|
|
|
|
#endregion
|
2017-08-06 20:05:44 +00:00
|
|
|
|
}
|
|
|
|
|
}
|