2017-08-06 20:05:44 +00:00
|
|
|
|
using Discord;
|
|
|
|
|
using Discord.WebSocket;
|
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
using Noikoio.RegexBot.ConfigItem;
|
|
|
|
|
using System;
|
2017-08-11 16:29:52 +00:00
|
|
|
|
using System.Text.RegularExpressions;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2017-11-12 03:12:24 +00:00
|
|
|
|
namespace Noikoio.RegexBot.Module.ModTools.Commands
|
2017-08-06 20:05:44 +00:00
|
|
|
|
{
|
2017-10-09 18:54:12 +00:00
|
|
|
|
|
|
|
|
|
class BanKick : CommandBase
|
2017-08-06 20:05:44 +00:00
|
|
|
|
{
|
2017-10-09 18:54:12 +00:00
|
|
|
|
// Ban and kick commands are highly similar in implementation, and thus are handled in a single class.
|
|
|
|
|
protected enum CommandMode { Ban, Kick }
|
|
|
|
|
private readonly CommandMode _mode;
|
|
|
|
|
|
2017-08-06 20:05:44 +00:00
|
|
|
|
private readonly bool _forceReason;
|
|
|
|
|
private readonly int _purgeDays;
|
2017-10-09 18:54:12 +00:00
|
|
|
|
private readonly string _successMsg;
|
2017-11-29 23:20:45 +00:00
|
|
|
|
private readonly string _notifyMsg;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
|
|
|
|
|
// Configuration:
|
|
|
|
|
// "forcereason" - boolean; Force a reason to be given. Defaults to false.
|
2017-10-09 18:54:12 +00:00
|
|
|
|
// "purgedays" - integer; Number of days of target's post history to delete, if banning.
|
|
|
|
|
// Must be between 0-7 inclusive. Defaults to 0.
|
|
|
|
|
// "successmsg" - Message to display on command success. Overrides default.
|
2017-11-29 23:20:45 +00:00
|
|
|
|
// "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.
|
2017-10-09 18:54:12 +00:00
|
|
|
|
protected BanKick(ModTools l, string label, JObject conf, CommandMode mode) : base(l, label, conf)
|
2017-08-06 20:05:44 +00:00
|
|
|
|
{
|
2017-10-09 18:54:12 +00:00
|
|
|
|
_mode = mode;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
_forceReason = conf["forcereason"]?.Value<bool>() ?? false;
|
|
|
|
|
_purgeDays = conf["purgedays"]?.Value<int>() ?? 0;
|
2017-10-09 18:54:12 +00:00
|
|
|
|
if (_mode == CommandMode.Ban && (_purgeDays > 7 || _purgeDays < 0))
|
2017-08-06 20:05:44 +00:00
|
|
|
|
{
|
|
|
|
|
throw new RuleImportException("The value of 'purgedays' must be between 0 and 7.");
|
|
|
|
|
}
|
2017-10-09 18:54:12 +00:00
|
|
|
|
_successMsg = conf["successmsg"]?.Value<string>();
|
2017-11-29 23:20:45 +00:00
|
|
|
|
if (conf["notifymsg"] == null)
|
|
|
|
|
{
|
|
|
|
|
// Message not specified - use default
|
|
|
|
|
string act = _mode == CommandMode.Ban ? "banned" : "kicked";
|
|
|
|
|
_notifyMsg = $"You have been {act} from $s for the following reason:\n$r";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
string val = conf["notifymsg"].Value<string>();
|
|
|
|
|
if (string.IsNullOrWhiteSpace(val)) _notifyMsg = null; // empty value - disable message
|
|
|
|
|
else _notifyMsg = val;
|
|
|
|
|
}
|
2017-08-06 20:05:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Usage: (command) (mention) (reason)
|
|
|
|
|
public override async Task Invoke(SocketGuild g, SocketMessage msg)
|
|
|
|
|
{
|
|
|
|
|
string[] line = msg.Content.Split(new char[] { ' ' }, 3, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
string targetstr;
|
2017-11-29 23:20:45 +00:00
|
|
|
|
string reason;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
if (line.Length < 2)
|
|
|
|
|
{
|
|
|
|
|
await SendUsageMessage(msg, null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
targetstr = line[1];
|
|
|
|
|
|
|
|
|
|
if (line.Length == 3)
|
|
|
|
|
{
|
2017-11-29 23:20:45 +00:00
|
|
|
|
// Reason given - keep it
|
|
|
|
|
reason = line[2];
|
2017-08-06 20:05:44 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// No reason given
|
|
|
|
|
if (_forceReason)
|
|
|
|
|
{
|
2017-10-09 18:54:12 +00:00
|
|
|
|
await SendUsageMessage(msg, ":x: **You must specify a reason.**");
|
2017-08-06 20:05:44 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2017-11-29 23:20:45 +00:00
|
|
|
|
reason = null;
|
2017-08-06 20:05:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-11 16:29:52 +00:00
|
|
|
|
// Getting SocketGuildUser target
|
|
|
|
|
Match m = UserMention.Match(targetstr);
|
|
|
|
|
if (m.Success) targetstr = m.Groups["snowflake"].Value;
|
|
|
|
|
|
2017-08-06 20:05:44 +00:00
|
|
|
|
SocketGuildUser targetobj = null;
|
2017-08-11 16:29:52 +00:00
|
|
|
|
ulong targetuid;
|
|
|
|
|
string targetdisp;
|
|
|
|
|
if (ulong.TryParse(targetstr, out targetuid))
|
2017-08-06 20:05:44 +00:00
|
|
|
|
{
|
2017-08-11 16:29:52 +00:00
|
|
|
|
targetobj = g.GetUser(targetuid);
|
2017-10-09 18:54:12 +00:00
|
|
|
|
targetdisp = (targetobj == null ? $"ID {targetuid}" : targetobj.ToString());
|
2017-08-06 20:05:44 +00:00
|
|
|
|
}
|
2017-08-11 16:29:52 +00:00
|
|
|
|
else
|
2017-08-06 20:05:44 +00:00
|
|
|
|
{
|
|
|
|
|
await SendUsageMessage(msg, ":x: **Unable to determine the target user.**");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-09 18:54:12 +00:00
|
|
|
|
if (_mode == CommandMode.Kick && targetobj == null)
|
|
|
|
|
{
|
|
|
|
|
// Can't kick without obtaining the user object
|
|
|
|
|
await SendUsageMessage(msg, ":x: **Unable to find the target user.**");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-29 23:20:45 +00:00
|
|
|
|
// Send out message
|
|
|
|
|
bool notifyfail = false;
|
|
|
|
|
if (_notifyMsg != null && targetobj != null)
|
|
|
|
|
{
|
|
|
|
|
var ch = targetobj.GetOrCreateDMChannelAsync();
|
|
|
|
|
string outresult = _notifyMsg;
|
|
|
|
|
outresult = outresult.Replace("$s", g.Name);
|
|
|
|
|
outresult = outresult.Replace("$r", reason ?? "No reason specified.");
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await (await ch).SendMessageAsync(outresult);
|
|
|
|
|
}
|
|
|
|
|
catch (Discord.Net.HttpException ex)
|
|
|
|
|
{
|
|
|
|
|
await Log("Failed to send out notification to target over DM: "
|
|
|
|
|
+ Enum.GetName(typeof(System.Net.HttpStatusCode), ex.HttpCode));
|
|
|
|
|
notifyfail = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Do the action
|
2017-08-06 20:05:44 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2017-11-29 23:20:45 +00:00
|
|
|
|
string reasonlog = $"Invoked by {msg.Author.ToString()}.";
|
|
|
|
|
if (reason != null) reasonlog += $" Reason: {reason}";
|
|
|
|
|
reasonlog = Uri.EscapeDataString(reasonlog);
|
2017-10-21 19:05:46 +00:00
|
|
|
|
#warning Remove EscapeDataString call on next Discord.Net update
|
2017-11-29 23:20:45 +00:00
|
|
|
|
if (_mode == CommandMode.Ban) await g.AddBanAsync(targetuid, _purgeDays, reasonlog);
|
2017-10-09 18:54:12 +00:00
|
|
|
|
else await targetobj.KickAsync(reason);
|
|
|
|
|
string resultmsg = BuildSuccessMessage(targetdisp);
|
2017-11-29 23:20:45 +00:00
|
|
|
|
if (notifyfail)
|
|
|
|
|
{
|
|
|
|
|
resultmsg += $"\n(Failed to send " + (_mode == CommandMode.Ban ? "ban" : "kick") + " notification to user.)";
|
|
|
|
|
}
|
2017-10-09 18:54:12 +00:00
|
|
|
|
await msg.Channel.SendMessageAsync(resultmsg);
|
2017-08-06 20:05:44 +00:00
|
|
|
|
}
|
|
|
|
|
catch (Discord.Net.HttpException ex)
|
|
|
|
|
{
|
2017-10-09 18:54:12 +00:00
|
|
|
|
string err = ":x: **Failed to " + (_mode == CommandMode.Ban ? "ban" : "kick") + " user:** ";
|
2017-08-06 20:05:44 +00:00
|
|
|
|
if (ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
|
|
|
|
|
{
|
2017-10-09 18:54:12 +00:00
|
|
|
|
await msg.Channel.SendMessageAsync(err + "I do not have sufficient permissions to do that action.");
|
2017-08-06 20:05:44 +00:00
|
|
|
|
}
|
|
|
|
|
else if (ex.HttpCode == System.Net.HttpStatusCode.NotFound)
|
|
|
|
|
{
|
2017-10-09 18:54:12 +00:00
|
|
|
|
await msg.Channel.SendMessageAsync(err + "The target user appears to no longer exist.");
|
2017-08-06 20:05:44 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-10-09 18:54:12 +00:00
|
|
|
|
await msg.Channel.SendMessageAsync(err + "An unknown error occurred. Details have been logged.");
|
2017-08-06 20:05:44 +00:00
|
|
|
|
await Log(ex.ToString());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task SendUsageMessage(SocketMessage m, string message)
|
|
|
|
|
{
|
|
|
|
|
string desc = $"{this.Command} [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)
|
|
|
|
|
desc += $"\nAdditionally removes the user's post history for the last {_purgeDays} day(s).";
|
|
|
|
|
|
|
|
|
|
var usageEmbed = new EmbedBuilder()
|
|
|
|
|
{
|
|
|
|
|
Title = "Usage",
|
|
|
|
|
Description = desc
|
|
|
|
|
};
|
|
|
|
|
await m.Channel.SendMessageAsync(message ?? "", embed: usageEmbed);
|
|
|
|
|
}
|
2017-10-09 18:54:12 +00:00
|
|
|
|
|
|
|
|
|
private string BuildSuccessMessage(string targetstr)
|
|
|
|
|
{
|
|
|
|
|
const string defaultmsgBan = ":white_check_mark: Banned user **$target**.";
|
|
|
|
|
const string defaultmsgKick = ":white_check_mark: Kicked user **$target**.";
|
|
|
|
|
|
|
|
|
|
string msg = _successMsg ?? (_mode == CommandMode.Ban ? defaultmsgBan : defaultmsgKick);
|
|
|
|
|
|
|
|
|
|
return msg.Replace("$target", targetstr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Ban : BanKick
|
|
|
|
|
{
|
|
|
|
|
public Ban(ModTools l, string label, JObject conf)
|
|
|
|
|
: base(l, label, conf, CommandMode.Ban) { }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Kick : BanKick
|
|
|
|
|
{
|
|
|
|
|
public Kick(ModTools l, string label, JObject conf)
|
|
|
|
|
: base(l, label, conf, CommandMode.Kick) { }
|
2017-08-06 20:05:44 +00:00
|
|
|
|
}
|
|
|
|
|
}
|