diff --git a/ConfigItem/EntityList.cs b/ConfigItem/EntityList.cs index a448757..2a3893b 100644 --- a/ConfigItem/EntityList.cs +++ b/ConfigItem/EntityList.cs @@ -7,8 +7,6 @@ using System.Linq; namespace Noikoio.RegexBot.ConfigItem { - enum FilterType { None, Whitelist, Blacklist } - /// /// Represents a structure in bot configuration that contains a list of /// channels, roles, and users. @@ -70,7 +68,7 @@ namespace Noikoio.RegexBot.ConfigItem /// /// An incoming message. /// - /// True if the occurred within a channel specified in this list, + /// True if '' occurred within a channel specified in this list, /// or if the message author belongs to one or more roles in this list, or if the user itself /// is defined within this list. /// @@ -125,28 +123,5 @@ namespace Noikoio.RegexBot.ConfigItem // No match. return false; } - - /// - /// Helper method for reading whitelist and blacklist filtering lists. - /// - /// - /// A JSON object which presumably contains an array named "whitelist" or "blacklist". - /// - public static (FilterType, EntityList) GetFilterList(JObject section) - { - var mode = FilterType.None; - EntityList list; - if (section["whitelist"] != null) mode = FilterType.Whitelist; - if (section["blacklist"] != null) - { - if (mode == FilterType.Whitelist) - throw new RuleImportException("Cannot have whitelist AND blacklist defined."); - mode = FilterType.Blacklist; - } - if (mode == FilterType.None) list = new EntityList(); // might even be fine to keep it null? - else list = new EntityList(section[mode == FilterType.Whitelist ? "whitelist" : "blacklist"]); - - return (mode, list); - } } } diff --git a/ConfigItem/FilterList.cs b/ConfigItem/FilterList.cs new file mode 100644 index 0000000..972f78e --- /dev/null +++ b/ConfigItem/FilterList.cs @@ -0,0 +1,91 @@ +using Discord.WebSocket; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Noikoio.RegexBot.ConfigItem +{ + enum FilterType { None, Whitelist, Blacklist } + + /// + /// Represents whitelist/blacklist configuration, including exemptions. + /// + struct FilterList + { + FilterType _type; + EntityList _filterList; + EntityList _exemptions; + + public FilterType FilterMode => _type; + public EntityList FilterEntities => _filterList; + public EntityList FilterExemptions => _exemptions; + + /// + /// Gets the + /// + /// + /// A JSON object which presumably contains an array named "whitelist" or "blacklist", + /// and optionally one named "exempt". + /// + /// + /// Thrown if both "whitelist" and "blacklist" definitions were found, if + /// "exempt" was found without a corresponding "whitelist" or "blacklist", + /// or if there was an issue parsing an EntityList within these definitions. + /// + public FilterList(JObject conf) + { + _type = FilterType.None; + + if (conf["whitelist"] != null) _type = FilterType.Whitelist; + if (conf["blacklist"] != null) + { + if (_type != FilterType.None) + throw new RuleImportException("Cannot have both 'whitelist' and 'blacklist' values defined."); + _type = FilterType.Blacklist; + } + if (_type == FilterType.None) + { + _filterList = null; + _exemptions = null; + if (conf["exempt"] != null) + throw new RuleImportException("Cannot have 'exempt' defined if no corresponding " + + "'whitelist' or 'blacklist' has been defined in the same section."); + } + else + { + _filterList = new EntityList(conf[_type == FilterType.Whitelist ? "whitelist" : "blacklist"]); + _exemptions = new EntityList(conf["exempt"]); // EntityList constructor checks for null value + } + } + + /// + /// Determines if the parameters of '' are a match with filtering + /// rules defined in this instance. + /// + /// An incoming message. + /// + /// True if the user or channel specified by '' is filtered by + /// the configuration defined in this instance. + /// + public bool IsFiltered(SocketMessage msg) + { + if (FilterMode == FilterType.None) return false; + + bool inFilter = FilterEntities.ExistsInList(msg); + + if (FilterMode == FilterType.Whitelist) + { + if (!inFilter) return true; + return FilterExemptions.ExistsInList(msg); + } + else if (FilterMode == FilterType.Blacklist) + { + if (!inFilter) return false; + return !FilterExemptions.ExistsInList(msg); + } + + throw new Exception("this shouldn't happen"); + } + } +} diff --git a/Feature/RegexResponder/EventProcessor.cs b/Feature/RegexResponder/EventProcessor.cs index 75d0af2..e78f50e 100644 --- a/Feature/RegexResponder/EventProcessor.cs +++ b/Feature/RegexResponder/EventProcessor.cs @@ -128,7 +128,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder // Moderator bypass check if (rule.AllowModBypass == true && srv.Moderators.ExistsInList(msg)) return; // Individual rule filtering check - if (IsFiltered(rule, msg)) return; + if (rule.Filter.IsFiltered(msg)) return; // And finally, pattern matching checks bool success = false; @@ -220,26 +220,6 @@ namespace Noikoio.RegexBot.Feature.RegexResponder return result.ToString(); } - - private bool IsFiltered(RuleConfig r, SocketMessage m) - { - if (r.FilterMode == FilterType.None) return false; - - bool inFilter = r.FilterList.ExistsInList(m); - - if (r.FilterMode == FilterType.Whitelist) - { - if (!inFilter) return true; - return r.FilterExemptions.ExistsInList(m); - } - else if (r.FilterMode == FilterType.Blacklist) - { - if (!inFilter) return false; - return !r.FilterExemptions.ExistsInList(m); - } - - return false; // this shouldn't happen™ - } private string[] SplitParams(string cmd, int? limit = null) { diff --git a/Feature/RegexResponder/RuleConfig.cs b/Feature/RegexResponder/RuleConfig.cs index f263d18..8668c9e 100644 --- a/Feature/RegexResponder/RuleConfig.cs +++ b/Feature/RegexResponder/RuleConfig.cs @@ -15,9 +15,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder private string _displayName; private IEnumerable _regex; private IEnumerable _responses; - private FilterType _filtermode; - private EntityList _filterlist; - private EntityList _filterexempt; + private FilterList _filter; private int? _minLength; private int? _maxLength; private bool _modBypass; @@ -26,9 +24,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder public string DisplayName => _displayName; public IEnumerable Regex => _regex; public IEnumerable Responses => _responses; - public FilterType FilterMode => _filtermode; - public EntityList FilterList => _filterlist; - public EntityList FilterExemptions => _filterexempt; + public FilterList Filter => _filter; public int? MinLength => _minLength; public int? MaxLength => _maxLength; public bool AllowModBypass => _modBypass; @@ -131,9 +127,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder _responses = responses.ToArray(); // (white|black)list filtering - (_filtermode, _filterlist) = EntityList.GetFilterList(ruleconf); - // filtering exemptions - _filterexempt = new EntityList(ruleconf["exempt"]); + _filter = new FilterList(ruleconf); // moderator bypass toggle - true by default, must be explicitly set to false bool? modoverride = ruleconf["AllowModBypass"]?.Value();