feat: guild ban add/remove logging, store banned systems in database

This commit is contained in:
sam 2024-10-13 14:58:44 +02:00
parent ca99bdfb94
commit 8e030acaf3
12 changed files with 227 additions and 36 deletions

View file

@ -15,7 +15,6 @@ using Remora.Discord.Commands.Extensions;
using Remora.Discord.Commands.Feedback.Services;
using Remora.Discord.Commands.Services;
using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Gateway;
using Remora.Results;
using IClock = NodaTime.IClock;
using IResult = Remora.Results.IResult;

View file

@ -0,0 +1,90 @@
using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Database;
using Catalogger.Backend.Database.Queries;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Gateway.Responders;
using Remora.Results;
namespace Catalogger.Backend.Bot.Responders.Guilds;
public class GuildBanAddResponder(
ILogger logger,
DatabaseContext db,
WebhookExecutorService webhookExecutor,
UserCache userCache,
AuditLogCache auditLogCache,
PluralkitApiService pluralkitApi
) : IResponder<IGuildBanAdd>
{
private readonly ILogger _logger = logger.ForContext<GuildBanAddResponder>();
public async Task<Result> RespondAsync(IGuildBanAdd evt, CancellationToken ct = default)
{
var guildConfig = await db.GetGuildAsync(evt.GuildID, ct);
// Delay 2 seconds for the audit log
await Task.Delay(2000, ct);
var embed = new EmbedBuilder()
.WithTitle("User banned")
.WithAuthor(evt.User.Tag(), null, evt.User.AvatarUrl())
.WithDescription($"<@{evt.User.ID}>")
.WithColour(DiscordUtils.Red)
.WithFooter($"User ID: {evt.User.ID}")
.WithCurrentTimestamp();
if (auditLogCache.TryGetBan(evt.GuildID, evt.User.ID, out var actionData))
{
embed.AddField(
"Responsible moderator",
await userCache.TryFormatUserAsync(actionData.ModeratorId)
);
embed.AddField("Reason", actionData.Reason ?? "No reason given");
}
else
{
embed.AddField("Responsible moderator", "*(unknown)*");
embed.AddField("Reason", "*(unknown)*");
}
// Get PluralKit system, if any, and add it to the guild's banned systems list
var pkSystem = await pluralkitApi.GetPluralKitSystemAsync(evt.User.ID.Value, ct);
if (pkSystem != null)
{
if (!guildConfig.IsSystemBanned(pkSystem))
{
_logger.Information(
"PluralKit system {SystemHid} will be banned from guild {GuildId}",
pkSystem.Id,
evt.GuildID
);
guildConfig.BannedSystems.Add(pkSystem.Id);
guildConfig.BannedSystems.Add(pkSystem.Uuid.ToString());
db.Update(guildConfig);
await db.SaveChangesAsync(ct);
}
embed.AddField(
"PluralKit system",
$"""
**ID:** {pkSystem.Id}
**UUID:** `{pkSystem.Uuid}`
**Name:** {pkSystem.Name ?? "*(none)*"}
**Tag:** {pkSystem.Tag ?? "*(none)*"}
This system has been marked as banned. You will be warned if another account linked to this system joins.
"""
);
}
webhookExecutor.QueueLog(
guildConfig,
LogChannelType.GuildBanAdd,
embed.Build().GetOrThrow()
);
return Result.Success;
}
}

View file

@ -0,0 +1,82 @@
using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Database;
using Catalogger.Backend.Database.Queries;
using Catalogger.Backend.Extensions;
using Catalogger.Backend.Services;
using Remora.Discord.API.Abstractions.Gateway.Events;
using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Gateway.Responders;
using Remora.Results;
namespace Catalogger.Backend.Bot.Responders.Guilds;
public class GuildBanRemoveResponder(
ILogger logger,
DatabaseContext db,
WebhookExecutorService webhookExecutor,
UserCache userCache,
AuditLogCache auditLogCache,
PluralkitApiService pluralkitApi
) : IResponder<IGuildBanRemove>
{
private readonly ILogger _logger = logger.ForContext<GuildBanRemoveResponder>();
public async Task<Result> RespondAsync(IGuildBanRemove evt, CancellationToken ct = default)
{
var guildConfig = await db.GetGuildAsync(evt.GuildID, ct);
// Delay 2 seconds for the audit log
await Task.Delay(2000, ct);
var embed = new EmbedBuilder()
.WithTitle("User unbanned")
.WithAuthor(evt.User.Tag(), null, evt.User.AvatarUrl())
.WithDescription($"<@{evt.User.ID}>")
.WithColour(DiscordUtils.Green)
.WithFooter($"User ID: {evt.User.ID}")
.WithCurrentTimestamp();
if (auditLogCache.TryGetUnban(evt.GuildID, evt.User.ID, out var actionData))
{
embed.AddField(
"Responsible moderator",
await userCache.TryFormatUserAsync(actionData.ModeratorId)
);
embed.AddField("Reason", actionData.Reason ?? "No reason given");
}
else
{
embed.AddField("Responsible moderator", "*(unknown)*");
embed.AddField("Reason", "*(unknown)*");
}
var pkSystem = await pluralkitApi.GetPluralKitSystemAsync(evt.User.ID.Value, ct);
if (pkSystem != null)
{
guildConfig.BannedSystems.Remove(pkSystem.Id);
guildConfig.BannedSystems.Remove(pkSystem.Uuid.ToString());
db.Update(guildConfig);
await db.SaveChangesAsync(ct);
embed.AddField(
"PluralKit system",
$"""
**ID:** {pkSystem.Id}
**UUID:** `{pkSystem.Uuid}`
**Name:** {pkSystem.Name ?? "*(none)*"}
**Tag:** {pkSystem.Tag ?? "*(none)*"}
This system has been unbanned.
Note that other accounts linked to the system might still be banned, check `pk;system {pkSystem.Id}` for the linked accounts.
"""
);
}
webhookExecutor.QueueLog(
guildConfig,
LogChannelType.GuildBanRemove,
embed.Build().GetOrThrow()
);
return Result.Success;
}
}

