[AB-xxx] reworking ban logging to use audit log instead of actively logging or using the banned event

partially fixing broken infraction handling
adding CompletableFutureMap to handle futures easier
updating user display object to also hold name
replaced some references to UserObjects in models with UserDisplay objects
This commit is contained in:
Sheldan
2024-05-05 01:58:26 +02:00
parent ca45137cc6
commit 234aae3783
24 changed files with 279 additions and 259 deletions

View File

@@ -5,13 +5,14 @@ import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncLeaveListener;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.listener.MemberLeaveModel;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
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.logging.config.LoggingFeatureDefinition;
import dev.sheldan.abstracto.logging.config.LoggingPostTarget;
import dev.sheldan.abstracto.logging.model.template.MemberLeaveLogModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -40,10 +41,10 @@ public class LeaveLogger implements AsyncLeaveListener {
.userId(listenerModel.getUser().getIdLong())
.serverId(listenerModel.getServerId())
.build();
MemberLeaveModel model = MemberLeaveModel
MemberLeaveLogModel model = MemberLeaveLogModel
.builder()
.leavingUser(leavingUser)
.user(listenerModel.getUser())
.user(UserDisplay.fromUser(listenerModel.getUser()))
.build();
log.debug("Logging leave event for user {} in server {}.", listenerModel.getUser().getIdLong(), listenerModel.getServerId());
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_LEAVE_TEMPLATE, model, listenerModel.getServerId());

View File

@@ -1,13 +1,15 @@
package dev.sheldan.abstracto.logging.model.template;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
@Getter
@Setter
@Builder
public class MemberLeaveLogModel {
private Member member;
private ServerUser leavingUser;
private UserDisplay user;
}

View File

@@ -11,7 +11,6 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
import dev.sheldan.abstracto.moderation.config.ModerationSlashCommandNames;
@@ -51,7 +50,7 @@ public class UnBan extends AbstractConditionableCommand {
String userIdStr = (String) parameters.get(0);
Long userId = Long.parseLong(userIdStr);
return userService.retrieveUserForId(userId)
.thenCompose(user -> banService.unBanUserWithNotification(userId, ServerUser.fromMember(commandContext.getAuthor()), commandContext.getGuild()))
.thenCompose(user -> banService.unbanUser(commandContext.getGuild(), userId))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@@ -60,7 +59,7 @@ public class UnBan extends AbstractConditionableCommand {
String userIdStr = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, String.class);
Long userId = Long.parseLong(userIdStr);
return userService.retrieveUserForId(userId)
.thenCompose(user -> banService.unBanUserWithNotification(userId, ServerUser.fromMember(event.getMember()), event.getGuild()))
.thenCompose(user -> banService.unbanUser(event.getGuild(), userId))
.thenCompose(unused -> interactionService.replyEmbed(UN_BAN_RESPONSE, event))
.thenApply(interactionHook -> CommandResult.fromSuccess());
}

View File

@@ -4,23 +4,17 @@ import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncUserBannedListener;
import dev.sheldan.abstracto.core.models.listener.UserBannedModel;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
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.ModerationPostTarget;
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedListenerModel;
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedListenerLogModel;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.audit.ActionType;
import net.dv8tion.jda.api.audit.AuditLogEntry;
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
@@ -32,53 +26,20 @@ public class UserBannedListener implements AsyncUserBannedListener {
@Autowired
private PostTargetService postTargetService;
@Autowired
private UserBannedListener self;
private static final String USER_BANNED_NOTIFICATION_TEMPLATE = "userBanned_listener_notification";
public static final String USER_BANNED_NOTIFICATION_TEMPLATE = "userBanned_listener_notification";
@Override
public DefaultListenerResult execute(UserBannedModel model) {
model.getGuild()
.retrieveAuditLogs()
.type(ActionType.BAN)
.limit(5)
.queue(auditLogEntries -> {
if(auditLogEntries.isEmpty()) {
log.info("Did not find recent bans in guild {}.", model.getServerId());
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
return;
}
Optional<AuditLogEntry> banEntryOptional = auditLogEntries
.stream()
.filter(auditLogEntry -> auditLogEntry.getTargetIdLong() == model.getBannedUser().getUserId())
.findFirst();
if(banEntryOptional.isPresent()) {
AuditLogEntry auditLogEntry = banEntryOptional.get();
if(!model.getGuild().getJDA().getSelfUser().equals(auditLogEntry.getUser())) {
self.sendBannedNotification(model.getUser(), auditLogEntry.getUser(), auditLogEntry.getReason(), model.getServerId());
}
} else {
log.info("Did not find the banned user in the most recent bans for guild {}. Not adding audit log information.", model.getServerId());
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
}
}, throwable -> {
log.error("Retrieving bans for guild {} failed - logging ban regardless.", model.getServerId());
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
});
return DefaultListenerResult.PROCESSED;
}
@Transactional
public CompletableFuture<Void> sendBannedNotification(User bannedUser, User banningUser, String reason, Long serverId) {
UserBannedListenerModel model = UserBannedListenerModel
public DefaultListenerResult execute(UserBannedModel eventModel) {
log.info("Notifying about ban of user {} in guild {}.", eventModel.getBannedServerUser().getUserId(), eventModel.getServerId());
UserBannedListenerLogModel model = UserBannedListenerLogModel
.builder()
.bannedUser(bannedUser)
.banningUser(banningUser)
.reason(reason)
.bannedUser(eventModel.getBannedUser() != null ? UserDisplay.fromUser(eventModel.getBannedUser()) : UserDisplay.fromId(eventModel.getBannedServerUser().getUserId()))
.banningUser(eventModel.getBanningUser() != null ? UserDisplay.fromUser(eventModel.getBanningUser()) : UserDisplay.fromServerUser(eventModel.getBanningServerUser()))
.reason(eventModel.getReason())
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_BANNED_NOTIFICATION_TEMPLATE, model, serverId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, serverId));
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_BANNED_NOTIFICATION_TEMPLATE, model, eventModel.getServerId());
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, eventModel.getServerId()));
return DefaultListenerResult.PROCESSED;
}
@Override

