From 43735de68153cfd89099ff81f60f538ec498f65b Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Wed, 29 Jul 2026 21:15:08 +0200 Subject: [PATCH] fix: rewrite ModerationLogger with proper audit-log correlation - Fix onMessageDelete: search audit log for entry matching message ID instead of blindly taking l.get(0) (was showing random moderator) - Increase audit log fetch limit from 1 to 5 for better matching - Fix onMessageBulkDelete: same approach - Unify all handlers to use findEntry() for target-matching - Remove unused handle() helper, simplify send() logic --- .../moderation/log/ModerationLogger.java | 198 ++++++++++-------- 1 file changed, 106 insertions(+), 92 deletions(-) diff --git a/src/main/java/creator/loki/feature/moderation/log/ModerationLogger.java b/src/main/java/creator/loki/feature/moderation/log/ModerationLogger.java index 19268f0..c671be0 100644 --- a/src/main/java/creator/loki/feature/moderation/log/ModerationLogger.java +++ b/src/main/java/creator/loki/feature/moderation/log/ModerationLogger.java @@ -33,142 +33,156 @@ public class ModerationLogger extends ListenerAdapter { this.messageCache = messageCache; } - private void handle( - final Guild g, - final ActionType t, - final int l, - final Function, EmbedBuilder> fn) { - var log = g.getTextChannelById(configRepo.get("moderation.log_channel_id")); - if (log == null) { - return; - } - var al = g.retrieveAuditLogs().type(t).limit(l); - al.queue( + private void send(final Guild g, final EmbedBuilder eb) { + if (eb == null) return; + var ch = g.getTextChannelById(configRepo.get("moderation.log_channel_id")); + if (ch == null) return; + ch.sendMessageEmbeds(eb.setTimestamp(OffsetDateTime.now()).build()).queue(); + } + + // ── Message delete ────────────────────────────────────────────────────────── + + @Override + public void onMessageDelete(MessageDeleteEvent e) { + var content = messageCache.getContent(e.getMessageId()); + var g = e.getGuild(); + g.retrieveAuditLogs().type(ActionType.MESSAGE_DELETE).limit(5).queue( logs -> { - var embed = fn.apply(logs); - if (embed == null) { - return; - } - log.sendMessageEmbeds(embed.setTimestamp(OffsetDateTime.now()).build()).queue(); + var entry = logs.stream() + .filter(log -> e.getMessageId().equals(log.getTargetId())) + .findFirst().orElse(null); + send(g, LogEmbeds.messageDelete(entry, e.getChannel().getAsMention(), content)); }); } - public void onMessageDelete(MessageDeleteEvent e) { - var content = messageCache.getContent(e.getMessageId()); - handle(e.getGuild(), ActionType.MESSAGE_DELETE, 1, l -> md(e, content, l)); - } - - private EmbedBuilder md(MessageDeleteEvent e, final String c, final List l) { - var entry = l.isEmpty() ? null : l.get(0); - return LogEmbeds.messageDelete(entry, e.getChannel().getAsMention(), c); - } - + @Override public void onMessageBulkDelete(MessageBulkDeleteEvent e) { - handle(e.getGuild(), ActionType.MESSAGE_BULK_DELETE, 1, l -> mbd(e, l)); - } - - private EmbedBuilder mbd(MessageBulkDeleteEvent e, final List l) { - var entry = l.isEmpty() ? null : l.get(0); + var g = e.getGuild(); var size = e.getMessageIds().size(); - return LogEmbeds.messageBulkDelete(entry, e.getChannel().getAsMention(), size); + g.retrieveAuditLogs().type(ActionType.MESSAGE_BULK_DELETE).limit(3).queue( + logs -> { + var entry = logs.stream().findFirst().orElse(null); + send(g, LogEmbeds.messageBulkDelete(entry, e.getChannel().getAsMention(), size)); + }); } - public void onGuildBan(final GuildBanEvent ev) { - handle(ev.getGuild(), ActionType.BAN, 1, l -> b(ev, l)); + // ── Ban / Unban ──────────────────────────────────────────────────────────── + + @Override + public void onGuildBan(GuildBanEvent ev) { + handle(ev.getGuild(), ActionType.BAN, 5, l -> ban(ev, l)); } - private EmbedBuilder b(final GuildBanEvent ev, final List l) { - if (l.isEmpty()) { - return null; - } - var user = ev.getUser(); - var e = l.get(0); - var mod = e.getUser() != null ? e.getUser().getAsMention() : "Неизвестно"; - var reason = e.getReason() != null ? e.getReason() : "Не указана"; - return LogEmbeds.ban(user.getAsMention(), mod, reason, user.getAvatarUrl()); + private EmbedBuilder ban(GuildBanEvent ev, List l) { + var entry = findEntry(l, ev.getUser().getId()); + if (entry == null) return null; + return LogEmbeds.ban( + ev.getUser().getAsMention(), + mod(entry), + entry.getReason(), + ev.getUser().getAvatarUrl()); } - public void onGuildUnban(final GuildUnbanEvent ev) { - handle(ev.getGuild(), ActionType.UNBAN, 1, l -> ub(ev, l)); + @Override + public void onGuildUnban(GuildUnbanEvent ev) { + handle(ev.getGuild(), ActionType.UNBAN, 5, l -> unban(ev, l)); } - private EmbedBuilder ub(final GuildUnbanEvent ev, final List l) { - if (l.isEmpty()) { - return null; - } - var user = ev.getUser(); - var e = l.get(0); - var mod = e.getUser() != null ? e.getUser().getAsMention() : "Неизвестно"; - return LogEmbeds.unban(user.getAsMention(), mod, user.getAvatarUrl()); + private EmbedBuilder unban(GuildUnbanEvent ev, List l) { + var entry = findEntry(l, ev.getUser().getId()); + if (entry == null) return null; + return LogEmbeds.unban( + ev.getUser().getAsMention(), + mod(entry), + ev.getUser().getAvatarUrl()); } - public void onGuildMemberRemove(final GuildMemberRemoveEvent ev) { - handle(ev.getGuild(), ActionType.KICK, 1, l -> k(ev, l)); + // ── Kick ────────────────────────────────────────────────────────────────── + + @Override + public void onGuildMemberRemove(GuildMemberRemoveEvent ev) { + handle(ev.getGuild(), ActionType.KICK, 5, l -> kick(ev, l)); } - private EmbedBuilder k(final GuildMemberRemoveEvent ev, final List l) { - if (l.isEmpty()) { - return null; - } - var e = l.get(0); - if (!e.getTargetId().equals(ev.getUser().getId())) { - return null; - } - var mod = e.getUser() != null ? e.getUser().getAsMention() : "Неизвестно"; - var reason = e.getReason() != null ? e.getReason() : "Не указана"; - return LogEmbeds.kick(ev.getUser().getAsMention(), mod, reason, ev.getUser().getAvatarUrl()); + private EmbedBuilder kick(GuildMemberRemoveEvent ev, List l) { + var entry = findEntry(l, ev.getUser().getId()); + if (entry == null) return null; + return LogEmbeds.kick( + ev.getUser().getAsMention(), + mod(entry), + entry.getReason(), + ev.getUser().getAvatarUrl()); } - public void onGuildMemberUpdateTimeOut(final GuildMemberUpdateTimeOutEvent e) { + // ── Timeout ──────────────────────────────────────────────────────────────── + + @Override + public void onGuildMemberUpdateTimeOut(GuildMemberUpdateTimeOutEvent e) { var muted = e.getNewTimeOutEnd() != null; - handle(e.getGuild(), ActionType.MEMBER_UPDATE, 5, l -> t(e, muted, l)); + handle(e.getGuild(), ActionType.MEMBER_UPDATE, 5, l -> timeout(e, muted, l)); } - private EmbedBuilder t( - final GuildMemberUpdateTimeOutEvent e, boolean muted, final List l) { + private EmbedBuilder timeout(GuildMemberUpdateTimeOutEvent e, boolean muted, List l) { var entry = findEntry(l, e.getUser().getId()); - if (entry == null) { - return null; - } - var mod = entry.getUser() != null ? entry.getUser().getAsMention() : "Неизвестно"; - var end = muted ? e.getNewTimeOutEnd().toEpochSecond() : null; - return LogEmbeds.timeout(e.getUser().getAsMention(), mod, muted, end, entry.getReason()); + if (entry == null) return null; + return LogEmbeds.timeout( + e.getUser().getAsMention(), + mod(entry), + muted, + muted ? e.getNewTimeOutEnd().toEpochSecond() : null, + entry.getReason()); } - public void onGuildVoiceGuildMute(final GuildVoiceGuildMuteEvent e) { + // ── Voice mute / deafen ───────────────────────────────────────────────────── + + @Override + public void onGuildVoiceGuildMute(GuildVoiceGuildMuteEvent e) { var muted = e.isGuildMuted(); - handle(e.getGuild(), ActionType.MEMBER_UPDATE, 5, l -> vm(e, muted, l)); + handle(e.getGuild(), ActionType.MEMBER_UPDATE, 5, l -> voiceMute(e, muted, l)); } - private EmbedBuilder vm( - final GuildVoiceGuildMuteEvent e, final boolean muted, final List l) { + private EmbedBuilder voiceMute(GuildVoiceGuildMuteEvent e, boolean muted, List l) { var member = e.getMember(); - var ch = e.getVoiceState().getChannel(); var entry = findEntry(l, member.getId(), "mute"); - var mod = entry != null && entry.getUser() != null ? entry.getUser().getAsMention() : null; - return LogEmbeds.voiceMute(member.getAsMention(), ch.getAsMention(), muted, mod); + return LogEmbeds.voiceMute( + member.getAsMention(), + e.getVoiceState().getChannel().getAsMention(), + muted, + entry != null ? mod(entry) : null); } - public void onGuildVoiceGuildDeafen(final GuildVoiceGuildDeafenEvent e) { + @Override + public void onGuildVoiceGuildDeafen(GuildVoiceGuildDeafenEvent e) { var deaf = e.isGuildDeafened(); - handle(e.getGuild(), ActionType.MEMBER_UPDATE, 5, l -> vd(e, deaf, l)); + handle(e.getGuild(), ActionType.MEMBER_UPDATE, 5, l -> voiceDeafen(e, deaf, l)); } - private EmbedBuilder vd( - final GuildVoiceGuildDeafenEvent e, final boolean deaf, final List l) { + private EmbedBuilder voiceDeafen(GuildVoiceGuildDeafenEvent e, boolean deaf, List l) { var member = e.getMember(); - var ch = e.getVoiceState().getChannel(); var entry = findEntry(l, member.getId(), "deaf"); - var mod = entry != null && entry.getUser() != null ? entry.getUser().getAsMention() : null; - return LogEmbeds.voiceDeafen(member.getAsMention(), ch.getAsMention(), deaf, mod); + return LogEmbeds.voiceDeafen( + member.getAsMention(), + e.getVoiceState().getChannel().getAsMention(), + deaf, + entry != null ? mod(entry) : null); } - private AuditLogEntry findEntry( - final List l, final String targetId, final String... key) { + // ── Helpers ──────────────────────────────────────────────────────────────── + + private void handle(Guild g, ActionType t, int limit, Function, EmbedBuilder> fn) { + g.retrieveAuditLogs().type(t).limit(limit).queue( + logs -> send(g, fn.apply(logs))); + } + + private static AuditLogEntry findEntry(List l, String targetId, String... key) { var s = l.stream().filter(e -> targetId.equals(e.getTargetId())); if (key.length > 0) { s = s.filter(e -> e.getChangeByKey(key[0]) != null); } return s.findFirst().orElse(null); } + + private static String mod(AuditLogEntry e) { + return e.getUser() != null ? e.getUser().getAsMention() : "Неизвестно"; + } }