Added FilterList

Several upcoming features will be making use of the same whitelist
and blacklist filtering concept that RegexResponder currently uses.
This commit is contained in:
Noikoio 2017-08-10 12:53:46 -07:00
parent bd46c53a02
commit 8d228a4d7c
4 changed files with 96 additions and 56 deletions

View file

@ -7,8 +7,6 @@ using System.Linq;
namespace Noikoio.RegexBot.ConfigItem namespace Noikoio.RegexBot.ConfigItem
{ {
enum FilterType { None, Whitelist, Blacklist }
/// <summary> /// <summary>
/// Represents a structure in bot configuration that contains a list of /// Represents a structure in bot configuration that contains a list of
/// channels, roles, and users. /// channels, roles, and users.
@ -70,7 +68,7 @@ namespace Noikoio.RegexBot.ConfigItem
/// </summary> /// </summary>
/// <param name="msg">An incoming message.</param> /// <param name="msg">An incoming message.</param>
/// <returns> /// <returns>
/// True if the <see cref="SocketMessage"/> occurred within a channel specified in this list, /// True if '<paramref name="msg"/>' 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 /// or if the message author belongs to one or more roles in this list, or if the user itself
/// is defined within this list. /// is defined within this list.
/// </returns> /// </returns>
@ -125,28 +123,5 @@ namespace Noikoio.RegexBot.ConfigItem
// No match. // No match.
return false; return false;
} }
/// <summary>
/// Helper method for reading whitelist and blacklist filtering lists.
/// </summary>
/// <param name="section">
/// A JSON object which presumably contains an array named "whitelist" or "blacklist".
/// </param>
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);
}
} }
} }

91
ConfigItem/FilterList.cs Normal file
View file

@ -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 }
/// <summary>
/// Represents whitelist/blacklist configuration, including exemptions.
/// </summary>
struct FilterList
{
FilterType _type;
EntityList _filterList;
EntityList _exemptions;
public FilterType FilterMode => _type;
public EntityList FilterEntities => _filterList;
public EntityList FilterExemptions => _exemptions;
/// <summary>
/// Gets the
/// </summary>
/// <param name="conf">
/// A JSON object which presumably contains an array named "whitelist" or "blacklist",
/// and optionally one named "exempt".
/// </param>
/// <exception cref="RuleImportException">
/// 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.
/// </exception>
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
}
}
/// <summary>
/// Determines if the parameters of '<paramref name="msg"/>' are a match with filtering
/// rules defined in this instance.
/// </summary>
/// <param name="msg">An incoming message.</param>
/// <returns>
/// True if the user or channel specified by '<paramref name="msg"/>' is filtered by
/// the configuration defined in this instance.
/// </returns>
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");
}
}
}

View file

@ -128,7 +128,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
// Moderator bypass check // Moderator bypass check
if (rule.AllowModBypass == true && srv.Moderators.ExistsInList(msg)) return; if (rule.AllowModBypass == true && srv.Moderators.ExistsInList(msg)) return;
// Individual rule filtering check // Individual rule filtering check
if (IsFiltered(rule, msg)) return; if (rule.Filter.IsFiltered(msg)) return;
// And finally, pattern matching checks // And finally, pattern matching checks
bool success = false; bool success = false;
@ -220,26 +220,6 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
return result.ToString(); 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) private string[] SplitParams(string cmd, int? limit = null)
{ {

View file

@ -15,9 +15,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
private string _displayName; private string _displayName;
private IEnumerable<Regex> _regex; private IEnumerable<Regex> _regex;
private IEnumerable<string> _responses; private IEnumerable<string> _responses;
private FilterType _filtermode; private FilterList _filter;
private EntityList _filterlist;
private EntityList _filterexempt;
private int? _minLength; private int? _minLength;
private int? _maxLength; private int? _maxLength;
private bool _modBypass; private bool _modBypass;
@ -26,9 +24,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
public string DisplayName => _displayName; public string DisplayName => _displayName;
public IEnumerable<Regex> Regex => _regex; public IEnumerable<Regex> Regex => _regex;
public IEnumerable<string> Responses => _responses; public IEnumerable<string> Responses => _responses;
public FilterType FilterMode => _filtermode; public FilterList Filter => _filter;
public EntityList FilterList => _filterlist;
public EntityList FilterExemptions => _filterexempt;
public int? MinLength => _minLength; public int? MinLength => _minLength;
public int? MaxLength => _maxLength; public int? MaxLength => _maxLength;
public bool AllowModBypass => _modBypass; public bool AllowModBypass => _modBypass;
@ -131,9 +127,7 @@ namespace Noikoio.RegexBot.Feature.RegexResponder
_responses = responses.ToArray(); _responses = responses.ToArray();
// (white|black)list filtering // (white|black)list filtering
(_filtermode, _filterlist) = EntityList.GetFilterList(ruleconf); _filter = new FilterList(ruleconf);
// filtering exemptions
_filterexempt = new EntityList(ruleconf["exempt"]);
// moderator bypass toggle - true by default, must be explicitly set to false // moderator bypass toggle - true by default, must be explicitly set to false
bool? modoverride = ruleconf["AllowModBypass"]?.Value<bool>(); bool? modoverride = ruleconf["AllowModBypass"]?.Value<bool>();