View file

@ -15,7 +15,7 @@ using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Gateway.Responders;
using Remora.Results;
namespace Catalogger.Backend.Bot.Responders.Guilds;
namespace Catalogger.Backend.Bot.Responders.Members;
public class GuildMemberAddResponder(
ILogger logger,
@ -48,9 +48,9 @@ public class GuildMemberAddResponder(
var guildConfig = await db.GetGuildAsync(member.GuildID, ct);
var guildRes = await guildApi.GetGuildAsync(member.GuildID, withCounts: true, ct);
if (guildRes.IsSuccess)
if (guildRes.IsSuccess && guildRes.Entity.ApproximateMemberCount.IsDefined())
builder.Description +=
$"\n{guildRes.Entity.ApproximateMemberCount.Value.Ordinalize()} to join";
$"\n{guildRes.Entity.ApproximateMemberCount.OrDefault(1).Ordinalize()} to join";
builder.Description +=
$"\ncreated {user.ID.Timestamp.Prettify()} ago\n<t:{user.ID.Timestamp.ToUnixTimeSeconds()}:F>";
@ -171,25 +171,19 @@ public class GuildMemberAddResponder(
);
}
if (pkSystem != null)
if (pkSystem != null && guildConfig.IsSystemBanned(pkSystem))
{
if (
guildConfig.BannedSystems.Contains(pkSystem.Id)
|| guildConfig.BannedSystems.Contains(pkSystem.Uuid.ToString())
)
{
embeds.Add(
new EmbedBuilder()
.WithTitle("Banned system")
.WithDescription(
"\u26a0\ufe0f The system associated with this account has been banned from the server."
)
.WithColour(DiscordUtils.Red)
.WithFooter($"ID: {pkSystem.Id}")
.Build()
.GetOrThrow()
);
}
embeds.Add(
new EmbedBuilder()
.WithTitle("Banned system")
.WithDescription(
"\u26a0\ufe0f The system associated with this account has been banned from the server."
)
.WithColour(DiscordUtils.Red)
.WithFooter($"ID: {pkSystem.Id}")
.Build()
.GetOrThrow()
);
}
if (embeds.Count > 1)

View file

@ -9,7 +9,7 @@ using Remora.Discord.Extensions.Embeds;
using Remora.Discord.Gateway.Responders;
using Remora.Results;
namespace Catalogger.Backend.Bot.Responders.Guilds;
namespace Catalogger.Backend.Bot.Responders.Members;
public class GuildMemberRemoveResponder(
ILogger logger,
@ -98,7 +98,7 @@ public class GuildMemberRemoveResponder(
kick.AddField(
"Responsible moderator",
await userCache.TryFormatModeratorAsync(actionData)
await userCache.TryFormatUserAsync(actionData.ModeratorId)
);
kick.AddField("Reason", actionData.Reason ?? "No reason given");

View file

@ -11,7 +11,7 @@ using Remora.Discord.Gateway.Responders;
using Remora.Rest.Core;
using Remora.Results;
namespace Catalogger.Backend.Bot.Responders.Guilds;
namespace Catalogger.Backend.Bot.Responders.Members;
public class GuildMemberUpdateResponder(
ILogger logger,
@ -220,7 +220,7 @@ public class GuildMemberUpdateResponder(
if (auditLogCache.TryGetMemberUpdate(member.GuildID, member.User.ID, out var actionData))
{
var moderator = await userCache.TryFormatModeratorAsync(actionData);
var moderator = await userCache.TryFormatUserAsync(actionData.ModeratorId);
embed.AddField("Responsible moderator", moderator);
embed.AddField("Reason", actionData.Reason ?? "No reason given");
}
@ -331,7 +331,7 @@ public class GuildMemberUpdateResponder(
auditLogCache.TryGetMemberUpdate(member.GuildID, member.User.ID, out var actionData)
)
{
var moderator = await userCache.TryFormatModeratorAsync(actionData);
var moderator = await userCache.TryFormatUserAsync(actionData.ModeratorId);
keyRoleUpdate.AddField("Responsible moderator", moderator);
}
else

View file

@ -17,7 +17,7 @@ public class ReadyResponder(ILogger logger, WebhookExecutorService webhookExecut
? (shard.ShardID, shard.ShardCount)
: (0, 1);
_logger.Information(
"Ready as {User} on shard {ShardId} / {ShardCount}",
"Ready as {User} on shard {ShardId}/{ShardCount}",
gatewayEvent.User.Tag(),
shardId.Item1,
shardId.Item2