Added proper MessageCache configuration
Though it's now capable of loading configuration for the full ModLogs module, not all features are available yet.
This commit is contained in:
parent
419370c379
commit
6ca73a9b6b
4 changed files with 166 additions and 42 deletions
24
Module/ModLogs/EventType.cs
Normal file
24
Module/ModLogs/EventType.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Noikoio.RegexBot.Module.ModLogs
|
||||||
|
{
|
||||||
|
// Types of non-custom events that can be referenced by ModLogs in configuration.
|
||||||
|
// Enum value names will show themselves to the user in the form of strings valid in configuration,
|
||||||
|
// so try not to change those without good reason.
|
||||||
|
[Flags]
|
||||||
|
enum EventType
|
||||||
|
{
|
||||||
|
None = 0x0,
|
||||||
|
Note = 0x1,
|
||||||
|
Warn = 0x2,
|
||||||
|
Kick = 0x4,
|
||||||
|
Ban = 0x8,
|
||||||
|
JoinGuild = 0x10,
|
||||||
|
LeaveGuild = 0x20,
|
||||||
|
NameChange = 0x40,
|
||||||
|
MsgEdit = 0x80,
|
||||||
|
MsgDelete = 0x100
|
||||||
|
}
|
||||||
|
}
|
127
Module/ModLogs/GuildConfig.cs
Normal file
127
Module/ModLogs/GuildConfig.cs
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Noikoio.RegexBot.ConfigItem;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Noikoio.RegexBot.Module.ModLogs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ModLogs guild-specific configuration values.
|
||||||
|
/// </summary>
|
||||||
|
class GuildConfig
|
||||||
|
{
|
||||||
|
// Event reporting
|
||||||
|
private readonly EntityName _rptTarget;
|
||||||
|
private EventType _rptTypes;
|
||||||
|
/// <summary>
|
||||||
|
/// Target reporting channel.
|
||||||
|
/// </summary>
|
||||||
|
public EntityName? RptTarget => _rptTarget;
|
||||||
|
/// <summary>
|
||||||
|
/// Event types to send to the reporting channel.
|
||||||
|
/// </summary>
|
||||||
|
public EventType RptTypes => _rptTypes;
|
||||||
|
|
||||||
|
// Query command
|
||||||
|
private readonly string _qCmd; // command name
|
||||||
|
private readonly EntityList _qAccess; // list of those able to issue the command
|
||||||
|
private readonly EventType _qDefaultAnswer; // default entry types to display
|
||||||
|
/// <summary>
|
||||||
|
/// Query command. The first word in an incoming message, including prefix, that triggers a query.
|
||||||
|
/// </summary>
|
||||||
|
public string QrCommand => _qCmd;
|
||||||
|
/// <summary>
|
||||||
|
/// List of users permitted to invoke the query command.
|
||||||
|
/// If null, refer to the guild's Moderators list.
|
||||||
|
/// </summary>
|
||||||
|
public EntityList QrPermittedUsers => _qAccess;
|
||||||
|
/// <summary>
|
||||||
|
/// Event types to display in a query.
|
||||||
|
/// </summary>
|
||||||
|
public EventType QrTypes => _qDefaultAnswer;
|
||||||
|
|
||||||
|
public GuildConfig(JObject cfgRoot)
|
||||||
|
{
|
||||||
|
// AutoReporting settings
|
||||||
|
var arcfg = cfgRoot["AutoReporting"];
|
||||||
|
if (arcfg == null)
|
||||||
|
{
|
||||||
|
_rptTarget = default(EntityName); // NOTE: Change this if EntityName becomes a class later
|
||||||
|
_rptTypes = EventType.None;
|
||||||
|
}
|
||||||
|
else if (arcfg.Type == JTokenType.Object)
|
||||||
|
{
|
||||||
|
string chval = arcfg["Channel"]?.Value<string>();
|
||||||
|
if (chval == null) throw new RuleImportException("Reporting channel is not defined.");
|
||||||
|
if (!string.IsNullOrWhiteSpace(chval) && chval[0] == '#')
|
||||||
|
_rptTarget = new EntityName(chval.Substring(1, chval.Length-1), EntityType.Channel);
|
||||||
|
else
|
||||||
|
throw new RuleImportException("Reporting channel is not properly defined.");
|
||||||
|
// Require the channel's ID for now.
|
||||||
|
if (!_rptTarget.Id.HasValue) throw new RuleImportException("Reporting channel's ID must be specified.");
|
||||||
|
|
||||||
|
// TODO make optional
|
||||||
|
string rpval = arcfg["Events"]?.Value<string>();
|
||||||
|
_rptTypes = GetTypesFromString(rpval);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new RuleImportException("Section for AutoReporting is not correctly defined.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryCommand settings
|
||||||
|
var qccfg = cfgRoot["QueryCommand"];
|
||||||
|
if (qccfg == null)
|
||||||
|
{
|
||||||
|
_qCmd = null;
|
||||||
|
_qAccess = null;
|
||||||
|
_qDefaultAnswer = EventType.None;
|
||||||
|
}
|
||||||
|
else if (arcfg.Type == JTokenType.Object)
|
||||||
|
{
|
||||||
|
_qCmd = arcfg["Command"]?.Value<string>();
|
||||||
|
if (string.IsNullOrWhiteSpace(_qCmd))
|
||||||
|
throw new RuleImportException("Query command option must have a value.");
|
||||||
|
if (_qCmd.Contains(" "))
|
||||||
|
throw new RuleImportException("Query command must not contain spaces.");
|
||||||
|
|
||||||
|
var acl = arcfg["AllowedUsers"];
|
||||||
|
if (acl == null) _qAccess = null;
|
||||||
|
else _qAccess = new EntityList(acl);
|
||||||
|
|
||||||
|
// TODO make optional
|
||||||
|
string ansval = arcfg["DefaultEvents"]?.Value<string>();
|
||||||
|
_qDefaultAnswer = GetTypesFromString(ansval);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new RuleImportException("Section for QueryCommand is not correctly defined.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EventType GetTypesFromString(string input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
throw new RuleImportException("Types are not properly defined.");
|
||||||
|
|
||||||
|
var strTypes = input.Split(
|
||||||
|
new char[] { ' ', ',', '/', '+' }, // and more?
|
||||||
|
StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
EventType endResult = EventType.None;
|
||||||
|
foreach (var item in strTypes)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = Enum.Parse<EventType>(item, true);
|
||||||
|
endResult |= result;
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
throw new RuleImportException($"Unable to determine the given event type \"{item}\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return endResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,9 +9,9 @@ using System.Threading.Tasks;
|
||||||
namespace Noikoio.RegexBot.Module.ModLogs
|
namespace Noikoio.RegexBot.Module.ModLogs
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper class for <see cref="ModLogs"/>. Keeps a database-backed cache of recent messages and assists
|
/// Helper class for <see cref="ModLogs"/>. Keeps a database-backed cache of recent messages for use
|
||||||
/// in reporting message changes and deletions, if configured to do so.
|
/// in reporting message changes and deletions, if configured to do so.
|
||||||
/// Does not manipulate the moderation log managed by the main class, but rather provides supplemental features.
|
/// Despite its place, it does not manipulate moderation logs. It simply pulls from the same configuration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class MessageCache
|
class MessageCache
|
||||||
{
|
{
|
||||||
|
@ -66,6 +66,7 @@ namespace Noikoio.RegexBot.Module.ModLogs
|
||||||
|
|
||||||
private async Task Client_MessageDeleted(Cacheable<Discord.IMessage, ulong> msg, ISocketMessageChannel channel)
|
private async Task Client_MessageDeleted(Cacheable<Discord.IMessage, ulong> msg, ISocketMessageChannel channel)
|
||||||
{
|
{
|
||||||
|
if (channel is IDMChannel) return; // No DMs
|
||||||
await ProcessReportMessage(true, msg.Id, channel, null);
|
await ProcessReportMessage(true, msg.Id, channel, null);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -84,11 +85,13 @@ namespace Noikoio.RegexBot.Module.ModLogs
|
||||||
else return;
|
else return;
|
||||||
|
|
||||||
// Check if this feature is enabled before doing anything else.
|
// Check if this feature is enabled before doing anything else.
|
||||||
var rptTarget = _outGetConfig(guildId) as ConfigItem.EntityName?;
|
var cfg = _outGetConfig(guildId) as GuildConfig;
|
||||||
if (!rptTarget.HasValue) return;
|
if (cfg == null) return;
|
||||||
|
if (isDelete && (cfg.RptTypes & EventType.MsgDelete) == 0) return;
|
||||||
|
if (!isDelete && (cfg.RptTypes & EventType.MsgEdit) == 0) return;
|
||||||
|
|
||||||
// Ignore if it's a message being deleted withing the reporting channel.
|
// Ignore if it's a message being deleted withing the reporting channel.
|
||||||
if (isDelete && rptTarget.Value.Id.Value == ch.Id) return;
|
if (isDelete && cfg.RptTarget.Value.Id.Value == ch.Id) return;
|
||||||
|
|
||||||
// Regardless of delete or edit, it is necessary to get the equivalent database information.
|
// Regardless of delete or edit, it is necessary to get the equivalent database information.
|
||||||
EntityCache.CacheUser ucd = null;
|
EntityCache.CacheUser ucd = null;
|
||||||
|
@ -128,11 +131,11 @@ namespace Noikoio.RegexBot.Module.ModLogs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find target channel, prepare and send out message
|
// Find target channel, prepare and send out message
|
||||||
var rptTargetChannel = _dClient.GetGuild(guildId)?.GetTextChannel(rptTarget.Value.Id.Value);
|
var g = _dClient.GetGuild(guildId);
|
||||||
|
var rptTargetChannel = g?.GetTextChannel(cfg.RptTarget.Value.Id.Value);
|
||||||
if (rptTargetChannel == null)
|
if (rptTargetChannel == null)
|
||||||
{
|
{
|
||||||
await _outLog("Target channel not found.");
|
await _outLog($"WARNING: Reporting channel {cfg.RptTarget.Value.ToString()} could not be determined.");
|
||||||
// TODO make a more descriptive error message
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var em = CreateReportEmbed(isDelete, ucd, messageId, ch, (cacheMsg, editMsg));
|
var em = CreateReportEmbed(isDelete, ucd, messageId, ch, (cacheMsg, editMsg));
|
||||||
|
|
|
@ -23,9 +23,11 @@ namespace Noikoio.RegexBot.Module.ModLogs
|
||||||
// Do nothing if database unavailable. The user will be informed by ProcessConfiguration.
|
// Do nothing if database unavailable. The user will be informed by ProcessConfiguration.
|
||||||
if (!RegexBot.Config.DatabaseAvailable) return;
|
if (!RegexBot.Config.DatabaseAvailable) return;
|
||||||
|
|
||||||
|
// MessageCache (reporting of MessageEdit, MessageDelete) handled by helper class
|
||||||
_msgCacheInstance = new MessageCache(client, Log, GetConfig);
|
_msgCacheInstance = new MessageCache(client, Log, GetConfig);
|
||||||
|
|
||||||
//throw new NotImplementedException();
|
// TODO add handlers for detecting joins, leaves, bans, kicks, user edits (nick/username/discr)
|
||||||
|
// TODO add handler for processing the log query command
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConfigSection("ModLogs")]
|
[ConfigSection("ModLogs")]
|
||||||
|
@ -33,7 +35,6 @@ namespace Noikoio.RegexBot.Module.ModLogs
|
||||||
{
|
{
|
||||||
if (configSection.Type != JTokenType.Object)
|
if (configSection.Type != JTokenType.Object)
|
||||||
throw new RuleImportException("Configuration for this section is invalid.");
|
throw new RuleImportException("Configuration for this section is invalid.");
|
||||||
var conf = (JObject)configSection;
|
|
||||||
|
|
||||||
if (!RegexBot.Config.DatabaseAvailable)
|
if (!RegexBot.Config.DatabaseAvailable)
|
||||||
{
|
{
|
||||||
|
@ -41,38 +42,7 @@ namespace Noikoio.RegexBot.Module.ModLogs
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
return new GuildConfig((JObject)configSection);
|
||||||
{
|
|
||||||
// MessageCache testing: will store an EntityName or die trying
|
|
||||||
EntityName? mctarget = new EntityName(conf["mctarget"].Value<string>(), EntityType.Channel);
|
|
||||||
await Log("Enabled MessageCache test on " + mctarget.Value.ToString());
|
|
||||||
return mctarget;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// well, not really die
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Concept:
|
|
||||||
* "ModLogs": {
|
|
||||||
* "AutoReporting": {
|
|
||||||
* // behavior for how to output to the reporting channel
|
|
||||||
* // MessageCache looks for configuration values within here.
|
|
||||||
* "Channel": "something compatible with EntityName",
|
|
||||||
* "Events": "perhaps a single string of separated event types"
|
|
||||||
* },
|
|
||||||
* "QueryOptions": {
|
|
||||||
* // Behavior for the query command (which is defined here rather than ModTools)
|
|
||||||
* // Need to stress in the documentation that "msgedit" and "msgdelete" events
|
|
||||||
* // are not kept and cannot be queried
|
|
||||||
* "QueryCommand": "!modlogs",
|
|
||||||
* "Permission": "Moderators", // either a string that says "Moderators" or an EntityList
|
|
||||||
* "DefaultQueryEvents": "another single string of separated event types",
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue