Ejemplo n.º 1
0
            public async Task Last(CommandContext ctx, [Description("Optional number of items to show. Default is 10")] int number = 10)
            {
                var isMod           = ctx.User.IsWhitelisted(ctx.Client, ctx.Guild);
                var showRetractions = ctx.Channel.IsPrivate && isMod;

                if (number < 1)
                {
                    number = 10;
                }
                var table = new AsciiTable(
                    new AsciiColumn("ID", alignToRight: true),
                    new AsciiColumn("±", disabled: !showRetractions),
                    new AsciiColumn("Username", maxWidth: 24),
                    new AsciiColumn("User ID", disabled: !ctx.Channel.IsPrivate, alignToRight: true),
                    new AsciiColumn("Issued by", maxWidth: 15),
                    new AsciiColumn("On date (UTC)"),
                    new AsciiColumn("Reason"),
                    new AsciiColumn("Context", disabled: !ctx.Channel.IsPrivate)
                    );

                using (var db = new BotDb())
                {
                    IOrderedQueryable <Warning> query;
                    if (showRetractions)
                    {
                        query = from warn in db.Warning
                                orderby warn.Id descending
                                select warn;
                    }
                    else
                    {
                        query = from warn in db.Warning
                                where !warn.Retracted
                                orderby warn.Id descending
                                select warn;
                    }
                    foreach (var row in query.Take(number))
                    {
                        var username = await ctx.GetUserNameAsync(row.DiscordId).ConfigureAwait(false);

                        var modname = await ctx.GetUserNameAsync(row.IssuerId, defaultName : "Unknown mod").ConfigureAwait(false);

                        var timestamp = row.Timestamp.HasValue ? new DateTime(row.Timestamp.Value, DateTimeKind.Utc).ToString("u") : null;
                        if (row.Retracted)
                        {
                            var modnameRetracted = row.RetractedBy.HasValue ? await ctx.GetUserNameAsync(row.RetractedBy.Value, defaultName : "Unknown mod").ConfigureAwait(false) : "";

                            var timestampRetracted = row.RetractionTimestamp.HasValue ? new DateTime(row.RetractionTimestamp.Value, DateTimeKind.Utc).ToString("u") : null;
                            table.Add(row.Id.ToString(), "-", username, row.DiscordId.ToString(), modnameRetracted, timestampRetracted, row.RetractionReason, "");
                            table.Add(row.Id.ToString(), "+", username.StrikeThrough(), row.DiscordId.ToString().StrikeThrough(), modname.StrikeThrough(), timestamp.StrikeThrough(), row.Reason.StrikeThrough(), row.FullReason.StrikeThrough());
                        }
                        else
                        {
                            table.Add(row.Id.ToString(), "+", username, row.DiscordId.ToString(), modname, timestamp, row.Reason, row.FullReason);
                        }
                    }
                }
                await ctx.SendAutosplitMessageAsync(new StringBuilder("Recent warnings:").Append(table)).ConfigureAwait(false);
            }
Ejemplo n.º 2
0
            public async Task By(CommandContext ctx, ulong moderatorId, [Description("Optional number of items to show. Default is 10")] int number = 10)
            {
                if (number < 1)
                {
                    number = 10;
                }
                var table = new AsciiTable(
                    new AsciiColumn("ID", alignToRight: true),
                    new AsciiColumn("Username", maxWidth: 24),
                    new AsciiColumn("User ID", disabled: !ctx.Channel.IsPrivate, alignToRight: true),
                    new AsciiColumn("On date (UTC)"),
                    new AsciiColumn("Reason"),
                    new AsciiColumn("Context", disabled: !ctx.Channel.IsPrivate)
                    );

                await using var db = new BotDb();
                var query = from warn in db.Warning
                            where warn.IssuerId == moderatorId && !warn.Retracted
                            orderby warn.Id descending
                            select warn;

                foreach (var row in query.Take(number))
                {
                    var username = await ctx.GetUserNameAsync(row.DiscordId).ConfigureAwait(false);

                    var timestamp = row.Timestamp.HasValue ? new DateTime(row.Timestamp.Value, DateTimeKind.Utc).ToString("u") : "";
                    table.Add(row.Id.ToString(), username, row.DiscordId.ToString(), timestamp, row.Reason, row.FullReason);
                }
                var modName = await ctx.GetUserNameAsync(moderatorId, defaultName : "Unknown mod").ConfigureAwait(false);

                await ctx.SendAutosplitMessageAsync(new StringBuilder($"Recent warnings issued by {modName}:").Append(table)).ConfigureAwait(false);
            }
