public async Task CleanupOldMessages()
        {
            var deleteThresholdInstant   = _clock.GetCurrentInstant() - CommandMessageRetention;
            var deleteThresholdSnowflake = DiscordUtils.InstantToSnowflake(deleteThresholdInstant);

            var deletedRows = await _db.Execute(conn => _repo.DeleteCommandMessagesBefore(conn, deleteThresholdSnowflake));

            _logger.Information("Pruned {DeletedRows} command messages older than retention {Retention} (older than {DeleteThresholdInstant} / {DeleteThresholdSnowflake})",
                                deletedRows, CommandMessageRetention, deleteThresholdInstant, deleteThresholdSnowflake);
        }
示例#2
0
        public async ValueTask HandleLoggerBotCleanup(DiscordMessage msg)
        {
            if (msg.Channel.Type != ChannelType.Text)
            {
                return;
            }
            if (!msg.Channel.BotHasAllPermissions(Permissions.ManageMessages))
            {
                return;
            }

            // If this message is from a *webhook*, check if the name matches one of the bots we know
            // TODO: do we need to do a deeper webhook origin check, or would that be too hard on the rate limit?
            // If it's from a *bot*, check the bot ID to see if we know it.
            LoggerBot bot = null;

            if (msg.WebhookMessage)
            {
                _botsByWebhookName.TryGetValue(msg.Author.Username, out bot);
            }
            else if (msg.Author.IsBot)
            {
                _bots.TryGetValue(msg.Author.Id, out bot);
            }

            // If we didn't find anything before, or what we found is an unsupported bot, bail
            if (bot == null)
            {
                return;
            }

            try
            {
                // We try two ways of extracting the actual message, depending on the bots
                if (bot.FuzzyExtractFunc != null)
                {
                    // Some bots (Carl, Circle, etc) only give us a user ID and a rough timestamp, so we try our best to
                    // "cross-reference" those with the message DB. We know the deletion event happens *after* the message
                    // was sent, so we're checking for any messages sent in the same guild within 3 seconds before the
                    // delete event timestamp, which is... good enough, I think? Potential for false positives and negatives
                    // either way but shouldn't be too much, given it's constrained by user ID and guild.
                    var fuzzy = bot.FuzzyExtractFunc(msg);
                    if (fuzzy == null)
                    {
                        return;
                    }

                    using var conn = await _db.Obtain();

                    var mid = await conn.QuerySingleOrDefaultAsync <ulong?>(
                        "select mid from messages where sender = @User and mid > @ApproxID and guild = @Guild limit 1",
                        new
                    {
                        fuzzy.Value.User,
                        Guild    = msg.Channel.GuildId,
                        ApproxId = DiscordUtils.InstantToSnowflake(
                            fuzzy.Value.ApproxTimestamp - TimeSpan.FromSeconds(3))
                    });

                    if (mid == null)
                    {
                        return;              // If we didn't find a corresponding message, bail
                    }
                    // Otherwise, we can *reasonably assume* that this is a logged deletion, so delete the log message.
                    await msg.DeleteAsync();
                }
                else if (bot.ExtractFunc != null)
                {
                    // Other bots give us the message ID itself, and we can just extract that from the database directly.
                    var extractedId = bot.ExtractFunc(msg);
                    if (extractedId == null)
                    {
                        return;                      // If we didn't find anything, bail.
                    }
                    using var conn = await _db.Obtain();

                    // We do this through an inline query instead of through DataStore since we don't need all the joins it does
                    var mid = await conn.QuerySingleOrDefaultAsync <ulong?>(
                        "select mid from messages where original_mid = @Mid", new { Mid = extractedId.Value });

                    if (mid == null)
                    {
                        return;
                    }

                    // If we've gotten this far, we found a logged deletion of a trigger message. Just yeet it!
                    await msg.DeleteAsync();
                } // else should not happen, but idk, it might
            }
            catch (NotFoundException)
            {
                // Sort of a temporary measure: getting an error in Sentry about a NotFoundException from D#+ here
                // The only thing I can think of that'd cause this are the DeleteAsync() calls which 404 when
                // the message doesn't exist anyway - so should be safe to just ignore it, right?
            }
        }