discord-bot/CompatBot/Commands/Warnings.ListGroup.cs

214 lines
11 KiB
C#

using CompatApiClient.Utils;
using CompatBot.Database;
using CompatBot.Database.Providers;
using DSharpPlus.Commands.Converters;
namespace CompatBot.Commands;
internal static partial class Warnings
{
[Command("list")]
[Description("Allows to list warnings in various ways. Users can only see their own warnings.")]
internal static class ListGroup
{
[Command("user")]
[Description("Show warning list for a user")]
public static async ValueTask List(SlashCommandContext ctx, DiscordUser user)
{
await ctx.DeferResponseAsync(true).ConfigureAwait(false);
if (await CheckListPermissionAsync(ctx, user.Id).ConfigureAwait(false))
await ListUserWarningsAsync(ctx.Client, ctx.Interaction, user.Id, user.Username.Sanitize(), skipIfOne: false, useFollowup: true);
}
[Command("top")]
[Description("List top users with warnings")]
public static async ValueTask Users(
SlashCommandContext ctx,
[Description("Number of items to show. Default is 10")]
int number = 10
)
{
var ephemeral = !ctx.Channel.IsSpamChannel() && !ctx.Channel.IsOfftopicChannel();
await ctx.DeferResponseAsync(ephemeral).ConfigureAwait(false);
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 = await BotDb.OpenReadAsync().ConfigureAwait(false);
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());
}
var pages = AutosplitResponseHelper.AutosplitMessage(new StringBuilder("Warning count per user:").Append(table).ToString());
await ctx.RespondAsync(pages[0], ephemeral: ephemeral).ConfigureAwait(false);
foreach (var page in pages.Skip(1).Take(EmbedPager.MaxFollowupMessages))
await ctx.FollowupAsync(page, ephemeral: ephemeral).ConfigureAwait(false);
}
catch (Exception e)
{
Config.Log.Error(e);
await ctx.RespondAsync($"{Config.Reactions.Failure} Failed to execute the command: {e.Message}".Trim(EmbedPager.MaxMessageLength), ephemeral: true).ConfigureAwait(false);
}
}
[Command("mods")]
[Description("List top bot mods giving warnings")]
public static async ValueTask Mods(
SlashCommandContext ctx,
[Description("Number of items to show. Default is 10")]
int number = 10
)
{
var ephemeral = !ctx.Channel.IsSpamChannel() && !ctx.Channel.IsOfftopicChannel();
await ctx.DeferResponseAsync(ephemeral).ConfigureAwait(false);
try
{
if (number < 1)
number = 10;
var table = new AsciiTable(
new AsciiColumn("Username", maxWidth: 24),
new AsciiColumn("Issuer ID", disabled: !ctx.Channel.IsPrivate, alignToRight: true),
new AsciiColumn("Warnings given", alignToRight: true),
new AsciiColumn("Including retracted", alignToRight: true)
);
await using var db = await BotDb.OpenReadAsync().ConfigureAwait(false);
var query = from warn in db.Warning.AsEnumerable()
group warn by warn.IssuerId
into modGroup
let row = new {userId = modGroup.Key, count = modGroup.Count(w => !w.Retracted), total = modGroup.Count()}
orderby row.count descending
select row;
foreach (var row in query.Take(number))
{
var username = await ctx.GetUserNameAsync(row.userId).ConfigureAwait(false);
if (username is null or "")
username = "Unknown";
table.Add(username, row.userId.ToString(), row.count.ToString(), row.total.ToString());
}
var pages = AutosplitResponseHelper.AutosplitMessage(new StringBuilder("Warnings issued per bot mod:").Append(table).ToString());
await ctx.RespondAsync(pages[0], ephemeral: ephemeral).ConfigureAwait(false);
foreach (var page in pages.Skip(1).Take(EmbedPager.MaxFollowupMessages))
await ctx.FollowupAsync(page, ephemeral: ephemeral).ConfigureAwait(false);
}
catch (Exception e)
{
Config.Log.Error(e);
await ctx.RespondAsync($"{Config.Reactions.Failure} Failed to execute the command: {e.Message}".Trim(EmbedPager.MaxMessageLength), ephemeral: true).ConfigureAwait(false);
}
}
[Command("by")]
[Description("Shows warnings issued by the specified moderator")]
public static async ValueTask By(
SlashCommandContext ctx,
DiscordUser moderator,
[Description("Number of items to show. Default is 10")] int number = 10
)
{
await ctx.DeferResponseAsync(true).ConfigureAwait(false);
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 = await BotDb.OpenReadAsync().ConfigureAwait(false);
var query = from warn in db.Warning
where warn.IssuerId == moderator.Id && !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 = moderator.Username;
var pages = AutosplitResponseHelper.AutosplitMessage(new StringBuilder($"Recent warnings issued by {modName}:").Append(table).ToString());
await ctx.RespondAsync(pages[0], ephemeral: true).ConfigureAwait(false);
foreach (var page in pages.Skip(1).Take(EmbedPager.MaxFollowupMessages))
await ctx.FollowupAsync(page, ephemeral: true).ConfigureAwait(false);
}
[Command("recent")]
[Description("Show last issued warnings")]
public static async ValueTask Last(
SlashCommandContext ctx,
[Description("Number of items to show. Default is 10")]
int number = 10
)
{
await ctx.DeferResponseAsync(true).ConfigureAwait(false);
var isMod = await ctx.User.IsWhitelistedAsync(ctx.Client, ctx.Guild).ConfigureAwait(false);
if (number < 1)
number = 10;
var table = new AsciiTable(
new AsciiColumn("ID", alignToRight: true),
new AsciiColumn("±", disabled: !isMod),
new AsciiColumn("Username", maxWidth: 24),
new AsciiColumn("User ID", disabled: !isMod, alignToRight: true),
new AsciiColumn("Issued by", maxWidth: 15),
new AsciiColumn("On date (UTC)"),
new AsciiColumn("Reason"),
new AsciiColumn("Context", disabled: !isMod)
);
await using var db = await BotDb.OpenReadAsync().ConfigureAwait(false);
IOrderedQueryable<Warning> query;
if (isMod)
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") : "";
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") : "";
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);
}
var pages = AutosplitResponseHelper.AutosplitMessage(new StringBuilder("Recent warnings:").Append(table).ToString());
await ctx.RespondAsync(pages[0], ephemeral: true).ConfigureAwait(false);
foreach (var page in pages.Skip(1).Take(EmbedPager.MaxFollowupMessages))
await ctx.FollowupAsync(page, ephemeral: true).ConfigureAwait(false);
}
private static async ValueTask<bool> CheckListPermissionAsync(SlashCommandContext ctx, ulong userId)
{
if (userId == ctx.User.Id || ModProvider.IsMod(ctx.User.Id))
return true;
await ctx.RespondAsync($"{Config.Reactions.Denied} You can only view your own warnings").ConfigureAwait(false);
return false;
}
}
}