diff --git a/Module/ModLogs/GuildState.cs b/Module/ModLogs/GuildState.cs
index 313bf0f..80930ea 100644
--- a/Module/ModLogs/GuildState.cs
+++ b/Module/ModLogs/GuildState.cs
@@ -1,6 +1,9 @@
-using Newtonsoft.Json.Linq;
+using Discord;
+using Discord.Webhook;
+using Newtonsoft.Json.Linq;
using Noikoio.RegexBot.ConfigItem;
using System;
+using System.Text.RegularExpressions;
namespace Noikoio.RegexBot.Module.ModLogs
{
@@ -10,16 +13,21 @@ namespace Noikoio.RegexBot.Module.ModLogs
class GuildState
{
// Event reporting
- private readonly EntityName _rptTarget;
+ private DiscordWebhookClient _rptTarget;
private LogEntry.LogType _rptTypes;
+ private ulong _rptIgnore;
///
- /// Target reporting channel.
+ /// Webhook for log reporting.
///
- public EntityName? RptTarget => _rptTarget;
+ public DiscordWebhookClient RptTarget => _rptTarget;
///
/// Event types to send to the reporting channel.
///
public LogEntry.LogType RptTypes => _rptTypes;
+ ///
+ /// Channel for AutoReporting to ignore.
+ ///
+ public ulong RptIgnore => _rptIgnore;
// Query command
private readonly string _qCmd; // command name
@@ -45,19 +53,21 @@ namespace Noikoio.RegexBot.Module.ModLogs
var arcfg = cfgRoot["AutoReporting"];
if (arcfg == null)
{
- _rptTarget = default(EntityName); // NOTE: Change this if EntityName becomes a class later
+ _rptTarget = null;
_rptTypes = LogEntry.LogType.None;
+ _rptIgnore = 0;
}
else if (arcfg.Type == JTokenType.Object)
{
- string chval = arcfg["Channel"]?.Value();
- 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.");
+ string whurl = arcfg["WebhookUrl"]?.Value();
+ if (whurl == null) throw new RuleImportException("Webhook URL for log reporting is not specified.");
+ var wrx = WebhookUrlParts.Match(whurl);
+ if (!wrx.Success) throw new RuleImportException("Webhook URL for log reporting is not valid.");
+ var wid = ulong.Parse(wrx.Groups[1].Value);
+ var wtk = wrx.Groups[2].Value;
+ _rptTarget = new DiscordWebhookClient(wid, wtk,
+ new Discord.Rest.DiscordRestConfig() { DefaultRetryMode = RetryMode.RetryRatelimit });
+ // TODO figure out how to hook up the webhook client's log event
// TODO make optional
string rpval = arcfg["Events"]?.Value();
@@ -69,6 +79,13 @@ namespace Noikoio.RegexBot.Module.ModLogs
{
throw new RuleImportException(ex.Message);
}
+
+ var ignoreId = arcfg["CacheIgnore"]?.Value();
+ if (string.IsNullOrWhiteSpace(ignoreId)) _rptIgnore = 0;
+ else if (!ulong.TryParse(ignoreId, out _rptIgnore))
+ {
+ throw new RuleImportException("CacheIgnore was not set to a valid channel ID.");
+ }
}
else
{
@@ -112,5 +129,8 @@ namespace Noikoio.RegexBot.Module.ModLogs
throw new RuleImportException("Section for QueryCommand is not correctly defined.");
}
}
+
+ private static Regex WebhookUrlParts =
+ new Regex(@"https?:\/\/discordapp.com\/api\/webhooks\/(\d+)\/([^/]+)?", RegexOptions.Compiled);
}
}
diff --git a/Module/ModLogs/MessageCache.cs b/Module/ModLogs/MessageCache.cs
index 7533e6f..18612ef 100644
--- a/Module/ModLogs/MessageCache.cs
+++ b/Module/ModLogs/MessageCache.cs
@@ -38,7 +38,8 @@ namespace Noikoio.RegexBot.Module.ModLogs
#region Event handling
private async Task Client_MessageReceived(SocketMessage arg)
{
- if (arg.Author.IsBot) return;
+ if (arg.Author.IsWebhook) return;
+ if (arg.Channel is IDMChannel) return; // No DMs
await AddOrUpdateCacheItemAsync(arg);
}
@@ -46,13 +47,11 @@ namespace Noikoio.RegexBot.Module.ModLogs
private async Task Client_MessageUpdated(
Cacheable before, SocketMessage after, ISocketMessageChannel channel)
{
- if (after.Author.IsBot) return;
+ if (after.Author.IsWebhook) return;
// We only want channel messages
if (after is SocketUserMessage afterMsg && !(afterMsg is IDMChannel))
{
- if (after.Author.IsBot) return;
-
// We're not interested in all message updates, only those that leave a timestamp.
if (!afterMsg.EditedTimestamp.HasValue) return;
}
@@ -88,11 +87,9 @@ namespace Noikoio.RegexBot.Module.ModLogs
// Check if this feature is enabled before doing anything else.
var cfg = _outGetConfig(guildId);
if (cfg == null) return;
- if (isDelete && (cfg.RptTypes & LogEntry.LogType.MsgDelete) == 0) return;
- if (!isDelete && (cfg.RptTypes & LogEntry.LogType.MsgEdit) == 0) return;
-
- // Ignore if it's a message being deleted withing the reporting channel.
- if (isDelete && cfg.RptTarget.Value.Id.Value == ch.Id) return;
+ if (cfg.RptIgnore != 0 && ch.Id == cfg.RptIgnore) return; // ignored channel
+ if (isDelete && (cfg.RptTypes & LogEntry.LogType.MsgDelete) == 0) return; // not reporting deletions
+ if (!isDelete && (cfg.RptTypes & LogEntry.LogType.MsgEdit) == 0) return; // not reporting edits
// Regardless of delete or edit, it is necessary to get the equivalent database information.
EntityCache.CacheUser ucd = null;
@@ -131,16 +128,9 @@ namespace Noikoio.RegexBot.Module.ModLogs
cacheMsg = "**Database error. See log.**";
}
- // Find target channel, prepare and send out message
- var g = _dClient.GetGuild(guildId);
- var rptTargetChannel = g?.GetTextChannel(cfg.RptTarget.Value.Id.Value);
- if (rptTargetChannel == null)
- {
- await _outLog($"WARNING: Reporting channel {cfg.RptTarget.Value.ToString()} could not be determined.");
- return;
- }
+ // Prepare and send out message
var em = CreateReportEmbed(isDelete, ucd, messageId, ch, (cacheMsg, editMsg));
- await rptTargetChannel.SendMessageAsync("", embed: em);
+ await cfg.RptTarget.SendMessageAsync("", embeds: new Embed[] { em });
}
const int ReportCutoffLength = 500;
@@ -173,8 +163,7 @@ namespace Noikoio.RegexBot.Module.ModLogs
Fields = new System.Collections.Generic.List(),
Footer = new EmbedFooterBuilder()
{
- Text = "User ID: " + ucd?.UserId.ToString() ?? "Unknown",
- IconUrl = _dClient.CurrentUser.GetAvatarUrl()
+ Text = "User ID: " + ucd?.UserId.ToString() ?? "Unknown"
},
Timestamp = DateTimeOffset.UtcNow
};
diff --git a/Module/ModLogs/ModLogs.cs b/Module/ModLogs/ModLogs.cs
index 9d53c8a..c216655 100644
--- a/Module/ModLogs/ModLogs.cs
+++ b/Module/ModLogs/ModLogs.cs
@@ -40,8 +40,7 @@ namespace Noikoio.RegexBot.Module.ModLogs
}
var conf = new GuildState((JObject)configSection);
- if (conf.RptTypes != LogEntry.LogType.None)
- await Log("Enabled event autoreporting to " + conf.RptTarget);
+ if (conf.RptTypes != LogEntry.LogType.None) await Log("Enabled event autoreporting.");
return conf;
}
diff --git a/docs/modlogs.md b/docs/modlogs.md
index 318918a..da8c878 100644
--- a/docs/modlogs.md
+++ b/docs/modlogs.md
@@ -8,7 +8,8 @@ Sample within a [server definition](serverdef.html):
"ModLogs": {
"AutoReporting": {
"Channel": "#99999999:mod-events",
- "Events": "msgedit,msgdelete"
+ "Events": "msgedit,msgdelete",
+ "CacheIgnore": 1230000000000
}
}
```
@@ -24,9 +25,10 @@ The following values can be defined within the `ModLogs` object:
As its name implies, the `AutoReporting` section allows the bot operator to configure automatic reporting of one or more events as they occur to a designated reporting channel. Omitting this section in configuration disables this function.
The following values are accepted within this object:
-* Channel (*string*) - **Required.** The channel name in which to report events.
- * The channel ID is currently required to be specified (see [EntityList](entitylist.html)). This limitation will be removed in a future update.
-* Events (*string*) - **Required** for now. A comma-separated list of event types to be sent to the reporting channel.
+* WebhookUrl (*string*) - **Required.** A webhook URL to be used by the bot for sending events.
+* Events (*string*) - **Required** at the moment. A comma-separated list of event types to be sent to the reporting channel.
+* CacheIgnore (*number*) - Channel (ID) to ignore for MsgEdit and MsgDelete autoreporting. (Optional.)
+ * It is **highly recommended** that the reporting channel be specified, otherwise deleting a report within it will cause another report to appear.
#### Event types
All events fall into one of a number of categories.