From ca45137cc60b2a09a9321708690d5c2d8d139571 Mon Sep 17 00:00:00 2001 From: Sheldan <5037282+Sheldan@users.noreply.github.com> Date: Sat, 4 May 2024 20:35:56 +0200 Subject: [PATCH] [AB-xxx] reworking mute logging to use audit log events instead of active logging and member update events --- .../listener/MemberTimeoutListener.java | 139 ------------------ .../listener/MemberTimeoutLoggerListener.java | 60 ++++++++ .../moderation/service/MuteServiceBean.java | 80 ++-------- .../model/template/command/UnMuteLog.java | 71 --------- .../jda/AsyncMemberTimeoutListenerBean.java | 63 ++++++-- .../listener/MemberTimeoutUpdatedModel.java | 4 +- 6 files changed, 118 insertions(+), 299 deletions(-) delete mode 100644 abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MemberTimeoutListener.java create mode 100644 abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MemberTimeoutLoggerListener.java delete mode 100644 abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/template/command/UnMuteLog.java diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MemberTimeoutListener.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MemberTimeoutListener.java deleted file mode 100644 index e91f9f8bc..000000000 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MemberTimeoutListener.java +++ /dev/null @@ -1,139 +0,0 @@ -package dev.sheldan.abstracto.moderation.listener; - -import dev.sheldan.abstracto.core.config.FeatureDefinition; -import dev.sheldan.abstracto.core.listener.DefaultListenerResult; -import dev.sheldan.abstracto.core.listener.async.jda.AsyncMemberTimeoutUpdatedListener; -import dev.sheldan.abstracto.core.models.ServerUser; -import dev.sheldan.abstracto.core.models.listener.MemberTimeoutUpdatedModel; -import dev.sheldan.abstracto.core.models.template.display.MemberDisplay; -import dev.sheldan.abstracto.core.service.MemberService; -import dev.sheldan.abstracto.core.service.PostTargetService; -import dev.sheldan.abstracto.core.templating.model.MessageToSend; -import dev.sheldan.abstracto.core.templating.service.TemplateService; -import dev.sheldan.abstracto.core.utils.FutureUtils; -import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition; -import dev.sheldan.abstracto.moderation.config.posttarget.MutingPostTarget; -import dev.sheldan.abstracto.moderation.model.template.command.MuteListenerModel; -import dev.sheldan.abstracto.moderation.service.MuteServiceBean; -import lombok.extern.slf4j.Slf4j; -import net.dv8tion.jda.api.audit.ActionType; -import net.dv8tion.jda.api.audit.AuditLogEntry; -import net.dv8tion.jda.api.audit.AuditLogKey; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.User; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Optional; -import java.util.concurrent.CompletableFuture; - -@Component -@Slf4j -public class MemberTimeoutListener implements AsyncMemberTimeoutUpdatedListener { - - @Autowired - private TemplateService templateService; - - @Autowired - private PostTargetService postTargetService; - - @Autowired - private MemberTimeoutListener self; - - @Autowired - private MemberService memberService; - - @Override - public DefaultListenerResult execute(MemberTimeoutUpdatedModel model) { - Guild guild = model.getGuild(); - guild.retrieveAuditLogs() - .type(ActionType.MEMBER_UPDATE) - .limit(10) - .queue(auditLogEntries -> { - CompletableFuture notificationFuture = null; - if(auditLogEntries.isEmpty()) { - log.info("Did not find recent timeouts in guild {}.", model.getServerId()); - notificationFuture = self.sendMutingUpdateNotification(model, null); - } else { - Optional timeoutEntryOptional = auditLogEntries - .stream() - .filter(auditLogEntry -> auditLogEntry.getChangeByKey(AuditLogKey.MEMBER_TIME_OUT) != null - && auditLogEntry.getTargetIdLong() == model.getTimeoutUser().getUserId()) - .findFirst(); - if(timeoutEntryOptional.isPresent()) { - AuditLogEntry auditLogEntry = timeoutEntryOptional.get(); - User responsibleUser = auditLogEntry.getUser(); - if(guild.getSelfMember().getIdLong() != responsibleUser.getIdLong()) { - notificationFuture = self.sendMutingUpdateNotification(model, auditLogEntry); - } - } else { - notificationFuture = self.sendMutingUpdateNotification(model, null); - } - } - if(notificationFuture != null) { - notificationFuture.thenAccept(unused -> { - log.info("Sent notification about timeout change {} -> {} of user {} in server {}.", - model.getOldTimeout(), model.getNewTimeout(), model.getTimeoutUser().getUserId(), model.getServerId()); - }).exceptionally(throwable -> { - log.info("Sent notification about timeout change {} -> {} of user {} in server {}.", - model.getOldTimeout(), model.getNewTimeout(), model.getTimeoutUser().getUserId(), model.getServerId()); - return null; - }); - } - }); - return DefaultListenerResult.PROCESSED; - } - - @Transactional - public CompletableFuture sendMutingUpdateNotification(MemberTimeoutUpdatedModel model, AuditLogEntry logEntry) { - User responsibleUser; - Guild guild = model.getGuild(); - CompletableFuture future; - String reason; - if(logEntry != null) { - responsibleUser = logEntry.getUser(); - if(responsibleUser != null) { - ServerUser responsibleServerUser = ServerUser - .builder() - .serverId(guild.getIdLong()) - .isBot(responsibleUser.isBot()) - .userId(responsibleUser.getIdLong()) - .build(); - future = memberService.retrieveMemberInServer(responsibleServerUser); - } else { - future = CompletableFuture.completedFuture(null); - } - reason = logEntry.getReason(); - } else { - future = CompletableFuture.completedFuture(null); - reason = null; - } - CompletableFuture returningFuture = new CompletableFuture<>(); - future.whenComplete((aVoid, throwable) -> { - try { - MuteListenerModel muteLogModel = MuteListenerModel - .builder() - .muteTargetDate(model.getNewTimeout() != null ? model.getNewTimeout().toInstant() : null) - .oldMuteTargetDate(model.getOldTimeout() != null ? model.getOldTimeout().toInstant() : null) - .mutingUser(future.isCompletedExceptionally() ? null : MemberDisplay.fromMember(future.join())) - .mutedUser(MemberDisplay.fromMember(model.getMember())) - .reason(reason) - .build(); - MessageToSend message = templateService.renderEmbedTemplate(MuteServiceBean.MUTE_LOG_TEMPLATE, muteLogModel, guild.getIdLong()); - FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, model.getServerId())); - returningFuture.complete(null); - } catch (Exception exception) { - log.error("Failed to log timeout update event for user {} in guild {}.", model.getTimeoutUser().getUserId(), model.getServerId(), exception); - } - }); - - return returningFuture; - } - - @Override - public FeatureDefinition getFeature() { - return ModerationFeatureDefinition.MUTING; - } -} diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MemberTimeoutLoggerListener.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MemberTimeoutLoggerListener.java new file mode 100644 index 000000000..ce30cfc1e --- /dev/null +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MemberTimeoutLoggerListener.java @@ -0,0 +1,60 @@ +package dev.sheldan.abstracto.moderation.listener; + +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.listener.DefaultListenerResult; +import dev.sheldan.abstracto.core.listener.async.jda.AsyncMemberTimeoutUpdatedListener; +import dev.sheldan.abstracto.core.models.listener.MemberTimeoutUpdatedModel; +import dev.sheldan.abstracto.core.models.template.display.MemberDisplay; +import dev.sheldan.abstracto.core.service.PostTargetService; +import dev.sheldan.abstracto.core.templating.model.MessageToSend; +import dev.sheldan.abstracto.core.templating.service.TemplateService; +import dev.sheldan.abstracto.core.utils.FutureUtils; +import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition; +import dev.sheldan.abstracto.moderation.config.posttarget.MutingPostTarget; +import dev.sheldan.abstracto.moderation.model.template.command.MuteListenerModel; +import dev.sheldan.abstracto.moderation.service.MuteServiceBean; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Guild; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.time.Instant; + +@Component +@Slf4j +public class MemberTimeoutLoggerListener implements AsyncMemberTimeoutUpdatedListener { + + @Autowired + private TemplateService templateService; + + @Autowired + private PostTargetService postTargetService; + + @Override + public DefaultListenerResult execute(MemberTimeoutUpdatedModel model) { + Guild guild = model.getGuild(); + MemberDisplay memberDisplay = model.getMember() != null ? MemberDisplay.fromMember(model.getMember()) : MemberDisplay.fromServerUser(model.getTimeoutUser()); + Duration duration = null; + if(model.getNewTimeout() != null) { + duration = Duration.between(Instant.now(), model.getNewTimeout()); + } + MuteListenerModel muteLogModel = MuteListenerModel + .builder() + .muteTargetDate(model.getNewTimeout() != null ? model.getNewTimeout().toInstant() : null) + .oldMuteTargetDate(model.getOldTimeout() != null ? model.getOldTimeout().toInstant() : null) + .mutingUser(MemberDisplay.fromIds(model.getServerId(), model.getResponsibleUserId())) + .mutedUser(memberDisplay) + .duration(duration) + .reason(model.getReason()) + .build(); + MessageToSend message = templateService.renderEmbedTemplate(MuteServiceBean.MUTE_LOG_TEMPLATE, muteLogModel, guild.getIdLong()); + FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, model.getServerId())); + return DefaultListenerResult.PROCESSED; + } + + @Override + public FeatureDefinition getFeature() { + return ModerationFeatureDefinition.MUTING; + } +} diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java index 060ea908a..ccfab20af 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/MuteServiceBean.java @@ -6,30 +6,23 @@ import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer; -import dev.sheldan.abstracto.core.models.template.display.MemberDisplay; import dev.sheldan.abstracto.core.service.*; import dev.sheldan.abstracto.core.service.management.ChannelManagementService; import dev.sheldan.abstracto.core.service.management.ServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; -import dev.sheldan.abstracto.core.templating.model.MessageToSend; import dev.sheldan.abstracto.core.templating.service.TemplateService; -import dev.sheldan.abstracto.core.utils.FutureUtils; import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition; import dev.sheldan.abstracto.moderation.config.feature.MutingFeatureConfig; -import dev.sheldan.abstracto.moderation.config.posttarget.MutingPostTarget; import dev.sheldan.abstracto.moderation.exception.NoMuteFoundException; import dev.sheldan.abstracto.moderation.model.MuteResult; import dev.sheldan.abstracto.moderation.model.database.Infraction; import dev.sheldan.abstracto.moderation.model.database.Mute; -import dev.sheldan.abstracto.moderation.model.template.command.MuteListenerModel; import dev.sheldan.abstracto.moderation.model.template.command.MuteNotification; -import dev.sheldan.abstracto.moderation.model.template.command.UnMuteLog; import dev.sheldan.abstracto.moderation.service.management.MuteManagementService; import dev.sheldan.abstracto.scheduling.model.JobParameters; import dev.sheldan.abstracto.scheduling.service.SchedulerService; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Message; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @@ -175,14 +168,13 @@ public class MuteServiceBean implements MuteService { Long muteId = counterService.getNextCounterValue(server, MUTE_COUNTER_KEY); CompletableFuture result = muteUserInServer(guild, userToMute, reason, duration); return result - .thenCompose(unused -> self.sendMuteLog(userToMute, mutingUser, duration, reason)) - .thenCompose(logMessage -> self.evaluateAndStoreInfraction(userToMute, mutingUser, reason, targetDate, logMessage)) + .thenCompose(logMessage -> self.evaluateAndStoreInfraction(userToMute, mutingUser, reason, targetDate)) .thenAccept(infractionId -> self.persistMute(userToMute, mutingUser, targetDate, muteId, reason, infractionId, origin)) .thenApply(unused -> result.join()); } @Transactional - public CompletableFuture evaluateAndStoreInfraction(ServerUser userToMute, ServerUser mutingUser, String reason, Instant targetDate, Message logMessage) { + public CompletableFuture evaluateAndStoreInfraction(ServerUser userToMute, ServerUser mutingUser, String reason, Instant targetDate) { Long serverId = userToMute.getServerId(); if(featureFlagService.getFeatureFlagValue(ModerationFeatureDefinition.INFRACTIONS, serverId)) { Long infractionPoints = configService.getLongValueOrConfigDefault(MutingFeatureConfig.MUTE_INFRACTION_POINTS, serverId); @@ -190,7 +182,7 @@ public class MuteServiceBean implements MuteService { AUserInAServer mutingUserInAServer = userInServerManagementService.loadOrCreateUser(mutingUser); Map parameters = new HashMap<>(); parameters.put(INFRACTION_PARAMETER_DURATION_KEY, templateService.renderDuration(Duration.between(Instant.now(), targetDate), serverId)); - return infractionService.createInfractionWithNotification(mutedUserInAServer, infractionPoints, MUTE_INFRACTION_TYPE, reason, mutingUserInAServer, parameters, logMessage) + return infractionService.createInfractionWithNotification(mutedUserInAServer, infractionPoints, MUTE_INFRACTION_TYPE, reason, mutingUserInAServer, parameters) .thenApply(Infraction::getId); } else { return CompletableFuture.completedFuture(null); @@ -204,48 +196,13 @@ public class MuteServiceBean implements MuteService { createMuteObject(userToMute, mutingUser, reason, targetDate, muteId, triggerKey, infractionId, origin); } - @Transactional - public CompletableFuture sendMuteLog(ServerUser userBeingMuted, ServerUser mutingUser, Duration duration, String reason) { - Instant targetDate = Instant.now().plus(duration); - MuteListenerModel model = MuteListenerModel - .builder() - .mutedUser(MemberDisplay.fromServerUser(userBeingMuted)) - .mutingUser(MemberDisplay.fromServerUser(mutingUser)) - .oldMuteTargetDate(null) - .duration(duration) - .muteTargetDate(targetDate) - .reason(reason) - .build(); - log.debug("Sending mute log to the mute post target."); - Long serverId = userBeingMuted.getServerId(); - MessageToSend message = templateService.renderEmbedTemplate(MUTE_LOG_TEMPLATE, model, serverId); - List> futures = postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, serverId); - return FutureUtils.toSingleFutureGeneric(futures).thenApply(unused -> futures.get(0).join()); - } - - private CompletableFuture sendUnMuteLogMessage(UnMuteLog muteLogModel, AServer server) { - MuteListenerModel model = MuteListenerModel - .builder() - .mutedUser(muteLogModel.getUnMutedUser()) - .mutingUser(muteLogModel.getMutingUser()) - .oldMuteTargetDate(muteLogModel.getMute() != null ? muteLogModel.getMute().getMuteTargetDate() : null) - .muteTargetDate(null) - .build(); - if(muteLogModel.getMute() != null) { - log.debug("Sending unMute log for mute {} to the mute posttarget in server {}", muteLogModel.getMute().getMuteId().getId(), server.getId()); - } - MessageToSend message = templateService.renderEmbedTemplate(MUTE_LOG_TEMPLATE, model, server.getId()); - return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, server.getId())); - } - @Override @Transactional public CompletableFuture unMuteUser(ServerUser userToUnmute, ServerUser unMutingUser, Guild guild) { AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(userToUnmute); boolean muteActive = muteManagementService.hasActiveMute(aUserInAServer); if(!muteActive) { - return memberService.removeTimeout(guild, userToUnmute, null) - .thenCompose(unused -> self.sendUnmuteLog(null, guild, userToUnmute, unMutingUser)); + return memberService.removeTimeout(guild, userToUnmute, null); } else { Mute mute = muteManagementService.getAMuteOf(aUserInAServer); return endMute(mute, guild); @@ -261,32 +218,13 @@ public class MuteServiceBean implements MuteService { Long muteId = mute.getMuteId().getId(); AServer mutingServer = mute.getServer(); ServerUser mutedUser = ServerUser.fromAUserInAServer(mute.getMutedUser()); - ServerUser mutingUser = ServerUser.fromAUserInAServer(mute.getMutingUser()); log.info("UnMuting {} in server {}", mute.getMutedUser().getUserReference().getId(), mutingServer.getId()); return memberService.removeTimeout(guild, mutedUser, null) - .thenCompose(unused -> self.sendUnmuteLog(muteId, guild, mutedUser, mutingUser)); - } - - - @Transactional - public CompletableFuture sendUnmuteLog(Long muteId, Guild guild, ServerUser unMutedMember, ServerUser mutingMember) { - Mute mute = null; - if(muteId != null) { - mute = muteManagementService.findMute(muteId, guild.getIdLong()); - } - AServer mutingServer = serverManagementService.loadServer(guild.getIdLong()); - UnMuteLog unMuteLog = UnMuteLog - .builder() - .mute(mute) - .mutingUser(MemberDisplay.fromServerUser(mutingMember)) - .unMutedUser(MemberDisplay.fromServerUser(unMutedMember)) - .build(); - CompletableFuture notificationFuture = sendUnMuteLogMessage(unMuteLog, mutingServer); - return CompletableFuture.allOf(notificationFuture).thenAccept(aVoid -> { - if(muteId != null) { - self.endMuteInDatabase(muteId, guild.getIdLong()); - } - }); + .thenAccept(unused -> { + if(muteId != null) { + self.endMuteInDatabase(muteId, guild.getIdLong()); + } + }); } @Transactional diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/template/command/UnMuteLog.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/template/command/UnMuteLog.java deleted file mode 100644 index 5779f74e3..000000000 --- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/template/command/UnMuteLog.java +++ /dev/null @@ -1,71 +0,0 @@ -package dev.sheldan.abstracto.moderation.model.template.command; - -import dev.sheldan.abstracto.core.models.template.display.MemberDisplay; -import dev.sheldan.abstracto.core.utils.MessageUtils; -import dev.sheldan.abstracto.moderation.model.database.Mute; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.experimental.SuperBuilder; - -import java.time.Duration; -import java.time.Instant; - -/** - * Used when rendering the notification when a member was muted. The template is: "unmute_log_embed" - */ -@Getter -@SuperBuilder -@Setter -@NoArgsConstructor -@AllArgsConstructor -public class UnMuteLog { - /** - * The un-muted Member, is null if the member left the server - */ - private MemberDisplay unMutedUser; - /** - * The user casting the mute, is null if the member left the server - */ - private MemberDisplay mutingUser; - /** - * The persisted mute object from the database containing the information about the mute - */ - private Mute mute; - - /** - * The actual duration between the date the mute started and the current time - * @return The difference between mute start and now - */ - public Duration getMuteDuration() { - return Duration.between(mute.getMuteDate(), Instant.now()); - } - - /** - * The duration between the date the mute started and the un-mute planned - * @return The difference between mute start and the target date - */ - public Duration getPlannedMuteDuration() { - return Duration.between(mute.getMuteDate(), mute.getMuteTargetDate()); - } - - /** - * The un-mute date, which is now, because this is the un-mute log message. - * @return The current time stamp - */ - public Instant getUnmuteDate() { - return Instant.now(); - } - - /** - * Builds the link to the original message triggering the mute - * @return A string containing an URL leading to the message where the mute was triggered - */ - public String getMessageUrl() { - if(this.mute.getMessageId() != null && this.mute.getMutingChannel() != null) { - return MessageUtils.buildMessageUrl(this.mute.getServer().getId(), this.mute.getMutingChannel().getId(), this.mute.getMessageId()); - } - return null; - } -} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/async/jda/AsyncMemberTimeoutListenerBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/async/jda/AsyncMemberTimeoutListenerBean.java index 9d332b0e4..38f806701 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/async/jda/AsyncMemberTimeoutListenerBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/async/jda/AsyncMemberTimeoutListenerBean.java @@ -3,8 +3,13 @@ package dev.sheldan.abstracto.core.listener.async.jda; import dev.sheldan.abstracto.core.listener.ListenerService; import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.listener.MemberTimeoutUpdatedModel; +import dev.sheldan.abstracto.core.service.MemberService; import lombok.extern.slf4j.Slf4j; -import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateTimeOutEvent; +import net.dv8tion.jda.api.audit.ActionType; +import net.dv8tion.jda.api.audit.AuditLogChange; +import net.dv8tion.jda.api.audit.AuditLogKey; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.events.guild.GuildAuditLogEntryCreateEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -12,7 +17,10 @@ import org.springframework.core.task.TaskExecutor; import org.springframework.stereotype.Component; import javax.annotation.Nonnull; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.concurrent.CompletableFuture; @Component @Slf4j @@ -27,23 +35,46 @@ public class AsyncMemberTimeoutListenerBean extends ListenerAdapter { @Autowired private ListenerService listenerService; + @Autowired + private MemberService memberService; + @Override - public void onGuildMemberUpdateTimeOut(@Nonnull GuildMemberUpdateTimeOutEvent event) { + public void onGuildAuditLogEntryCreate(@Nonnull GuildAuditLogEntryCreateEvent event) { if(listenerList == null) return; - MemberTimeoutUpdatedModel model = getModel(event); - listenerList.forEach(leaveListener -> listenerService.executeFeatureAwareListener(leaveListener, model, memberTimeoutExecutor)); + if(event.getEntry().getType().equals(ActionType.MEMBER_UPDATE)) { + AuditLogChange memberTimeoutChange = event.getEntry().getChangeByKey(AuditLogKey.MEMBER_TIME_OUT); + if(memberTimeoutChange != null) { + CompletableFuture memberInstanceFuture = memberService.retrieveMemberInServer(ServerUser.fromId(event.getGuild().getIdLong(), event.getEntry().getTargetIdLong())); + memberInstanceFuture.whenComplete((member, throwable) -> { + executeListeners(memberTimeoutChange, event, member); + }).exceptionally(throwable -> { + Long memberId = event.getEntry().getTargetIdLong(); + Long serverId = event.getGuild().getIdLong(); + log.warn("Failed to load member {} for member update audit log in server {}.", memberId, serverId, throwable); + executeListeners(memberTimeoutChange, event, null); + return null; + }); + + } + } } - private MemberTimeoutUpdatedModel getModel(GuildMemberUpdateTimeOutEvent event) { - return MemberTimeoutUpdatedModel - .builder() - .oldTimeout(event.getOldTimeOutEnd()) - .newTimeout(event.getNewTimeOutEnd()) - .member(event.getMember()) - .event(event) - .timeoutUser(ServerUser.fromMember(event.getMember())) - .guild(event.getGuild()) - .user(event.getUser()) - .build(); - } + private void executeListeners(AuditLogChange change, GuildAuditLogEntryCreateEvent event, Member member) { + DateTimeFormatter timeFormatter = DateTimeFormatter.ISO_DATE_TIME; + OffsetDateTime timeoutAfter = change.getNewValue() != null ? OffsetDateTime.parse(change.getNewValue(), timeFormatter) : null; + OffsetDateTime timeoutBefore = change.getOldValue() != null ? OffsetDateTime.parse(change.getOldValue(), timeFormatter) : null; + String reason = event.getEntry().getReason(); + MemberTimeoutUpdatedModel model = MemberTimeoutUpdatedModel + .builder() + .oldTimeout(timeoutBefore) + .newTimeout(timeoutAfter) + .responsibleUserId(event.getEntry().getUserIdLong()) + .member(member) + .reason(reason) + .guild(event.getGuild()) + .user(member != null ? member.getUser() : null) + .timeoutUser(ServerUser.fromId(event.getGuild().getIdLong(), event.getEntry().getTargetIdLong())) + .build(); + listenerList.forEach(leaveListener -> listenerService.executeFeatureAwareListener(leaveListener, model, memberTimeoutExecutor)); + } } diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/listener/MemberTimeoutUpdatedModel.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/listener/MemberTimeoutUpdatedModel.java index 1ee46740b..09c46be17 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/listener/MemberTimeoutUpdatedModel.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/listener/MemberTimeoutUpdatedModel.java @@ -8,7 +8,6 @@ import lombok.Setter; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; -import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateTimeOutEvent; import java.time.OffsetDateTime; @@ -19,10 +18,11 @@ public class MemberTimeoutUpdatedModel implements FeatureAwareListenerModel { private ServerUser timeoutUser; private User user; private Guild guild; + private String reason; + private Long responsibleUserId; private OffsetDateTime oldTimeout; private OffsetDateTime newTimeout; private Member member; - private GuildMemberUpdateTimeOutEvent event; @Override public Long getServerId() { return guild.getIdLong();