feat: member avatar/name logging, timeout logging
This commit is contained in:
parent
c906a4d6b6
commit
d445b5ba44
10 changed files with 350 additions and 17 deletions
|
|
@ -1,12 +1,25 @@
|
|||
using Catalogger.Backend.Cache;
|
||||
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.API.Abstractions.Objects;
|
||||
using Remora.Discord.Extensions.Embeds;
|
||||
using Remora.Discord.Gateway.Responders;
|
||||
using Remora.Results;
|
||||
|
||||
namespace Catalogger.Backend.Bot.Responders.MemberUpdate;
|
||||
|
||||
public class GuildMemberUpdateResponder(ILogger logger, IMemberCache memberCache)
|
||||
: IResponder<IGuildMemberUpdate>
|
||||
public class GuildMemberUpdateResponder(
|
||||
ILogger logger,
|
||||
DatabaseContext db,
|
||||
UserCache userCache,
|
||||
IMemberCache memberCache,
|
||||
WebhookExecutorService webhookExecutor,
|
||||
AuditLogCache auditLogCache
|
||||
) : IResponder<IGuildMemberUpdate>
|
||||
{
|
||||
private readonly ILogger _logger = logger.ForContext<GuildMemberUpdateResponder>();
|
||||
|
||||
|
|
@ -27,12 +40,198 @@ public class GuildMemberUpdateResponder(ILogger logger, IMemberCache memberCache
|
|||
);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
var oldUser = oldMember.User.GetOrThrow();
|
||||
|
||||
if (
|
||||
!Equals(oldMember.Avatar.OrDefault(), newMember.Avatar.OrDefault())
|
||||
|| !Equals(oldUser.Avatar, newMember.User.Avatar)
|
||||
)
|
||||
{
|
||||
return await HandleAvatarUpdateAsync(newMember, oldMember, ct);
|
||||
}
|
||||
|
||||
if (
|
||||
newMember.Nickname.OrDefault() != oldMember.Nickname.OrDefault()
|
||||
|| newMember.User.Tag() != oldUser.Tag()
|
||||
|| newMember.User.GlobalName.OrDefault() != oldUser.GlobalName.OrDefault()
|
||||
)
|
||||
{
|
||||
return await HandleNameUpdateAsync(newMember, oldMember, ct);
|
||||
}
|
||||
|
||||
if (
|
||||
newMember.CommunicationDisabledUntil.OrDefault()
|
||||
!= oldMember.CommunicationDisabledUntil.OrDefault()
|
||||
)
|
||||
{
|
||||
return await HandleTimeoutAsync(newMember, ct);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await memberCache.UpdateAsync(newMember);
|
||||
userCache.UpdateUser(newMember.User);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private async Task<Result> HandleAvatarUpdateAsync(
|
||||
IGuildMemberUpdate newMember,
|
||||
IGuildMember oldMember,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
IEmbed embed;
|
||||
|
||||
if (!Equals(oldMember.Avatar.OrDefault(), newMember.Avatar.OrDefault()))
|
||||
{
|
||||
var builder = new EmbedBuilder()
|
||||
.WithAuthor(newMember.User.Tag(), null, newMember.User.AvatarUrl())
|
||||
.WithColour(DiscordUtils.Green)
|
||||
.WithFooter($"User ID: {newMember.User.ID}")
|
||||
.WithCurrentTimestamp();
|
||||
|
||||
if (newMember.Avatar.IsDefined())
|
||||
{
|
||||
builder = builder
|
||||
.WithTitle("Changed server avatar")
|
||||
.WithThumbnailUrl(newMember.AvatarUrl(1024)!);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder = builder.WithTitle("Removed server avatar");
|
||||
}
|
||||
|
||||
embed = builder.Build().GetOrThrow();
|
||||
}
|
||||
else
|
||||
{
|
||||
embed = new EmbedBuilder()
|
||||
.WithAuthor(newMember.User.Tag(), null, newMember.User.AvatarUrl())
|
||||
.WithTitle("Changed avatar")
|
||||
.WithThumbnailUrl(newMember.User.AvatarUrl(1024))
|
||||
.WithColour(DiscordUtils.Green)
|
||||
.WithFooter($"User ID: {newMember.User.ID}")
|
||||
.WithCurrentTimestamp()
|
||||
.Build()
|
||||
.GetOrThrow();
|
||||
}
|
||||
|
||||
var guildConfig = await db.GetGuildAsync(newMember.GuildID, ct);
|
||||
webhookExecutor.QueueLog(guildConfig, LogChannelType.GuildMemberAvatarUpdate, embed);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private async Task<Result> HandleNameUpdateAsync(
|
||||
IGuildMemberUpdate newMember,
|
||||
IGuildMember oldMember,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
var oldUser = oldMember.User.GetOrThrow();
|
||||
|
||||
var builder = new EmbedBuilder()
|
||||
.WithAuthor(newMember.User.Tag(), null, newMember.User.AvatarUrl())
|
||||
.WithColour(DiscordUtils.Green)
|
||||
.WithFooter($"User ID: {newMember.User.ID}")
|
||||
.WithCurrentTimestamp();
|
||||
|
||||
if (newMember.Nickname.OrDefault() != oldMember.Nickname.OrDefault())
|
||||
{
|
||||
builder.AddField(
|
||||
"Changed nickname",
|
||||
$"""
|
||||
**Before:** {oldMember.Nickname.OrDefault("*(none)*")}
|
||||
**After:** {newMember.Nickname.OrDefault("*(none)*")}
|
||||
"""
|
||||
);
|
||||
}
|
||||
|
||||
if (newMember.User.GlobalName.OrDefault() != oldUser.GlobalName.OrDefault())
|
||||
{
|
||||
builder.AddField(
|
||||
"Changed display name",
|
||||
$"""
|
||||
**Before:** {oldUser.GlobalName.OrDefault("*(none)*")}
|
||||
**After:** {newMember.User.GlobalName.OrDefault("*(none)*")}
|
||||
"""
|
||||
);
|
||||
}
|
||||
|
||||
if (newMember.User.Tag() != oldUser.Tag())
|
||||
{
|
||||
builder.AddField(
|
||||
"Changed username",
|
||||
$"""
|
||||
**Before:** {oldUser.Tag()}
|
||||
**After:** {newMember.User.Tag()}
|
||||
"""
|
||||
);
|
||||
}
|
||||
|
||||
var guildConfig = await db.GetGuildAsync(newMember.GuildID, ct);
|
||||
webhookExecutor.QueueLog(
|
||||
guildConfig,
|
||||
LogChannelType.GuildMemberNickUpdate,
|
||||
builder.Build().GetOrThrow()
|
||||
);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private async Task<Result> HandleTimeoutAsync(
|
||||
IGuildMemberUpdate member,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
// Delay 2 seconds to make sure the timeout audit log got cached
|
||||
await Task.Delay(2000, ct);
|
||||
|
||||
var timeoutUntil = member.CommunicationDisabledUntil.OrDefault();
|
||||
|
||||
var embed = new EmbedBuilder()
|
||||
.WithAuthor(member.User.Tag(), null, member.User.AvatarUrl())
|
||||
.WithTitle(
|
||||
timeoutUntil != null ? "Member timed out" : "Member removed from timeout early"
|
||||
)
|
||||
.WithDescription($"<@{member.User.ID}>")
|
||||
.WithColour(DiscordUtils.Red)
|
||||
.WithFooter($"User ID: {member.User.ID}")
|
||||
.WithCurrentTimestamp();
|
||||
|
||||
if (timeoutUntil != null)
|
||||
{
|
||||
embed.AddField(
|
||||
"Until",
|
||||
$"<t:{timeoutUntil.Value.ToUnixTimeSeconds()}>\nin {timeoutUntil.Value.AddSeconds(5).Prettify()}"
|
||||
);
|
||||
}
|
||||
|
||||
if (auditLogCache.TryGetMemberUpdate(member.GuildID, member.User.ID, out var actionData))
|
||||
{
|
||||
var moderator = await userCache.GetUserAsync(actionData.ModeratorId);
|
||||
embed.AddField(
|
||||
"Responsible moderator",
|
||||
moderator == null
|
||||
? $"*(unknown user {actionData.ModeratorId}) <@{actionData.ModeratorId}>*"
|
||||
: $"{moderator.Tag()} <@{moderator.ID}>"
|
||||
);
|
||||
|
||||
embed.AddField("Reason", actionData.Reason ?? "No reason given");
|
||||
}
|
||||
else
|
||||
{
|
||||
embed.AddField("Responsible moderator", "*(unknown)*");
|
||||
embed.AddField("Reason", "*(unknown)*");
|
||||
}
|
||||
|
||||
var guildConfig = await db.GetGuildAsync(member.GuildID, ct);
|
||||
webhookExecutor.QueueLog(
|
||||
guildConfig,
|
||||
LogChannelType.GuildMemberTimeout,
|
||||
embed.Build().GetOrThrow()
|
||||
);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue