using Newtonsoft.Json.Linq;
using Noikoio.RegexBot.ConfigItem;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Noikoio.RegexBot.Feature.RegexResponder
{
///
/// Represents configuration for a single rule.
///
[System.Diagnostics.DebuggerDisplay("Rule: {DisplayName}")]
internal struct RuleConfig
{
private string _displayName;
private IEnumerable _regex;
private IEnumerable _responses;
private FilterList _filter;
private int? _minLength;
private int? _maxLength;
private bool _modBypass;
private bool _matchEmbeds;
public string DisplayName => _displayName;
public IEnumerable Regex => _regex;
public IEnumerable Responses => _responses;
public FilterList Filter => _filter;
public int? MinLength => _minLength;
public int? MaxLength => _maxLength;
public bool AllowModBypass => _modBypass;
public bool MatchEmbeds => _matchEmbeds;
///
/// Takes the JObject for a single rule and retrieves all data for use as a struct.
///
/// Rule configuration input
///
/// Thrown when encountering a missing or invalid value.
///
public RuleConfig(JObject ruleconf)
{
// display name - validation should've been done outside this constructor already
_displayName = ruleconf["name"]?.Value();
if (_displayName == null)
throw new RuleImportException("Display name not defined.");
// regex options
RegexOptions opts = RegexOptions.Compiled | RegexOptions.CultureInvariant;
// TODO consider adding an option to specify Singleline and Multiline matching
opts |= RegexOptions.Singleline;
// case sensitivity must be explicitly defined, else not case sensitive by default
bool? regexci = ruleconf["ignorecase"]?.Value();
opts |= RegexOptions.IgnoreCase;
if (regexci.HasValue && regexci.Value == false)
opts &= ~RegexOptions.IgnoreCase;
// regex
const string RegexError = "No regular expression patterns are defined.";
var regexes = new List();
var rxconf = ruleconf["regex"];
if (rxconf == null)
{
throw new RuleImportException(RegexError);
}
if (rxconf.Type == JTokenType.Array)
{
foreach (var input in rxconf.Values())
{
try
{
Regex r = new Regex(input, opts);
regexes.Add(r);
}
catch (ArgumentException)
{
throw new RuleImportException("Failed to parse regular expression pattern: " + input);
}
}
}
else
{
string rxstr = rxconf.Value();
try
{
var rxx = new Regex(rxstr, opts);
regexes.Add(rxx);
}
catch (ArgumentException)
{
throw new RuleImportException("Failed to parse regular expression pattern: " + rxstr);
}
}
if (regexes.Count == 0)
{
throw new RuleImportException(RegexError);
}
_regex = regexes.ToArray();
// min/max length
try
{
_minLength = ruleconf["min"]?.Value();
_maxLength = ruleconf["max"]?.Value();
}
catch (FormatException)
{
throw new RuleImportException("Minimum/maximum values must be an integer.");
}
// responses
const string ResponseError = "No responses have been defined for this rule.";
var responses = new List();
var rsconf = ruleconf["response"];
if (rsconf == null)
{
throw new RuleImportException(ResponseError);
}
if (rsconf.Type == JTokenType.Array)
{
foreach (var input in rsconf.Values()) responses.Add(input);
}
else
{
responses.Add(rsconf.Value());
}
// TODO a bit of response validation here. at least check for blanks or something.
_responses = responses.ToArray();
// (white|black)list filtering
_filter = new FilterList(ruleconf);
// moderator bypass toggle - true by default, must be explicitly set to false
bool? modoverride = ruleconf["AllowModBypass"]?.Value();
_modBypass = modoverride.HasValue ? modoverride.Value : true;
// embed matching mode
bool? embedmode = ruleconf["MatchEmbeds"]?.Value();
_matchEmbeds = (embedmode.HasValue && embedmode == true);
}
}
}