From f4ba54e39baf74ec7df3451bee5c1cd38bf92c4b Mon Sep 17 00:00:00 2001 From: Noikoio Date: Thu, 5 Apr 2018 15:46:15 -0700 Subject: [PATCH] Many small changes/fixes, and switching autoreporting to webhook --- Module/ModLogs/GuildState.cs | 46 ++++++++++++++++++++++++---------- Module/ModLogs/MessageCache.cs | 29 +++++++-------------- Module/ModLogs/ModLogs.cs | 3 +-- docs/modlogs.md | 10 +++++--- 4 files changed, 49 insertions(+), 39 deletions(-) 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.