View File

@@ -4,30 +4,21 @@ import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncUserUnBannedListener;
import dev.sheldan.abstracto.core.models.listener.UserUnBannedModel;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
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.ModerationPostTarget;
import dev.sheldan.abstracto.moderation.model.template.listener.UserUnBannedListenerModel;
import dev.sheldan.abstracto.moderation.model.template.listener.UserUnBannedListenerLogModel;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.audit.ActionType;
import net.dv8tion.jda.api.audit.AuditLogEntry;
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 UserUnBannedListener implements AsyncUserUnBannedListener {
@Autowired
private FeatureModeService featureModeService;
@Autowired
private TemplateService templateService;
@@ -35,56 +26,20 @@ public class UserUnBannedListener implements AsyncUserUnBannedListener {
@Autowired
private PostTargetService postTargetService;
@Autowired
private UserUnBannedListener self;
private static final String USER_UN_BANNED_NOTIFICATION_TEMPLATE = "userUnBanned_listener_notification";
@Override
public DefaultListenerResult execute(UserUnBannedModel model) {
log.info("Notifying about unban of user {} in guild {}.", model.getUnbannedUser().getUserId(), model.getServerId());
model.getGuild()
.retrieveAuditLogs()
.type(ActionType.UNBAN)
.limit(5)
.queue(auditLogEntries -> {
try {
if(auditLogEntries.isEmpty()) {
log.info("Did not find recent bans in guild {}.", model.getServerId());
return;
}
Optional<AuditLogEntry> banEntryOptional = auditLogEntries
.stream()
.filter(auditLogEntry -> auditLogEntry.getTargetIdLong() == model.getUnbannedUser().getUserId())
.findFirst();
if(banEntryOptional.isPresent()) {
AuditLogEntry auditLogEntry = banEntryOptional.get();
if(!model.getGuild().getJDA().getSelfUser().equals(auditLogEntry.getUser())) {
self.sendUnBannedNotification(model.getUser(), auditLogEntry.getUser(), model.getServerId());
}
} else {
log.info("Did not find the un-banned user in the most recent un-bans for guild {}. Not adding audit log information.", model.getServerId());
self.sendUnBannedNotification(model.getUser(), null, model.getServerId());
}
} catch (Exception exception) {
log.error("Failed to properly send un ban log with error.", exception);
}
}, throwable -> {
log.error("Failed to retrieve audit log entries for unban log.", throwable);
self.sendUnBannedNotification(model.getUser(), null, model.getServerId());
});
return DefaultListenerResult.PROCESSED;
}
@Transactional
public CompletableFuture<Void> sendUnBannedNotification(User unbannedUser, User unbanningUser, Long serverId) {
UserUnBannedListenerModel model = UserUnBannedListenerModel
public DefaultListenerResult execute(UserUnBannedModel eventModel) {
log.info("Notifying about unban of user {} in guild {}.", eventModel.getUnBannedServerUser().getUserId(), eventModel.getServerId());
UserUnBannedListenerLogModel model = UserUnBannedListenerLogModel
.builder()
.unBannedUser(unbannedUser)
.unBanningUser(unbanningUser)
.unBannedUser(eventModel.getUnBannedUser() != null ? UserDisplay.fromUser(eventModel.getUnBannedUser()) : UserDisplay.fromId(eventModel.getUnBannedServerUser().getUserId()))
.unBanningUser(eventModel.getUnBanningUser() != null ? UserDisplay.fromUser(eventModel.getUnBanningUser()) : UserDisplay.fromServerUser(eventModel.getUnBanningServerUser()))
.reason(eventModel.getReason())
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_UN_BANNED_NOTIFICATION_TEMPLATE, model, serverId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, serverId));
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_UN_BANNED_NOTIFICATION_TEMPLATE, model, eventModel.getServerId());
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, eventModel.getServerId()));
return DefaultListenerResult.PROCESSED;
}
@Override

