From d49299e063799af45b99fa2ca93fd67f9b257d5d Mon Sep 17 00:00:00 2001 From: Noikoio Date: Fri, 8 Dec 2017 13:57:35 -0800 Subject: [PATCH] Added Config class to replace that previous mess --- Module/ModTools/ConfigItem.cs | 75 +++++++++++++++++++++++ Module/ModTools/ModTools.cs | 108 ++++++++++++---------------------- 2 files changed, 113 insertions(+), 70 deletions(-) create mode 100644 Module/ModTools/ConfigItem.cs diff --git a/Module/ModTools/ConfigItem.cs b/Module/ModTools/ConfigItem.cs new file mode 100644 index 0000000..a135a3f --- /dev/null +++ b/Module/ModTools/ConfigItem.cs @@ -0,0 +1,75 @@ +using Newtonsoft.Json.Linq; +using Noikoio.RegexBot.ConfigItem; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace Noikoio.RegexBot.Module.ModTools +{ + /// + /// Represents ModTools configuration within one server. + /// + class ConfigItem + { + private EntityName? _petitionReportCh; + private readonly ReadOnlyDictionary _cmdInstances; + + public EntityName? PetitionReportingChannel => _petitionReportCh; + public ReadOnlyDictionary Commands => _cmdInstances; + + public ConfigItem(ModTools instance, JToken inconf) + { + if (inconf.Type != JTokenType.Object) + { + throw new RuleImportException("Configuration for this section is invalid."); + } + var config = (JObject)inconf; + + // Ban petition reporting channel + var petitionstr = config["PetitionRelay"]?.Value(); + if (string.IsNullOrEmpty(petitionstr)) _petitionReportCh = null; + else if (petitionstr.Length > 1 && petitionstr[0] != '#') + { + // Not a channel. + throw new RuleImportException("PetitionRelay value must be set to a channel."); + } + else + { + _petitionReportCh = new EntityName(petitionstr.Substring(1), EntityType.Channel); + } + + // Command instances + var commands = new Dictionary(StringComparer.OrdinalIgnoreCase); + var commandconf = config["Commands"]; + if (commandconf != null) + { + if (commandconf.Type != JTokenType.Object) + { + throw new RuleImportException("CommandDefs is not properly defined."); + } + + foreach (var def in commandconf.Children()) + { + string label = def.Name; + var cmd = CommandBase.CreateInstance(instance, def); + if (commands.ContainsKey(cmd.Command)) + 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}."); + + commands.Add(cmd.Command, cmd); + } + } + _cmdInstances = new ReadOnlyDictionary(commands); + } + + public void UpdatePetitionChannel(ulong id) + { + if (!PetitionReportingChannel.HasValue) return; + if (PetitionReportingChannel.Value.Id.HasValue) return; // nothing to update + + // For lack of a better option - create a new EntityName with ID already provided + _petitionReportCh = new EntityName($"{id}::{PetitionReportingChannel.Value.Name}", EntityType.Channel); + } + } +} diff --git a/Module/ModTools/ModTools.cs b/Module/ModTools/ModTools.cs index a9d4632..0c505b8 100644 --- a/Module/ModTools/ModTools.cs +++ b/Module/ModTools/ModTools.cs @@ -4,7 +4,6 @@ using Newtonsoft.Json.Linq; using Noikoio.RegexBot.ConfigItem; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; @@ -28,71 +27,23 @@ namespace Noikoio.RegexBot.Module.ModTools if (arg.Channel is IDMChannel) await PetitionRelayCheck(arg); else if (arg.Channel is IGuildChannel) await CommandCheckInvoke(arg); } - - #region Config + [ConfigSection("modtools")] public override async Task ProcessConfiguration(JToken configSection) { - if (configSection.Type != JTokenType.Object) - { - throw new RuleImportException("Configuration for this section is invalid."); - } - var config = (JObject)configSection; + // Constructor throws exception on config errors + var conf = new ConfigItem(this, configSection); - // Ban petition reporting channel - EntityName? petitionrpt; - var petitionstr = config["PetitionRelay"]?.Value(); - if (string.IsNullOrEmpty(petitionstr)) petitionrpt = null; - else if (petitionstr.Length > 1 && petitionstr[0] != '#') - { - // Not a channel. - throw new RuleImportException("PetitionRelay value must be set to a channel."); - } - else - { - petitionrpt = new EntityName(petitionstr.Substring(1), EntityType.Channel); - } + // Log results + if (conf.Commands.Count > 0) + await Log(conf.Commands.Count + " command definition(s) loaded."); + if (conf.PetitionReportingChannel.HasValue) + await Log("Ban petitioning has been enabled."); - // And then the commands - var commands = new Dictionary(StringComparer.OrdinalIgnoreCase); - var commandconf = config["Commands"]; - if (commandconf != null) - { - if (commandconf.Type != JTokenType.Object) - { - throw new RuleImportException("CommandDefs is not properly defined."); - } - - foreach (var def in commandconf.Children()) - { - string label = def.Name; - var cmd = CommandBase.CreateInstance(this, def); - if (commands.ContainsKey(cmd.Command)) - 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}."); - - commands.Add(cmd.Command, cmd); - } - await Log($"Loaded {commands.Count} command definition(s)."); - } - - return new Tuple, EntityName?>( - new ReadOnlyDictionary(commands), petitionrpt); + return conf; } - /* - * Config is stored in a tuple. I admit, not the best choice... - * Consider a different approach if more data needs to be stored in the future. - * Item 1: Command config (Dictionary) - * Item 2: Ban petition channel (EntityName) - */ - - private new Tuple, EntityName?> GetConfig(ulong guildId) - => (Tuple, EntityName?>)base.GetConfig(guildId); - private ReadOnlyDictionary GetCommandConfig(ulong guild) => GetConfig(guild).Item1; - private EntityName? GetPetitionConfig(ulong guild) => GetConfig(guild).Item2; - #endregion + private new ConfigItem GetConfig(ulong guildId) => (ConfigItem)base.GetConfig(guildId); public new Task Log(string text) => base.Log(text); @@ -115,7 +66,7 @@ namespace Noikoio.RegexBot.Module.ModTools int spc = arg.Content.IndexOf(' '); if (spc != -1) cmdchk = arg.Content.Substring(0, spc); else cmdchk = arg.Content; - if ((GetCommandConfig(g.Id)).TryGetValue(cmdchk, out var c)) + if (GetConfig(g.Id).Commands.TryGetValue(cmdchk, out var c)) { try { @@ -130,6 +81,7 @@ namespace Noikoio.RegexBot.Module.ModTools } } + #region Ban petitions /// /// List of available appeals. Key is user (for quick lookup). Value is guild (for quick config resolution). /// TODO expiration? @@ -138,13 +90,17 @@ namespace Noikoio.RegexBot.Module.ModTools public void AddPetition(ulong guild, ulong user) { // Do nothing if disabled - if (GetPetitionConfig(guild) == null) return; + if (!GetConfig(guild).PetitionReportingChannel.HasValue) return; lock (_openPetitions) _openPetitions[user] = guild; } private async Task PetitionRelayCheck(SocketMessage msg) { const string PetitionAccepted = "Your petition has been forwarded to the moderators for review."; const string PetitionDenied = "You may not submit a ban petition."; + + // It's possible the sender may still block messages sent to them, + // hence the empty catch blocks you'll see up ahead. + if (!msg.Content.StartsWith("!petition ", StringComparison.InvariantCultureIgnoreCase)) return; // Input validation @@ -154,6 +110,13 @@ namespace Noikoio.RegexBot.Module.ModTools // Just ignore. return; } + if (ptext.Length > 1000) + { + // Enforce petition length limit. + try { await msg.Author.SendMessageAsync("Your petition message is too long. Try again with a shorter message."); } + catch (Discord.Net.HttpException) { } + return; + } ulong targetGuild = 0; lock (_openPetitions) @@ -163,12 +126,10 @@ namespace Noikoio.RegexBot.Module.ModTools _openPetitions.Remove(msg.Author.Id); } } - - // It's possible the sender may still block messages sent to them, - // hence the empty catch blocks you'll see up ahead. - + if (targetGuild == 0) { + // Not in the list. Nothing to do. try { await msg.Author.SendMessageAsync(PetitionDenied); } catch (Discord.Net.HttpException) { } return; @@ -182,9 +143,9 @@ namespace Noikoio.RegexBot.Module.ModTools return; } - // Get petition reporting target if not already known - var pcv = GetPetitionConfig(targetGuild); - if (!pcv.HasValue) return; // No target. How'd we get here, anyway? + // Get petition reporting target + var pcv = GetConfig(targetGuild).PetitionReportingChannel; + if (!pcv.HasValue) return; // No target. This should be logically impossible, but... just in case. var rch = pcv.Value; ISocketMessageChannel rchObj; if (!rch.Id.HasValue) @@ -192,11 +153,17 @@ namespace Noikoio.RegexBot.Module.ModTools rchObj = gObj.TextChannels .Where(c => c.Name.Equals(rch.Name, StringComparison.InvariantCultureIgnoreCase)) .FirstOrDefault(); + // Update value if found + if (rchObj != null) + { + GetConfig(targetGuild).UpdatePetitionChannel(rchObj.Id); + } } else { - rchObj = (ISocketMessageChannel)gObj.GetChannel(rch.Id.Value); + rchObj = gObj.GetChannel(rch.Id.Value) as ISocketMessageChannel; } + if (rchObj == null) { // Channel not found. @@ -206,7 +173,7 @@ namespace Noikoio.RegexBot.Module.ModTools return; } - // Ready to relay as embed + // Ready to relay try { await rchObj.SendMessageAsync("", embed: new EmbedBuilder() @@ -240,5 +207,6 @@ namespace Noikoio.RegexBot.Module.ModTools try { await msg.Author.SendMessageAsync(PetitionAccepted); } catch (Discord.Net.HttpException) { } } + #endregion } }