diff --git a/Modules/ModCommands/Commands/BanKick.cs b/Modules/ModCommands/Commands/BanKick.cs index eece26d..e5c02fb 100644 --- a/Modules/ModCommands/Commands/BanKick.cs +++ b/Modules/ModCommands/Commands/BanKick.cs @@ -60,7 +60,7 @@ abstract class BanKick : CommandConfig { // "PurgeDays" - integer; Number of days of target's post history to delete, if banning. // Must be between 0-7 inclusive. Defaults to 0. // "SendNotify" - boolean; Whether to send a notification DM explaining the action. Defaults to true. - // "SuccessMessage" - string; Message to display on command success. Overrides default. + // "SuccessMessage" - string; Additional message to display on command success. protected BanKick(ModCommands module, JObject config, bool ban) : base(module, config) { ForceReason = config[nameof(ForceReason)]?.Value() ?? false; PurgeDays = config[nameof(PurgeDays)]?.Value() ?? 0; @@ -68,7 +68,7 @@ abstract class BanKick : CommandConfig { SendNotify = config[nameof(SendNotify)]?.Value() ?? true; SuccessMessage = config[nameof(SuccessMessage)]?.Value(); - _usage = $"{Command} `user or user ID` `" + (ForceReason ? "reason" : "[reason]") + "`\n" + _usage = $"{Command} `user ID or tag` `" + (ForceReason ? "reason" : "[reason]") + "`\n" + "Removes the given user from this server" + (ban ? " and prevents the user from rejoining" : "") + ". " + (ForceReason ? "L" : "Optionally l") + "ogs the reason for the " @@ -80,7 +80,7 @@ abstract class BanKick : CommandConfig { private readonly string _usage; protected override string DefaultUsageMsg => _usage; - // Usage: (command) (mention) (reason) + // Usage: (command) (user) (reason) public override async Task Invoke(SocketGuild g, SocketMessage msg) { var line = msg.Content.Split(new char[] { ' ' }, 3, StringSplitOptions.RemoveEmptyEntries); string targetstr; diff --git a/Modules/ModCommands/Commands/NoteWarn.cs b/Modules/ModCommands/Commands/NoteWarn.cs new file mode 100644 index 0000000..4b820f1 --- /dev/null +++ b/Modules/ModCommands/Commands/NoteWarn.cs @@ -0,0 +1,76 @@ +using RegexBot.Common; + +namespace RegexBot.Modules.ModCommands.Commands; +// Note and Warn commands are highly similar in implementnation, and thus are handled in a single class. +class Note : NoteWarn { + public Note(ModCommands module, JObject config) : base(module, config) { } + + protected override string DefaultUsageMsg => string.Format(_usageHeader, Command) + + "Appends a note to the moderation log for the given user."; + protected override async Task ContinueInvoke(SocketGuild g, SocketMessage msg, string logMessage, SocketUser targetUser) { + var result = await Module.Bot.AddUserNoteAsync(g, targetUser.Id, msg.Author.AsEntityNameString(), logMessage); + await msg.Channel.SendMessageAsync($":white_check_mark: Note \\#{result.LogId} logged for {targetUser}."); + } +} + +class Warn : NoteWarn { + public Warn(ModCommands module, JObject config) : base(module, config) { } + + protected override string DefaultUsageMsg => string.Format(_usageHeader, Command) + + "Issues a warning to the given user, logging the instance to this bot's moderation log " + + "and notifying the offending user over DM of the issued warning."; + + protected override async Task ContinueInvoke(SocketGuild g, SocketMessage msg, string logMessage, SocketUser targetUser) { + // Won't warn a bot + if (targetUser.IsBot) { + await SendUsageMessageAsync(msg.Channel, ":x: I don't want to do that. If you must, please warn bots manually."); + return; + } + + var (_, result) = await Module.Bot.AddUserWarnAsync(g, targetUser.Id, msg.Author.AsEntityNameString(), logMessage); + await msg.Channel.SendMessageAsync(result.GetResultString()); + } +} + +abstract class NoteWarn : CommandConfig { + protected string? SuccessMessage { get; } + + protected const string _usageHeader = "{0} `user ID or tag` `message`\n"; + + // Configuration: + // "SuccessMessage" - string; Additional message to display on command success. + protected NoteWarn(ModCommands module, JObject config) : base(module, config) { + SuccessMessage = config[nameof(SuccessMessage)]?.Value(); + } + + // Usage: (command) (user) (message) + public override async Task Invoke(SocketGuild g, SocketMessage msg) { + var line = msg.Content.Split(new char[] { ' ' }, 3, StringSplitOptions.RemoveEmptyEntries); + if (line.Length != 3) { + await SendUsageMessageAsync(msg.Channel, ":x: Not all required parameters were specified."); + return; + } + var targetstr = line[1]; + var logMessage = line[2]; + + // Get target user. Required to find for our purposes. + var targetQuery = Module.Bot.EcQueryGuildUser(g.Id, targetstr); + ulong targetId; + if (targetQuery != null) targetId = (ulong)targetQuery.UserId; + else if (ulong.TryParse(targetstr, out var parsed)) targetId = parsed; + else { + await SendUsageMessageAsync(msg.Channel, TargetNotFound); + return; + } + var targetUser = g.GetUser(targetId); + + // Go to specific action + try { + await ContinueInvoke(g, msg, logMessage, targetUser); + } catch (Discord.Net.HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.Forbidden) { + await msg.Channel.SendMessageAsync(":x: " + Messages.ForbiddenGenericError); + } + } + + protected abstract Task ContinueInvoke(SocketGuild g, SocketMessage msg, string logMessage, SocketUser targetUser); +} \ No newline at end of file diff --git a/Modules/ModCommands/ModuleConfig.cs b/Modules/ModCommands/ModuleConfig.cs index ca0ac3f..b8f9fe7 100644 --- a/Modules/ModCommands/ModuleConfig.cs +++ b/Modules/ModCommands/ModuleConfig.cs @@ -33,6 +33,9 @@ class ModuleConfig { { "kick", typeof(Kick) }, { "say", typeof(Say) }, { "unban", typeof(Unban) }, + { "note", typeof(Note) }, + { "addnote", typeof(Note) }, + { "warn", typeof(Warn) }, { "addrole", typeof(RoleAdd) }, { "roleadd", typeof(RoleAdd) }, { "delrole", typeof(RoleDel) },