Ejemplo n.º 3
0
            public async Task Users(CommandContext ctx, [Description("Optional number of items to show. Default is 10")] int number = 10)
            {
                try
                {
                    if (number < 1)
                    {
                        number = 10;
                    }
                    var table = new AsciiTable(
                        new AsciiColumn("Username", maxWidth: 24),
                        new AsciiColumn("User ID", disabled: !ctx.Channel.IsPrivate, alignToRight: true),
                        new AsciiColumn("Count", alignToRight: true),
                        new AsciiColumn("All time", alignToRight: true)
                        );
                    await using var db = new BotDb();
                    var query = from warn in db.Warning.AsEnumerable()
                                group warn by warn.DiscordId
                                into userGroup
                                let row = new { discordId = userGroup.Key, count = userGroup.Count(w => !w.Retracted), total = userGroup.Count() }
                    orderby row.count descending
                    select row;
                    foreach (var row in query.Take(number))
                    {
                        var username = await ctx.GetUserNameAsync(row.discordId).ConfigureAwait(false);

                        table.Add(username, row.discordId.ToString(), row.count.ToString(), row.total.ToString());
                    }
                    await ctx.SendAutosplitMessageAsync(new StringBuilder("Warning count per user:"******"SQL query for this command is broken at the moment", true).ConfigureAwait(false);
                }
            }
Ejemplo n.º 4
0
            public async Task Users(CommandContext ctx, [Description("Optional number of items to show. Default is 10")] int number = 10)
            {
                var isMod = ctx.User.IsWhitelisted(ctx.Client, ctx.Guild);

                if (number < 1)
                {
                    number = 10;
                }
                var table = new AsciiTable(
                    new AsciiColumn("Username", maxWidth: 24),
                    new AsciiColumn("User ID", disabled: !ctx.Channel.IsPrivate, alignToRight: true),
                    new AsciiColumn("Count", alignToRight: true),
                    new AsciiColumn("All time", alignToRight: true)
                    );

                using (var db = new BotDb())
                {
                    var query = from warn in db.Warning
                                group warn by warn.DiscordId
                                into userGroup
                                let row = new { discordId = userGroup.Key, count = userGroup.Count(w => !w.Retracted), total = userGroup.Count() }
                    orderby row.count descending
                    select row;
                    foreach (var row in query.Take(number))
                    {
                        var username = await ctx.GetUserNameAsync(row.discordId).ConfigureAwait(false);

                        table.Add(username, row.discordId.ToString(), row.count.ToString(), row.total.ToString());
                    }
                }
                await ctx.SendAutosplitMessageAsync(new StringBuilder("Warning count per user:").Append(table)).ConfigureAwait(false);
            }
Ejemplo n.º 5
0
            public async Task List(CommandContext ctx)
            {
                var table = new AsciiTable(
                    new AsciiColumn("Username", maxWidth: 32),
                    new AsciiColumn("Sudo")
                    );

                foreach (var mod in ModProvider.Mods.Values.OrderByDescending(m => m.Sudoer))
                {
                    table.Add(await ctx.GetUserNameAsync(mod.DiscordId), mod.Sudoer ? "✅" :"");
                }
                await ctx.SendAutosplitMessageAsync(table.ToString()).ConfigureAwait(false);
            }
Ejemplo n.º 6
0
        public async Task List(CommandContext ctx)
        {
            using var db = new BotDb();
            var selectExpr = db.ForcedNicknames.AsNoTracking();

            if (ctx.Guild != null)
            {
                selectExpr = selectExpr.Where(mem => mem.GuildId == ctx.Guild.Id);
            }

            var forcedNicknames = (
                from m in selectExpr.AsEnumerable()
                orderby m.UserId, m.Nickname
                let result = new { m.UserId, m.Nickname }
                select result
                ).ToList();

            if (forcedNicknames.Count == 0)
            {
                await ctx.RespondAsync("No users with forced nicknames").ConfigureAwait(false);

                return;
            }

            var table = new AsciiTable(
                new AsciiColumn("ID", !ctx.Channel.IsPrivate || !ctx.User.IsWhitelisted(ctx.Client)),
                new AsciiColumn("Username"),
                new AsciiColumn("Forced nickname")
                );
            var previousUser = 0ul;

            foreach (var forcedNickname in forcedNicknames.Distinct())
            {
                var sameUser = forcedNickname.UserId == previousUser;
                var username = sameUser ? "" : await ctx.GetUserNameAsync(forcedNickname.UserId).ConfigureAwait(false);

                table.Add(sameUser ? "" : forcedNickname.UserId.ToString(), username, forcedNickname.Nickname);
                previousUser = forcedNickname.UserId;
            }
            await ctx.SendAutosplitMessageAsync(table.ToString()).ConfigureAwait(false);
        }
Ejemplo n.º 7
0
        public async Task List(CommandContext ctx)
        {
            const string linkPrefix = "discord.gg/";

            await using var db = new BotDb();
            var whitelistedInvites = await db.WhitelistedInvites.ToListAsync().ConfigureAwait(false);

            if (whitelistedInvites.Count == 0)
            {
                await ctx.Channel.SendMessageAsync("There are no whitelisted discord servers").ConfigureAwait(false);

                return;
            }

            var table = new AsciiTable(
                new AsciiColumn("ID", alignToRight: true),
                new AsciiColumn("Server ID", alignToRight: true),
                new AsciiColumn("Invite", disabled: !ctx.Channel.IsPrivate),
                new AsciiColumn("Server Name")
                );

            foreach (var item in whitelistedInvites)
            {
                string?guildName = null;
                if (!string.IsNullOrEmpty(item.InviteCode))
                {
                    try
                    {
                        var invite = await ctx.Client.GetInviteByCodeAsync(item.InviteCode).ConfigureAwait(false);

                        guildName = invite.Guild.Name;
                    }
                    catch { }
                }
                if (string.IsNullOrEmpty(guildName))
                {
                    try
                    {
                        var guild = await ctx.Client.GetGuildAsync(item.GuildId).ConfigureAwait(false);

                        guildName = guild.Name;
                    }
                    catch { }
                }
                if (string.IsNullOrEmpty(guildName))
                {
                    guildName = item.Name ?? "";
                }
                var link = "";
                if (!string.IsNullOrEmpty(item.InviteCode))
                {
                    link = linkPrefix + item.InviteCode;
                }
                //discord expands invite links even if they're inside the code block for some reason
                table.Add(item.Id.ToString(), item.GuildId.ToString(), link + StringUtils.InvisibleSpacer, guildName.Sanitize());
            }
            var result = new StringBuilder()
                         .AppendLine("Whitelisted discord servers:")
                         .Append(table);
            await ctx.SendAutosplitMessageAsync(result).ConfigureAwait(false);
        }
Ejemplo n.º 8
0
        //note: be sure to pass a sanitized userName
        private static async Task ListUserWarningsAsync(DiscordClient client, DiscordMessage message, ulong userId, string userName, bool skipIfOne = true)
        {
            try
            {
                var isWhitelisted = client.GetMember(message.Author)?.IsWhitelisted() is true;
                if (message.Author.Id != userId && !isWhitelisted)
                {
                    Config.Log.Error($"Somehow {message.Author.Username} ({message.Author.Id}) triggered warning list for {userId}");
                    return;
                }

                var  channel = message.Channel;
                var  isPrivate = channel.IsPrivate;
                int  count, removed;
                bool isKot, isDoggo;
                await using var db = new BotDb();
                count = await db.Warning.CountAsync(w => w.DiscordId == userId && !w.Retracted).ConfigureAwait(false);

                removed = await db.Warning.CountAsync(w => w.DiscordId == userId && w.Retracted).ConfigureAwait(false);

                isKot   = db.Kot.Any(k => k.UserId == userId);
                isDoggo = db.Doggo.Any(d => d.UserId == userId);
                if (count == 0)
                {
                    if (isKot && isDoggo)
                    {
                        if (new Random().NextDouble() < 0.5)
                        {
                            isKot = false;
                        }
                        else
                        {
                            isDoggo = false;
                        }
                    }
                    var msg = (removed, isPrivate, isKot, isDoggo) switch
                    {
                        (0, _, true, false) => $"{userName} has no warnings, is an upstanding kot, and a paw bean of this community",
                        (0, _, false, true) => $"{userName} has no warnings, is a good boy, and a wiggling tail of this community",
                        (0, _, _, _) => $"{userName} has no warnings, is an upstanding citizen, and a pillar of this community",
                        (_, true, _, _) => $"{userName} has no warnings ({removed} retracted warning{(removed == 1 ? "" : "s")})",
                        (_, _, true, false) => $"{userName} has no warnings, but are they a good kot?",
                        (_, _, false, true) => $"{userName} has no warnings, but are they a good boy?",
                        _ => $"{userName} has no warnings",
                    };
                    await message.Channel.SendMessageAsync(msg).ConfigureAwait(false);

                    if (!isPrivate || removed == 0)
                    {
                        return;
                    }
                }

                if (count == 1 && skipIfOne)
                {
                    return;
                }

                const int maxWarningsInPublicChannel = 3;
                var       showCount = Math.Min(maxWarningsInPublicChannel, count);
                var       table     = new AsciiTable(
                    new AsciiColumn("ID", alignToRight: true),
                    new AsciiColumn("±", disabled: !isPrivate || !isWhitelisted),
                    new AsciiColumn("By", maxWidth: 15),
                    new AsciiColumn("On date (UTC)"),
                    new AsciiColumn("Reason"),
                    new AsciiColumn("Context", disabled: !isPrivate, maxWidth: 4096)
                    );
                IQueryable <Warning> query = db.Warning.Where(w => w.DiscordId == userId).OrderByDescending(w => w.Id);
                if (!isPrivate || !isWhitelisted)
                {
                    query = query.Where(w => !w.Retracted);
                }
                if (!isPrivate && !isWhitelisted)
                {
                    query = query.Take(maxWarningsInPublicChannel);
                }
                foreach (var warning in await query.ToListAsync().ConfigureAwait(false))
                {
                    if (warning.Retracted)
                    {
                        if (isWhitelisted && isPrivate)
                        {
                            var retractedByName = warning.RetractedBy.HasValue
                                ? await client.GetUserNameAsync(channel, warning.RetractedBy.Value, isPrivate, "unknown mod").ConfigureAwait(false)
                                : "";

                            var retractionTimestamp = warning.RetractionTimestamp.HasValue
                                ? new DateTime(warning.RetractionTimestamp.Value, DateTimeKind.Utc).ToString("u")
                                : "";
                            table.Add(warning.Id.ToString(), "-", retractedByName, retractionTimestamp, warning.RetractionReason ?? "", "");

                            var issuerName = warning.IssuerId == 0
                                ? ""
                                : await client.GetUserNameAsync(channel, warning.IssuerId, isPrivate, "unknown mod").ConfigureAwait(false);

                            var timestamp = warning.Timestamp.HasValue
                                ? new DateTime(warning.Timestamp.Value, DateTimeKind.Utc).ToString("u")
                                : "";
                            table.Add(warning.Id.ToString().StrikeThrough(), "+", issuerName.StrikeThrough(), timestamp.StrikeThrough(), warning.Reason.StrikeThrough(), warning.FullReason.StrikeThrough());
                        }
                    }
                    else
                    {
                        var issuerName = warning.IssuerId == 0
                            ? ""
                            : await client.GetUserNameAsync(channel, warning.IssuerId, isPrivate, "unknown mod").ConfigureAwait(false);

                        var timestamp = warning.Timestamp.HasValue
                            ? new DateTime(warning.Timestamp.Value, DateTimeKind.Utc).ToString("u")
                            : "";
                        table.Add(warning.Id.ToString(), "+", issuerName, timestamp, warning.Reason, warning.FullReason);
                    }
                }
                var result = new StringBuilder("Warning list for ").Append(userName);
                if (!isPrivate && !isWhitelisted && count > maxWarningsInPublicChannel)
                {
                    result.Append($" (last {showCount} of {count}, full list in DMs)");
                }
                result.AppendLine(":").Append(table);
                await channel.SendAutosplitMessageAsync(result).ConfigureAwait(false);
            }
Ejemplo n.º 9
0
        public async Task List(CommandContext ctx)
        {
            var table = new AsciiTable(
                new AsciiColumn("ID", alignToRight: true),
                new AsciiColumn("Trigger"),
                new AsciiColumn("Validation"),
                new AsciiColumn("Context"),
                new AsciiColumn("Actions"),
                new AsciiColumn("Custom message")
                );

            using (var db = new BotDb())
            {
                var duplicates = new Dictionary <string, FilterContext>(StringComparer.InvariantCultureIgnoreCase);
                var filters    = await db.Piracystring.Where(ps => !ps.Disabled).OrderBy(ps => ps.String.ToUpperInvariant()).AsNoTracking().ToListAsync().ConfigureAwait(false);

                var nonUniqueTriggers = (
                    from f in filters
                    group f by f.String.ToUpperInvariant()
                    into g
                    where g.Count() > 1
                    select g.Key
                    ).ToList();
                foreach (var t in nonUniqueTriggers)
                {
                    var duplicateFilters = filters.Where(ps => ps.String.Equals(t, StringComparison.InvariantCultureIgnoreCase)).ToList();
                    foreach (FilterContext fctx in Enum.GetValues(typeof(FilterContext)))
                    {
                        if (duplicateFilters.Count(f => (f.Context & fctx) == fctx) > 1)
                        {
                            if (duplicates.TryGetValue(t, out var fctxs))
                            {
                                duplicates[t] = fctxs | fctx;
                            }
                            else
                            {
                                duplicates[t] = fctx;
                            }
                        }
                    }
                }
                foreach (var item in filters)
                {
                    var ctxl = item.Context.ToString();
                    if (duplicates.Count > 0 &&
                        duplicates.TryGetValue(item.String, out var fctx) &&
                        (item.Context & fctx) != 0)
                    {
                        ctxl = "❗ " + ctxl;
                    }
                    table.Add(
                        item.Id.ToString(),
                        item.String.Sanitize(),
                        item.ValidatingRegex,
                        ctxl,
                        item.Actions.ToFlagsString(),
                        string.IsNullOrEmpty(item.CustomMessage) ? "" : "✅"
                        );
                }
            }
            await ctx.SendAutosplitMessageAsync(table.ToString()).ConfigureAwait(false);

            await ctx.RespondAsync(FilterActionExtensions.GetLegend()).ConfigureAwait(false);
        }
Ejemplo n.º 10
0
        //note: be sure to pass a sanitized userName
        private static async Task ListUserWarningsAsync(DiscordClient client, DiscordMessage message, ulong userId, string userName, bool skipIfOne = true)
        {
            try
            {
                var isWhitelisted = client.GetMember(message.Author)?.IsWhitelisted() ?? false;
                if (message.Author.Id != userId && !isWhitelisted)
                {
                    Config.Log.Error($"Somehow {message.Author.Username} ({message.Author.Id}) triggered warning list for {userId}");
                    return;
                }

                var channel = message.Channel;
                var isPrivate = channel.IsPrivate;
                int count, removed;
                using (var db = new BotDb())
                {
                    count = await db.Warning.CountAsync(w => w.DiscordId == userId && !w.Retracted).ConfigureAwait(false);

                    removed = await db.Warning.CountAsync(w => w.DiscordId == userId && w.Retracted).ConfigureAwait(false);
                }
                if (count == 0)
                {
                    if (removed == 0)
                    {
                        await message.RespondAsync(userName + " has no warnings, is a standup citizen, and a pillar of this community").ConfigureAwait(false);
                    }
                    else
                    {
                        await message.RespondAsync(userName + " has no warnings" + (isPrivate ? $" ({removed} retracted warning{(removed == 1 ? "" : "s")})" : "")).ConfigureAwait(false);
                    }
                    return;
                }

                if (count == 1 && skipIfOne)
                {
                    return;
                }

                const int maxWarningsInPublicChannel = 3;
                using (var db = new BotDb())
                {
                    var showCount = Math.Min(maxWarningsInPublicChannel, count);
                    var table     = new AsciiTable(
                        new AsciiColumn("ID", alignToRight: true),
                        new AsciiColumn("±", disabled: !isPrivate || !isWhitelisted),
                        new AsciiColumn("By", maxWidth: 15),
                        new AsciiColumn("On date (UTC)"),
                        new AsciiColumn("Reason"),
                        new AsciiColumn("Context", disabled: !isPrivate, maxWidth: 4096)
                        );
                    IQueryable <Warning> query = db.Warning.Where(w => w.DiscordId == userId).OrderByDescending(w => w.Id);
                    if (!isPrivate || !isWhitelisted)
                    {
                        query = query.Where(w => !w.Retracted);
                    }
                    if (!isPrivate && !isWhitelisted)
                    {
                        query = query.Take(maxWarningsInPublicChannel);
                    }
                    foreach (var warning in await query.ToListAsync().ConfigureAwait(false))
                    {
                        if (warning.Retracted)
                        {
                            if (isWhitelisted && isPrivate)
                            {
                                var retractedByName = !warning.RetractedBy.HasValue
                                    ? ""
                                    : await client.GetUserNameAsync(channel, warning.RetractedBy.Value, isPrivate, "unknown mod").ConfigureAwait(false);

                                var retractionTimestamp = warning.RetractionTimestamp.HasValue
                                    ? new DateTime(warning.RetractionTimestamp.Value, DateTimeKind.Utc).ToString("u")
                                    : null;
                                table.Add(warning.Id.ToString(), "-", retractedByName, retractionTimestamp, warning.RetractionReason, "");

                                var issuerName = warning.IssuerId == 0
                                    ? ""
                                    : await client.GetUserNameAsync(channel, warning.IssuerId, isPrivate, "unknown mod").ConfigureAwait(false);

                                var timestamp = warning.Timestamp.HasValue
                                    ? new DateTime(warning.Timestamp.Value, DateTimeKind.Utc).ToString("u")
                                    : null;
                                table.Add(warning.Id.ToString().StrikeThrough(), "+", issuerName.StrikeThrough(), timestamp.StrikeThrough(), warning.Reason.StrikeThrough(), warning.FullReason.StrikeThrough());
                            }
                        }
                        else
                        {
                            var issuerName = warning.IssuerId == 0
                                ? ""
                                : await client.GetUserNameAsync(channel, warning.IssuerId, isPrivate, "unknown mod").ConfigureAwait(false);

                            var timestamp = warning.Timestamp.HasValue
                                ? new DateTime(warning.Timestamp.Value, DateTimeKind.Utc).ToString("u")
                                : null;
                            table.Add(warning.Id.ToString(), "+", issuerName, timestamp, warning.Reason, warning.FullReason);
                        }
                    }
                    var result = new StringBuilder("Warning list for ").Append(userName);
                    if (!isPrivate && !isWhitelisted && count > maxWarningsInPublicChannel)
                    {
                        result.Append($" (last {showCount} of {count}, full list in DMs)");
                    }
                    result.AppendLine(":").Append(table);
                    await channel.SendAutosplitMessageAsync(result).ConfigureAwait(false);
                }
            }
            catch (Exception e)
            {
                Config.Log.Warn(e);
            }
        }
Ejemplo n.º 11
0
        public async Task List(CommandContext ctx)
        {
            var table = new AsciiTable(
                new AsciiColumn("ID", alignToRight: true),
                new AsciiColumn("Trigger"),
                new AsciiColumn("Validation", maxWidth: 2048),
                new AsciiColumn("Context", maxWidth: 4096),
                new AsciiColumn("Actions"),
                new AsciiColumn("Custom message", maxWidth: 2048)
                );

            await using var db = new BotDb();
            var duplicates        = new Dictionary <string, FilterContext>(StringComparer.InvariantCultureIgnoreCase);
            var filters           = db.Piracystring.Where(ps => !ps.Disabled).AsNoTracking().AsEnumerable().OrderBy(ps => ps.String.ToUpperInvariant()).ToList();
            var nonUniqueTriggers = (
                from f in filters
                group f by f.String.ToUpperInvariant()
                into g
                where g.Count() > 1
                select g.Key
                ).ToList();

            foreach (var t in nonUniqueTriggers)
            {
                var duplicateFilters = filters.Where(ps => ps.String.Equals(t, StringComparison.InvariantCultureIgnoreCase)).ToList();
                foreach (FilterContext fctx in Enum.GetValues(typeof(FilterContext)))
                {
                    if (duplicateFilters.Count(f => (f.Context & fctx) == fctx) > 1)
                    {
                        if (duplicates.TryGetValue(t, out var fctxDup))
                        {
                            duplicates[t] = fctxDup | fctx;
                        }
                        else
                        {
                            duplicates[t] = fctx;
                        }
                    }
                }
            }
            foreach (var item in filters)
            {
                var ctxl = item.Context.ToString();
                if (duplicates.Count > 0 &&
                    duplicates.TryGetValue(item.String, out var fctx) &&
                    (item.Context & fctx) != 0)
                {
                    ctxl = "❗ " + ctxl;
                }
                table.Add(
                    item.Id.ToString(),
                    item.String.Sanitize(),
                    item.ValidatingRegex ?? "",
                    ctxl,
                    item.Actions.ToFlagsString(),
                    item.CustomMessage ?? ""
                    );
            }
            var result = new StringBuilder(table.ToString(false)).AppendLine()
                         .AppendLine(FilterActionExtensions.GetLegend(""));

            await using var output = Config.MemoryStreamManager.GetStream();
            //await using (var gzip = new GZipStream(output, CompressionLevel.Optimal, true))
            await using (var writer = new StreamWriter(output, leaveOpen: true))
                await writer.WriteAsync(result.ToString()).ConfigureAwait(false);
            output.Seek(0, SeekOrigin.Begin);
            await ctx.RespondAsync(new DiscordMessageBuilder().WithFile("filters.txt", output)).ConfigureAwait(false);
        }