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
This commit is contained in:
loki5512344 2026-07-29 21:15:08 +02:00
parent 7cc21213b1
commit 43735de681
Signed by: boba
GPG key ID: 253067914055423B

View file

@ -33,142 +33,156 @@ public class ModerationLogger extends ListenerAdapter {
this.messageCache = messageCache; this.messageCache = messageCache;
} }
private void handle( private void send(final Guild g, final EmbedBuilder eb) {
final Guild g, if (eb == null) return;
final ActionType t, var ch = g.getTextChannelById(configRepo.get("moderation.log_channel_id"));
final int l, if (ch == null) return;
final Function<List<AuditLogEntry>, EmbedBuilder> fn) { ch.sendMessageEmbeds(eb.setTimestamp(OffsetDateTime.now()).build()).queue();
var log = g.getTextChannelById(configRepo.get("moderation.log_channel_id"));
if (log == null) {
return;
} }
var al = g.retrieveAuditLogs().type(t).limit(l);
al.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 -> { logs -> {
var embed = fn.apply(logs); var entry = logs.stream()
if (embed == null) { .filter(log -> e.getMessageId().equals(log.getTargetId()))
return; .findFirst().orElse(null);
} send(g, LogEmbeds.messageDelete(entry, e.getChannel().getAsMention(), content));
log.sendMessageEmbeds(embed.setTimestamp(OffsetDateTime.now()).build()).queue();
}); });
} }
public void onMessageDelete(MessageDeleteEvent e) { @Override
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<AuditLogEntry> l) {
var entry = l.isEmpty() ? null : l.get(0);
return LogEmbeds.messageDelete(entry, e.getChannel().getAsMention(), c);
}
public void onMessageBulkDelete(MessageBulkDeleteEvent e) { public void onMessageBulkDelete(MessageBulkDeleteEvent e) {
handle(e.getGuild(), ActionType.MESSAGE_BULK_DELETE, 1, l -> mbd(e, l)); var g = e.getGuild();
}
private EmbedBuilder mbd(MessageBulkDeleteEvent e, final List<AuditLogEntry> l) {
var entry = l.isEmpty() ? null : l.get(0);
var size = e.getMessageIds().size(); 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) { // ── Ban / Unban ────────────────────────────────────────────────────────────
handle(ev.getGuild(), ActionType.BAN, 1, l -> b(ev, l));
@Override
public void onGuildBan(GuildBanEvent ev) {
handle(ev.getGuild(), ActionType.BAN, 5, l -> ban(ev, l));
} }
private EmbedBuilder b(final GuildBanEvent ev, final List<AuditLogEntry> l) { private EmbedBuilder ban(GuildBanEvent ev, List<AuditLogEntry> l) {
if (l.isEmpty()) { var entry = findEntry(l, ev.getUser().getId());
return null; if (entry == null) return null;
} return LogEmbeds.ban(
var user = ev.getUser(); ev.getUser().getAsMention(),
var e = l.get(0); mod(entry),
var mod = e.getUser() != null ? e.getUser().getAsMention() : "Неизвестно"; entry.getReason(),
var reason = e.getReason() != null ? e.getReason() : "Не указана"; ev.getUser().getAvatarUrl());
return LogEmbeds.ban(user.getAsMention(), mod, reason, user.getAvatarUrl());
} }
public void onGuildUnban(final GuildUnbanEvent ev) { @Override
handle(ev.getGuild(), ActionType.UNBAN, 1, l -> ub(ev, l)); public void onGuildUnban(GuildUnbanEvent ev) {
handle(ev.getGuild(), ActionType.UNBAN, 5, l -> unban(ev, l));
} }
private EmbedBuilder ub(final GuildUnbanEvent ev, final List<AuditLogEntry> l) { private EmbedBuilder unban(GuildUnbanEvent ev, List<AuditLogEntry> l) {
if (l.isEmpty()) { var entry = findEntry(l, ev.getUser().getId());
return null; if (entry == null) return null;
} return LogEmbeds.unban(
var user = ev.getUser(); ev.getUser().getAsMention(),
var e = l.get(0); mod(entry),
var mod = e.getUser() != null ? e.getUser().getAsMention() : "Неизвестно"; ev.getUser().getAvatarUrl());
return LogEmbeds.unban(user.getAsMention(), mod, user.getAvatarUrl());
} }
public void onGuildMemberRemove(final GuildMemberRemoveEvent ev) { // ── Kick ──────────────────────────────────────────────────────────────────
handle(ev.getGuild(), ActionType.KICK, 1, l -> k(ev, l));
@Override
public void onGuildMemberRemove(GuildMemberRemoveEvent ev) {
handle(ev.getGuild(), ActionType.KICK, 5, l -> kick(ev, l));
} }
private EmbedBuilder k(final GuildMemberRemoveEvent ev, final List<AuditLogEntry> l) { private EmbedBuilder kick(GuildMemberRemoveEvent ev, List<AuditLogEntry> l) {
if (l.isEmpty()) { var entry = findEntry(l, ev.getUser().getId());
return null; if (entry == null) return null;
} return LogEmbeds.kick(
var e = l.get(0); ev.getUser().getAsMention(),
if (!e.getTargetId().equals(ev.getUser().getId())) { mod(entry),
return null; entry.getReason(),
} ev.getUser().getAvatarUrl());
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());
} }
public void onGuildMemberUpdateTimeOut(final GuildMemberUpdateTimeOutEvent e) { // ── Timeout ────────────────────────────────────────────────────────────────
@Override
public void onGuildMemberUpdateTimeOut(GuildMemberUpdateTimeOutEvent e) {
var muted = e.getNewTimeOutEnd() != null; 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( private EmbedBuilder timeout(GuildMemberUpdateTimeOutEvent e, boolean muted, List<AuditLogEntry> l) {
final GuildMemberUpdateTimeOutEvent e, boolean muted, final List<AuditLogEntry> l) {
var entry = findEntry(l, e.getUser().getId()); var entry = findEntry(l, e.getUser().getId());
if (entry == null) { if (entry == null) return null;
return null; return LogEmbeds.timeout(
} e.getUser().getAsMention(),
var mod = entry.getUser() != null ? entry.getUser().getAsMention() : "Неизвестно"; mod(entry),
var end = muted ? e.getNewTimeOutEnd().toEpochSecond() : null; muted,
return LogEmbeds.timeout(e.getUser().getAsMention(), mod, muted, end, entry.getReason()); 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(); 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( private EmbedBuilder voiceMute(GuildVoiceGuildMuteEvent e, boolean muted, List<AuditLogEntry> l) {
final GuildVoiceGuildMuteEvent e, final boolean muted, final List<AuditLogEntry> l) {
var member = e.getMember(); var member = e.getMember();
var ch = e.getVoiceState().getChannel();
var entry = findEntry(l, member.getId(), "mute"); var entry = findEntry(l, member.getId(), "mute");
var mod = entry != null && entry.getUser() != null ? entry.getUser().getAsMention() : null; return LogEmbeds.voiceMute(
return LogEmbeds.voiceMute(member.getAsMention(), ch.getAsMention(), muted, mod); 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(); 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( private EmbedBuilder voiceDeafen(GuildVoiceGuildDeafenEvent e, boolean deaf, List<AuditLogEntry> l) {
final GuildVoiceGuildDeafenEvent e, final boolean deaf, final List<AuditLogEntry> l) {
var member = e.getMember(); var member = e.getMember();
var ch = e.getVoiceState().getChannel();
var entry = findEntry(l, member.getId(), "deaf"); var entry = findEntry(l, member.getId(), "deaf");
var mod = entry != null && entry.getUser() != null ? entry.getUser().getAsMention() : null; return LogEmbeds.voiceDeafen(
return LogEmbeds.voiceDeafen(member.getAsMention(), ch.getAsMention(), deaf, mod); member.getAsMention(),
e.getVoiceState().getChannel().getAsMention(),
deaf,
entry != null ? mod(entry) : null);
} }
private AuditLogEntry findEntry( // ── Helpers ────────────────────────────────────────────────────────────────
final List<AuditLogEntry> l, final String targetId, final String... key) {
private void handle(Guild g, ActionType t, int limit, Function<List<AuditLogEntry>, EmbedBuilder> fn) {
g.retrieveAuditLogs().type(t).limit(limit).queue(
logs -> send(g, fn.apply(logs)));
}
private static AuditLogEntry findEntry(List<AuditLogEntry> l, String targetId, String... key) {
var s = l.stream().filter(e -> targetId.equals(e.getTargetId())); var s = l.stream().filter(e -> targetId.equals(e.getTargetId()));
if (key.length > 0) { if (key.length > 0) {
s = s.filter(e -> e.getChangeByKey(key[0]) != null); s = s.filter(e -> e.getChangeByKey(key[0]) != null);
} }
return s.findFirst().orElse(null); return s.findFirst().orElse(null);
} }
private static String mod(AuditLogEntry e) {
return e.getUser() != null ? e.getUser().getAsMention() : "Неизвестно";
}
} }