View File

@@ -3,34 +3,32 @@ package dev.sheldan.abstracto.moderation.listener.infraction;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.listener.InfractionUpdatedDescriptionListener;
import dev.sheldan.abstracto.moderation.model.database.Infraction;
import dev.sheldan.abstracto.moderation.model.database.InfractionParameter;
import dev.sheldan.abstracto.moderation.model.listener.InfractionDescriptionEventModel;
import dev.sheldan.abstracto.moderation.model.template.command.BanLog;
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedListenerLogModel;
import dev.sheldan.abstracto.moderation.service.BanService;
import dev.sheldan.abstracto.moderation.service.BanServiceBean;
import dev.sheldan.abstracto.moderation.service.management.InfractionManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.moderation.listener.UserBannedListener.USER_BANNED_NOTIFICATION_TEMPLATE;
@Component
@Slf4j
public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionListener {
@@ -45,7 +43,7 @@ public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionLis
private UserService userService;
@Autowired
private BanServiceBean banServiceBean;
private TemplateService templateService;
@Autowired
private BanReasonUpdatedListener self;
@@ -60,7 +58,7 @@ public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionLis
public CompletableFuture<DefaultListenerResult> execute(InfractionDescriptionEventModel model) {
Infraction infraction = infractionManagementService.loadInfraction(model.getInfractionId());
CompletableFuture<User> infractionUser = userService.retrieveUserForId(infraction.getUser().getUserReference().getId());
CompletableFuture<Member> creatorUser = memberService.retrieveMemberInServer(ServerUser.fromAUserInAServer(infraction.getInfractionCreator()));
CompletableFuture<User> creatorUser = userService.retrieveUserForId(infraction.getInfractionCreator().getUserReference().getId());
CompletableFuture<DefaultListenerResult> returningFuture = new CompletableFuture<>();
Long infractionId = infraction.getId();
CompletableFuture.allOf(infractionUser, creatorUser)
@@ -74,26 +72,17 @@ public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionLis
}
@Transactional
public void handleBanUpdate(InfractionDescriptionEventModel model, CompletableFuture<User> infractionUser, CompletableFuture<Member> infractionCreator, CompletableFuture<DefaultListenerResult> returningFuture) {
public void handleBanUpdate(InfractionDescriptionEventModel model, CompletableFuture<User> infractionUser, CompletableFuture<User> infractionCreator, CompletableFuture<DefaultListenerResult> returningFuture) {
Infraction infraction = infractionManagementService.loadInfraction(model.getInfractionId());
GuildMessageChannel messageChannel = channelService.getMessageChannelFromServer(model.getServerId(), infraction.getLogChannel().getId());
Duration deletionDuration = infraction
.getParameters()
.stream()
.filter(infractionParameter -> infractionParameter.getInfractionParameterId().getName().equals(BanService.INFRACTION_PARAMETER_DELETION_DURATION_KEY))
.findAny()
.map(InfractionParameter::getValue)
.map(Duration::parse)
.orElse(Duration.ZERO);
BanLog banLog = BanLog
UserBannedListenerLogModel banLog = UserBannedListenerLogModel
.builder()
.bannedUser(infractionUser.isCompletedExceptionally() ? null : UserDisplay.fromUser(infractionUser.join()))
.banningMember(infractionCreator.isCompletedExceptionally() ? null : MemberDisplay.fromMember(infractionCreator.join()))
.deletionDuration(deletionDuration)
.banningUser(infractionCreator.isCompletedExceptionally() ? null : UserDisplay.fromUser(infractionCreator.join()))
.reason(model.getNewDescription())
.build();
MessageToSend message = banServiceBean.renderBanMessage(banLog, model.getServerId());
MessageToSend message = templateService.renderEmbedTemplate(USER_BANNED_NOTIFICATION_TEMPLATE, banLog, model.getServerId());
messageService.editMessageInChannel(messageChannel, message, infraction.getLogMessageId())
.thenAccept(unused1 -> returningFuture.complete(DefaultListenerResult.PROCESSED))
.exceptionally(throwable1 -> {

View File

@@ -2,30 +2,25 @@ package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureConfig;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.config.posttarget.ModerationPostTarget;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.moderation.model.BanResult;
import dev.sheldan.abstracto.moderation.model.database.Infraction;
import dev.sheldan.abstracto.moderation.model.template.command.BanLog;
import dev.sheldan.abstracto.moderation.model.template.command.BanNotificationModel;
import dev.sheldan.abstracto.moderation.model.template.command.UnBanLog;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.UserSnowflake;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -34,16 +29,11 @@ import java.util.concurrent.TimeUnit;
@Slf4j
public class BanServiceBean implements BanService {
public static final String BAN_LOG_TEMPLATE = "ban_log";
public static final String UN_BAN_LOG_TEMPLATE = "unBan_log";
public static final String BAN_NOTIFICATION = "ban_notification";
@Autowired
private TemplateService templateService;
@Autowired
private PostTargetService postTargetService;
@Autowired
private BanServiceBean self;
@@ -64,13 +54,6 @@ public class BanServiceBean implements BanService {
@Override
public CompletableFuture<BanResult> banUserWithNotification(ServerUser userToBeBanned, String reason, ServerUser banningUser, Guild guild, Duration deletionDuration) {
BanLog banLog = BanLog
.builder()
.bannedUser(UserDisplay.fromServerUser(userToBeBanned))
.banningMember(MemberDisplay.fromServerUser(banningUser))
.deletionDuration(deletionDuration)
.reason(reason)
.build();
BanResult[] result = {BanResult.SUCCESSFUL};
return sendBanNotification(userToBeBanned, reason, guild)
.exceptionally(throwable -> {
@@ -78,13 +61,12 @@ public class BanServiceBean implements BanService {
return null;
})
.thenCompose(unused -> banUser(guild, userToBeBanned, deletionDuration, reason))
.thenCompose(unused -> sendBanLogMessage(banLog, guild.getIdLong()))
.thenAccept(banLogMessage -> self.evaluateAndStoreInfraction(userToBeBanned, guild, reason, banningUser, banLogMessage, deletionDuration))
.thenAccept(banLogMessage -> self.evaluateAndStoreInfraction(userToBeBanned, guild, reason, banningUser, deletionDuration))
.thenApply(unused -> result[0]);
}
@Transactional
public CompletableFuture<Long> evaluateAndStoreInfraction(ServerUser user, Guild guild, String reason, ServerUser banningMember, Message banLogMessage, Duration deletionDuration) {
public CompletableFuture<Long> evaluateAndStoreInfraction(ServerUser user, Guild guild, String reason, ServerUser banningMember, Duration deletionDuration) {
if(featureFlagService.getFeatureFlagValue(ModerationFeatureDefinition.INFRACTIONS, guild.getIdLong())) {
Long infractionPoints = configService.getLongValueOrConfigDefault(ModerationFeatureConfig.BAN_INFRACTION_POINTS, guild.getIdLong());
AUserInAServer bannedUser = userInServerManagementService.loadOrCreateUser(guild.getIdLong(), user.getUserId());
@@ -94,7 +76,7 @@ public class BanServiceBean implements BanService {
deletionDuration = Duration.ZERO;
}
parameters.put(INFRACTION_PARAMETER_DELETION_DURATION_KEY, deletionDuration.toString());
return infractionService.createInfractionWithNotification(bannedUser, infractionPoints, BAN_INFRACTION_TYPE, reason, banningUser, parameters, banLogMessage)
return infractionService.createInfractionWithNotification(bannedUser, infractionPoints, BAN_INFRACTION_TYPE, reason, banningUser, parameters)
.thenApply(Infraction::getId);
} else {
return CompletableFuture.completedFuture(null);
@@ -111,17 +93,6 @@ public class BanServiceBean implements BanService {
return messageService.sendMessageToUser(serverUser, message).thenAccept(message1 -> {});
}
@Override
public CompletableFuture<Void> unBanUserWithNotification(Long userId, ServerUser unBanningMember, Guild guild) {
UnBanLog banLog = UnBanLog
.builder()
.bannedUser(UserDisplay.fromId(userId))
.unBanningMember(MemberDisplay.fromServerUser(unBanningMember))
.build();
return unbanUser(guild, userId)
.thenCompose(unused -> self.sendUnBanLogMessage(banLog, guild.getIdLong(), UN_BAN_LOG_TEMPLATE));
}
@Override
public CompletableFuture<Void> banUser(Guild guild, ServerUser userToBeBanned, Duration deletionDuration, String reason) {
log.info("Banning user {} in guild {}.", userToBeBanned.getUserId(), guild.getId());
@@ -143,20 +114,4 @@ public class BanServiceBean implements BanService {
.thenCompose(unused -> unbanUser(guild, user.getUserId()));
}
public CompletableFuture<Message> sendBanLogMessage(BanLog banLog, Long guildId) {
MessageToSend banLogMessage = renderBanMessage(banLog, guildId);
log.debug("Sending ban log message in guild {}.", guildId);
List<CompletableFuture<Message>> messageFutures = postTargetService.sendEmbedInPostTarget(banLogMessage, ModerationPostTarget.BAN_LOG, guildId);
return FutureUtils.toSingleFutureGeneric(messageFutures).thenApply(unused -> messageFutures.get(0).join());
}
public MessageToSend renderBanMessage(BanLog banLog, Long guildId) {
return templateService.renderEmbedTemplate(BAN_LOG_TEMPLATE, banLog, guildId);
}
public CompletableFuture<Void> sendUnBanLogMessage(UnBanLog banLog, Long guildId, String template) {
MessageToSend banLogMessage = templateService.renderEmbedTemplate(template, banLog, guildId);
log.debug("Sending unban log message in guild {}.", guildId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(banLogMessage, ModerationPostTarget.UN_BAN_LOG, guildId));
}
}

View File

@@ -81,7 +81,7 @@ public class InfractionServiceBean implements InfractionService {
List<Infraction> infractions = infractionManagementService.getActiveInfractionsForUser(aUserInAServer);
log.info("Calculating points for user {} in server {} with {} infractions.",
aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId(), infractions.size());
return infractions.stream().collect(Collectors.summarizingLong(Infraction::getPoints)).getCount();
return infractions.stream().collect(Collectors.summarizingLong(Infraction::getPoints)).getSum();
}
@Override
@@ -112,8 +112,8 @@ public class InfractionServiceBean implements InfractionService {
public CompletableFuture<Void> createInfractionNotification(AUserInAServer aUserInAServer, Long points, String type, String description) {
Long serverId = aUserInAServer.getServerReference().getId();
Long currentPoints = getActiveInfractionPointsForUser(aUserInAServer);
Long newPoints = currentPoints + points;
Pair<Integer, Integer> levelChange = infractionLevelChanged(serverId, newPoints, currentPoints);
Long oldPoints = currentPoints - points;
Pair<Integer, Integer> levelChange = infractionLevelChanged(serverId, currentPoints, oldPoints);
Integer oldLevel = levelChange.getFirst();
Integer newLevel = levelChange.getSecond();
if(!oldLevel.equals(newLevel)) {
@@ -124,10 +124,10 @@ public class InfractionServiceBean implements InfractionService {
.oldLevel(oldLevel)
.type(type)
.description(description)
.oldPoints(currentPoints)
.newPoints(newPoints)
.oldPoints(oldPoints)
.newPoints(currentPoints)
.build();
infractionLevelChangedListenerManager.sendInfractionLevelChangedEvent(newLevel, oldLevel, newPoints, currentPoints, ServerUser.fromAUserInAServer(aUserInAServer));
infractionLevelChangedListenerManager.sendInfractionLevelChangedEvent(newLevel, oldLevel, currentPoints, oldPoints, ServerUser.fromAUserInAServer(aUserInAServer));
MessageToSend messageToSend = templateService.renderEmbedTemplate(INFRACTION_NOTIFICATION_TEMPLATE_KEY, model, serverId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, InfractionPostTarget.INFRACTION_NOTIFICATION, serverId));
} else {

View File

@@ -32,6 +32,7 @@ abstracto.postTargets.banLog.name=banLog
abstracto.postTargets.unBanLog.name=unBanLog
abstracto.postTargets.muteLog.name=muteLog
abstracto.postTargets.decayLog.name=decayLog
abstracto.postTargets.infractionNotification.name=infractionNotification
abstracto.featureModes.warnDecayLogging.featureName=warnings
abstracto.featureModes.warnDecayLogging.mode=warnDecayLogging
@@ -49,20 +50,20 @@ abstracto.featureModes.reactionReportActions.featureName=reportReactions
abstracto.featureModes.reactionReportActions.mode=reactionReportActions
abstracto.featureModes.reactionReportActions.enabled=false
abstracto.systemConfigs.infractionLvl1.name=infractionLvl1
abstracto.systemConfigs.infractionLvl1.longValue=10
abstracto.systemConfigs.infractionLevel1.name=infractionLevel1
abstracto.systemConfigs.infractionLevel1.longValue=10
abstracto.systemConfigs.infractionLvl2.name=infractionLvl2
abstracto.systemConfigs.infractionLvl2.longValue=20
abstracto.systemConfigs.infractionLevel2.name=infractionLevel2
abstracto.systemConfigs.infractionLevel2.longValue=20
abstracto.systemConfigs.infractionLvl3.name=infractionLvl3
abstracto.systemConfigs.infractionLvl3.longValue=30
abstracto.systemConfigs.infractionLevel3.name=infractionLevel3
abstracto.systemConfigs.infractionLevel3.longValue=30
abstracto.systemConfigs.infractionLvl4.name=infractionLvl4
abstracto.systemConfigs.infractionLvl4.longValue=40
abstracto.systemConfigs.infractionLevel4.name=infractionLevel4
abstracto.systemConfigs.infractionLevel4.longValue=40
abstracto.systemConfigs.infractionLvl5.name=infractionLvl5
abstracto.systemConfigs.infractionLvl5.longValue=50
abstracto.systemConfigs.infractionLevel5.name=infractionLevel5
abstracto.systemConfigs.infractionLevel5.longValue=50
abstracto.systemConfigs.infractionLevels.name=infractionLevels
abstracto.systemConfigs.infractionLevels.longValue=5

View File

@@ -1,15 +1,15 @@
package dev.sheldan.abstracto.moderation.model.template.listener;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.User;
@Getter
@Setter
@Builder
public class UserBannedListenerModel {
private User bannedUser;
public class UserBannedListenerLogModel {
private UserDisplay bannedUser;
private String reason;
private User banningUser;
private UserDisplay banningUser;
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.moderation.model.template.listener;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class UserUnBannedListenerLogModel {
private UserDisplay unBannedUser;
private String reason;
private UserDisplay unBanningUser;
}

View File

@@ -1,14 +0,0 @@
package dev.sheldan.abstracto.moderation.model.template.listener;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.User;
@Getter
@Setter
@Builder
public class UserUnBannedListenerModel {
private User unBannedUser;
private User unBanningUser;
}

View File

@@ -12,7 +12,6 @@ public interface BanService {
String BAN_INFRACTION_TYPE = "ban";
String INFRACTION_PARAMETER_DELETION_DURATION_KEY = "DELETION_DURATION";
CompletableFuture<BanResult> banUserWithNotification(ServerUser userToBeBanned, String reason, ServerUser banningUser, Guild guild, Duration deletionDuration);
CompletableFuture<Void> unBanUserWithNotification(Long userId, ServerUser unBanningMember, Guild guild);
CompletableFuture<Void> banUser(Guild guild, ServerUser userToBeBanned, Duration deletionDuration, String reason);
CompletableFuture<Void> unbanUser(Guild guild, Long userId);
CompletableFuture<Void> softBanUser(Guild guild, ServerUser user, Duration delDays);

View File

@@ -13,6 +13,7 @@ import dev.sheldan.abstracto.core.models.FullGuild;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.UndoActionInstance;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
@@ -914,7 +915,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.build();
Long modmailThreadId = modMailThread.getId();
return userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenApply(user -> {
headerModel.setUser(user);
headerModel.setUser(UserDisplay.fromUser(user));
return self.sendClosingHeader(headerModel, modmailThreadId).get(0);
}).thenCompose(Function.identity());
}

View File

@@ -1,11 +1,11 @@
package dev.sheldan.abstracto.modmail.model.template;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import java.time.Duration;
import java.time.Instant;
@@ -39,7 +39,7 @@ public class ModMailClosingHeaderModel {
private Member closingMember;
private Boolean silently;
private User user;
private UserDisplay user;
private Long serverId;
private Long modmailThreadId;
}

View File

@@ -3,8 +3,12 @@ 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.UserBannedModel;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.core.utils.CompletableFutureMap;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.guild.GuildBanEvent;
import net.dv8tion.jda.api.audit.ActionType;
import net.dv8tion.jda.api.entities.User;
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,11 +16,13 @@ import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.List;
@Component
@Slf4j
public class AsyncUserBannedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncUserBannedListener> listenerList;
@@ -27,25 +33,48 @@ public class AsyncUserBannedListenerBean extends ListenerAdapter {
@Autowired
private ListenerService listenerService;
@Autowired
private UserService userService;
@Override
public void onGuildBan(@Nonnull GuildBanEvent event) {
public void onGuildAuditLogEntryCreate(@Nonnull GuildAuditLogEntryCreateEvent event) {
if(listenerList == null) return;
UserBannedModel model = getModel(event);
listenerList.forEach(leaveListener -> listenerService.executeFeatureAwareListener(leaveListener, model, leaveListenerExecutor));
if(event.getEntry().getType().equals(ActionType.BAN)) {
log.info("Handling ban audit log entry created for user {} in server {}.", event.getEntry().getTargetIdLong(), event.getGuild().getIdLong());
CompletableFutureMap<Long, User> longUserCompletableFutureMap = userService.retrieveUsersMapped(Arrays.asList(event.getEntry().getTargetIdLong(), event.getEntry().getUserIdLong()));
longUserCompletableFutureMap.getMainFuture().thenAccept(avoid -> {
User bannedUser = longUserCompletableFutureMap.getElement(event.getEntry().getTargetIdLong());
User banningUser = longUserCompletableFutureMap.getElement(event.getEntry().getUserIdLong());
UserBannedModel model = getModel(event, bannedUser, banningUser);
listenerList.forEach(leaveListener -> listenerService.executeFeatureAwareListener(leaveListener, model, leaveListenerExecutor));
}).exceptionally(throwable -> {
log.warn("Failed to fetch users {} or {} for banned event.", event.getEntry().getTargetIdLong(), event.getEntry().getUserIdLong(), throwable);
UserBannedModel model = getModel(event, null, null);
listenerList.forEach(leaveListener -> listenerService.executeFeatureAwareListener(leaveListener, model, leaveListenerExecutor));
return null;
});
}
}
private UserBannedModel getModel(GuildBanEvent event) {
ServerUser serverUser = ServerUser
private UserBannedModel getModel(GuildAuditLogEntryCreateEvent event, User bannedUser, User banningUser) {
ServerUser bannedServerUser = ServerUser
.builder()
.serverId(event.getGuild().getIdLong())
.userId(event.getUser().getIdLong())
.isBot(event.getUser().isBot())
.userId(event.getEntry().getTargetIdLong())
.build();
ServerUser banningServerUser = ServerUser
.builder()
.serverId(event.getGuild().getIdLong())
.userId(event.getEntry().getUserIdLong())
.build();
return UserBannedModel
.builder()
.bannedUser(serverUser)
.bannedServerUser(bannedServerUser)
.banningUser(banningUser)
.banningServerUser(banningServerUser)
.guild(event.getGuild())
.user(event.getUser())
.reason(event.getEntry().getReason())
.bannedUser(bannedUser)
.build();
}
}

View File

@@ -3,8 +3,12 @@ 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.UserUnBannedModel;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.core.utils.CompletableFutureMap;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.guild.GuildUnbanEvent;
import net.dv8tion.jda.api.audit.ActionType;
import net.dv8tion.jda.api.entities.User;
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,6 +16,7 @@ import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.List;
@Component
@@ -27,25 +32,48 @@ public class AsyncUserUnBannedListenerBean extends ListenerAdapter {
@Autowired
private ListenerService listenerService;
@Autowired
private UserService userService;
@Override
public void onGuildUnban(@Nonnull GuildUnbanEvent event) {
public void onGuildAuditLogEntryCreate(@Nonnull GuildAuditLogEntryCreateEvent event) {
if(listenerList == null) return;
UserUnBannedModel model = getModel(event);
listenerList.forEach(leaveListener -> listenerService.executeFeatureAwareListener(leaveListener, model, leaveListenerExecutor));
if(event.getEntry().getType().equals(ActionType.UNBAN)) {
log.info("Handling unBan audit log entry created for user {} in server {}.", event.getEntry().getTargetIdLong(), event.getGuild().getIdLong());
CompletableFutureMap<Long, User> longUserCompletableFutureMap = userService.retrieveUsersMapped(Arrays.asList(event.getEntry().getTargetIdLong(), event.getEntry().getUserIdLong()));
longUserCompletableFutureMap.getMainFuture().thenAccept(avoid -> {
User unBannedUser = longUserCompletableFutureMap.getElement(event.getEntry().getTargetIdLong());
User unBanningUser = longUserCompletableFutureMap.getElement(event.getEntry().getUserIdLong());
UserUnBannedModel model = getModel(event, unBannedUser, unBanningUser);
listenerList.forEach(leaveListener -> listenerService.executeFeatureAwareListener(leaveListener, model, leaveListenerExecutor));
}).exceptionally(throwable -> {
log.warn("Failed to fetch users {} or {} for unbanned event.", event.getEntry().getTargetIdLong(), event.getEntry().getUserIdLong(), throwable);
UserUnBannedModel model = getModel(event, null, null);
listenerList.forEach(leaveListener -> listenerService.executeFeatureAwareListener(leaveListener, model, leaveListenerExecutor));
return null;
});
}
}
private UserUnBannedModel getModel(GuildUnbanEvent event) {
ServerUser serverUser = ServerUser
private UserUnBannedModel getModel(GuildAuditLogEntryCreateEvent event, User unBannedUser, User unBanningUser) {
ServerUser unBannedServerUser = ServerUser
.builder()
.serverId(event.getGuild().getIdLong())
.userId(event.getUser().getIdLong())
.isBot(event.getUser().isBot())
.userId(event.getEntry().getTargetIdLong())
.build();
ServerUser unBanningServerUser = ServerUser
.builder()
.serverId(event.getGuild().getIdLong())
.userId(event.getEntry().getUserIdLong())
.build();
return UserUnBannedModel
.builder()
.unbannedUser(serverUser)
.unBannedServerUser(unBannedServerUser)
.unBanningUser(unBanningUser)
.unBanningServerUser(unBanningServerUser)
.guild(event.getGuild())
.user(event.getUser())
.reason(event.getEntry().getReason())
.unBannedUser(unBannedUser)
.build();
}
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import dev.sheldan.abstracto.core.utils.CompletableFutureMap;
import net.dv8tion.jda.api.entities.SelfUser;
import net.dv8tion.jda.api.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
@@ -8,6 +9,7 @@ import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
@Component
@@ -30,6 +32,12 @@ public class UserServiceBean implements UserService {
return new CompletableFutureList<>(userFutures);
}
@Override
public CompletableFutureMap<Long, User> retrieveUsersMapped(List<Long> ids) {
return new CompletableFutureMap<>(ids.stream()
.collect(Collectors.toMap(Function.identity(), this::retrieveUserForId)));
}
@Override
public SelfUser getSelfUser() {
return botService.getInstance().getSelfUser();

View File

@@ -1,21 +1,51 @@
package dev.sheldan.abstracto.core.models.frontend;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.utils.MemberUtils;
import lombok.Builder;
import lombok.Getter;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
@Getter
@Builder
public class UserDisplay {
private String avatarUrl;
private String name;
private String id;
private String discriminator;
private String userMention;
private Long id;
public static UserDisplay fromMember(Member member) {
return builder()
.avatarUrl(member.getEffectiveAvatarUrl())
.name(member.getEffectiveName())
.id(member.getId())
.userMention(MemberUtils.getUserAsMention(member.getIdLong()))
.id(member.getIdLong())
.build();
}
public static UserDisplay fromUser(User user) {
return builder()
.discriminator(user.getDiscriminator())
.avatarUrl(user.getEffectiveAvatarUrl())
.name(user.getEffectiveName())
.userMention(MemberUtils.getUserAsMention(user.getIdLong()))
.id(user.getIdLong())
.build();
}
public static UserDisplay fromServerUser(ServerUser user) {
return builder()
.userMention(MemberUtils.getUserAsMention(user.getUserId()))
.id(user.getUserId())
.build();
}
public static UserDisplay fromId(Long id) {
return builder()
.userMention(MemberUtils.getUserAsMention(id))
.id(id)
.build();
}
}

View File

@@ -12,9 +12,12 @@ import net.dv8tion.jda.api.entities.User;
@Setter
@Builder
public class UserBannedModel implements FeatureAwareListenerModel {
private ServerUser bannedUser;
private User user;
private ServerUser bannedServerUser;
private ServerUser banningServerUser;
private User bannedUser;
private User banningUser;
private Guild guild;
private String reason;
@Override
public Long getServerId() {
return guild.getIdLong();

View File

@@ -12,9 +12,12 @@ import net.dv8tion.jda.api.entities.User;
@Setter
@Builder
public class UserUnBannedModel implements FeatureAwareListenerModel {
private ServerUser unbannedUser;
private User user;
private ServerUser unBannedServerUser;
private ServerUser unBanningServerUser;
private User unBannedUser;
private User unBanningUser;
private Guild guild;
private String reason;
@Override
public Long getServerId() {
return guild.getIdLong();

View File

@@ -11,14 +11,18 @@ import net.dv8tion.jda.api.entities.User;
@Setter
@Builder
public class UserDisplay {
private Long userId;
private Long id;
private String userMention;
private String discriminator;
private String name;
public static UserDisplay fromUser(User user) {
return UserDisplay
.builder()
.userMention(MemberUtils.getUserAsMention(user.getIdLong()))
.userId(user.getIdLong())
.name(user.getEffectiveName())
.discriminator(user.getDiscriminator())
.id(user.getIdLong())
.build();
}
@@ -26,7 +30,7 @@ public class UserDisplay {
return UserDisplay
.builder()
.userMention(MemberUtils.getUserAsMention(serverUser.getUserId()))
.userId(serverUser.getUserId())
.id(serverUser.getUserId())
.build();
}
@@ -34,7 +38,7 @@ public class UserDisplay {
return UserDisplay
.builder()
.userMention(MemberUtils.getUserAsMention(id))
.userId(id)
.id(id)
.build();
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import dev.sheldan.abstracto.core.utils.CompletableFutureMap;
import net.dv8tion.jda.api.entities.SelfUser;
import net.dv8tion.jda.api.entities.User;
@@ -10,5 +11,6 @@ import java.util.concurrent.CompletableFuture;
public interface UserService {
CompletableFuture<User> retrieveUserForId(Long id);
CompletableFutureList<User> retrieveUsers(List<Long> ids);
CompletableFutureMap<Long, User> retrieveUsersMapped(List<Long> ids);
SelfUser getSelfUser();
}

View File

@@ -0,0 +1,49 @@
package dev.sheldan.abstracto.core.utils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@Getter
@Slf4j
public class CompletableFutureMap<L, T> {
private final CompletableFuture<Void> mainFuture;
private final Map<L, CompletableFuture<T>> futures;
public CompletableFutureMap(Map<L ,CompletableFuture<T>> futures) {
this.mainFuture = CompletableFuture.allOf(futures.values().toArray(new CompletableFuture[0]));
this.futures = futures;
}
public List<T> getObjects() {
List<T> result = new ArrayList<>();
futures.values().forEach(future -> {
if(!future.isCompletedExceptionally()) {
result.add(future.join());
} else {
try {
future.join();
} catch (Exception exception) {
log.warn("Future completed with exception.", exception);
}
}
});
return result;
}
public T getElement(L key) {
if(!getFutures().containsKey(key)) {
return null;
}
CompletableFuture<T> future = getFutures().get(key);
if(!future.isCompletedExceptionally()) {
return future.join();
} else {
return null;
}
}
}