[AB-139] changing the places at which there might be an uncached member object

fixing integer validator
fixing syncing roles not working if the role did not change in the database
fixing warn id being flipped
fixing star stats model
This commit is contained in:
Sheldan
2020-10-11 22:04:18 +02:00
parent 1d3c414d6b
commit 36d11371cb
105 changed files with 1762 additions and 1086 deletions

View File

@@ -554,7 +554,7 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
public void showAssignablePlaceConfig(AServer server, String name, MessageChannel channel) { public void showAssignablePlaceConfig(AServer server, String name, MessageChannel channel) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name); AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
List<AssignablePostConfigRole> roles = new ArrayList<>(); List<AssignablePostConfigRole> roles = new ArrayList<>();
Guild guild = botService.getGuildByIdNullable(server.getId()); Guild guild = botService.getGuildById(server.getId());
log.info("Showing assignable role place config for place {} in channel {} on server {}.", place.getId(), channel.getId(), server.getId()); log.info("Showing assignable role place config for place {} in channel {} on server {}.", place.getId(), channel.getId(), server.getId());
List<AssignableRole> assignableRoles = place.getAssignableRoles().stream().sorted(Comparator.comparingInt(AssignableRole::getPosition)).collect(Collectors.toList()); List<AssignableRole> assignableRoles = place.getAssignableRoles().stream().sorted(Comparator.comparingInt(AssignableRole::getPosition)).collect(Collectors.toList());
for (AssignableRole role : assignableRoles) { for (AssignableRole role : assignableRoles) {
@@ -609,17 +609,16 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
@Override @Override
public CompletableFuture<Void> removeExistingReactionsAndRoles(AssignableRolePlace place, AssignedRoleUser user) { public CompletableFuture<Void> removeExistingReactionsAndRoles(AssignableRolePlace place, AssignedRoleUser user) {
Member memberInServer = botService.getMemberInServer(user.getUser());
List<CompletableFuture<Void>> futures = new ArrayList<>(); List<CompletableFuture<Void>> futures = new ArrayList<>();
log.info("Removing all existing reactions and roles by user {} on assignable role place {} in server {}.", user.getId(), place.getId(), user.getUser().getServerReference().getId()); log.info("Removing all existing reactions and roles by user {} on assignable role place {} in server {}.", user.getId(), place.getId(), user.getUser().getServerReference().getId());
user.getRoles().forEach(assignableRole -> { user.getRoles().forEach(assignableRole -> {
futures.add(roleService.removeAssignableRoleFromUser(assignableRole, memberInServer)); futures.add(roleService.removeAssignableRoleFromUser(assignableRole, user.getUser()));
log.trace("Removing role {} from user {} in server {} because of assignable role clearing.", assignableRole.getRole().getId(), memberInServer.getId(), place.getServer().getId()); log.trace("Removing role {} from user {} in server {} because of assignable role clearing.", assignableRole.getRole().getId(), user.getUser().getUserReference().getId(), place.getServer().getId());
AEmote emoteToUseObject = emoteManagementService.loadEmote(assignableRole.getEmote().getId()); AEmote emoteToUseObject = emoteManagementService.loadEmote(assignableRole.getEmote().getId());
AssignableRolePlacePost assignablePlacePost = assignableRole.getAssignableRolePlacePost(); AssignableRolePlacePost assignablePlacePost = assignableRole.getAssignableRolePlacePost();
log.trace("Removing reaction with emote {} from user {} in server {} because of assignable role clearing.", emoteToUseObject.getId(), user.getUser().getUserReference().getId(), place.getServer().getId()); log.trace("Removing reaction with emote {} from user {} in server {} because of assignable role clearing.", emoteToUseObject.getId(), user.getUser().getUserReference().getId(), place.getServer().getId());
futures.add(messageService.removeReactionOfUserFromMessageWithFuture(emoteToUseObject, place.getServer().getId(), futures.add(messageService.removeReactionOfUserFromMessageWithFuture(emoteToUseObject, place.getServer().getId(),
assignablePlacePost.getUsedChannel().getId(), assignablePlacePost.getId(), memberInServer)); assignablePlacePost.getUsedChannel().getId(), assignablePlacePost.getId(), user.getUser().getUserReference().getId()));
}); });
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
} }

View File

@@ -7,6 +7,7 @@ import dev.sheldan.abstracto.assignableroles.service.management.AssignableRoleMa
import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementService; import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementServiceBean; import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementServiceBean;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.RoleService; import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -39,6 +40,9 @@ public class AssignableRoleServiceBean implements AssignableRoleService {
@Autowired @Autowired
private AssignableRoleServiceBean self; private AssignableRoleServiceBean self;
@Autowired
private BotService botService;
@Override @Override
public CompletableFuture<Void> assignAssignableRoleToUser(Long assignableRoleId, Member toAdd) { public CompletableFuture<Void> assignAssignableRoleToUser(Long assignableRoleId, Member toAdd) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId); AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
@@ -69,14 +73,29 @@ public class AssignableRoleServiceBean implements AssignableRoleService {
@Override @Override
public CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, Member member) { public CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, Member member) {
log.info("Removing assignable role {} from user {} in server {}.", assignableRole.getId(), member.getId(), member.getGuild().getId()); log.info("Removing assignable role {} from user {} in server {}.", assignableRole.getId(), member.getId(), member.getGuild().getId());
return roleService.removeRoleFromMemberFuture(member, assignableRole.getRole()); return roleService.removeRoleFromMemberAsync(member, assignableRole.getRole());
}
@Override
@Transactional
public CompletableFuture<Void> removeAssignableRoleFromUser(Long assignableRoleId, Member member) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
return self.removeAssignableRoleFromUser(role, member);
}
@Override
public CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, AUserInAServer aUserInAServer) {
Long assignableRoleId = assignableRole.getId();
return botService.getMemberInServerAsync(aUserInAServer).thenCompose(member ->
self.removeAssignableRoleFromUser(assignableRoleId, member)
);
} }
@Override @Override
public CompletableFuture<Void> fullyRemoveAssignableRoleFromUser(AssignableRole assignableRole, Member member) { public CompletableFuture<Void> fullyRemoveAssignableRoleFromUser(AssignableRole assignableRole, Member member) {
Long assignableRoleId = assignableRole.getId(); Long assignableRoleId = assignableRole.getId();
return this.removeAssignableRoleFromUser(assignableRole, member).thenAccept(aVoid -> return this.removeAssignableRoleFromUser(assignableRole, member).thenAccept(aVoid ->
self.removeRoleFromUser(assignableRoleId, member) self.persistRoleRemovalFromUser(assignableRoleId, member)
); );
} }
@@ -114,7 +133,7 @@ public class AssignableRoleServiceBean implements AssignableRoleService {
} }
@Transactional @Transactional
public void removeRoleFromUser(Long assignableRoleId, Member member) { public void persistRoleRemovalFromUser(Long assignableRoleId, Member member) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId); AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(member); AUserInAServer aUserInAServer = userInServerManagementService.loadUser(member);
removeRoleFromUser(role, aUserInAServer); removeRoleFromUser(role, aUserInAServer);

View File

@@ -12,6 +12,8 @@ public interface AssignableRoleService {
void clearAllRolesOfUserInPlace(AssignableRolePlace place, AUserInAServer user); void clearAllRolesOfUserInPlace(AssignableRolePlace place, AUserInAServer user);
CompletableFuture<Void> fullyAssignAssignableRoleToUser(Long assignableRoleId, Member toAdd); CompletableFuture<Void> fullyAssignAssignableRoleToUser(Long assignableRoleId, Member toAdd);
CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, Member member); CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, Member member);
CompletableFuture<Void> removeAssignableRoleFromUser(Long assignableRoleId, Member member);
CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, AUserInAServer aUserInAServer);
CompletableFuture<Void> fullyRemoveAssignableRoleFromUser(AssignableRole assignableRole, Member member); CompletableFuture<Void> fullyRemoveAssignableRoleFromUser(AssignableRole assignableRole, Member member);
void addRoleToUser(Long assignableRoleId, AUserInAServer aUserInAServer); void addRoleToUser(Long assignableRoleId, AUserInAServer aUserInAServer);
void addRoleToUser(AssignableRole assignableRole, AUserInAServer aUserInAServer); void addRoleToUser(AssignableRole assignableRole, AUserInAServer aUserInAServer);

View File

@@ -16,6 +16,7 @@ import dev.sheldan.abstracto.experience.config.features.ExperienceFeature;
import dev.sheldan.abstracto.experience.converter.LeaderBoardModelConverter; import dev.sheldan.abstracto.experience.converter.LeaderBoardModelConverter;
import dev.sheldan.abstracto.experience.models.LeaderBoard; import dev.sheldan.abstracto.experience.models.LeaderBoard;
import dev.sheldan.abstracto.experience.models.LeaderBoardEntry; import dev.sheldan.abstracto.experience.models.LeaderBoardEntry;
import dev.sheldan.abstracto.experience.models.templates.LeaderBoardEntryModel;
import dev.sheldan.abstracto.experience.models.templates.LeaderBoardModel; import dev.sheldan.abstracto.experience.models.templates.LeaderBoardModel;
import dev.sheldan.abstracto.experience.service.AUserExperienceService; import dev.sheldan.abstracto.experience.service.AUserExperienceService;
import dev.sheldan.abstracto.templating.model.MessageToSend; import dev.sheldan.abstracto.templating.model.MessageToSend;
@@ -28,6 +29,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/** /**
* Shows the experience gain information of the top 10 users in the server, or if a page number is provided as a parameter, only the members which are on this page. * Shows the experience gain information of the top 10 users in the server, or if a page number is provided as a parameter, only the members which are on this page.
@@ -57,15 +59,22 @@ public class LeaderBoardCommand extends AbstractConditionableCommand {
// parameter is optional, in case its not present, we default to the 0th page // parameter is optional, in case its not present, we default to the 0th page
Integer page = !parameters.isEmpty() ? (Integer) parameters.get(0) : 1; Integer page = !parameters.isEmpty() ? (Integer) parameters.get(0) : 1;
LeaderBoard leaderBoard = userExperienceService.findLeaderBoardData(commandContext.getUserInitiatedContext().getServer(), page); LeaderBoard leaderBoard = userExperienceService.findLeaderBoardData(commandContext.getUserInitiatedContext().getServer(), page);
LeaderBoardModel leaderBoardModel = (LeaderBoardModel) ContextConverter.fromCommandContext(commandContext, LeaderBoardModel.class); LeaderBoardModel leaderBoardModel = (LeaderBoardModel) ContextConverter.slimFromCommandContext(commandContext, LeaderBoardModel.class);
leaderBoardModel.setUserExperiences(converter.fromLeaderBoard(leaderBoard)); List<CompletableFuture<LeaderBoardEntryModel>> futures = new ArrayList<>();
List<CompletableFuture<LeaderBoardEntryModel>> completableFutures = converter.fromLeaderBoard(leaderBoard);
futures.addAll(completableFutures);
log.info("Rendering leaderboard for page {} in server {} for user {}.", page, commandContext.getAuthor().getId(), commandContext.getGuild().getId()); log.info("Rendering leaderboard for page {} in server {} for user {}.", page, commandContext.getAuthor().getId(), commandContext.getGuild().getId());
LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(commandContext.getUserInitiatedContext().getAUserInAServer()); LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(commandContext.getUserInitiatedContext().getAUserInAServer());
leaderBoardModel.setUserExecuting(converter.fromLeaderBoardEntry(userRank)); CompletableFuture<LeaderBoardEntryModel> userRankFuture = converter.fromLeaderBoardEntry(userRank);
MessageToSend messageToSend = templateService.renderEmbedTemplate(LEADER_BOARD_POST_EMBED_TEMPLATE, leaderBoardModel); futures.add(userRankFuture);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel())) return FutureUtils.toSingleFutureGeneric(futures).thenCompose(aVoid -> {
.thenApply(aVoid -> CommandResult.fromSuccess()); List<LeaderBoardEntryModel> finalModels = completableFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
leaderBoardModel.setUserExperiences(finalModels);
leaderBoardModel.setUserExecuting(userRankFuture.join());
MessageToSend messageToSend = templateService.renderEmbedTemplate(LEADER_BOARD_POST_EMBED_TEMPLATE, leaderBoardModel);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()));
}).thenApply(aVoid -> CommandResult.fromSuccess());
} }

View File

@@ -8,20 +8,25 @@ import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter; import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureEnum; import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils; import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.experience.config.features.ExperienceFeature; import dev.sheldan.abstracto.experience.config.features.ExperienceFeature;
import dev.sheldan.abstracto.experience.converter.LeaderBoardModelConverter; import dev.sheldan.abstracto.experience.converter.LeaderBoardModelConverter;
import dev.sheldan.abstracto.experience.models.LeaderBoardEntry; import dev.sheldan.abstracto.experience.models.LeaderBoardEntry;
import dev.sheldan.abstracto.experience.models.database.AUserExperience; import dev.sheldan.abstracto.experience.models.database.AUserExperience;
import dev.sheldan.abstracto.experience.models.templates.LeaderBoardEntryModel;
import dev.sheldan.abstracto.experience.models.templates.RankModel; import dev.sheldan.abstracto.experience.models.templates.RankModel;
import dev.sheldan.abstracto.experience.service.ExperienceLevelService; import dev.sheldan.abstracto.experience.service.ExperienceLevelService;
import dev.sheldan.abstracto.experience.service.AUserExperienceService; import dev.sheldan.abstracto.experience.service.AUserExperienceService;
import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService;
import dev.sheldan.abstracto.templating.model.MessageToSend; import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService; import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -50,19 +55,35 @@ public class Rank extends AbstractConditionableCommand {
@Autowired @Autowired
private ChannelService channelService; private ChannelService channelService;
@Autowired
private Rank self;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private UserExperienceManagementService userExperienceManagementService;
@Override @Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext); checkParameters(commandContext);
RankModel rankModel = (RankModel) ContextConverter.fromCommandContext(commandContext, RankModel.class); RankModel rankModel = (RankModel) ContextConverter.slimFromCommandContext(commandContext, RankModel.class);
LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(commandContext.getUserInitiatedContext().getAUserInAServer()); LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(commandContext.getUserInitiatedContext().getAUserInAServer());
rankModel.setRankUser(converter.fromLeaderBoardEntry(userRank)); CompletableFuture<LeaderBoardEntryModel> future = converter.fromLeaderBoardEntry(userRank);
AUserExperience experienceObj = userRank.getExperience(); return future.thenCompose(leaderBoardEntryModel ->
self.renderAndSendRank(commandContext, rankModel, leaderBoardEntryModel)
).thenApply(result -> CommandResult.fromSuccess());
}
@Transactional
public CompletableFuture<Void> renderAndSendRank(CommandContext commandContext, RankModel rankModel, LeaderBoardEntryModel leaderBoardEntryModel) {
rankModel.setRankUser(leaderBoardEntryModel);
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(commandContext.getAuthor());
AUserExperience experienceObj = userExperienceManagementService.findUserInServer(aUserInAServer);
log.info("Rendering rank for user {} in server {}.", commandContext.getAuthor().getId(), commandContext.getGuild().getId()); log.info("Rendering rank for user {} in server {}.", commandContext.getAuthor().getId(), commandContext.getGuild().getId());
rankModel.setExperienceToNextLevel(experienceLevelService.calculateExperienceToNextLevel(experienceObj.getCurrentLevel().getLevel(), experienceObj.getExperience())); rankModel.setExperienceToNextLevel(experienceLevelService.calculateExperienceToNextLevel(experienceObj.getCurrentLevel().getLevel(), experienceObj.getExperience()));
MessageToSend messageToSend = templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel); MessageToSend messageToSend = templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel())) return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()));
.thenApply(aVoid -> CommandResult.fromSuccess());
} }
@Override @Override

View File

@@ -12,6 +12,7 @@ import org.springframework.stereotype.Component;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
/** /**
* Converter used to convert from {@link LeaderBoard} to a list of {@link LeaderBoardEntryModel} * Converter used to convert from {@link LeaderBoard} to a list of {@link LeaderBoardEntryModel}
@@ -30,11 +31,11 @@ public class LeaderBoardModelConverter {
* @return The list of {@link LeaderBoardEntryModel} which contain the fully fledged information provided to the * @return The list of {@link LeaderBoardEntryModel} which contain the fully fledged information provided to the
* leader board template * leader board template
*/ */
public List<LeaderBoardEntryModel> fromLeaderBoard(LeaderBoard leaderBoard) { public List<CompletableFuture<LeaderBoardEntryModel>> fromLeaderBoard(LeaderBoard leaderBoard) {
List<LeaderBoardEntryModel> models = new ArrayList<>(); List<CompletableFuture<LeaderBoardEntryModel>> models = new ArrayList<>();
log.trace("Converting {} entries to a list of leaderbord entries.", leaderBoard.getEntries().size()); log.trace("Converting {} entries to a list of leaderbord entries.", leaderBoard.getEntries().size());
leaderBoard.getEntries().forEach(leaderBoardEntry -> { leaderBoard.getEntries().forEach(leaderBoardEntry -> {
LeaderBoardEntryModel entry = fromLeaderBoardEntry(leaderBoardEntry); CompletableFuture<LeaderBoardEntryModel> entry = fromLeaderBoardEntry(leaderBoardEntry);
models.add(entry); models.add(entry);
}); });
return models; return models;
@@ -47,14 +48,15 @@ public class LeaderBoardModelConverter {
* @return The {@link LeaderBoardEntryModel} accompanied with the {@link Member} reference, might be null, if the * @return The {@link LeaderBoardEntryModel} accompanied with the {@link Member} reference, might be null, if the
* user left the guild * user left the guild
*/ */
public LeaderBoardEntryModel fromLeaderBoardEntry(LeaderBoardEntry leaderBoardEntry) { public CompletableFuture<LeaderBoardEntryModel> fromLeaderBoardEntry(LeaderBoardEntry leaderBoardEntry) {
AUserInAServer entryUser = leaderBoardEntry.getExperience().getUser(); AUserInAServer entryUser = leaderBoardEntry.getExperience().getUser();
Member entryMember = botService.getMemberInServer(entryUser.getServerReference().getId(), entryUser.getUserReference().getId()); return botService.getMemberInServerAsync(entryUser.getServerReference().getId(), entryUser.getUserReference().getId()).thenApply(member ->
return LeaderBoardEntryModel LeaderBoardEntryModel
.builder() .builder()
.experience(leaderBoardEntry.getExperience()) .experience(leaderBoardEntry.getExperience())
.member(entryMember).rank(leaderBoardEntry.getRank()) .member(member).rank(leaderBoardEntry.getRank())
.rank(leaderBoardEntry.getRank()) .rank(leaderBoardEntry.getRank())
.build(); .build()
);
} }
} }

View File

@@ -186,7 +186,7 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
AUserInAServer userInAServer = userInServerManagementService.loadUser(userInAServerId); AUserInAServer userInAServer = userInServerManagementService.loadUser(userInAServerId);
gainedExperience = (int) Math.floor(gainedExperience * multiplier); gainedExperience = (int) Math.floor(gainedExperience * multiplier);
Member member = botService.getMemberInServer(userInAServer); Member member = botService.getMemberInServer(userInAServer);
if(!roleService.hasAnyOfTheRoles(member, disabledRoles)) { if(member != null && !roleService.hasAnyOfTheRoles(member, disabledRoles)) {
log.trace("Handling {}. The user gains {}", userInAServer.getUserReference().getId(), gainedExperience); log.trace("Handling {}. The user gains {}", userInAServer.getUserReference().getId(), gainedExperience);
Optional<AUserExperience> aUserExperienceOptional = userExperienceManagementService.findByUserInServerIdOptional(userInAServerId); Optional<AUserExperience> aUserExperienceOptional = userExperienceManagementService.findByUserInServerIdOptional(userInAServerId);
if(aUserExperienceOptional.isPresent()) { if(aUserExperienceOptional.isPresent()) {
@@ -230,7 +230,7 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
futures.add(resultFuture); futures.add(resultFuture);
} }
} else { } else {
log.trace("User {} has a role which makes the user unable to gain experience.", userInAServer.getUserInServerId()); log.trace("User {} has a role which makes the user unable to gain experience or the member could not be found in the server.", userInAServer.getUserInServerId());
} }
}); });
}); });
@@ -321,43 +321,62 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
.userInServerId(user.getUserInServerId()) .userInServerId(user.getUserInServerId())
.experienceRoleId(null) .experienceRoleId(null)
.build(); .build();
if(!botService.isUserInGuild(userExperience.getUser())) {
log.trace("User {} is not in server {} anymore. No role calculation done.", userExperience.getUser().getUserInServerId(), userExperience.getUser().getServerReference().getId());
return CompletableFuture.completedFuture(returnNullRole.apply(null));
}
Long userInServerId = user.getUserInServerId(); Long userInServerId = user.getUserInServerId();
log.trace("Updating experience role for user {} in server {}", user.getUserReference().getId(), user.getServerReference().getId()); Long serverId = user.getServerReference().getId();
log.trace("Updating experience role for user {} in server {}", user.getUserReference().getId(), serverId);
AExperienceRole role = experienceRoleService.calculateRole(roles, currentLevel); AExperienceRole role = experienceRoleService.calculateRole(roles, currentLevel);
Member member = botService.getMemberInServer(user.getServerReference(), user.getUserReference());
boolean currentlyHasNoExperienceRole = userExperience.getCurrentExperienceRole() == null; boolean currentlyHasNoExperienceRole = userExperience.getCurrentExperienceRole() == null;
// if calculation results in no role, do not add a role
if(role == null) { if(role == null) {
log.trace("User {} in server {} does not have an experience role, according to new calculation.", log.trace("User {} in server {} does not have an experience role, according to new calculation.",
user.getUserReference().getId(), user.getServerReference().getId()); user.getUserReference().getId(), serverId);
// if the user has a experience role currently, remove it
if(!currentlyHasNoExperienceRole){ if(!currentlyHasNoExperienceRole){
return roleService.removeRoleFromUserFuture(user, userExperience.getCurrentExperienceRole().getRole()) return roleService.removeRoleFromUserFuture(user, userExperience.getCurrentExperienceRole().getRole())
.thenApply(returnNullRole); .thenApply(returnNullRole);
} }
return CompletableFuture.completedFuture(returnNullRole.apply(null)); return CompletableFuture.completedFuture(returnNullRole.apply(null));
} }
boolean userHasRoleAlready = roleService.memberHasRole(member, role.getRole());
Long experienceRoleId = role.getId(); Long experienceRoleId = role.getId();
Function<Void, RoleCalculationResult> fullResult = aVoid -> RoleCalculationResult Long roleId = role.getRole().getId();
.builder() // if the new role is already the one configured in the database
.experienceRoleId(experienceRoleId) Long userId = user.getUserReference().getId();
.userInServerId(userInServerId) Long oldUserExperienceRoleId = currentlyHasNoExperienceRole ? 0L : userExperience.getCurrentExperienceRole().getRole().getId();
.build(); return botService.getMemberInServerAsync(user).thenCompose(member -> {
if(!userHasRoleAlready && (currentlyHasNoExperienceRole || !role.getRole().getId().equals(userExperience.getCurrentExperienceRole().getRole().getId()))) { boolean userHasRoleAlready = roleService.memberHasRole(member, roleId);
log.info("User {} in server {} gets a new role {} because of experience.", user.getUserReference().getId(), user.getServerReference().getId(), role.getRole().getId()); boolean userHasOldRole = false;
CompletableFuture<Void> removalFuture; boolean rolesChanged = true;
if(!currentlyHasNoExperienceRole && botService.isUserInGuild(userExperience.getUser())) { if(!currentlyHasNoExperienceRole) {
removalFuture = roleService.removeRoleFromUserFuture(user, userExperience.getCurrentExperienceRole().getRole()); userHasOldRole = roleService.memberHasRole(member, oldUserExperienceRoleId);
} else { rolesChanged = !roleId.equals(oldUserExperienceRoleId);
removalFuture = CompletableFuture.completedFuture(null);
} }
CompletableFuture<Void> addRoleFuture = roleService.addRoleToUserFuture(user, role.getRole()); Function<Void, RoleCalculationResult> fullResult = aVoid -> RoleCalculationResult
return CompletableFuture.allOf(removalFuture, addRoleFuture).thenApply(fullResult); .builder()
} .experienceRoleId(experienceRoleId)
return CompletableFuture.completedFuture(fullResult.apply(null)); .userInServerId(userInServerId)
.build();
// if the roles changed or
// the user does not have the new target role already
// the user still has the old role
if((!userHasRoleAlready || userHasOldRole)) {
log.info("User {} in server {} gets a new role {} because of experience.", userId, serverId, roleId);
CompletableFuture<Void> removalFuture;
if(userHasOldRole && rolesChanged) {
removalFuture = roleService.removeRoleFromMemberAsync(member, oldUserExperienceRoleId);
} else {
removalFuture = CompletableFuture.completedFuture(null);
}
CompletableFuture<Void> addRoleFuture;
if(!userHasRoleAlready) {
addRoleFuture = roleService.addRoleToMemberFuture(member, roleId);
} else {
addRoleFuture = CompletableFuture.completedFuture(null);
}
return CompletableFuture.allOf(removalFuture, addRoleFuture).thenApply(fullResult);
}
// we are turning the full calculation result regardless
return CompletableFuture.completedFuture(fullResult.apply(null));
});
} }
/** /**
@@ -405,7 +424,7 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
if(result != null) { if(result != null) {
AUserInAServer user = userInServerManagementService.loadUser(result.getUserInServerId()); AUserInAServer user = userInServerManagementService.loadUser(result.getUserInServerId());
AUserExperience userExperience = userExperienceManagementService.findUserInServer(user); AUserExperience userExperience = userExperienceManagementService.findUserInServer(user);
log.trace("Updating experience role for {} in server {} to {}", user.getUserInServerId(), user.getServerReference(), result.getExperienceRoleId()); log.trace("Updating experience role for {} in server {} to {}", user.getUserInServerId(), user.getServerReference().getId(), result.getExperienceRoleId());
if(result.getExperienceRoleId() != null) { if(result.getExperienceRoleId() != null) {
log.trace("User experience {} gets new experience role with id {}.", userExperience.getId(), result.getExperienceRoleId()); log.trace("User experience {} gets new experience role with id {}.", userExperience.getId(), result.getExperienceRoleId());
AExperienceRole role = experienceRoleManagementService.getExperienceRoleById(result.getExperienceRoleId()); AExperienceRole role = experienceRoleManagementService.getExperienceRoleById(result.getExperienceRoleId());

View File

@@ -83,7 +83,7 @@ public class ExperienceRoleManagementServiceBean implements ExperienceRoleManage
public AExperienceRole setLevelToRole(AExperienceLevel level, ARole role) { public AExperienceRole setLevelToRole(AExperienceLevel level, ARole role) {
Optional<AExperienceRole> byRoleServerAndRoleOptional = getRoleInServerOptional(role); Optional<AExperienceRole> byRoleServerAndRoleOptional = getRoleInServerOptional(role);
AExperienceRole experienceRole; AExperienceRole experienceRole;
log.info("Setting role {} in server {} to level {}.", role.getId(), role.getServer().getId(), level); log.info("Setting role {} in server {} to level {}.", role.getId(), role.getServer().getId(), level.getLevel());
if(byRoleServerAndRoleOptional.isPresent()) { if(byRoleServerAndRoleOptional.isPresent()) {
log.trace("Role already existed. Updating."); log.trace("Role already existed. Updating.");
experienceRole = byRoleServerAndRoleOptional.get(); experienceRole = byRoleServerAndRoleOptional.get();

View File

@@ -66,7 +66,7 @@ public class LeaderBoardCommandTest {
LeaderBoardEntry executingUserRank = LeaderBoardEntry.builder().build(); LeaderBoardEntry executingUserRank = LeaderBoardEntry.builder().build();
when(userExperienceService.getRankOfUserInServer(context.getUserInitiatedContext().getAUserInAServer())).thenReturn(executingUserRank); when(userExperienceService.getRankOfUserInServer(context.getUserInitiatedContext().getAUserInAServer())).thenReturn(executingUserRank);
LeaderBoardEntryModel leaderBoardEntryModel = LeaderBoardEntryModel.builder().build(); LeaderBoardEntryModel leaderBoardEntryModel = LeaderBoardEntryModel.builder().build();
when(converter.fromLeaderBoardEntry(executingUserRank)).thenReturn(leaderBoardEntryModel); when(converter.fromLeaderBoardEntry(executingUserRank)).thenReturn(CompletableFuture.completedFuture(leaderBoardEntryModel));
MessageToSend messageToSend = MessageToSend.builder().build(); MessageToSend messageToSend = MessageToSend.builder().build();
when(templateService.renderEmbedTemplate(eq(LeaderBoardCommand.LEADER_BOARD_POST_EMBED_TEMPLATE), any(LeaderBoardModel.class))).thenReturn(messageToSend); when(templateService.renderEmbedTemplate(eq(LeaderBoardCommand.LEADER_BOARD_POST_EMBED_TEMPLATE), any(LeaderBoardModel.class))).thenReturn(messageToSend);
CompletableFuture<CommandResult> result = testUnit.executeAsync(context); CompletableFuture<CommandResult> result = testUnit.executeAsync(context);

View File

@@ -2,7 +2,9 @@ package dev.sheldan.abstracto.experience.commands;
import dev.sheldan.abstracto.core.command.execution.CommandContext; import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.experience.converter.LeaderBoardModelConverter; import dev.sheldan.abstracto.experience.converter.LeaderBoardModelConverter;
import dev.sheldan.abstracto.experience.models.LeaderBoardEntry; import dev.sheldan.abstracto.experience.models.LeaderBoardEntry;
import dev.sheldan.abstracto.experience.models.database.AExperienceLevel; import dev.sheldan.abstracto.experience.models.database.AExperienceLevel;
@@ -11,6 +13,7 @@ import dev.sheldan.abstracto.experience.models.templates.LeaderBoardEntryModel;
import dev.sheldan.abstracto.experience.models.templates.RankModel; import dev.sheldan.abstracto.experience.models.templates.RankModel;
import dev.sheldan.abstracto.experience.service.AUserExperienceService; import dev.sheldan.abstracto.experience.service.AUserExperienceService;
import dev.sheldan.abstracto.experience.service.ExperienceLevelService; import dev.sheldan.abstracto.experience.service.ExperienceLevelService;
import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService;
import dev.sheldan.abstracto.templating.model.MessageToSend; import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService; import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.test.command.CommandConfigValidator; import dev.sheldan.abstracto.test.command.CommandConfigValidator;
@@ -22,8 +25,10 @@ import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.experience.commands.Rank.RANK_POST_EMBED_TEMPLATE;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
@@ -47,23 +52,49 @@ public class RankTest {
@Mock @Mock
private ChannelService channelService; private ChannelService channelService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private Rank self;
@Mock
private AUserInAServer aUserInAServer;
@Mock
private UserExperienceManagementService userExperienceManagementService;
@Test @Test
public void testRankExecution() { public void testRankExecution() {
CommandContext context = CommandTestUtilities.getNoParameters();
LeaderBoardEntry leaderBoardEntry = Mockito.mock(LeaderBoardEntry.class);
when(userExperienceService.getRankOfUserInServer(context.getUserInitiatedContext().getAUserInAServer())).thenReturn(leaderBoardEntry);
LeaderBoardEntryModel leaderBoardEntryModel = Mockito.mock(LeaderBoardEntryModel.class);
when(converter.fromLeaderBoardEntry(leaderBoardEntry)).thenReturn(CompletableFuture.completedFuture(leaderBoardEntryModel));
when(self.renderAndSendRank(eq(context), any(RankModel.class), eq(leaderBoardEntryModel))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
}
@Test
public void testRenderAndSend() {
int currentLevelValue = 50; int currentLevelValue = 50;
long currentExperience = 50L; long currentExperience = 50L;
AExperienceLevel currentLevel = Mockito.mock(AExperienceLevel.class);
when(currentLevel.getLevel()).thenReturn(currentLevelValue);
AUserExperience aUserExperience = Mockito.mock(AUserExperience.class);
when(aUserExperience.getCurrentLevel()).thenReturn(currentLevel);
when(aUserExperience.getExperience()).thenReturn(currentExperience);
CommandContext context = CommandTestUtilities.getNoParameters(); CommandContext context = CommandTestUtilities.getNoParameters();
AExperienceLevel currentLevel = AExperienceLevel.builder().level(currentLevelValue).build(); RankModel rankModel = Mockito.mock(RankModel.class);
AUserExperience aUserExperience = AUserExperience.builder().experience(currentExperience).currentLevel(currentLevel).build(); LeaderBoardEntryModel leaderBoardEntryModel = Mockito.mock(LeaderBoardEntryModel.class);
LeaderBoardEntry leaderBoardEntry = LeaderBoardEntry.builder().experience(aUserExperience).build(); when(userInServerManagementService.loadUser(context.getAuthor())).thenReturn(aUserInAServer);
when(userExperienceService.getRankOfUserInServer(context.getUserInitiatedContext().getAUserInAServer())).thenReturn(leaderBoardEntry); when(userExperienceManagementService.findUserInServer(aUserInAServer)).thenReturn(aUserExperience);
LeaderBoardEntryModel leaderBoardEntryModel = LeaderBoardEntryModel.builder().build();
when(converter.fromLeaderBoardEntry(leaderBoardEntry)).thenReturn(leaderBoardEntryModel);
when(experienceLevelService.calculateExperienceToNextLevel(currentLevelValue, currentExperience)).thenReturn(140L); when(experienceLevelService.calculateExperienceToNextLevel(currentLevelValue, currentExperience)).thenReturn(140L);
MessageToSend messageToSend = MessageToSend.builder().build(); MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
when(templateService.renderEmbedTemplate(eq(Rank.RANK_POST_EMBED_TEMPLATE), any(RankModel.class))).thenReturn(messageToSend); when(templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel)).thenReturn(messageToSend);
CompletableFuture<CommandResult> result = testUnit.executeAsync(context); when(channelService.sendMessageToSendToChannel(messageToSend, context.getChannel())).thenReturn(Arrays.asList(CompletableFuture.completedFuture(null)));
verify(channelService, Mockito.times(1)).sendMessageToSendToChannel(messageToSend, context.getChannel()); testUnit.renderAndSendRank(context, rankModel, leaderBoardEntryModel).join();
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
} }
@Test @Test

View File

@@ -20,6 +20,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -43,11 +44,14 @@ public class LeaderBoardModelConverterTest extends ExperienceRelatedTest {
LeaderBoardEntry entry2 = getEntry(server, secondExperience, secondRank); LeaderBoardEntry entry2 = getEntry(server, secondExperience, secondRank);
List<LeaderBoardEntry> entries = Arrays.asList(entry, entry2); List<LeaderBoardEntry> entries = Arrays.asList(entry, entry2);
LeaderBoard leaderBoard = LeaderBoard.builder().entries(entries).build(); LeaderBoard leaderBoard = LeaderBoard.builder().entries(entries).build();
List<LeaderBoardEntryModel> leaderBoardEntryModels = testUnit.fromLeaderBoard(leaderBoard); Member member = Mockito.mock(Member.class);
LeaderBoardEntryModel firstEntry = leaderBoardEntryModels.get(0); when(botService.getMemberInServerAsync(server.getId(), entry.getExperience().getUser().getUserReference().getId())).thenReturn(CompletableFuture.completedFuture(member));
when(botService.getMemberInServerAsync(server.getId(), entry2.getExperience().getUser().getUserReference().getId())).thenReturn(CompletableFuture.completedFuture(member));
List<CompletableFuture<LeaderBoardEntryModel>> leaderBoardEntryModels = testUnit.fromLeaderBoard(leaderBoard);
LeaderBoardEntryModel firstEntry = leaderBoardEntryModels.get(0).join();
Assert.assertEquals(firstRank, firstEntry.getRank().intValue()); Assert.assertEquals(firstRank, firstEntry.getRank().intValue());
Assert.assertEquals(firstExperience, firstEntry.getExperience().getExperience().longValue()); Assert.assertEquals(firstExperience, firstEntry.getExperience().getExperience().longValue());
LeaderBoardEntryModel secondEntry = leaderBoardEntryModels.get(1); LeaderBoardEntryModel secondEntry = leaderBoardEntryModels.get(1).join();
Assert.assertEquals(secondRank, secondEntry.getRank().intValue()); Assert.assertEquals(secondRank, secondEntry.getRank().intValue());
Assert.assertEquals(secondExperience, secondEntry.getExperience().getExperience().longValue()); Assert.assertEquals(secondExperience, secondEntry.getExperience().getExperience().longValue());
Assert.assertEquals(entries.size(), leaderBoardEntryModels.size()); Assert.assertEquals(entries.size(), leaderBoardEntryModels.size());
@@ -63,11 +67,12 @@ public class LeaderBoardModelConverterTest extends ExperienceRelatedTest {
User user = Mockito.mock(User.class); User user = Mockito.mock(User.class);
when(user.getIdLong()).thenReturn(userId); when(user.getIdLong()).thenReturn(userId);
when(member.getUser()).thenReturn(user); when(member.getUser()).thenReturn(user);
when(botService.getMemberInServer(server.getId(), experience.getUser().getUserReference().getId())).thenReturn(member); when(botService.getMemberInServerAsync(server.getId(), experience.getUser().getUserReference().getId())).thenReturn(CompletableFuture.completedFuture(member));
LeaderBoardEntryModel leaderBoardEntryModel = testUnit.fromLeaderBoardEntry(entry); CompletableFuture<LeaderBoardEntryModel> leaderBoardEntryModel = testUnit.fromLeaderBoardEntry(entry);
Assert.assertEquals(1, leaderBoardEntryModel.getRank().intValue()); LeaderBoardEntryModel entryModel = leaderBoardEntryModel.join();
Assert.assertEquals(experience.getUser().getUserReference().getId(), leaderBoardEntryModel.getExperience().getUser().getUserReference().getId()); Assert.assertEquals(1, entryModel.getRank().intValue());
Assert.assertEquals(experience.getUser().getUserReference().getId().longValue(), leaderBoardEntryModel.getMember().getUser().getIdLong()); Assert.assertEquals(experience.getUser().getUserReference().getId(), entryModel.getExperience().getUser().getUserReference().getId());
Assert.assertEquals(experience.getUser().getUserReference().getId().longValue(), entryModel.getMember().getUser().getIdLong());
} }
private LeaderBoardEntry getEntry(AServer server, Integer experienceParameter, Integer rank) { private LeaderBoardEntry getEntry(AServer server, Integer experienceParameter, Integer rank) {

View File

@@ -36,7 +36,6 @@ import static org.mockito.Mockito.*;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest { public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
@@ -92,12 +91,21 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
@Mock @Mock
private AUserExperience userExperience; private AUserExperience userExperience;
@Mock
private AUserExperience userExperience2;
@Mock @Mock
private AUserInAServer aUserInAServer; private AUserInAServer aUserInAServer;
@Mock
private AUserInAServer aUserInAServer2;
@Mock @Mock
private AUser user; private AUser user;
@Mock
private AUser user2;
@Mock @Mock
private ServerExperience serverExperience; private ServerExperience serverExperience;
@@ -108,7 +116,10 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
private AServer server; private AServer server;
@Mock @Mock
private Member member; private Member firstMember;
@Mock
private Member secondMember;
private static final Long USER_IN_SERVER_ID = 4L; private static final Long USER_IN_SERVER_ID = 4L;
private static final Long USER_ID = 8L; private static final Long USER_ID = 8L;
@@ -166,7 +177,6 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
AExperienceRole previousExperienceRole = experienceRoles.get(1); AExperienceRole previousExperienceRole = experienceRoles.get(1);
setupSimpleSingleUserTest(levels, experienceRoles); setupSimpleSingleUserTest(levels, experienceRoles);
when(userExperience.getCurrentExperienceRole()).thenReturn(previousExperienceRole); when(userExperience.getCurrentExperienceRole()).thenReturn(previousExperienceRole);
when(botService.isUserInGuild(aUserInAServer)).thenReturn(true);
when(roleService.removeRoleFromUserFuture(userExperience.getUser(), userExperience.getCurrentExperienceRole().getRole())).thenReturn(CompletableFuture.completedFuture(null)); when(roleService.removeRoleFromUserFuture(userExperience.getUser(), userExperience.getCurrentExperienceRole().getRole())).thenReturn(CompletableFuture.completedFuture(null));
when(userExperienceManagementService.findByUserInServerIdOptional(USER_IN_SERVER_ID)).thenReturn(Optional.of(userExperience)); when(userExperienceManagementService.findByUserInServerIdOptional(USER_IN_SERVER_ID)).thenReturn(Optional.of(userExperience));
testUnit.handleExperienceGain(servers).join(); testUnit.handleExperienceGain(servers).join();
@@ -184,9 +194,8 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
when(userExperience.getExperience()).thenReturn(199L); when(userExperience.getExperience()).thenReturn(199L);
AExperienceRole newRole = experienceRoles.get(2); AExperienceRole newRole = experienceRoles.get(2);
when(experienceRoleService.calculateRole(eq(experienceRoles), any())).thenReturn(newRole); when(experienceRoleService.calculateRole(eq(experienceRoles), any())).thenReturn(newRole);
AExperienceRole newAwardedRole = experienceRoles.get(3); AExperienceRole newAwardedRole = experienceRoles.get(3);
when(roleService.memberHasRole(member, newRole.getRole())).thenReturn(true); when(roleService.memberHasRole(firstMember, newRole.getRole().getId())).thenReturn(true);
when(userExperienceManagementService.findByUserInServerIdOptional(USER_IN_SERVER_ID)).thenReturn(Optional.of(userExperience)); when(userExperienceManagementService.findByUserInServerIdOptional(USER_IN_SERVER_ID)).thenReturn(Optional.of(userExperience));
testUnit.handleExperienceGain(servers).join(); testUnit.handleExperienceGain(servers).join();
verify(roleService, times(0)).addRoleToUser(aUserInAServer, newAwardedRole.getRole()); verify(roleService, times(0)).addRoleToUser(aUserInAServer, newAwardedRole.getRole());
@@ -200,15 +209,14 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
List<AExperienceRole> experienceRoles = getExperienceRoles(levels); List<AExperienceRole> experienceRoles = getExperienceRoles(levels);
AExperienceRole previousExperienceRole = experienceRoles.get(1); AExperienceRole previousExperienceRole = experienceRoles.get(1);
setupSimpleSingleUserTest(levels, experienceRoles); setupSimpleSingleUserTest(levels, experienceRoles);
when(botService.getMemberInServer(server, aUserInAServer.getUserReference())).thenReturn(member);
AExperienceRole newRole = experienceRoles.get(1); AExperienceRole newRole = experienceRoles.get(1);
when(userExperience.getCurrentExperienceRole()).thenReturn(newRole);
when(experienceRoleService.calculateRole(eq(experienceRoles), any())).thenReturn(newRole); when(experienceRoleService.calculateRole(eq(experienceRoles), any())).thenReturn(newRole);
AExperienceRole newAwardedRole = experienceRoles.get(3); when(roleService.memberHasRole(firstMember, newRole.getRole().getId())).thenReturn(true);
when(roleService.memberHasRole(member, newRole.getRole())).thenReturn(true);
when(userExperienceManagementService.findByUserInServerIdOptional(USER_IN_SERVER_ID)).thenReturn(Optional.of(userExperience)); when(userExperienceManagementService.findByUserInServerIdOptional(USER_IN_SERVER_ID)).thenReturn(Optional.of(userExperience));
testUnit.handleExperienceGain(servers).join(); testUnit.handleExperienceGain(servers).join();
verify(roleService, times(0)).addRoleToUser(aUserInAServer, newAwardedRole.getRole()); verify(roleService, times(0)).addRoleToUser(aUserInAServer, newRole.getRole());
verify(roleService, times(0)).removeRoleFromUser(aUserInAServer, previousExperienceRole.getRole()); verify(roleService, times(0)).removeRoleFromUser(aUserInAServer, previousExperienceRole.getRole());
} }
@@ -220,16 +228,15 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
AExperienceRole previousExperienceRole = null; AExperienceRole previousExperienceRole = null;
when(userExperience.getCurrentExperienceRole()).thenReturn(previousExperienceRole); when(userExperience.getCurrentExperienceRole()).thenReturn(previousExperienceRole);
setupSimpleSingleUserTest(levels, experienceRoles); setupSimpleSingleUserTest(levels, experienceRoles);
when(botService.getMemberInServer(server, aUserInAServer.getUserReference())).thenReturn(member); when(botService.getMemberInServer(aUserInAServer)).thenReturn(firstMember);
AExperienceRole newRole = experienceRoles.get(1); AExperienceRole newRole = experienceRoles.get(1);
when(experienceRoleService.calculateRole(eq(experienceRoles), any())).thenReturn(newRole); when(experienceRoleService.calculateRole(eq(experienceRoles), any())).thenReturn(newRole);
when(roleService.memberHasRole(member, newRole.getRole())).thenReturn(false); when(roleService.memberHasRole(firstMember, newRole.getRole().getId())).thenReturn(false);
when(roleService.addRoleToUserFuture(userExperience.getUser(), newRole.getRole())).thenReturn(CompletableFuture.completedFuture(null)); when(roleService.addRoleToMemberFuture(firstMember, newRole.getRole().getId())).thenReturn(CompletableFuture.completedFuture(null));
when(userExperienceManagementService.findByUserInServerIdOptional(USER_IN_SERVER_ID)).thenReturn(Optional.of(userExperience)); when(userExperienceManagementService.findByUserInServerIdOptional(USER_IN_SERVER_ID)).thenReturn(Optional.of(userExperience));
when(roleService.addRoleToMemberFuture(firstMember, newRole.getRole().getId())).thenReturn(CompletableFuture.completedFuture(null));
testUnit.handleExperienceGain(servers).join(); testUnit.handleExperienceGain(servers).join();
verify(roleService, times(0)).removeRoleFromUserFuture(eq(aUserInAServer), any());
verify(roleService, times(1)).addRoleToUserFuture(eq(aUserInAServer), any());
} }
@Test @Test
@@ -242,7 +249,7 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
when(userExperience.getExperience()).thenReturn(50L); when(userExperience.getExperience()).thenReturn(50L);
when(serverExperience.getUserInServerIds()).thenReturn(Arrays.asList(USER_IN_SERVER_ID)); when(serverExperience.getUserInServerIds()).thenReturn(Arrays.asList(USER_IN_SERVER_ID));
when(userExperience.getCurrentExperienceRole()).thenReturn(previousExperienceRole); when(userExperience.getCurrentExperienceRole()).thenReturn(previousExperienceRole);
when(botService.getMemberInServer(server, aUserInAServer.getUserReference())).thenReturn(member); when(botService.getMemberInServer(aUserInAServer)).thenReturn(firstMember);
AExperienceRole newRole = null; AExperienceRole newRole = null;
when(experienceRoleService.calculateRole(eq(experienceRoles), any())).thenReturn(newRole); when(experienceRoleService.calculateRole(eq(experienceRoles), any())).thenReturn(newRole);
@@ -262,7 +269,7 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
when(serverExperience.getUserInServerIds()).thenReturn(Arrays.asList(USER_IN_SERVER_ID)); when(serverExperience.getUserInServerIds()).thenReturn(Arrays.asList(USER_IN_SERVER_ID));
AExperienceRole previousExperienceRole = experienceRoles.get(0); AExperienceRole previousExperienceRole = experienceRoles.get(0);
when(userExperience.getCurrentExperienceRole()).thenReturn(previousExperienceRole); when(userExperience.getCurrentExperienceRole()).thenReturn(previousExperienceRole);
when(botService.getMemberInServer(server, aUserInAServer.getUserReference())).thenReturn(member); when(botService.getMemberInServer(aUserInAServer)).thenReturn(firstMember);
AExperienceRole newRole = null; AExperienceRole newRole = null;
when(experienceRoleService.calculateRole(eq(experienceRoles), any())).thenReturn(newRole); when(experienceRoleService.calculateRole(eq(experienceRoles), any())).thenReturn(newRole);
@@ -284,10 +291,9 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
when(experienceRoleManagementService.getExperienceRolesForServer(server)).thenReturn(experienceRoles); when(experienceRoleManagementService.getExperienceRolesForServer(server)).thenReturn(experienceRoles);
when(disabledExpRoleManagementService.getDisabledRolesForServer(server)).thenReturn(new ArrayList<>()); when(disabledExpRoleManagementService.getDisabledRolesForServer(server)).thenReturn(new ArrayList<>());
when(userInServerManagementService.loadUser(USER_IN_SERVER_ID)).thenReturn(aUserInAServer); when(userInServerManagementService.loadUser(USER_IN_SERVER_ID)).thenReturn(aUserInAServer);
when(user.getId()).thenReturn(USER_ID);
when(aUserInAServer.getUserReference()).thenReturn(user);
when(aUserInAServer.getUserReference()).thenReturn(user); when(aUserInAServer.getUserReference()).thenReturn(user);
when(serverExperience.getUserInServerIds()).thenReturn(Arrays.asList(USER_IN_SERVER_ID)); when(serverExperience.getUserInServerIds()).thenReturn(Arrays.asList(USER_IN_SERVER_ID));
when(botService.getMemberInServer(aUserInAServer)).thenReturn(firstMember);
when(userExperience.getExperienceGainDisabled()).thenReturn(true); when(userExperience.getExperienceGainDisabled()).thenReturn(true);
when(userExperienceManagementService.findByUserInServerIdOptional(USER_IN_SERVER_ID)).thenReturn(Optional.of(userExperience)); when(userExperienceManagementService.findByUserInServerIdOptional(USER_IN_SERVER_ID)).thenReturn(Optional.of(userExperience));
@@ -323,6 +329,8 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
when(aUserInAServer.getUserReference()).thenReturn(user); when(aUserInAServer.getUserReference()).thenReturn(user);
when(aUserInAServer.getServerReference()).thenReturn(server); when(aUserInAServer.getServerReference()).thenReturn(server);
when(server.getId()).thenReturn(8L); when(server.getId()).thenReturn(8L);
when(botService.getMemberInServer(aUserInAServer)).thenReturn(firstMember);
when(roleService.hasAnyOfTheRoles(eq(firstMember), anyList())).thenReturn(false);
when(user.getId()).thenReturn(7L); when(user.getId()).thenReturn(7L);
CompletableFuture<Void> future = testUnit.handleExperienceGain(servers); CompletableFuture<Void> future = testUnit.handleExperienceGain(servers);
future.join(); future.join();
@@ -331,35 +339,94 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
} }
@Test @Test
public void testSyncNoRoleUserGettingRole() { public void testSyncNoRoleUserGettingRole2() {
List<AExperienceRole> usedExperienceRoles = getExperienceRoles(getLevelConfiguration()); AExperienceLevel level0 = Mockito.mock(AExperienceLevel.class);
AExperienceRole previousRole = null; AExperienceLevel level1 = Mockito.mock(AExperienceLevel.class);
List<AExperienceRole> usedExperienceRoles = getExperienceRoles(Arrays.asList(level0, level1));
userExperience.setCurrentExperienceRole(null);
AExperienceRole afterRole = usedExperienceRoles.get(0); AExperienceRole afterRole = usedExperienceRoles.get(0);
executeSyncSingleUserTest(server, usedExperienceRoles, previousRole, afterRole); when(aUserInAServer.getUserReference()).thenReturn(user);
when(aUserInAServer.getServerReference()).thenReturn(server);
when(userExperience.getUser()).thenReturn(aUserInAServer);
when(botService.getMemberInServerAsync(aUserInAServer)).thenReturn(CompletableFuture.completedFuture(firstMember));
when(experienceRoleManagementService.getExperienceRolesForServer(server)).thenReturn(usedExperienceRoles);
when(experienceRoleService.calculateRole(usedExperienceRoles, userExperience.getLevelOrDefault())).thenReturn(afterRole);
when(botService.getMemberInServerAsync(userExperience.getUser())).thenReturn(CompletableFuture.completedFuture(firstMember));
when(roleService.addRoleToMemberFuture(firstMember, afterRole.getRole().getId())).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<RoleCalculationResult> calculationFuture = testUnit.syncForSingleUser(userExperience);
RoleCalculationResult result = calculationFuture.join();
Assert.assertEquals(afterRole.getRole().getId(), result.getExperienceRoleId());
} }
@Test @Test
public void testSyncUserLosingRole() { public void testSyncUserLosingRole() {
List<AExperienceRole> usedExperienceRoles = getExperienceRoles(getLevelConfiguration()); AExperienceLevel level0 = Mockito.mock(AExperienceLevel.class);
AExperienceRole previousRole = usedExperienceRoles.get(0); AExperienceLevel level1 = Mockito.mock(AExperienceLevel.class);
List<AExperienceRole> usedExperienceRoles = getExperienceRoles(Arrays.asList(level0, level1));
AExperienceRole beforeRole = usedExperienceRoles.get(0);
when(userExperience.getCurrentExperienceRole()).thenReturn(beforeRole);
AExperienceRole afterRole = null; AExperienceRole afterRole = null;
executeSyncSingleUserTest(server, usedExperienceRoles, previousRole, afterRole); when(aUserInAServer.getUserReference()).thenReturn(user);
when(aUserInAServer.getServerReference()).thenReturn(server);
when(userExperience.getUser()).thenReturn(aUserInAServer);
when(experienceRoleManagementService.getExperienceRolesForServer(server)).thenReturn(usedExperienceRoles);
when(experienceRoleService.calculateRole(usedExperienceRoles, userExperience.getLevelOrDefault())).thenReturn(afterRole);
when(roleService.removeRoleFromUserFuture(aUserInAServer, beforeRole.getRole())).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<RoleCalculationResult> calculationFuture = testUnit.syncForSingleUser(userExperience);
RoleCalculationResult result = calculationFuture.join();
Assert.assertNull(result.getExperienceRoleId());
} }
@Test @Test
public void testSyncUserKeepRole() { public void testSyncUserKeepingRole() {
List<AExperienceRole> usedExperienceRoles = getExperienceRoles(getLevelConfiguration()); AExperienceLevel level0 = Mockito.mock(AExperienceLevel.class);
AExperienceRole previousRole = usedExperienceRoles.get(0); AExperienceLevel level1 = Mockito.mock(AExperienceLevel.class);
List<AExperienceRole> usedExperienceRoles = getExperienceRoles(Arrays.asList(level0, level1));
AExperienceRole beforeRole = usedExperienceRoles.get(0);
when(userExperience.getCurrentExperienceRole()).thenReturn(beforeRole);
AExperienceRole afterRole = usedExperienceRoles.get(0); AExperienceRole afterRole = usedExperienceRoles.get(0);
executeSyncSingleUserTest(server, usedExperienceRoles, previousRole, afterRole); when(aUserInAServer.getUserReference()).thenReturn(user);
when(aUserInAServer.getServerReference()).thenReturn(server);
when(userExperience.getUser()).thenReturn(aUserInAServer);
when(botService.getMemberInServerAsync(aUserInAServer)).thenReturn(CompletableFuture.completedFuture(firstMember));
when(experienceRoleManagementService.getExperienceRolesForServer(server)).thenReturn(usedExperienceRoles);
when(experienceRoleService.calculateRole(usedExperienceRoles, userExperience.getLevelOrDefault())).thenReturn(afterRole);
when(botService.getMemberInServerAsync(userExperience.getUser())).thenReturn(CompletableFuture.completedFuture(firstMember));
when(roleService.memberHasRole(firstMember, afterRole.getRole().getId())).thenReturn(true);
CompletableFuture<RoleCalculationResult> calculationFuture = testUnit.syncForSingleUser(userExperience);
RoleCalculationResult result = calculationFuture.join();
Assert.assertEquals(afterRole.getRole().getId(), result.getExperienceRoleId());
} }
@Test @Test
public void testSyncUserChangingRole() { public void testSyncUserChangingRole() {
List<AExperienceRole> usedExperienceRoles = getExperienceRoles(getLevelConfiguration()); AExperienceLevel level0 = Mockito.mock(AExperienceLevel.class);
AExperienceRole previousRole = usedExperienceRoles.get(0); AExperienceLevel level1 = Mockito.mock(AExperienceLevel.class);
List<AExperienceRole> usedExperienceRoles = getExperienceRoles(Arrays.asList(level0, level1));
AExperienceRole beforeRole = usedExperienceRoles.get(0);
when(userExperience.getCurrentExperienceRole()).thenReturn(beforeRole);
AExperienceRole afterRole = usedExperienceRoles.get(1); AExperienceRole afterRole = usedExperienceRoles.get(1);
executeSyncSingleUserTest(server, usedExperienceRoles, previousRole, afterRole); when(aUserInAServer.getUserReference()).thenReturn(user);
when(aUserInAServer.getServerReference()).thenReturn(server);
when(userExperience.getUser()).thenReturn(aUserInAServer);
when(roleService.memberHasRole(firstMember, beforeRole.getRole().getId())).thenReturn(true);
when(botService.getMemberInServerAsync(aUserInAServer)).thenReturn(CompletableFuture.completedFuture(firstMember));
when(experienceRoleManagementService.getExperienceRolesForServer(server)).thenReturn(usedExperienceRoles);
when(experienceRoleService.calculateRole(usedExperienceRoles, userExperience.getLevelOrDefault())).thenReturn(afterRole);
when(botService.getMemberInServerAsync(userExperience.getUser())).thenReturn(CompletableFuture.completedFuture(firstMember));
when(roleService.memberHasRole(firstMember, afterRole.getRole().getId())).thenReturn(false);
when(roleService.removeRoleFromMemberAsync(firstMember, beforeRole.getRole().getId())).thenReturn(CompletableFuture.completedFuture(null));
when(roleService.addRoleToMemberFuture(firstMember, afterRole.getRole().getId())).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<RoleCalculationResult> calculationFuture = testUnit.syncForSingleUser(userExperience);
RoleCalculationResult result = calculationFuture.join();
Assert.assertEquals(afterRole.getRole().getId(), result.getExperienceRoleId());
} }
@Test @Test
@@ -415,39 +482,51 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
@Test @Test
public void testSyncAllUsers() { public void testSyncAllUsers() {
List<AExperienceRole> usedExperienceRoles = getExperienceRoles(getLevelConfiguration()); AExperienceLevel level0 = Mockito.mock(AExperienceLevel.class);
AExperienceRole firstPreviousRole = null; AExperienceLevel level1 = Mockito.mock(AExperienceLevel.class);
AExperienceRole firstAfterRole = usedExperienceRoles.get(0); List<AExperienceRole> usedExperienceRoles = getExperienceRoles(Arrays.asList(level0, level1));
AExperienceRole secondPreviousRole = null; AExperienceRole beforeRole = usedExperienceRoles.get(0);
AExperienceRole secondAfterRole = null;
AExperienceLevel level0 = AExperienceLevel.builder().level(0).build(); when(userExperience.getCurrentExperienceRole()).thenReturn(beforeRole);
AExperienceLevel level1 = AExperienceLevel.builder().level(1).build(); AExperienceRole afterRole = usedExperienceRoles.get(1);
AUserExperience experience = AUserExperience.builder().experience(40L).user(MockUtils.getUserObject(3L, server)).currentLevel(level1).build(); when(aUserInAServer.getUserReference()).thenReturn(user);
AUserExperience experience2 = AUserExperience.builder().experience(201L).user(MockUtils.getUserObject(4L, server)).currentLevel(level0).build(); when(user.getId()).thenReturn(8L);
List<AUserExperience> experiences = Arrays.asList(experience, experience2); when(aUserInAServer.getServerReference()).thenReturn(server);
when(userExperience.getUser()).thenReturn(aUserInAServer);
when(userExperience.getCurrentLevel()).thenReturn(level0);
when(aUserInAServer2.getUserReference()).thenReturn(user2);
when(user2.getId()).thenReturn(9L);
when(aUserInAServer2.getServerReference()).thenReturn(server);
when(userExperience2.getUser()).thenReturn(aUserInAServer2);
when(userExperience2.getCurrentLevel()).thenReturn(level0);
when(userExperience2.getCurrentExperienceRole()).thenReturn(beforeRole);
when(botService.getMemberInServerAsync(aUserInAServer)).thenReturn(CompletableFuture.completedFuture(firstMember));
when(botService.getMemberInServerAsync(aUserInAServer2)).thenReturn(CompletableFuture.completedFuture(secondMember));
when(server.getId()).thenReturn(15L);
List<AUserInAServer> users = experiences.stream().map(AUserExperience::getUser).collect(Collectors.toList());
Member firstMember = Mockito.mock(Member.class);
when(botService.getMemberInServer(server, users.get(0).getUserReference())).thenReturn(firstMember);
Member secondMember = Mockito.mock(Member.class);
when(botService.getMemberInServer(server, users.get(1).getUserReference())).thenReturn(secondMember);
experience.setCurrentExperienceRole(firstPreviousRole);
experience2.setCurrentExperienceRole(secondPreviousRole);
when(userExperienceManagementService.loadAllUsers(server)).thenReturn(experiences);
when(experienceRoleManagementService.getExperienceRolesForServer(server)).thenReturn(usedExperienceRoles); when(experienceRoleManagementService.getExperienceRolesForServer(server)).thenReturn(usedExperienceRoles);
when(experienceRoleService.calculateRole(usedExperienceRoles, experience.getLevelOrDefault())).thenReturn(firstAfterRole); when(experienceRoleService.calculateRole(usedExperienceRoles, userExperience.getLevelOrDefault())).thenReturn(afterRole);
when(experienceRoleService.calculateRole(usedExperienceRoles, experience2.getLevelOrDefault())).thenReturn(secondAfterRole); when(experienceRoleService.calculateRole(usedExperienceRoles, userExperience2.getLevelOrDefault())).thenReturn(afterRole);
when(botService.getMemberInServer(server, experience.getUser().getUserReference())).thenReturn(firstMember); when(botService.getMemberInServerAsync(userExperience.getUser())).thenReturn(CompletableFuture.completedFuture(firstMember));
when(botService.getMemberInServer(server, experience2.getUser().getUserReference())).thenReturn(secondMember); when(botService.getMemberInServerAsync(userExperience2.getUser())).thenReturn(CompletableFuture.completedFuture(secondMember));
when(botService.isUserInGuild(experience.getUser())).thenReturn(true); when(roleService.memberHasRole(firstMember, afterRole.getRole().getId())).thenReturn(false);
when(botService.isUserInGuild(experience2.getUser())).thenReturn(true); when(roleService.memberHasRole(firstMember, beforeRole.getRole().getId())).thenReturn(true);
when(roleService.addRoleToUserFuture(experience.getUser(), firstAfterRole.getRole())).thenReturn(CompletableFuture.completedFuture(null)); when(roleService.memberHasRole(secondMember, afterRole.getRole().getId())).thenReturn(true);
when(roleService.memberHasRole(firstMember, firstAfterRole.getRole())).thenReturn(false); when(roleService.removeRoleFromMemberAsync(firstMember, beforeRole.getRole().getId())).thenReturn(CompletableFuture.completedFuture(null));
List<CompletableFuture<RoleCalculationResult>> futures = testUnit.syncUserRoles(server); when(roleService.addRoleToMemberFuture(firstMember, afterRole.getRole().getId())).thenReturn(CompletableFuture.completedFuture(null));
RoleCalculationResult result = futures.get(0).join(); List<AUserExperience> experiences = Arrays.asList(userExperience, userExperience2);
RoleCalculationResult result2 = futures.get(1).join(); when(userExperienceManagementService.loadAllUsers(server)).thenReturn(experiences);
Assert.assertEquals(firstAfterRole.getRole().getId(), result.getExperienceRoleId()); List<CompletableFuture<RoleCalculationResult>> calculationFutures = testUnit.syncUserRoles(server);
Assert.assertNull(result2.getExperienceRoleId()); verify(roleService, times(0)).removeRoleFromMemberAsync(secondMember, beforeRole.getRole().getId());
verify(roleService, times(0)).addRoleToMemberFuture(secondMember, afterRole.getRole().getId());
RoleCalculationResult firstResult = calculationFutures.get(0).join();
Assert.assertEquals(afterRole.getRole().getId(), firstResult.getExperienceRoleId());
RoleCalculationResult secondResult = calculationFutures.get(1).join();
Assert.assertEquals(afterRole.getRole().getId(), secondResult.getExperienceRoleId());
} }
@Test @Test
@@ -534,12 +613,12 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
when(userInServerManagementService.loadUser(USER_IN_SERVER_ID)).thenReturn(aUserInAServer); when(userInServerManagementService.loadUser(USER_IN_SERVER_ID)).thenReturn(aUserInAServer);
when(user.getId()).thenReturn(USER_ID); when(user.getId()).thenReturn(USER_ID);
when(aUserInAServer.getUserReference()).thenReturn(user); when(aUserInAServer.getUserReference()).thenReturn(user);
when(botService.getMemberInServer(server, aUserInAServer.getUserReference())).thenReturn(member); when(botService.getMemberInServer(aUserInAServer)).thenReturn(firstMember);
when(botService.getMemberInServerAsync(aUserInAServer)).thenReturn(CompletableFuture.completedFuture(firstMember));
when(userExperience.getExperience()).thenReturn(500L); when(userExperience.getExperience()).thenReturn(500L);
when(userExperience.getUser()).thenReturn(aUserInAServer); when(userExperience.getUser()).thenReturn(aUserInAServer);
when(aUserInAServer.getUserReference()).thenReturn(user); when(aUserInAServer.getUserReference()).thenReturn(user);
when(aUserInAServer.getServerReference()).thenReturn(server); when(aUserInAServer.getServerReference()).thenReturn(server);
when(botService.isUserInGuild(userExperience.getUser())).thenReturn(true);
when(server.getId()).thenReturn(SERVER_ID); when(server.getId()).thenReturn(SERVER_ID);
when(user.getId()).thenReturn(USER_ID); when(user.getId()).thenReturn(USER_ID);
} }
@@ -556,37 +635,6 @@ public class AUserExperienceServiceBeanTest extends ExperienceRelatedTest {
verify(messageService, times(messageCount)).updateStatusMessage(channel, messageId, statusMessage); verify(messageService, times(messageCount)).updateStatusMessage(channel, messageId, statusMessage);
} }
private void executeSyncSingleUserTest(AServer server, List<AExperienceRole> usedExperienceRoles, AExperienceRole previousRole, AExperienceRole afterRole) {
AExperienceLevel level0 = AExperienceLevel.builder().level(0).build();
AUserExperience experience = AUserExperience.builder().experience(40L).user(MockUtils.getUserObject(3L, server)).currentLevel(level0).build();
List<AUserExperience> experiences = Arrays.asList(experience);
List<AUserInAServer> users = experiences.stream().map(AUserExperience::getUser).collect(Collectors.toList());
when(botService.getMemberInServer(server, users.get(0).getUserReference())).thenReturn(member);
experience.setCurrentExperienceRole(previousRole);
when(experienceRoleManagementService.getExperienceRolesForServer(server)).thenReturn(usedExperienceRoles);
when(experienceRoleService.calculateRole(usedExperienceRoles, experience.getLevelOrDefault())).thenReturn(afterRole);
when(botService.getMemberInServer(server, experience.getUser().getUserReference())).thenReturn(member);
users.forEach(innerUser -> when(botService.isUserInGuild(innerUser)).thenReturn(true));
if(afterRole != null) {
when(roleService.addRoleToUserFuture(experience.getUser(), afterRole.getRole())).thenReturn(CompletableFuture.completedFuture(null));
}
if(previousRole != null) {
when(roleService.removeRoleFromUserFuture(experience.getUser(), previousRole.getRole())).thenReturn(CompletableFuture.completedFuture(null));
}
if(afterRole != null && previousRole != null) {
boolean sameRole = previousRole.getRole().getId().equals(afterRole.getRole().getId());
when(roleService.memberHasRole(member, afterRole.getRole())).thenReturn(sameRole);
}
CompletableFuture<RoleCalculationResult> calculationFuture = testUnit.syncForSingleUser(experience);
RoleCalculationResult result = calculationFuture.join();
if(afterRole != null) {
Assert.assertEquals(afterRole.getRole().getId(), result.getExperienceRoleId());
} else {
Assert.assertNull(result.getExperienceRoleId());
}
}
private void executeLeaderBoardTest(AServer server, Integer page) { private void executeLeaderBoardTest(AServer server, Integer page) {
int pageSize = 10; int pageSize = 10;
List<AUserExperience> experiences = getUserExperiences(pageSize, server); List<AUserExperience> experiences = getUserExperiences(pageSize, server);

View File

@@ -13,6 +13,7 @@ public class ExperienceGainResult {
private CompletableFuture<RoleCalculationResult> calculationResult; private CompletableFuture<RoleCalculationResult> calculationResult;
private Long userInServerId; private Long userInServerId;
private Long newExperience; private Long newExperience;
private Long serverId;
private Integer newLevel; private Integer newLevel;
private Long newMessageCount; private Long newMessageCount;
@Builder.Default @Builder.Default

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.experience.models.templates; package dev.sheldan.abstracto.experience.models.templates;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext; import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
@@ -15,7 +15,7 @@ import java.util.List;
@Getter @Getter
@Setter @Setter
@SuperBuilder @SuperBuilder
public class LeaderBoardModel extends UserInitiatedServerContext { public class LeaderBoardModel extends SlimUserInitiatedServerContext {
/** /**
* List of {@link LeaderBoardEntryModel} containing the information about the users from the requested page. * List of {@link LeaderBoardEntryModel} containing the information about the users from the requested page.
*/ */

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.experience.models.templates; package dev.sheldan.abstracto.experience.models.templates;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext; import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
@@ -13,7 +13,7 @@ import lombok.experimental.SuperBuilder;
@Getter @Getter
@Setter @Setter
@SuperBuilder @SuperBuilder
public class RankModel extends UserInitiatedServerContext { public class RankModel extends SlimUserInitiatedServerContext {
/** /**
* The {@link LeaderBoardEntryModel} containing the experience information about the user executing the rank * The {@link LeaderBoardEntryModel} containing the experience information about the user executing the rank
* command. * command.

View File

@@ -31,7 +31,7 @@ public class DeleteWarning extends AbstractConditionableCommand {
public CommandResult execute(CommandContext commandContext) { public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext); checkParameters(commandContext);
Long warnId = (Long) commandContext.getParameters().getParameters().get(0); Long warnId = (Long) commandContext.getParameters().getParameters().get(0);
Optional<Warning> optional = warnManagementService.findById(warnId, commandContext.getGuild().getIdLong()); Optional<Warning> optional = warnManagementService.findByIdOptional(warnId, commandContext.getGuild().getIdLong());
optional.ifPresent(warning -> warnManagementService.deleteWarning(warning)); optional.ifPresent(warning -> warnManagementService.deleteWarning(warning));
return CommandResult.fromSuccess(); return CommandResult.fromSuccess();
} }

View File

@@ -18,6 +18,7 @@ import dev.sheldan.abstracto.moderation.config.features.ModerationFeatures;
import dev.sheldan.abstracto.moderation.converter.UserNotesConverter; import dev.sheldan.abstracto.moderation.converter.UserNotesConverter;
import dev.sheldan.abstracto.moderation.models.database.UserNote; import dev.sheldan.abstracto.moderation.models.database.UserNote;
import dev.sheldan.abstracto.moderation.models.template.commands.ListNotesModel; import dev.sheldan.abstracto.moderation.models.template.commands.ListNotesModel;
import dev.sheldan.abstracto.moderation.models.template.commands.NoteEntryModel;
import dev.sheldan.abstracto.moderation.service.management.UserNoteManagementService; import dev.sheldan.abstracto.moderation.service.management.UserNoteManagementService;
import dev.sheldan.abstracto.templating.service.TemplateService; import dev.sheldan.abstracto.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
@@ -66,9 +67,12 @@ public class UserNotes extends AbstractConditionableCommand {
} else { } else {
userNotes = userNoteManagementService.loadNotesForServer(commandContext.getUserInitiatedContext().getServer()); userNotes = userNoteManagementService.loadNotesForServer(commandContext.getUserInitiatedContext().getServer());
} }
model.setUserNotes(userNotesConverter.fromNotes(userNotes)); CompletableFuture<List<NoteEntryModel>> listCompletableFuture = userNotesConverter.fromNotes(userNotes);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(USER_NOTES_RESPONSE_TEMPLATE, model, commandContext.getChannel())) return listCompletableFuture.thenCompose(noteEntryModels -> {
.thenApply(aVoid -> CommandResult.fromSuccess()); model.setUserNotes(noteEntryModels);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(USER_NOTES_RESPONSE_TEMPLATE, model, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess());
});
} }
@Override @Override

View File

@@ -22,9 +22,11 @@ import dev.sheldan.abstracto.moderation.service.management.WarnManagementService
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component @Component
public class Warnings extends AbstractConditionableCommand { public class Warnings extends AbstractConditionableCommand {
@@ -45,8 +47,11 @@ public class Warnings extends AbstractConditionableCommand {
@Autowired @Autowired
private EventWaiter eventWaiter; private EventWaiter eventWaiter;
@Autowired
private Warnings self;
@Override @Override
public CommandResult execute(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext); checkParameters(commandContext);
List<Warning> warnsToDisplay; List<Warning> warnsToDisplay;
if(!commandContext.getParameters().getParameters().isEmpty()) { if(!commandContext.getParameters().getParameters().isEmpty()) {
@@ -55,14 +60,21 @@ public class Warnings extends AbstractConditionableCommand {
} else { } else {
warnsToDisplay = warnManagementService.getAllWarningsOfServer(commandContext.getUserInitiatedContext().getServer()); warnsToDisplay = warnManagementService.getAllWarningsOfServer(commandContext.getUserInitiatedContext().getServer());
} }
List<WarnEntry> warnEntries = warnEntryConverter.fromWarnings(warnsToDisplay); return warnEntryConverter.fromWarnings(warnsToDisplay).thenApply(warnEntries -> {
self.renderWarnings(commandContext, warnEntries);
return CommandResult.fromSuccess();
});
WarningsModel model = (WarningsModel) ContextConverter.fromCommandContext(commandContext, WarningsModel.class);
}
@Transactional
public void renderWarnings(CommandContext commandContext, List<WarnEntry> warnEntries) {
WarningsModel model = (WarningsModel) ContextConverter.slimFromCommandContext(commandContext, WarningsModel.class);
model.setWarnings(warnEntries); model.setWarnings(warnEntries);
Paginator paginator = paginatorService.createPaginatorFromTemplate(WARNINGS_RESPONSE_TEMPLATE, model, eventWaiter); Paginator paginator = paginatorService.createPaginatorFromTemplate(WARNINGS_RESPONSE_TEMPLATE, model, eventWaiter);
paginator.display(commandContext.getChannel()); paginator.display(commandContext.getChannel());
return CommandResult.fromSuccess();
} }
@Override @Override
@@ -74,6 +86,7 @@ public class Warnings extends AbstractConditionableCommand {
.name("warnings") .name("warnings")
.module(ModerationModule.MODERATION) .module(ModerationModule.MODERATION)
.templated(true) .templated(true)
.async(true)
.causesReaction(true) .causesReaction(true)
.supportsEmbedException(true) .supportsEmbedException(true)
.parameters(parameters) .parameters(parameters)

View File

@@ -1,15 +1,23 @@
package dev.sheldan.abstracto.moderation.converter; package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.FullUserInServer; import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.models.database.UserNote; import dev.sheldan.abstracto.moderation.models.database.UserNote;
import dev.sheldan.abstracto.moderation.models.template.commands.NoteEntryModel; import dev.sheldan.abstracto.moderation.models.template.commands.NoteEntryModel;
import dev.sheldan.abstracto.moderation.service.management.UserNoteManagementService;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component @Component
public class UserNotesConverter { public class UserNotesConverter {
@@ -19,18 +27,45 @@ public class UserNotesConverter {
@Autowired @Autowired
private BotService botService; private BotService botService;
public List<NoteEntryModel> fromNotes(List<UserNote> userNotes){ @Autowired
List<NoteEntryModel> entryModels = new ArrayList<>(); private UserNotesConverter self;
@Autowired
private UserNoteManagementService userNoteManagementService;
public CompletableFuture<List<NoteEntryModel>> fromNotes(List<UserNote> userNotes){
List<CompletableFuture<Member>> memberFutures = new ArrayList<>();
HashMap<ServerSpecificId, CompletableFuture<Member>> noteMemberMap = new HashMap<>();
userNotes.forEach(userNote -> { userNotes.forEach(userNote -> {
CompletableFuture<Member> memberFuture = botService.getMemberInServerAsync(userNote.getUser());
memberFutures.add(memberFuture);
noteMemberMap.put(userNote.getUserNoteId(), memberFuture);
});
if(userNotes.isEmpty()) {
memberFutures.add(CompletableFuture.completedFuture(null));
}
return FutureUtils.toSingleFutureGeneric(memberFutures).thenApply(aVoid ->
self.loadFullNotes(noteMemberMap)
);
}
@Transactional
public List<NoteEntryModel> loadFullNotes(Map<ServerSpecificId, CompletableFuture<Member>> futureHashMap) {
List<NoteEntryModel> entryModels = new ArrayList<>();
futureHashMap.keySet().forEach(serverSpecificId -> {
Member member = futureHashMap.get(serverSpecificId).join();
UserNote note = userNoteManagementService.loadNote(serverSpecificId.getId(), serverSpecificId.getServerId());
FullUserInServer fullUser = FullUserInServer FullUserInServer fullUser = FullUserInServer
.builder() .builder()
.member(botService.getMemberInServer(userNote.getUser())) .member(member)
.aUserInAServer(userNote.getUser()) .aUserInAServer(note.getUser())
.build(); .build();
NoteEntryModel entryModel = NoteEntryModel NoteEntryModel entryModel = NoteEntryModel
.builder() .builder()
.note(note)
.fullUser(fullUser) .fullUser(fullUser)
.note(userNote)
.build(); .build();
entryModels.add(entryModel); entryModels.add(entryModel);
}); });

View File

@@ -1,14 +1,24 @@
package dev.sheldan.abstracto.moderation.converter; package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.FullUserInServer; import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.FutureMemberPair;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.models.database.Warning; import dev.sheldan.abstracto.moderation.models.database.Warning;
import dev.sheldan.abstracto.moderation.models.template.commands.WarnEntry; import dev.sheldan.abstracto.moderation.models.template.commands.WarnEntry;
import dev.sheldan.abstracto.moderation.service.management.WarnManagementService;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component @Component
public class WarnEntryConverter { public class WarnEntryConverter {
@@ -16,25 +26,56 @@ public class WarnEntryConverter {
@Autowired @Autowired
private BotService botService; private BotService botService;
public List<WarnEntry> fromWarnings(List<Warning> warnings) { @Autowired
List<WarnEntry> entries = new ArrayList<>(); private UserInServerManagementService userInServerManagementService;
@Autowired
private WarnManagementService warnManagementService;
@Autowired
private WarnEntryConverter self;
public CompletableFuture<List<WarnEntry>> fromWarnings(List<Warning> warnings) {
Map<ServerSpecificId, FutureMemberPair> loadedWarnings = new HashMap<>();
List<CompletableFuture<Member>> allFutures = new ArrayList<>();
// TODO maybe optimize to not need to look into the cache twice
warnings.forEach(warning -> { warnings.forEach(warning -> {
CompletableFuture<Member> warningMemberFuture = botService.getMemberInServerAsync(warning.getWarningUser());
CompletableFuture<Member> warnedMemberFuture = botService.getMemberInServerAsync(warning.getWarnedUser());
FutureMemberPair futurePair = FutureMemberPair.builder().firstMember(warningMemberFuture).secondMember(warnedMemberFuture).build();
loadedWarnings.put(warning.getWarnId(), futurePair);
allFutures.add(warningMemberFuture);
allFutures.add(warnedMemberFuture);
});
return FutureUtils.toSingleFutureGeneric(allFutures).thenApply(aVoid ->
self.loadFullWarnEntries(loadedWarnings)
);
}
@Transactional
public List<WarnEntry> loadFullWarnEntries(Map<ServerSpecificId, FutureMemberPair> loadedWarnInfo) {
List<WarnEntry> entries = new ArrayList<>();
loadedWarnInfo.keySet().forEach(warning -> {
FutureMemberPair memberPair = loadedWarnInfo.get(warning);
Member warnedMember = memberPair.getSecondMember().join();
FullUserInServer warnedUser = FullUserInServer FullUserInServer warnedUser = FullUserInServer
.builder() .builder()
.member(botService.getMemberInServer(warning.getWarnedUser())) .member(warnedMember)
.aUserInAServer(warning.getWarnedUser()) .aUserInAServer(userInServerManagementService.loadUser(warnedMember))
.build(); .build();
Member warningMember = memberPair.getFirstMember().join();
FullUserInServer warningUser = FullUserInServer FullUserInServer warningUser = FullUserInServer
.builder() .builder()
.member(botService.getMemberInServer(warning.getWarningUser())) .member(warningMember)
.aUserInAServer(warning.getWarningUser()) .aUserInAServer(userInServerManagementService.loadUser(warningMember))
.build(); .build();
WarnEntry entry = WarnEntry WarnEntry entry = WarnEntry
.builder() .builder()
.warnedUser(warnedUser) .warnedUser(warnedUser)
.warningUser(warningUser) .warningUser(warningUser)
.warning(warning) .warning(warnManagementService.findById(warning.getId(), warning.getServerId()))
.build(); .build();
entries.add(entry); entries.add(entry);
}); });

View File

@@ -20,8 +20,11 @@ public interface UserNoteRepository extends JpaRepository<UserNote, Long> {
List<UserNote> findByUser_ServerReference(AServer server); List<UserNote> findByUser_ServerReference(AServer server);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true")) @QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
boolean existsByUserNoteId_IdAndUserNoteId_ServerId(@NonNull Long aLong, Long serverId); boolean existsByUserNoteId_IdAndUserNoteId_ServerId(@NonNull Long userNoteId, Long serverId);
void deleteByUserNoteId_IdAndUserNoteId_ServerId(@NonNull Long aLong, Long serverId); void deleteByUserNoteId_IdAndUserNoteId_ServerId(@NonNull Long aLong, Long serverId);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
UserNote findByUserNoteId_IdAndUserNoteId_ServerId(Long userNoteId, Long serverId);
} }

View File

@@ -52,7 +52,7 @@ public class BanServiceBean implements BanService {
} }
private CompletableFuture<Void> banUser(Long guildId, Long userId, String reason) { private CompletableFuture<Void> banUser(Long guildId, Long userId, String reason) {
Optional<Guild> guildByIdOptional = botService.getGuildById(guildId); Optional<Guild> guildByIdOptional = botService.getGuildByIdOptional(guildId);
if(guildByIdOptional.isPresent()) { if(guildByIdOptional.isPresent()) {
return banUser(guildByIdOptional.get(), userId, reason); return banUser(guildByIdOptional.get(), userId, reason);
} else { } else {

View File

@@ -39,6 +39,7 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -110,22 +111,6 @@ public class MuteServiceBean implements MuteService {
return muteUserInServer(mutedUser, mutingUser, reason, unMuteDate, message); return muteUserInServer(mutedUser, mutingUser, reason, unMuteDate, message);
} }
@Override
public CompletableFuture<Void> muteAUserInAServer(AUserInAServer userBeingMuted, AUserInAServer userMuting, String reason, Instant unMuteDate, ServerChannelMessage message) {
FullUserInServer mutedUser = FullUserInServer
.builder()
.aUserInAServer(userBeingMuted)
.member(botService.getMemberInServer(userBeingMuted))
.build();
FullUserInServer mutingUser = FullUserInServer
.builder()
.aUserInAServer(userMuting)
.member(botService.getMemberInServer(userMuting))
.build();
return muteUserInServer(mutedUser, mutingUser, reason, unMuteDate, message);
}
@Override @Override
public CompletableFuture<Void> muteUserInServer(FullUserInServer userBeingMuted, FullUserInServer userMuting, String reason, Instant unMuteDate, ServerChannelMessage message) { public CompletableFuture<Void> muteUserInServer(FullUserInServer userBeingMuted, FullUserInServer userMuting, String reason, Instant unMuteDate, ServerChannelMessage message) {
AServer serverBeingMutedIn = userBeingMuted.getAUserInAServer().getServerReference(); AServer serverBeingMutedIn = userBeingMuted.getAUserInAServer().getServerReference();
@@ -251,7 +236,7 @@ public class MuteServiceBean implements MuteService {
return FutureUtils.toSingleFutureGeneric(completableFutures); return FutureUtils.toSingleFutureGeneric(completableFutures);
} }
private CompletableFuture<Void> sendUnMuteLog(UnMuteLog muteLogModel) { private CompletableFuture<Void> sendUnMuteLogMessage(UnMuteLog muteLogModel) {
log.trace("Sending unMute log to the mute posttarget."); log.trace("Sending unMute log to the mute posttarget.");
MessageToSend message = templateService.renderEmbedTemplate(UN_MUTE_LOG_TEMPLATE, muteLogModel); MessageToSend message = templateService.renderEmbedTemplate(UN_MUTE_LOG_TEMPLATE, muteLogModel);
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, muteLogModel.getServer().getId()); List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, muteLogModel.getServer().getId());
@@ -279,7 +264,7 @@ public class MuteServiceBean implements MuteService {
log.info("UnMuting {} in server {}", mute.getMutedUser().getUserReference().getId(), mutingServer.getId()); log.info("UnMuting {} in server {}", mute.getMutedUser().getUserReference().getId(), mutingServer.getId());
MuteRole muteRole = muteRoleManagementService.retrieveMuteRoleForServer(mutingServer); MuteRole muteRole = muteRoleManagementService.retrieveMuteRoleForServer(mutingServer);
log.trace("Using the mute role {} mapping to role {}", muteRole.getId(), muteRole.getRole().getId()); log.trace("Using the mute role {} mapping to role {}", muteRole.getId(), muteRole.getRole().getId());
Guild guild = botService.getGuildByIdNullable(mutingServer.getId()); Guild guild = botService.getGuildById(mutingServer.getId());
CompletableFuture<Void> roleRemovalFuture; CompletableFuture<Void> roleRemovalFuture;
if(botService.isUserInGuild(guild, mute.getMutedUser())) { if(botService.isUserInGuild(guild, mute.getMutedUser())) {
roleRemovalFuture = roleService.removeRoleFromUserFuture(mute.getMutedUser(), muteRole.getRole()); roleRemovalFuture = roleService.removeRoleFromUserFuture(mute.getMutedUser(), muteRole.getRole());
@@ -287,24 +272,40 @@ public class MuteServiceBean implements MuteService {
roleRemovalFuture = CompletableFuture.completedFuture(null); roleRemovalFuture = CompletableFuture.completedFuture(null);
log.info("User to unMute left the guild."); log.info("User to unMute left the guild.");
} }
Long serverId = mutingServer.getId(); CompletableFuture<Member> mutingMemberFuture = botService.getMemberInServerAsync(mute.getMutingUser());
CompletableFuture<Member> mutedMemberFuture = botService.getMemberInServerAsync(mute.getMutedUser());
CompletableFuture<Void> finalFuture = new CompletableFuture<>();
CompletableFuture.allOf(mutingMemberFuture, mutedMemberFuture, roleRemovalFuture, mutingMemberFuture, mutedMemberFuture).handle((aVoid, throwable) -> {
self.sendUnmuteLog(muteId, guild, mutingMemberFuture, mutedMemberFuture).thenAccept(aVoid1 ->
finalFuture.complete(null)
);
return null;
});
return finalFuture;
}
@Transactional
public CompletionStage<Void> sendUnmuteLog(Long muteId, Guild guild, CompletableFuture<Member> mutingMemberFuture, CompletableFuture<Member> mutedMemberFuture) {
Mute mute = muteManagementService.findMute(muteId, guild.getIdLong());
AServer mutingServer = serverManagementService.loadServer(guild.getIdLong());
UnMuteLog unMuteLog = UnMuteLog UnMuteLog unMuteLog = UnMuteLog
.builder() .builder()
.mute(mute) .mute(mute)
.mutingUser(botService.getMemberInServer(mute.getMutingUser())) .mutingUser(mutingMemberFuture.join())
.unMutedUser(botService.getMemberInServer(mute.getMutedUser())) .unMutedUser(mutedMemberFuture.join())
.guild(guild) .guild(guild)
.server(mutingServer) .server(mutingServer)
.build(); .build();
CompletableFuture<Void> notificationFuture = sendUnMuteLog(unMuteLog); CompletableFuture<Void> notificationFuture = sendUnMuteLogMessage(unMuteLog);
return CompletableFuture.allOf(roleRemovalFuture, notificationFuture).thenAccept(aVoid -> return CompletableFuture.allOf(notificationFuture).thenAccept(aVoid ->
self.endMuteInDatabase(muteId, serverId) self.endMuteInDatabase(muteId, guild.getIdLong())
); );
} }
@Transactional @Transactional
public void endMuteInDatabase(Long muteId, Long serverId) { public void endMuteInDatabase(Long muteId, Long serverId) {
Optional<Mute> muteOptional = muteManagementService.findMute(muteId, serverId); Optional<Mute> muteOptional = muteManagementService.findMuteOptional(muteId, serverId);
muteOptional.ifPresent(mute -> muteOptional.ifPresent(mute ->
completelyUnMuteUser(mute.getMutedUser()) completelyUnMuteUser(mute.getMutedUser())
); );
@@ -315,7 +316,7 @@ public class MuteServiceBean implements MuteService {
@Transactional @Transactional
public CompletableFuture<Void> endMute(Long muteId, Long serverId) { public CompletableFuture<Void> endMute(Long muteId, Long serverId) {
log.info("UnMuting the mute {} in server {}", muteId, serverId); log.info("UnMuting the mute {} in server {}", muteId, serverId);
Optional<Mute> muteOptional = muteManagementService.findMute(muteId, serverId); Optional<Mute> muteOptional = muteManagementService.findMuteOptional(muteId, serverId);
if(muteOptional.isPresent()) { if(muteOptional.isPresent()) {
return endMute(muteOptional.get()); return endMute(muteOptional.get());
} else { } else {

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.moderation.service; package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.models.FutureMemberPair;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.*; import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.ServerManagementService; import dev.sheldan.abstracto.core.service.management.ServerManagementService;
@@ -19,7 +21,6 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.templating.service.TemplateService; import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -27,9 +28,12 @@ import org.springframework.transaction.annotation.Transactional;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
@Slf4j @Slf4j
@Component @Component
@@ -120,7 +124,6 @@ public class WarnServiceBean implements WarnService {
); );
} }
@NotNull
private List<Long> flattenWarnings(List<Warning> warningsToDecay) { private List<Long> flattenWarnings(List<Warning> warningsToDecay) {
List<Long> warningIds = new ArrayList<>(); List<Long> warningIds = new ArrayList<>();
warningsToDecay.forEach(warning -> warningsToDecay.forEach(warning ->
@@ -134,7 +137,7 @@ public class WarnServiceBean implements WarnService {
Instant now = Instant.now(); Instant now = Instant.now();
log.info("Decaying {} warnings.", warningIds.size()); log.info("Decaying {} warnings.", warningIds.size());
warningIds.forEach(warningId -> { warningIds.forEach(warningId -> {
Optional<Warning> warningOptional = warnManagementService.findById(warningId, serverId); Optional<Warning> warningOptional = warnManagementService.findByIdOptional(warningId, serverId);
warningOptional.ifPresent(warning -> warningOptional.ifPresent(warning ->
decayWarning(warning, now) decayWarning(warning, now)
); );
@@ -152,20 +155,47 @@ public class WarnServiceBean implements WarnService {
} }
private CompletableFuture<Void> logDecayedWarnings(AServer server, List<Warning> warningsToDecay) { private CompletableFuture<Void> logDecayedWarnings(AServer server, List<Warning> warningsToDecay) {
log.trace("Logging decaying {} warnings in server {}.", warningsToDecay.size(), server.getId()); log.trace("Loading members decaying {} warnings in server {}.", warningsToDecay.size(), server.getId());
List<WarnDecayWarning> warnDecayWarnings = new ArrayList<>(); HashMap<ServerSpecificId, FutureMemberPair> warningMembers = new HashMap<>();
List<CompletableFuture<Member>> allFutures = new ArrayList<>();
Long serverId = server.getId();
warningsToDecay.forEach(warning -> { warningsToDecay.forEach(warning -> {
CompletableFuture<Member> warningMember = botService.getMemberInServerAsync(warning.getWarningUser());
CompletableFuture<Member> warnedMember = botService.getMemberInServerAsync(warning.getWarnedUser());
FutureMemberPair futurePair = FutureMemberPair.builder().firstMember(warningMember).secondMember(warnedMember).build();
warningMembers.put(warning.getWarnId(), futurePair);
});
CompletableFuture<Void> sendingFuture = new CompletableFuture<>();
FutureUtils.toSingleFutureGeneric(allFutures).handle((aVoid, throwable) -> {
self.renderAndSendWarnDecayLogs(serverId, warningMembers).thenAccept(aVoid1 ->
sendingFuture.complete(null)
);
return null;
});
return sendingFuture;
}
@Transactional
public CompletionStage<Void> renderAndSendWarnDecayLogs(Long serverId, Map<ServerSpecificId, FutureMemberPair> warningMembers) {
AServer server = serverManagementService.loadServer(serverId);
List<WarnDecayWarning> warnDecayWarnings = new ArrayList<>();
warningMembers.keySet().forEach(serverSpecificId -> {
Warning warning = warnManagementService.findById(serverSpecificId.getId(), serverSpecificId.getServerId());
FutureMemberPair pair = warningMembers.get(serverSpecificId);
// TODO add ids to render in case any member left the server
WarnDecayWarning warnDecayWarning = WarnDecayWarning WarnDecayWarning warnDecayWarning = WarnDecayWarning
.builder() .builder()
.warnedMember(botService.getMemberInServer(warning.getWarnedUser())) .warnedMember(pair.getFirstMember().join())
.warningMember(botService.getMemberInServer(warning.getWarningUser())) .warningMember(pair.getSecondMember().join())
.warning(warning) .warning(warning)
.build(); .build();
warnDecayWarnings.add(warnDecayWarning); warnDecayWarnings.add(warnDecayWarning);
}); });
WarnDecayLogModel warnDecayLogModel = WarnDecayLogModel WarnDecayLogModel warnDecayLogModel = WarnDecayLogModel
.builder() .builder()
.guild(botService.getGuildByIdNullable(server.getId())) .guild(botService.getGuildById(server.getId()))
.server(server) .server(server)
.warnings(warnDecayWarnings) .warnings(warnDecayWarnings)
.build(); .build();

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.moderation.service.management; package dev.sheldan.abstracto.moderation.service.management;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.AServerAChannelMessage; import dev.sheldan.abstracto.core.models.AServerAChannelMessage;
import dev.sheldan.abstracto.core.models.ServerSpecificId; import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
@@ -48,10 +49,15 @@ public class MuteManagementServiceBean implements MuteManagementService {
} }
@Override @Override
public Optional<Mute> findMute(Long muteId, Long serverId) { public Optional<Mute> findMuteOptional(Long muteId, Long serverId) {
return muteRepository.findByMuteId_IdAndMuteId_ServerId(muteId, serverId); return muteRepository.findByMuteId_IdAndMuteId_ServerId(muteId, serverId);
} }
@Override
public Mute findMute(Long muteId, Long serverId) {
return findMuteOptional(muteId, serverId).orElseThrow(() -> new AbstractoRunTimeException("Mute not found."));
}
@Override @Override
public Mute saveMute(Mute mute) { public Mute saveMute(Mute mute) {
muteRepository.save(mute); muteRepository.save(mute);

View File

@@ -46,6 +46,11 @@ public class UserNoteManagementServiceBean implements UserNoteManagementService
userNoteRepository.deleteByUserNoteId_IdAndUserNoteId_ServerId(id, server.getId()); userNoteRepository.deleteByUserNoteId_IdAndUserNoteId_ServerId(id, server.getId());
} }
@Override
public UserNote loadNote(Long userNoteId, Long serverId) {
return userNoteRepository.findByUserNoteId_IdAndUserNoteId_ServerId(userNoteId, serverId);
}
@Override @Override
public boolean noteExists(Long id, AServer server) { public boolean noteExists(Long id, AServer server) {
return userNoteRepository.existsByUserNoteId_IdAndUserNoteId_ServerId(id, server.getId()); return userNoteRepository.existsByUserNoteId_IdAndUserNoteId_ServerId(id, server.getId());

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.moderation.service.management; package dev.sheldan.abstracto.moderation.service.management;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.ServerSpecificId; import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.moderation.models.database.Warning; import dev.sheldan.abstracto.moderation.models.database.Warning;
@@ -24,7 +25,7 @@ public class WarnManagementServiceBean implements WarnManagementService {
public Warning createWarning(AUserInAServer warnedAUser, AUserInAServer warningAUser, String reason, Long warnId) { public Warning createWarning(AUserInAServer warnedAUser, AUserInAServer warningAUser, String reason, Long warnId) {
log.info("Creating warning with id {} for user {} in server {} cast by user {}.", log.info("Creating warning with id {} for user {} in server {} cast by user {}.",
warnId, warnedAUser.getUserReference().getId(), warningAUser.getServerReference().getId(), warningAUser.getUserReference().getId()); warnId, warnedAUser.getUserReference().getId(), warningAUser.getServerReference().getId(), warningAUser.getUserReference().getId());
ServerSpecificId warningId = new ServerSpecificId(warnId, warningAUser.getServerReference().getId()); ServerSpecificId warningId = new ServerSpecificId(warningAUser.getServerReference().getId(), warnId);
Warning warning = Warning.builder() Warning warning = Warning.builder()
.reason(reason) .reason(reason)
.warnedUser(warnedAUser) .warnedUser(warnedAUser)
@@ -64,10 +65,15 @@ public class WarnManagementServiceBean implements WarnManagementService {
} }
@Override @Override
public Optional<Warning> findById(Long id, Long serverId) { public Optional<Warning> findByIdOptional(Long id, Long serverId) {
return warnRepository.findByWarnId_IdAndWarnId_ServerId(id, serverId); return warnRepository.findByWarnId_IdAndWarnId_ServerId(id, serverId);
} }
@Override
public Warning findById(Long id, Long serverId) {
return findByIdOptional(id, serverId).orElseThrow(() -> new AbstractoRunTimeException("Warning not found."));
}
@Override @Override
public void deleteWarning(Warning warning) { public void deleteWarning(Warning warning) {
log.info("Deleting warning with id {} in server {}.", warning.getWarnId().getId(), warning.getWarnId().getServerId()); log.info("Deleting warning with id {} in server {}.", warning.getWarnId().getId(), warning.getWarnId().getServerId());

View File

@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.moderation.service.WarnService; import dev.sheldan.abstracto.moderation.service.WarnService;
import dev.sheldan.abstracto.test.command.CommandConfigValidator; import dev.sheldan.abstracto.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.test.command.CommandTestUtilities; import dev.sheldan.abstracto.test.command.CommandTestUtilities;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
@@ -16,6 +17,7 @@ import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
@Slf4j
public class DecayWarningsTest { public class DecayWarningsTest {
@InjectMocks @InjectMocks

View File

@@ -39,7 +39,7 @@ public class DeleteWarningTest {
AUserInAServer warningUser = MockUtils.getUserObject(6L, server); AUserInAServer warningUser = MockUtils.getUserObject(6L, server);
Warning existingWarning = Warning.builder().warnedUser(warnedUser).warningUser(warningUser).build(); Warning existingWarning = Warning.builder().warnedUser(warnedUser).warningUser(warningUser).build();
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(WARN_ID)); CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(WARN_ID));
when(warnManagementService.findById(WARN_ID, parameters.getGuild().getIdLong())).thenReturn(Optional.of(existingWarning)); when(warnManagementService.findByIdOptional(WARN_ID, parameters.getGuild().getIdLong())).thenReturn(Optional.of(existingWarning));
CommandResult result = testUnit.execute(parameters); CommandResult result = testUnit.execute(parameters);
verify(warnManagementService, times(1)).deleteWarning(existingWarning); verify(warnManagementService, times(1)).deleteWarning(existingWarning);
CommandTestUtilities.checkSuccessfulCompletion(result); CommandTestUtilities.checkSuccessfulCompletion(result);

View File

@@ -64,15 +64,16 @@ public class UserNotesTest {
when(userNoteManagementService.loadNotesForUser(userNoteUser)).thenReturn(userNotes); when(userNoteManagementService.loadNotesForUser(userNoteUser)).thenReturn(userNotes);
NoteEntryModel firstConvertedNote = NoteEntryModel.builder().build(); NoteEntryModel firstConvertedNote = NoteEntryModel.builder().build();
NoteEntryModel secondConvertedNote = NoteEntryModel.builder().build(); NoteEntryModel secondConvertedNote = NoteEntryModel.builder().build();
List<NoteEntryModel> convertedNotes = Arrays.asList(firstConvertedNote, secondConvertedNote); CompletableFuture<List<NoteEntryModel>> convertedNotes = CompletableFuture.completedFuture(Arrays.asList(firstConvertedNote, secondConvertedNote));
when(userNotesConverter.fromNotes(userNotes)).thenReturn(convertedNotes); when(userNotesConverter.fromNotes(userNotes)).thenReturn(convertedNotes);
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters); CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
verify(channelService, times(1)).sendEmbedTemplateInChannel(eq(UserNotes.USER_NOTES_RESPONSE_TEMPLATE), captor.capture(), eq(parameters.getChannel())); verify(channelService, times(1)).sendEmbedTemplateInChannel(eq(UserNotes.USER_NOTES_RESPONSE_TEMPLATE), captor.capture(), eq(parameters.getChannel()));
ListNotesModel usedModel = captor.getValue(); ListNotesModel usedModel = captor.getValue();
Assert.assertEquals(convertedNotes.size(), usedModel.getUserNotes().size()); List<NoteEntryModel> notes = convertedNotes.join();
Assert.assertEquals(notes.size(), usedModel.getUserNotes().size());
for (int i = 0; i < usedModel.getUserNotes().size(); i++) { for (int i = 0; i < usedModel.getUserNotes().size(); i++) {
NoteEntryModel usedEntry = usedModel.getUserNotes().get(i); NoteEntryModel usedEntry = usedModel.getUserNotes().get(i);
NoteEntryModel expectedEntry = convertedNotes.get(i); NoteEntryModel expectedEntry = notes.get(i);
Assert.assertEquals(expectedEntry, usedEntry); Assert.assertEquals(expectedEntry, usedEntry);
} }
Assert.assertEquals(userNoteUser, usedModel.getSpecifiedUser().getAUserInAServer()); Assert.assertEquals(userNoteUser, usedModel.getSpecifiedUser().getAUserInAServer());
@@ -89,15 +90,16 @@ public class UserNotesTest {
when(userNoteManagementService.loadNotesForServer(parameters.getUserInitiatedContext().getServer())).thenReturn(userNotes); when(userNoteManagementService.loadNotesForServer(parameters.getUserInitiatedContext().getServer())).thenReturn(userNotes);
NoteEntryModel firstConvertedNote = NoteEntryModel.builder().build(); NoteEntryModel firstConvertedNote = NoteEntryModel.builder().build();
NoteEntryModel secondConvertedNote = NoteEntryModel.builder().build(); NoteEntryModel secondConvertedNote = NoteEntryModel.builder().build();
List<NoteEntryModel> convertedNotes = Arrays.asList(firstConvertedNote, secondConvertedNote); CompletableFuture<List<NoteEntryModel>> convertedNotes = CompletableFuture.completedFuture(Arrays.asList(firstConvertedNote, secondConvertedNote));
when(userNotesConverter.fromNotes(userNotes)).thenReturn(convertedNotes); when(userNotesConverter.fromNotes(userNotes)).thenReturn(convertedNotes);
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters); CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
List<NoteEntryModel> notes = convertedNotes.join();
verify(channelService, times(1)).sendEmbedTemplateInChannel(eq(UserNotes.USER_NOTES_RESPONSE_TEMPLATE), captor.capture(), eq(parameters.getChannel())); verify(channelService, times(1)).sendEmbedTemplateInChannel(eq(UserNotes.USER_NOTES_RESPONSE_TEMPLATE), captor.capture(), eq(parameters.getChannel()));
ListNotesModel usedModel = captor.getValue(); ListNotesModel usedModel = captor.getValue();
Assert.assertEquals(convertedNotes.size(), usedModel.getUserNotes().size()); Assert.assertEquals(notes.size(), usedModel.getUserNotes().size());
for (int i = 0; i < usedModel.getUserNotes().size(); i++) { for (int i = 0; i < usedModel.getUserNotes().size(); i++) {
NoteEntryModel usedEntry = usedModel.getUserNotes().get(i); NoteEntryModel usedEntry = usedModel.getUserNotes().get(i);
NoteEntryModel expectedEntry = convertedNotes.get(i); NoteEntryModel expectedEntry = notes.get(i);
Assert.assertEquals(expectedEntry, usedEntry); Assert.assertEquals(expectedEntry, usedEntry);
} }
Assert.assertNull(usedModel.getSpecifiedUser()); Assert.assertNull(usedModel.getSpecifiedUser());

View File

@@ -13,7 +13,6 @@ import dev.sheldan.abstracto.moderation.models.database.Warning;
import dev.sheldan.abstracto.moderation.models.template.commands.WarnEntry; import dev.sheldan.abstracto.moderation.models.template.commands.WarnEntry;
import dev.sheldan.abstracto.moderation.models.template.commands.WarningsModel; import dev.sheldan.abstracto.moderation.models.template.commands.WarningsModel;
import dev.sheldan.abstracto.moderation.service.management.WarnManagementService; import dev.sheldan.abstracto.moderation.service.management.WarnManagementService;
import dev.sheldan.abstracto.test.MockUtils;
import dev.sheldan.abstracto.test.command.CommandConfigValidator; import dev.sheldan.abstracto.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.test.command.CommandTestUtilities; import dev.sheldan.abstracto.test.command.CommandTestUtilities;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
@@ -25,6 +24,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@@ -52,23 +52,37 @@ public class WarningsTest {
@Captor @Captor
private ArgumentCaptor<WarningsModel> captor; private ArgumentCaptor<WarningsModel> captor;
@Mock
private Warnings self;
@Test @Test
public void testNoParametersForWarningsCommand(){ public void testNoParametersForWarningsCommand(){
CommandContext noParams = CommandTestUtilities.getNoParameters(); CommandContext noParams = CommandTestUtilities.getNoParameters();
Warning firstWarning = Warning.builder().build(); Warning firstWarning = Mockito.mock(Warning.class);
WarnEntry firstModelWarning = WarnEntry.builder().build(); WarnEntry firstModelWarning = Mockito.mock(WarnEntry.class);
Warning secondWarning = Warning.builder().build(); Warning secondWarning = Mockito.mock(Warning.class);
WarnEntry secondModelWarning = WarnEntry.builder().build(); WarnEntry secondModelWarning = Mockito.mock(WarnEntry.class);
List<Warning> warningsToDisplay = Arrays.asList(firstWarning, secondWarning); List<Warning> warningsToDisplay = Arrays.asList(firstWarning, secondWarning);
List<WarnEntry> modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning); List<WarnEntry> modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning);
when(warnManagementService.getAllWarningsOfServer(noParams.getUserInitiatedContext().getServer())).thenReturn(warningsToDisplay); when(warnManagementService.getAllWarningsOfServer(noParams.getUserInitiatedContext().getServer())).thenReturn(warningsToDisplay);
when(warnEntryConverter.fromWarnings(warningsToDisplay)).thenReturn(modelWarnings); when(warnEntryConverter.fromWarnings(warningsToDisplay)).thenReturn(CompletableFuture.completedFuture(modelWarnings));
CompletableFuture<CommandResult> result = testUnit.executeAsync(noParams);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
verify(self, times(1)).renderWarnings(noParams, modelWarnings);
}
@Test
public void testWarningsRendering() {
CommandContext noParams = CommandTestUtilities.getNoParameters();
WarnEntry firstModelWarning = Mockito.mock(WarnEntry.class);
WarnEntry secondModelWarning = Mockito.mock(WarnEntry.class);
Paginator paginator = Mockito.mock(Paginator.class); Paginator paginator = Mockito.mock(Paginator.class);
when(paginatorService.createPaginatorFromTemplate(eq(Warnings.WARNINGS_RESPONSE_TEMPLATE), captor.capture(), eq(eventWaiter))).thenReturn(paginator); when(paginatorService.createPaginatorFromTemplate(eq(Warnings.WARNINGS_RESPONSE_TEMPLATE), captor.capture(), eq(eventWaiter))).thenReturn(paginator);
CommandResult result = testUnit.execute(noParams); List<WarnEntry> modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning);
CommandTestUtilities.checkSuccessfulCompletion(result); testUnit.renderWarnings(noParams, modelWarnings);
WarningsModel warningsModel = captor.getValue(); WarningsModel warningsModel = captor.getValue();
Assert.assertEquals(warningsToDisplay.size(), warningsModel.getWarnings().size());
Assert.assertEquals(firstModelWarning, warningsModel.getWarnings().get(0)); Assert.assertEquals(firstModelWarning, warningsModel.getWarnings().get(0));
Assert.assertEquals(secondModelWarning, warningsModel.getWarnings().get(1)); Assert.assertEquals(secondModelWarning, warningsModel.getWarnings().get(1));
} }
@@ -77,29 +91,26 @@ public class WarningsTest {
public void testExecuteWarningsForMember(){ public void testExecuteWarningsForMember(){
Member member = Mockito.mock(Member.class); Member member = Mockito.mock(Member.class);
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(member)); CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(member));
AUserInAServer warnedUser = MockUtils.getUserObject(5L, parameters.getUserInitiatedContext().getServer()); AUserInAServer warnedUser = Mockito.mock(AUserInAServer.class);
when(userInServerManagementService.loadUser(member)).thenReturn(warnedUser); Warning firstWarning = Mockito.mock(Warning.class);
Warning firstWarning = Warning.builder().build(); WarnEntry firstModelWarning = Mockito.mock(WarnEntry.class);
WarnEntry firstModelWarning = WarnEntry.builder().build(); Warning secondWarning = Mockito.mock(Warning.class);
Warning secondWarning = Warning.builder().build(); WarnEntry secondModelWarning = Mockito.mock(WarnEntry.class);
WarnEntry secondModelWarning = WarnEntry.builder().build();
List<Warning> warningsToDisplay = Arrays.asList(firstWarning, secondWarning); List<Warning> warningsToDisplay = Arrays.asList(firstWarning, secondWarning);
when(warnManagementService.getAllWarnsForUser(warnedUser)).thenReturn(warningsToDisplay);
List<WarnEntry> modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning); List<WarnEntry> modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning);
when(warnEntryConverter.fromWarnings(warningsToDisplay)).thenReturn(modelWarnings); when(userInServerManagementService.loadUser(member)).thenReturn(warnedUser);
Paginator paginator = Mockito.mock(Paginator.class); when(warnManagementService.getAllWarnsForUser(warnedUser)).thenReturn(warningsToDisplay);
when(paginatorService.createPaginatorFromTemplate(eq(Warnings.WARNINGS_RESPONSE_TEMPLATE), captor.capture(), eq(eventWaiter))).thenReturn(paginator); when(warnEntryConverter.fromWarnings(warningsToDisplay)).thenReturn(CompletableFuture.completedFuture(modelWarnings));
CommandResult result = testUnit.execute(parameters);
CommandTestUtilities.checkSuccessfulCompletion(result); CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
WarningsModel warningsModel = captor.getValue(); CommandTestUtilities.checkSuccessfulCompletionAsync(result);
Assert.assertEquals(warningsToDisplay.size(), warningsModel.getWarnings().size()); verify(self, times(1)).renderWarnings(parameters, modelWarnings);
Assert.assertEquals(firstModelWarning, warningsModel.getWarnings().get(0));
Assert.assertEquals(secondModelWarning, warningsModel.getWarnings().get(1));
} }
@Test(expected = IncorrectParameterTypeException.class) @Test(expected = IncorrectParameterTypeException.class)
public void testIncorrectParameterType() { public void testIncorrectParameterType() {
CommandTestUtilities.executeWrongParametersTest(testUnit); CommandTestUtilities.executeWrongParametersTestAsync(testUnit);
} }
@Test @Test

View File

@@ -1,11 +1,13 @@
package dev.sheldan.abstracto.moderation.converter; package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.moderation.models.database.UserNote; import dev.sheldan.abstracto.moderation.models.database.UserNote;
import dev.sheldan.abstracto.moderation.models.template.commands.NoteEntryModel; import dev.sheldan.abstracto.moderation.models.template.commands.NoteEntryModel;
import dev.sheldan.abstracto.moderation.service.management.UserNoteManagementService;
import dev.sheldan.abstracto.test.MockUtils; import dev.sheldan.abstracto.test.MockUtils;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import org.junit.Assert; import org.junit.Assert;
@@ -18,7 +20,9 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@@ -34,10 +38,16 @@ public class UserNotesConverterTest {
@Mock @Mock
private BotService botService; private BotService botService;
@Mock
private UserNotesConverter self;
@Mock
private UserNoteManagementService userNoteManagementService;
@Test @Test
public void testWithEmptyList() { public void testWithEmptyList() {
List<NoteEntryModel> entryModels = testUnit.fromNotes(Collections.emptyList()); CompletableFuture<List<NoteEntryModel>> entryModels = testUnit.fromNotes(Collections.emptyList());
Assert.assertEquals(0, entryModels.size()); Assert.assertEquals(0, entryModels.join().size());
} }
@Test @Test
@@ -45,15 +55,34 @@ public class UserNotesConverterTest {
AServer server = MockUtils.getServer(); AServer server = MockUtils.getServer();
AUserInAServer userInAServer = MockUtils.getUserObject(4L, server); AUserInAServer userInAServer = MockUtils.getUserObject(4L, server);
Member member = Mockito.mock(Member.class); Member member = Mockito.mock(Member.class);
when(botService.getMemberInServer(userInAServer)).thenReturn(member); when(botService.getMemberInServerAsync(userInAServer)).thenReturn(CompletableFuture.completedFuture(member));
UserNote firstNote = UserNote.builder().user(userInAServer).build(); UserNote firstNote = UserNote.builder().userNoteId(new ServerSpecificId(3L, 4L)).user(userInAServer).build();
UserNote secondNote = UserNote.builder().user(userInAServer).build(); UserNote secondNote = UserNote.builder().userNoteId(new ServerSpecificId(3L, 5L)).user(userInAServer).build();
List<NoteEntryModel> entryModels = testUnit.fromNotes(Arrays.asList(firstNote, secondNote)); testUnit.fromNotes(Arrays.asList(firstNote, secondNote));
Assert.assertEquals(2, entryModels.size()); verify(self, times(1)).loadFullNotes(any());
NoteEntryModel firstEntry = entryModels.get(0); }
@Test
public void testLoadingFullNotes() {
AUserInAServer userInAServer = Mockito.mock(AUserInAServer.class);
Member member = Mockito.mock(Member.class);
UserNote note1 = Mockito.mock(UserNote.class);
UserNote note2 = Mockito.mock(UserNote.class);
when(note1.getUser()).thenReturn(userInAServer);
when(note2.getUser()).thenReturn(userInAServer);
ServerSpecificId firstUserNoteId = new ServerSpecificId(3L, 4L);
ServerSpecificId secondUserNoteId = new ServerSpecificId(3L, 5L);
HashMap<ServerSpecificId, CompletableFuture<Member>> map = new HashMap<>();
map.put(firstUserNoteId, CompletableFuture.completedFuture(member));
map.put(secondUserNoteId, CompletableFuture.completedFuture(member));
when(userNoteManagementService.loadNote(4L, 3L)).thenReturn(note1);
when(userNoteManagementService.loadNote(5L, 3L)).thenReturn(note2);
List<NoteEntryModel> models = testUnit.loadFullNotes(map);
Assert.assertEquals(2, models.size());
NoteEntryModel firstEntry = models.get(0);
Assert.assertEquals(member, firstEntry.getFullUser().getMember()); Assert.assertEquals(member, firstEntry.getFullUser().getMember());
Assert.assertEquals(userInAServer, firstEntry.getFullUser().getAUserInAServer()); Assert.assertEquals(userInAServer, firstEntry.getFullUser().getAUserInAServer());
NoteEntryModel secondEntry = entryModels.get(1); NoteEntryModel secondEntry = models.get(1);
Assert.assertEquals(member, secondEntry.getFullUser().getMember()); Assert.assertEquals(member, secondEntry.getFullUser().getMember());
Assert.assertEquals(userInAServer, secondEntry.getFullUser().getAUserInAServer()); Assert.assertEquals(userInAServer, secondEntry.getFullUser().getAUserInAServer());
} }

View File

@@ -1,11 +1,14 @@
package dev.sheldan.abstracto.moderation.converter; package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.FutureMemberPair;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.moderation.models.database.Warning; import dev.sheldan.abstracto.moderation.models.database.Warning;
import dev.sheldan.abstracto.moderation.models.template.commands.WarnEntry; import dev.sheldan.abstracto.moderation.models.template.commands.WarnEntry;
import dev.sheldan.abstracto.test.MockUtils; import dev.sheldan.abstracto.moderation.service.management.WarnManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@@ -17,11 +20,14 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
@Slf4j
public class WarnEntryConverterTest { public class WarnEntryConverterTest {
@InjectMocks @InjectMocks
@@ -30,34 +36,74 @@ public class WarnEntryConverterTest {
@Mock @Mock
private BotService botService; private BotService botService;
@Mock
private WarnEntryConverter self;
@Mock
private WarnManagementService warnManagementService;
@Mock
private UserInServerManagementService userInServerManagementService;
private static final Long SERVER_ID = 5L;
private static final Long WARN_ID_1 = 6L;
private static final Long WARN_ID_2 = 7L;
@Test @Test
public void testWithEmptyList() { public void testWithEmptyList() {
List<WarnEntry> entryModels = testUnit.fromWarnings(Collections.emptyList()); CompletableFuture<List<WarnEntry>> entryModels = testUnit.fromWarnings(Collections.emptyList());
Assert.assertEquals(0, entryModels.size()); Assert.assertEquals(0, entryModels.join().size());
} }
@Test @Test
public void testWithSomeUserNotes() { public void testWithSomeUserNotes() {
AServer server = MockUtils.getServer(); AUserInAServer warnedUser = Mockito.mock(AUserInAServer.class);
AUserInAServer warnedUserInAServer = MockUtils.getUserObject(4L, server); AUserInAServer warningUser = Mockito.mock(AUserInAServer.class);
AUserInAServer warningUserInAServer = MockUtils.getUserObject(6L, server);
Member warnedMember = Mockito.mock(Member.class); Member warnedMember = Mockito.mock(Member.class);
Member warningMember = Mockito.mock(Member.class); Member warningMember = Mockito.mock(Member.class);
when(botService.getMemberInServer(warnedUserInAServer)).thenReturn(warnedMember); when(botService.getMemberInServerAsync(warnedUser)).thenReturn(CompletableFuture.completedFuture(warnedMember));
when(botService.getMemberInServer(warningUserInAServer)).thenReturn(warningMember); when(botService.getMemberInServerAsync(warningUser)).thenReturn(CompletableFuture.completedFuture(warningMember));
Warning firstNote = Warning.builder().warnedUser(warnedUserInAServer).warningUser(warningUserInAServer).build(); Warning firstNote = Warning.builder().warnId(new ServerSpecificId(3L, 4L)).warnedUser(warnedUser).warningUser(warningUser).build();
Warning secondNote = Warning.builder().warnedUser(warnedUserInAServer).warningUser(warningUserInAServer).build(); Warning secondNote = Warning.builder().warnId(new ServerSpecificId(3L, 5L)).warnedUser(warnedUser).warningUser(warningUser).build();
List<WarnEntry> entryModels = testUnit.fromWarnings(Arrays.asList(firstNote, secondNote)); testUnit.fromWarnings(Arrays.asList(firstNote, secondNote));
Assert.assertEquals(2, entryModels.size()); verify(self, times(1)).loadFullWarnEntries(any());
WarnEntry firstEntry = entryModels.get(0); }
@Test
public void testLoadingFullNotes() {
AUserInAServer warnedUser = Mockito.mock(AUserInAServer.class);
AUserInAServer warningUser = Mockito.mock(AUserInAServer.class);
Member warningMember = Mockito.mock(Member.class);
Member warnedMember = Mockito.mock(Member.class);
Warning warning1 = Mockito.mock(Warning.class);
Warning warning2 = Mockito.mock(Warning.class);
ServerSpecificId firstWarnId = new ServerSpecificId(SERVER_ID, WARN_ID_1);
when(warning1.getWarnId()).thenReturn(firstWarnId);
ServerSpecificId secondWarnId = new ServerSpecificId(SERVER_ID, WARN_ID_2);
when(warning2.getWarnId()).thenReturn(secondWarnId);
HashMap<ServerSpecificId, FutureMemberPair> map = new HashMap<>();
FutureMemberPair memberPair = FutureMemberPair.builder().firstMember(CompletableFuture.completedFuture(warningMember)).secondMember(CompletableFuture.completedFuture(warnedMember)).build();
map.put(firstWarnId, memberPair);
map.put(secondWarnId, memberPair);
when(warnManagementService.findById(WARN_ID_1, SERVER_ID)).thenReturn(warning1);
when(warnManagementService.findById(WARN_ID_2, SERVER_ID)).thenReturn(warning2);
when(userInServerManagementService.loadUser(warnedMember)).thenReturn(warnedUser);
when(userInServerManagementService.loadUser(warningMember)).thenReturn(warningUser);
List<WarnEntry> models = testUnit.loadFullWarnEntries(map);
Assert.assertEquals(2, models.size());
WarnEntry firstEntry = models.get(0);
Assert.assertEquals(warningMember, firstEntry.getWarningUser().getMember()); Assert.assertEquals(warningMember, firstEntry.getWarningUser().getMember());
Assert.assertEquals(warnedMember, firstEntry.getWarnedUser().getMember()); Assert.assertEquals(warnedMember, firstEntry.getWarnedUser().getMember());
Assert.assertEquals(warningUserInAServer, firstEntry.getWarningUser().getAUserInAServer()); Assert.assertEquals(warnedUser, firstEntry.getWarnedUser().getAUserInAServer());
Assert.assertEquals(warnedUserInAServer, firstEntry.getWarnedUser().getAUserInAServer()); Assert.assertEquals(warningUser, firstEntry.getWarningUser().getAUserInAServer());
WarnEntry secondEntry = entryModels.get(1); Assert.assertEquals(WARN_ID_1, firstEntry.getWarning().getWarnId().getId());
Assert.assertEquals(SERVER_ID, firstEntry.getWarning().getWarnId().getServerId());
WarnEntry secondEntry = models.get(1);
Assert.assertEquals(warningMember, secondEntry.getWarningUser().getMember()); Assert.assertEquals(warningMember, secondEntry.getWarningUser().getMember());
Assert.assertEquals(warnedMember, secondEntry.getWarnedUser().getMember()); Assert.assertEquals(warnedMember, secondEntry.getWarnedUser().getMember());
Assert.assertEquals(warningUserInAServer, secondEntry.getWarningUser().getAUserInAServer()); Assert.assertEquals(warnedUser, secondEntry.getWarnedUser().getAUserInAServer());
Assert.assertEquals(warnedUserInAServer, secondEntry.getWarnedUser().getAUserInAServer()); Assert.assertEquals(warningUser, secondEntry.getWarningUser().getAUserInAServer());
Assert.assertEquals(WARN_ID_2, secondEntry.getWarning().getWarnId().getId());
Assert.assertEquals(SERVER_ID, secondEntry.getWarning().getWarnId().getServerId());
} }
} }

View File

@@ -70,7 +70,7 @@ public class BanServiceBeanTest {
when(mockedGuild.ban(userId.toString(), 0, REASON)).thenReturn(mockedAction); when(mockedGuild.ban(userId.toString(), 0, REASON)).thenReturn(mockedAction);
MessageToSend mockedMessage = Mockito.mock(MessageToSend.class); MessageToSend mockedMessage = Mockito.mock(MessageToSend.class);
when(templateService.renderEmbedTemplate(BanServiceBean.BAN_ID_LOG_TEMPLATE, context)).thenReturn(mockedMessage); when(templateService.renderEmbedTemplate(BanServiceBean.BAN_ID_LOG_TEMPLATE, context)).thenReturn(mockedMessage);
when(botService.getGuildById(serverId)).thenReturn(Optional.of(mockedGuild)); when(botService.getGuildByIdOptional(serverId)).thenReturn(Optional.of(mockedGuild));
testUnit.banMember(serverId, userId, REASON, context); testUnit.banMember(serverId, userId, REASON, context);
verify(mockedGuild, times(1)).ban(userId.toString(), 0, REASON); verify(mockedGuild, times(1)).ban(userId.toString(), 0, REASON);
verify(postTargetService, times(1)).sendEmbedInPostTarget(mockedMessage, ModerationPostTarget.BAN_LOG, serverId); verify(postTargetService, times(1)).sendEmbedInPostTarget(mockedMessage, ModerationPostTarget.BAN_LOG, serverId);
@@ -81,7 +81,7 @@ public class BanServiceBeanTest {
Long userId = 8L; Long userId = 8L;
Long serverId = 5L; Long serverId = 5L;
ServerContext context = Mockito.mock(ServerContext.class); ServerContext context = Mockito.mock(ServerContext.class);
when(botService.getGuildById(serverId)).thenReturn(Optional.empty()); when(botService.getGuildByIdOptional(serverId)).thenReturn(Optional.empty());
testUnit.banMember(serverId, userId, REASON, context); testUnit.banMember(serverId, userId, REASON, context);
} }

View File

@@ -25,7 +25,6 @@ import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import dev.sheldan.abstracto.templating.model.MessageToSend; import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService; import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.test.MockUtils; import dev.sheldan.abstracto.test.MockUtils;
import dev.sheldan.abstracto.test.command.CommandTestUtilities;
import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.*;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -43,6 +42,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import static dev.sheldan.abstracto.moderation.service.MuteServiceBean.MUTE_NOTIFICATION_TEMPLATE; import static dev.sheldan.abstracto.moderation.service.MuteServiceBean.MUTE_NOTIFICATION_TEMPLATE;
import static dev.sheldan.abstracto.moderation.service.MuteServiceBean.UN_MUTE_LOG_TEMPLATE;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
@@ -234,24 +234,6 @@ public class MuteServiceBeanTest {
verifyDirectMute(); verifyDirectMute();
} }
@Test
public void testMuteAUserInAServer() {
when(botService.getMemberInServer(userBeingMuted)).thenReturn(memberBeingMuted);
when(botService.getMemberInServer(userMuting)).thenReturn(memberMuting);
Instant unMuteDate = shorterMute();
when(memberBeingMuted.getGuild()).thenReturn(guild);
when(memberBeingMuted.getUser()).thenReturn(jdaUserBeingMuted);
when(muteRoleManagementService.muteRoleForServerExists(server)).thenReturn(true);
when(muteRoleManagementService.retrieveMuteRoleForServer(server)).thenReturn(muteRole);
String notificationText = "text";
when(templateService.renderTemplate(eq(MUTE_NOTIFICATION_TEMPLATE), any(MuteNotification.class))).thenReturn(notificationText);
when(messageService.sendMessageToUser(memberBeingMuted.getUser(), notificationText)).thenReturn(CompletableFuture.completedFuture(null));
when(roleService.addRoleToUserFuture(userBeingMuted, muteRole.getRole())).thenReturn(CompletableFuture.completedFuture(null));
testUnit.muteAUserInAServer(userBeingMuted, userMuting, REASON, unMuteDate, cause);
verifyDirectMute();
}
@Test @Test
public void testMuteMemberWithLog() { public void testMuteMemberWithLog() {
when(userInServerManagementService.loadUser(memberBeingMuted)).thenReturn(userBeingMuted); when(userInServerManagementService.loadUser(memberBeingMuted)).thenReturn(userBeingMuted);
@@ -294,15 +276,30 @@ public class MuteServiceBeanTest {
public void testEndMute() { public void testEndMute() {
setupUnMuteMocks(true); setupUnMuteMocks(true);
when(mute.getMutedUser()).thenReturn(userBeingMuted); when(mute.getMutedUser()).thenReturn(userBeingMuted);
when(mute.getMutingUser()).thenReturn(userMuting);
when(mute.getServer()).thenReturn(server); when(mute.getServer()).thenReturn(server);
when(muteManagementService.findMute(MUTE_ID, SERVER_ID)).thenReturn(Optional.of(mute)); when(muteManagementService.findMuteOptional(MUTE_ID, SERVER_ID)).thenReturn(Optional.of(mute));
when(roleService.removeRoleFromUserFuture(userBeingMuted, aRole)).thenReturn(CompletableFuture.completedFuture(null)); when(roleService.removeRoleFromUserFuture(userBeingMuted, aRole)).thenReturn(CompletableFuture.completedFuture(null));
when(botService.getMemberInServerAsync(userBeingMuted)).thenReturn(CompletableFuture.completedFuture(memberBeingMuted));
when(botService.getMemberInServerAsync(userMuting)).thenReturn(CompletableFuture.completedFuture(memberMuting));
testUnit.endMute(MUTE_ID, SERVER_ID); testUnit.endMute(MUTE_ID, SERVER_ID);
verify(self, times(1)).sendUnmuteLog(eq(MUTE_ID), any(Guild.class), any(CompletableFuture.class), any(CompletableFuture.class));
}
@Test
public void testSendUnmuteLog() {
when(guild.getIdLong()).thenReturn(SERVER_ID);
when(muteManagementService.findMute(MUTE_ID, SERVER_ID)).thenReturn(mute);
when(serverManagementService.loadServer(SERVER_ID)).thenReturn(server);
when(templateService.renderEmbedTemplate(eq(UN_MUTE_LOG_TEMPLATE), any(UnMuteLog.class))).thenReturn(messageToSend);
when(postTargetService.sendEmbedInPostTarget(eq(messageToSend), eq(MutingPostTarget.MUTE_LOG), anyLong())).thenReturn(Arrays.asList(CompletableFuture.completedFuture(null)));
testUnit.sendUnmuteLog(MUTE_ID, guild, CompletableFuture.completedFuture(memberMuting), CompletableFuture.completedFuture(memberBeingMuted));
verify(self, times(1)).endMuteInDatabase(MUTE_ID, SERVER_ID);
} }
@Test(expected = NoMuteFoundException.class) @Test(expected = NoMuteFoundException.class)
public void testEndNonExistingMute() { public void testEndNonExistingMute() {
when(muteManagementService.findMute(MUTE_ID, SERVER_ID)).thenReturn(Optional.empty()); when(muteManagementService.findMuteOptional(MUTE_ID, SERVER_ID)).thenReturn(Optional.empty());
testUnit.endMute(MUTE_ID, SERVER_ID); testUnit.endMute(MUTE_ID, SERVER_ID);
} }
@@ -373,10 +370,12 @@ public class MuteServiceBeanTest {
private void executeUnMuteWithLogTest(boolean stillInGuild) { private void executeUnMuteWithLogTest(boolean stillInGuild) {
when(mute.getMutedUser()).thenReturn(userBeingMuted); when(mute.getMutedUser()).thenReturn(userBeingMuted);
when(mute.getMutingUser()).thenReturn(userMuting);
when(mute.getServer()).thenReturn(server); when(mute.getServer()).thenReturn(server);
setupUnMuteMocks(stillInGuild); setupUnMuteMocks(stillInGuild);
when(roleService.removeRoleFromUserFuture(userBeingMuted, aRole)).thenReturn(CompletableFuture.completedFuture(null)); when(roleService.removeRoleFromUserFuture(userBeingMuted, aRole)).thenReturn(CompletableFuture.completedFuture(null));
when(postTargetService.sendEmbedInPostTarget(messageToSend, MutingPostTarget.MUTE_LOG, server.getId())).thenReturn(CommandTestUtilities.messageFutureList()); when(botService.getMemberInServerAsync(userBeingMuted)).thenReturn(CompletableFuture.completedFuture(memberBeingMuted));
when(botService.getMemberInServerAsync(userMuting)).thenReturn(CompletableFuture.completedFuture(memberMuting));
testUnit.unMuteUser(userBeingMuted); testUnit.unMuteUser(userBeingMuted);
} }
@@ -385,10 +384,8 @@ public class MuteServiceBeanTest {
when(mute.getMuteId()).thenReturn(new ServerSpecificId(SERVER_ID, MUTE_ID)); when(mute.getMuteId()).thenReturn(new ServerSpecificId(SERVER_ID, MUTE_ID));
when(muteManagementService.getAMuteOf(userBeingMuted)).thenReturn(mute); when(muteManagementService.getAMuteOf(userBeingMuted)).thenReturn(mute);
when(muteRoleManagementService.retrieveMuteRoleForServer(server)).thenReturn(muteRole); when(muteRoleManagementService.retrieveMuteRoleForServer(server)).thenReturn(muteRole);
when(botService.getGuildByIdNullable(server.getId())).thenReturn(guild); when(botService.getGuildById(server.getId())).thenReturn(guild);
when(botService.isUserInGuild(guild, userBeingMuted)).thenReturn(stillInGuild); when(botService.isUserInGuild(guild, userBeingMuted)).thenReturn(stillInGuild);
when(botService.getMemberInServer(userBeingMuted)).thenReturn(memberBeingMuted);
when(templateService.renderEmbedTemplate(eq(MuteServiceBean.UN_MUTE_LOG_TEMPLATE), any(UnMuteLog.class))).thenReturn(messageToSend);
} }
private void verifyDirectMute() { private void verifyDirectMute() {

View File

@@ -57,6 +57,9 @@ public class WarnServiceBeanTest {
@Mock @Mock
private ConfigService configService; private ConfigService configService;
@Mock
private WarnServiceBean self;
@Mock @Mock
private Member warnedMember; private Member warnedMember;
@@ -128,14 +131,14 @@ public class WarnServiceBeanTest {
public void testDecayWarningsForServer() { public void testDecayWarningsForServer() {
setupWarnDecay(); setupWarnDecay();
testUnit.decayWarningsForServer(server); testUnit.decayWarningsForServer(server);
verifyWarnDecayWithLog(true); verify(self, times(1)).renderAndSendWarnDecayLogs(eq(SERVER_ID), any());
} }
@Test @Test
public void testDecayAllWarningsForServerWithLog() { public void testDecayAllWarningsForServerWithLog() {
setupWarnDecay(); setupWarnDecay();
testUnit.decayAllWarningsForServer(server, true); testUnit.decayAllWarningsForServer(server, true);
verifyWarnDecayWithLog(true); verify(self, times(1)).renderAndSendWarnDecayLogs(eq(SERVER_ID), any());
} }
@Test @Test
@@ -148,14 +151,10 @@ public class WarnServiceBeanTest {
@Test @Test
public void testDecayAllWarningsWithoutWarnings() { public void testDecayAllWarningsWithoutWarnings() {
List<Warning> warnings = Collections.emptyList(); List<Warning> warnings = Collections.emptyList();
when(botService.getGuildByIdNullable(server.getId())).thenReturn(guild); when(server.getId()).thenReturn(SERVER_ID);
when(templateService.renderEmbedTemplate(eq(WARN_DECAY_LOG_TEMPLATE_KEY), warnDecayLogModelArgumentCaptor.capture())).thenReturn(messageToSend);
when(warnManagementService.getActiveWarningsInServerOlderThan(eq(server), any(Instant.class))).thenReturn(warnings); when(warnManagementService.getActiveWarningsInServerOlderThan(eq(server), any(Instant.class))).thenReturn(warnings);
testUnit.decayAllWarningsForServer(server, true); testUnit.decayAllWarningsForServer(server, true);
verify(postTargetService, times(1)).sendEmbedInPostTarget(messageToSend, WarnDecayPostTarget.DECAY_LOG, server.getId()); verify(self, times(1)).renderAndSendWarnDecayLogs(eq(SERVER_ID), any());
WarnDecayLogModel model = warnDecayLogModelArgumentCaptor.getValue();
List<WarnDecayWarning> usedWarnings = model.getWarnings();
Assert.assertEquals(0, usedWarnings.size());
} }
@Test @Test
@@ -218,11 +217,9 @@ public class WarnServiceBeanTest {
setupWarnings(); setupWarnings();
when(configService.getLongValue(WarningDecayFeature.DECAY_DAYS_KEY, server.getId())).thenReturn(5L); when(configService.getLongValue(WarningDecayFeature.DECAY_DAYS_KEY, server.getId())).thenReturn(5L);
List<Warning> warnings = Arrays.asList(firstWarning, secondWarning); List<Warning> warnings = Arrays.asList(firstWarning, secondWarning);
when(botService.getMemberInServer(warningUser)).thenReturn(warningMember); when(botService.getMemberInServerAsync(warningUser)).thenReturn(CompletableFuture.completedFuture(warningMember));
when(botService.getMemberInServer(firstWarnedUser)).thenReturn(warnedMember); when(botService.getMemberInServerAsync(firstWarnedUser)).thenReturn(CompletableFuture.completedFuture(warnedMember));
when(botService.getMemberInServer(secondWarnedUser)).thenReturn(secondWarnedMember); when(botService.getMemberInServerAsync(secondWarnedUser)).thenReturn(CompletableFuture.completedFuture(secondWarnedMember));
when(botService.getGuildByIdNullable(server.getId())).thenReturn(guild);
when(templateService.renderEmbedTemplate(eq(WARN_DECAY_LOG_TEMPLATE_KEY), warnDecayLogModelArgumentCaptor.capture())).thenReturn(messageToSend);
when(warnManagementService.getActiveWarningsInServerOlderThan(eq(server), any(Instant.class))).thenReturn(warnings); when(warnManagementService.getActiveWarningsInServerOlderThan(eq(server), any(Instant.class))).thenReturn(warnings);
} }

View File

@@ -69,7 +69,7 @@ public class MuteManagementServiceBeanTest {
Long serverId = 7L; Long serverId = 7L;
Mute mute = Mute.builder().muteId(new ServerSpecificId(serverId, id)).build(); Mute mute = Mute.builder().muteId(new ServerSpecificId(serverId, id)).build();
when(muteRepository.findByMuteId_IdAndMuteId_ServerId(id, serverId)).thenReturn(Optional.of(mute)); when(muteRepository.findByMuteId_IdAndMuteId_ServerId(id, serverId)).thenReturn(Optional.of(mute));
Optional<Mute> foundMuteOptional = testUnit.findMute(id, serverId); Optional<Mute> foundMuteOptional = testUnit.findMuteOptional(id, serverId);
Assert.assertTrue(foundMuteOptional.isPresent()); Assert.assertTrue(foundMuteOptional.isPresent());
foundMuteOptional.ifPresent(foundMute -> Assert.assertEquals(id, foundMute.getMuteId().getId())); foundMuteOptional.ifPresent(foundMute -> Assert.assertEquals(id, foundMute.getMuteId().getId()));
} }
@@ -79,7 +79,7 @@ public class MuteManagementServiceBeanTest {
Long id = 5L; Long id = 5L;
Long serverId = 7L; Long serverId = 7L;
when(muteRepository.findByMuteId_IdAndMuteId_ServerId(id, serverId)).thenReturn(Optional.empty()); when(muteRepository.findByMuteId_IdAndMuteId_ServerId(id, serverId)).thenReturn(Optional.empty());
Optional<Mute> foundMuteOptional = testUnit.findMute(id, serverId); Optional<Mute> foundMuteOptional = testUnit.findMuteOptional(id, serverId);
Assert.assertFalse(foundMuteOptional.isPresent()); Assert.assertFalse(foundMuteOptional.isPresent());
} }

View File

@@ -99,7 +99,7 @@ public class WarnManagementServiceBeanTest {
Long serverId = 8L; Long serverId = 8L;
Warning existingWarning = getWarning(); Warning existingWarning = getWarning();
when(warnRepository.findByWarnId_IdAndWarnId_ServerId(warnId, serverId)).thenReturn(Optional.ofNullable(existingWarning)); when(warnRepository.findByWarnId_IdAndWarnId_ServerId(warnId, serverId)).thenReturn(Optional.ofNullable(existingWarning));
Optional<Warning> warningOptional = testUnit.findById(warnId, serverId); Optional<Warning> warningOptional = testUnit.findByIdOptional(warnId, serverId);
Assert.assertTrue(warningOptional.isPresent()); Assert.assertTrue(warningOptional.isPresent());
warningOptional.ifPresent(foundWarning -> Assert.assertEquals(existingWarning, foundWarning)); warningOptional.ifPresent(foundWarning -> Assert.assertEquals(existingWarning, foundWarning));
} }
@@ -109,7 +109,7 @@ public class WarnManagementServiceBeanTest {
Long warnId = 6L; Long warnId = 6L;
Long serverId = 8L; Long serverId = 8L;
when(warnRepository.findByWarnId_IdAndWarnId_ServerId(warnId, serverId)).thenReturn(Optional.ofNullable(null)); when(warnRepository.findByWarnId_IdAndWarnId_ServerId(warnId, serverId)).thenReturn(Optional.ofNullable(null));
Optional<Warning> warningOptional = testUnit.findById(warnId, serverId); Optional<Warning> warningOptional = testUnit.findByIdOptional(warnId, serverId);
Assert.assertFalse(warningOptional.isPresent()); Assert.assertFalse(warningOptional.isPresent());
} }

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.moderation.models.template.commands; package dev.sheldan.abstracto.moderation.models.template.commands;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext; import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
@@ -13,7 +13,7 @@ import java.util.List;
@Getter @Getter
@Setter @Setter
@SuperBuilder @SuperBuilder
public class WarningsModel extends UserInitiatedServerContext { public class WarningsModel extends SlimUserInitiatedServerContext {
/** /**
* A collection of {@link dev.sheldan.abstracto.moderation.models.database.Warning}s being rendered, might be all warnings of the server, or only the warnings of a specific user * A collection of {@link dev.sheldan.abstracto.moderation.models.database.Warning}s being rendered, might be all warnings of the server, or only the warnings of a specific user
*/ */

View File

@@ -12,7 +12,6 @@ import java.util.concurrent.CompletableFuture;
public interface MuteService { public interface MuteService {
CompletableFuture<Void> muteMember(Member memberToMute, Member userMuting, String reason, Instant unMuteDate, ServerChannelMessage message); CompletableFuture<Void> muteMember(Member memberToMute, Member userMuting, String reason, Instant unMuteDate, ServerChannelMessage message);
CompletableFuture<Void> muteAUserInAServer(AUserInAServer member, AUserInAServer userMuting, String reason, Instant unMuteDate, ServerChannelMessage message);
CompletableFuture<Void> muteUserInServer(FullUserInServer userToMute, FullUserInServer userMuting, String reason, Instant unMuteDate, ServerChannelMessage message); CompletableFuture<Void> muteUserInServer(FullUserInServer userToMute, FullUserInServer userMuting, String reason, Instant unMuteDate, ServerChannelMessage message);
CompletableFuture<Void> applyMuteRole(AUserInAServer aUserInAServer); CompletableFuture<Void> applyMuteRole(AUserInAServer aUserInAServer);
CompletableFuture<Void> muteMemberWithLog(MuteContext context); CompletableFuture<Void> muteMemberWithLog(MuteContext context);

View File

@@ -31,7 +31,8 @@ public interface MuteManagementService {
* @param muteId The id of the mute to search for * @param muteId The id of the mute to search for
* @return An optional containing a {@link Mute} if the ID exists, and null otherwise * @return An optional containing a {@link Mute} if the ID exists, and null otherwise
*/ */
Optional<Mute> findMute(Long muteId, Long serverId); Optional<Mute> findMuteOptional(Long muteId, Long serverId);
Mute findMute(Long muteId, Long serverId);
/** /**
* Saves the given mute to the database. * Saves the given mute to the database.

View File

@@ -9,6 +9,7 @@ import java.util.List;
public interface UserNoteManagementService { public interface UserNoteManagementService {
UserNote createUserNote(AUserInAServer aUserInAServer, String note); UserNote createUserNote(AUserInAServer aUserInAServer, String note);
void deleteNote(Long id, AServer server); void deleteNote(Long id, AServer server);
UserNote loadNote(Long userNoteId, Long serverId);
boolean noteExists(Long id, AServer server); boolean noteExists(Long id, AServer server);
List<UserNote> loadNotesForUser(AUserInAServer aUserInAServer); List<UserNote> loadNotesForUser(AUserInAServer aUserInAServer);
List<UserNote> loadNotesForServer(AServer server); List<UserNote> loadNotesForServer(AServer server);

View File

@@ -15,6 +15,7 @@ public interface WarnManagementService {
List<Warning> getAllWarnsForUser(AUserInAServer aUserInAServer); List<Warning> getAllWarnsForUser(AUserInAServer aUserInAServer);
List<Warning> getAllWarningsOfServer(AServer server); List<Warning> getAllWarningsOfServer(AServer server);
Long getActiveWarnsForUser(AUserInAServer aUserInAServer); Long getActiveWarnsForUser(AUserInAServer aUserInAServer);
Optional<Warning> findById(Long id, Long serverId); Optional<Warning> findByIdOptional(Long id, Long serverId);
Warning findById(Long id, Long serverId);
void deleteWarning(Warning warn); void deleteWarning(Warning warn);
} }

View File

@@ -8,6 +8,7 @@ import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext; import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureEnum; import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition; import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition;
import dev.sheldan.abstracto.modmail.config.ModMailFeatures; import dev.sheldan.abstracto.modmail.config.ModMailFeatures;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread; import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
@@ -36,14 +37,19 @@ public class AnonReply extends AbstractConditionableCommand {
@Autowired @Autowired
private ModMailThreadManagementService modMailThreadManagementService; private ModMailThreadManagementService modMailThreadManagementService;
@Autowired
private BotService botService;
@Override @Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters(); List<Object> parameters = commandContext.getParameters().getParameters();
// text is optional, for example if only an attachment is sent // text is optional, for example if only an attachment is sent
String text = parameters.size() == 1 ? (String) parameters.get(0) : ""; String text = parameters.size() == 1 ? (String) parameters.get(0) : "";
ModMailThread thread = modMailThreadManagementService.getByChannel(commandContext.getUserInitiatedContext().getChannel()); ModMailThread thread = modMailThreadManagementService.getByChannel(commandContext.getUserInitiatedContext().getChannel());
return modMailThreadService.relayMessageToDm(thread, text, commandContext.getMessage(), true, commandContext.getChannel(), commandContext.getUndoActions()) Long threadId = thread.getId();
.thenApply(aVoid -> CommandResult.fromSuccess()); return botService.getMemberInServerAsync(thread.getUser()).thenCompose(member ->
modMailThreadService.relayMessageToDm(threadId, text, commandContext.getMessage(), true, commandContext.getChannel(), commandContext.getUndoActions(), member)
).thenApply(aVoid -> CommandResult.fromSuccess());
} }
@Override @Override

View File

@@ -8,7 +8,6 @@ import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter; import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureEnum; import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
@@ -62,12 +61,7 @@ public class Contact extends AbstractConditionableCommand {
List<CompletableFuture<Message>> futures = channelService.sendEmbedTemplateInChannel("modmail_thread_already_exists", model, commandContext.getChannel()); List<CompletableFuture<Message>> futures = channelService.sendEmbedTemplateInChannel("modmail_thread_already_exists", model, commandContext.getChannel());
return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromSuccess()); return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromSuccess());
} else { } else {
FullUserInServer fullUser = FullUserInServer return modMailThreadService.createModMailThreadForUser(targetUser, null, commandContext.getChannel(), false, commandContext.getUndoActions())
.builder()
.aUserInAServer(user)
.member(targetUser)
.build();
return modMailThreadService.createModMailThreadForUser(fullUser, null, commandContext.getChannel(), false, commandContext.getUndoActions())
.thenApply(aVoid -> CommandResult.fromSuccess()); .thenApply(aVoid -> CommandResult.fromSuccess());
} }
} }

View File

@@ -8,6 +8,7 @@ import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext; import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureEnum; import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition; import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition;
import dev.sheldan.abstracto.modmail.config.ModMailFeatures; import dev.sheldan.abstracto.modmail.config.ModMailFeatures;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread; import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
@@ -35,13 +36,18 @@ public class Reply extends AbstractConditionableCommand {
@Autowired @Autowired
private ModMailThreadManagementService modMailThreadManagementService; private ModMailThreadManagementService modMailThreadManagementService;
@Autowired
private BotService botService;
@Override @Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters(); List<Object> parameters = commandContext.getParameters().getParameters();
String text = parameters.size() == 1 ? (String) parameters.get(0) : ""; String text = parameters.size() == 1 ? (String) parameters.get(0) : "";
ModMailThread thread = modMailThreadManagementService.getByChannel(commandContext.getUserInitiatedContext().getChannel()); ModMailThread thread = modMailThreadManagementService.getByChannel(commandContext.getUserInitiatedContext().getChannel());
return modMailThreadService.relayMessageToDm(thread, text, commandContext.getMessage(), false, commandContext.getChannel(), commandContext.getUndoActions()) Long threadId = thread.getId();
.thenApply(aVoid -> CommandResult.fromSuccess()); return botService.getMemberInServerAsync(thread.getUser()).thenCompose(member ->
modMailThreadService.relayMessageToDm(threadId, text, commandContext.getMessage(), false, commandContext.getChannel(), commandContext.getUndoActions(), member)
).thenApply(aVoid -> CommandResult.fromSuccess());
} }
@Override @Override

View File

@@ -41,13 +41,19 @@ public class ModMailMessageListener implements PrivateMessageReceivedListener {
@Override @Override
@Transactional @Transactional
public void execute(Message message) { public void execute(Message message) {
if(message.getAuthor().isBot()) {
return;
}
AUser user = userManagementService.loadUser(message.getAuthor().getIdLong()); AUser user = userManagementService.loadUser(message.getAuthor().getIdLong());
if(modMailThreadManagementService.hasOpenModMailThread(user)) { if(modMailThreadManagementService.hasOpenModMailThread(user)) {
log.trace("User {} has an open modmail thread. Forwarding message {}.", user.getId(), message.getId()); log.trace("User {} has an open modmail thread. Forwarding message {}.", user.getId(), message.getId());
// there is only one open mod mail thread for a user at a time, so we can select the first one // there is only one open mod mail thread for a user at a time, so we can select the first one
// we cannot use the AUserInAServer directly, because a message in a private channel does not have a Member // we cannot use the AUserInAServer directly, because a message in a private channel does not have a Member
ModMailThread existingThread = modMailThreadManagementService.getOpenModMailThreadsForUser(user).get(0); ModMailThread existingThread = modMailThreadManagementService.getOpenModMailThreadsForUser(user).get(0);
modMailThreadService.relayMessageToModMailThread(existingThread, message, new ArrayList<>()); modMailThreadService.relayMessageToModMailThread(existingThread, message, new ArrayList<>()).exceptionally(throwable -> {
log.error("Error when relaying message from user {} in modmail listener.", message.getAuthor().getIdLong(), throwable);
return null;
});
} else { } else {
log.info("User {} does not have an open modmail thread. Crating prompt.", user.getId()); log.info("User {} does not have an open modmail thread. Crating prompt.", user.getId());
modMailThreadService.createModMailPrompt(user, message); modMailThreadService.createModMailPrompt(user, message);

View File

@@ -1,10 +1,13 @@
package dev.sheldan.abstracto.modmail.service; package dev.sheldan.abstracto.modmail.service;
import dev.sheldan.abstracto.core.models.ServerChannelMessage; import dev.sheldan.abstracto.core.models.ServerChannelMessageUser;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.modmail.models.database.ModMailMessage; import dev.sheldan.abstracto.modmail.models.database.ModMailMessage;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread; import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
import dev.sheldan.abstracto.modmail.models.dto.LoadedModmailThreadMessage;
import dev.sheldan.abstracto.modmail.models.dto.LoadedModmailThreadMessageList;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -24,56 +27,75 @@ public class ModMailMessageServiceBean implements ModMailMessageService {
private BotService botService; private BotService botService;
@Override @Override
public List<CompletableFuture<Message>> loadModMailMessages(List<ModMailMessage> modMailMessages) { public LoadedModmailThreadMessageList loadModMailMessages(List<ModMailMessage> modMailMessages) {
if(modMailMessages.isEmpty()) { if(modMailMessages.isEmpty()) {
return new ArrayList<>(); return LoadedModmailThreadMessageList.builder().build();
} }
// all message must be from the same thread // all message must be from the same thread
ModMailThread thread = modMailMessages.get(0).getThreadReference(); ModMailThread thread = modMailMessages.get(0).getThreadReference();
log.trace("Loading {} mod mail messages from thread {} in server {}.", modMailMessages.size(), thread.getId(), thread.getServer().getId()); log.trace("Loading {} mod mail messages from thread {} in server {}.", modMailMessages.size(), thread.getId(), thread.getServer().getId());
List<ServerChannelMessage> messageIds = new ArrayList<>(); List<ServerChannelMessageUser> messageIds = new ArrayList<>();
modMailMessages.forEach(modMailMessage -> { modMailMessages.forEach(modMailMessage -> {
ServerChannelMessage.ServerChannelMessageBuilder serverChannelMessageBuilder = ServerChannelMessage ServerChannelMessageUser.ServerChannelMessageUserBuilder serverChannelMessageBuilder = ServerChannelMessageUser
.builder() .builder()
.messageId(modMailMessage.getMessageId()); .messageId(modMailMessage.getMessageId())
// if its not from a private chat, we need to set the server and channel ID in order to fetch the data .userId(modMailMessage.getAuthor().getUserReference().getId())
.serverId(thread.getServer().getId());
// if its not from a private chat, we need to set channel ID in order to fetch the data
if(Boolean.FALSE.equals(modMailMessage.getDmChannel())) { if(Boolean.FALSE.equals(modMailMessage.getDmChannel())) {
log.trace("Message {} was from DM.", modMailMessage.getMessageId()); log.trace("Message {} was from DM.", modMailMessage.getMessageId());
serverChannelMessageBuilder serverChannelMessageBuilder
.channelId(modMailMessage.getThreadReference().getChannel().getId()) .channelId(modMailMessage.getThreadReference().getChannel().getId());
.serverId(modMailMessage.getThreadReference().getServer().getId());
} }
messageIds.add(serverChannelMessageBuilder.build()); messageIds.add(serverChannelMessageBuilder.build());
}); });
List<CompletableFuture<Message>> messageFutures = new ArrayList<>(); List<LoadedModmailThreadMessage> messageFutures = new ArrayList<>();
// add the place holder futures, which are then resolved one by one // add the place holder futures, which are then resolved one by one
// because we cannot directly fetch the messages, in case they are in a private channel // because we cannot directly fetch the messages, in case they are in a private channel
// the opening of a private channel is a rest operation it itself, so we need // the opening of a private channel is a rest operation it itself, so we need
// to create the promises here already, else the list is empty for example // to create the promises here already, else the list is empty for example
modMailMessages.forEach(modMailMessage -> messageFutures.add(new CompletableFuture<>())); modMailMessages.forEach(modMailMessage -> messageFutures.add(getLoadedModmailThreadMessage()));
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(thread.getServer().getId(), thread.getChannel().getId()); Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(thread.getServer().getId(), thread.getChannel().getId());
if(textChannelFromServer.isPresent()) { if(textChannelFromServer.isPresent()) {
TextChannel modMailThread = textChannelFromServer.get(); TextChannel modMailThread = textChannelFromServer.get();
botService.getInstance().openPrivateChannelById(thread.getUser().getUserReference().getId()).queue(privateChannel -> { Long userId = thread.getUser().getUserReference().getId();
Iterator<CompletableFuture<Message>> iterator = messageFutures.iterator(); botService.getInstance().openPrivateChannelById(userId).queue(privateChannel -> {
Iterator<LoadedModmailThreadMessage> iterator = messageFutures.iterator();
messageIds.forEach(serverChannelMessage -> { messageIds.forEach(serverChannelMessage -> {
log.trace("Loading message {}.", serverChannelMessage.getMessageId()); log.trace("Loading message {}.", serverChannelMessage.getMessageId());
// TODO fix out of order promises CompletableFuture<Message> messageFuture;
// depending what the source of the message is, we need to fetch the message from the correct channel CompletableFuture<Member> memberFuture = botService.getMemberInServerAsync(serverChannelMessage.getServerId(), serverChannelMessage.getUserId());
if(serverChannelMessage.getChannelId() == null){ if(serverChannelMessage.getChannelId() == null){
privateChannel.retrieveMessageById(serverChannelMessage.getMessageId()).queue(message -> iterator.next().complete(message), throwable -> { messageFuture = privateChannel.retrieveMessageById(serverChannelMessage.getMessageId()).submit();
log.info("Failed to load message in private channel with user {}", thread.getUser().getUserReference().getId());
iterator.next().complete(null);
});
} else { } else {
modMailThread.retrieveMessageById(serverChannelMessage.getMessageId()).queue(message -> iterator.next().complete(message), throwable -> { messageFuture = modMailThread.retrieveMessageById(serverChannelMessage.getMessageId()).submit();
log.info("Failed to load message {} in thread {}", serverChannelMessage.getMessageId(), modMailThread.getIdLong());
iterator.next().complete(null);
});
} }
CompletableFuture.allOf(messageFuture, memberFuture).whenComplete((aVoid, throwable) -> {
LoadedModmailThreadMessage next = iterator.next();
if(messageFuture.isCompletedExceptionally()) {
log.warn("Message {} from user {} in server {} failed to load.", serverChannelMessage.getMessageId(), serverChannelMessage.getUserId(), serverChannelMessage.getServerId());
messageFuture.exceptionally(throwable1 -> {
log.warn("Failed with:", throwable1);
return null;
});
next.getMessageFuture().complete(null);
} else {
next.getMessageFuture().complete(messageFuture.join());
}
if(memberFuture.isCompletedExceptionally()) {
next.getMemberFuture().complete(null);
} else {
next.getMemberFuture().complete(memberFuture.join());
}
});
}); });
}); });
} }
return messageFutures; return LoadedModmailThreadMessageList.builder().messageList(messageFutures).build();
}
public LoadedModmailThreadMessage getLoadedModmailThreadMessage() {
return LoadedModmailThreadMessage.builder().memberFuture(new CompletableFuture<>()).messageFuture(new CompletableFuture<>()).build();
} }
} }

View File

@@ -11,13 +11,14 @@ import dev.sheldan.abstracto.core.models.UndoActionInstance;
import dev.sheldan.abstracto.core.models.database.*; import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.service.*; import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService; 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.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerService;
import dev.sheldan.abstracto.core.utils.CompletableFutureList; import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import dev.sheldan.abstracto.core.utils.FutureUtils; import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.modmail.config.*; import dev.sheldan.abstracto.modmail.config.*;
import dev.sheldan.abstracto.modmail.exception.ModMailCategoryIdException; import dev.sheldan.abstracto.modmail.exception.ModMailCategoryIdException;
import dev.sheldan.abstracto.modmail.exception.ModMailThreadNotFoundException; import dev.sheldan.abstracto.modmail.exception.ModMailThreadNotFoundException;
import dev.sheldan.abstracto.modmail.models.dto.LoadedModmailThreadMessageList;
import dev.sheldan.abstracto.modmail.models.database.*; import dev.sheldan.abstracto.modmail.models.database.*;
import dev.sheldan.abstracto.modmail.models.dto.ServerChoice; import dev.sheldan.abstracto.modmail.models.dto.ServerChoice;
import dev.sheldan.abstracto.modmail.models.template.*; import dev.sheldan.abstracto.modmail.models.template.*;
@@ -41,6 +42,7 @@ import java.util.concurrent.CompletableFuture;
@Component @Component
@Slf4j @Slf4j
@Transactional
public class ModMailThreadServiceBean implements ModMailThreadService { public class ModMailThreadServiceBean implements ModMailThreadService {
/** /**
@@ -89,9 +91,6 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Autowired @Autowired
private MessageService messageService; private MessageService messageService;
@Autowired
private UserInServerService userInServerService;
@Autowired @Autowired
private FeatureFlagService featureFlagService; private FeatureFlagService featureFlagService;
@@ -116,6 +115,9 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Autowired @Autowired
private ModMailFeatureValidator modMailFeatureValidator; private ModMailFeatureValidator modMailFeatureValidator;
@Autowired
private ServerManagementService serverManagementService;
@Autowired @Autowired
private ModMailThreadServiceBean self; private ModMailThreadServiceBean self;
@@ -129,80 +131,81 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Override @Override
public CompletableFuture<Void> createModMailThreadForUser(FullUserInServer aUserInAServer, Message initialMessage, MessageChannel feedBackChannel, boolean userInitiated, List<UndoActionInstance> undoActions) { public CompletableFuture<Void> createModMailThreadForUser(Member member, Message initialMessage, MessageChannel feedBackChannel, boolean userInitiated, List<UndoActionInstance> undoActions) {
Long serverId = aUserInAServer.getAUserInAServer().getServerReference().getId(); Long serverId = member.getGuild().getIdLong();
Long categoryId = configService.getLongValue(MODMAIL_CATEGORY, serverId); Long categoryId = configService.getLongValue(MODMAIL_CATEGORY, serverId);
User user = aUserInAServer.getMember().getUser(); AServer server = serverManagementService.loadServer(member.getGuild().getIdLong());
User user = member.getUser();
log.info("Creating modmail channel for user {} in category {} on server {}.", user.getId(), categoryId, serverId); log.info("Creating modmail channel for user {} in category {} on server {}.", user.getId(), categoryId, serverId);
CompletableFuture<TextChannel> textChannelFuture = channelService.createTextChannel(user.getName() + user.getDiscriminator(), aUserInAServer.getAUserInAServer().getServerReference(), categoryId); CompletableFuture<TextChannel> textChannelFuture = channelService.createTextChannel(user.getName() + user.getDiscriminator(), server, categoryId);
return textChannelFuture.thenCompose(channel -> { return textChannelFuture.thenCompose(channel -> {
undoActions.add(UndoActionInstance.getChannelDeleteAction(serverId, channel.getIdLong())); undoActions.add(UndoActionInstance.getChannelDeleteAction(serverId, channel.getIdLong()));
return self.performModMailThreadSetup(aUserInAServer, initialMessage, channel, userInitiated, undoActions); return self.performModMailThreadSetup(member, initialMessage, channel, userInitiated, undoActions);
}); });
} }
/** /**
* This method is responsible for creating the instance in the database, sending the header in the newly created text channel and forwarding the initial message * This method is responsible for creating the instance in the database, sending the header in the newly created text channel and forwarding the initial message
* by the user (if any), after this is complete, this method executes the method to perform the mod mail notification. * by the user (if any), after this is complete, this method executes the method to perform the mod mail notification.
* @param aUserInAServer The {@link FullUserInServer} for which a {@link ModMailThread} is being created * @param member The {@link Member} for which a {@link ModMailThread} is being created
* @param initialMessage The {@link Message} which was sent by the user to open a thread, this is null, if the thread was opened via a command * @param initialMessage The {@link Message} which was sent by the user to open a thread, this is null, if the thread was opened via a command
* @param channel The created {@link TextChannel} in which the mod mail thread is dealt with * @param channel The created {@link TextChannel} in which the mod mail thread is dealt with
* @param userInitiated Whether or not the thread was initiated by a member * @param userInitiated Whether or not the thread was initiated by a member
* @param undoActions The list of actions to undo, in case an exception occurs * @param undoActions The list of actions to undo, in case an exception occurs
*/ */
@Transactional @Transactional
public CompletableFuture<Void> performModMailThreadSetup(FullUserInServer aUserInAServer, Message initialMessage, TextChannel channel, boolean userInitiated, List<UndoActionInstance> undoActions) { public CompletableFuture<Void> performModMailThreadSetup(Member member, Message initialMessage, TextChannel channel, boolean userInitiated, List<UndoActionInstance> undoActions) {
log.info("Performing modmail thread setup for channel {} for user {} in server {}. It was initiated by a user: {}.", channel.getIdLong(), aUserInAServer.getMember().getId(), channel.getGuild().getId(), userInitiated); log.info("Performing modmail thread setup for channel {} for user {} in server {}. It was initiated by a user: {}.", channel.getIdLong(), member.getId(), channel.getGuild().getId(), userInitiated);
Long userInServerId = aUserInAServer.getAUserInAServer().getUserInServerId(); CompletableFuture<Void> headerFuture = sendModMailHeader(channel, member);
CompletableFuture<Void> headerFuture = sendModMailHeader(channel, aUserInAServer); CompletableFuture<Message> userReplyMessage;
CompletableFuture<Void> userReplyMessage;
if(initialMessage != null){ if(initialMessage != null){
log.trace("Sending initial message {} of user {} to modmail thread {}.", initialMessage.getId(), aUserInAServer.getMember().getId(), channel.getId()); log.trace("Sending initial message {} of user {} to modmail thread {}.", initialMessage.getId(), member.getId(), channel.getId());
userReplyMessage = self.sendUserReply(channel, null, initialMessage, aUserInAServer.getAUserInAServer()); userReplyMessage = self.sendUserReply(channel, 0L, initialMessage, member, false);
} else { } else {
log.trace("No initial message to send."); log.trace("No initial message to send.");
userReplyMessage = CompletableFuture.completedFuture(null); userReplyMessage = CompletableFuture.completedFuture(null);
} }
CompletableFuture notificationFuture; CompletableFuture notificationFuture;
if (userInitiated) { if (userInitiated) {
notificationFuture = self.sendModMailNotification(aUserInAServer); notificationFuture = self.sendModMailNotification(member);
} else { } else {
notificationFuture = CompletableFuture.completedFuture(null); notificationFuture = CompletableFuture.completedFuture(null);
} }
return CompletableFuture.allOf(headerFuture, notificationFuture, userReplyMessage).thenAccept(aVoid -> { return CompletableFuture.allOf(headerFuture, notificationFuture, userReplyMessage).thenAccept(aVoid -> {
undoActions.clear(); undoActions.clear();
self.setupModMailThreadInDB(initialMessage, channel, userInServerId); self.setupModMailThreadInDB(initialMessage, channel, member, userReplyMessage.join());
}); });
} }
@Transactional @Transactional
public void setupModMailThreadInDB(Message initialMessage, TextChannel channel, Long userInServerId) { public void setupModMailThreadInDB(Message initialMessage, TextChannel channel, Member member, Message sendMessage) {
log.info("Persisting info about modmail thread {} in database.", channel.getIdLong()); log.info("Persisting info about modmail thread {} in database.", channel.getIdLong());
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(userInServerId); AUserInAServer aUserInAServer = userInServerManagementService.loadUser(member);
ModMailThread thread = createThreadObject(channel, aUserInAServer); ModMailThread thread = createThreadObject(channel, aUserInAServer);
if(initialMessage != null) { if(initialMessage != null) {
log.trace("Adding initial message {} to modmail thread in channel {}.", initialMessage.getId(), channel.getId()); log.trace("Adding initial message {} to modmail thread in channel {}.", initialMessage.getId(), channel.getId());
modMailMessageManagementService.addMessageToThread(thread, initialMessage, aUserInAServer, false, false); modMailMessageManagementService.addMessageToThread(thread, sendMessage, aUserInAServer, false, false);
} }
} }
/** /**
* Sends the message containing the pings to notify the staff members to handle the opened {@link ModMailThread} * Sends the message containing the pings to notify the staff members to handle the opened {@link ModMailThread}
* @param aUserInAServer The {@link FullUserInServer} which opened the thread * @param member The {@link FullUserInServer} which opened the thread
*/ */
@Transactional @Transactional
public CompletableFuture<Void> sendModMailNotification(FullUserInServer aUserInAServer) { public CompletableFuture<Void> sendModMailNotification(Member member) {
log.info("Sending modmail notification for new modmail thread about user {} in server {}.", aUserInAServer.getMember().getId(), aUserInAServer.getMember().getGuild().getId()); Long serverId = member.getGuild().getIdLong();
List<ModMailRole> rolesToPing = modMailRoleManagementService.getRolesForServer(aUserInAServer.getAUserInAServer().getServerReference()); log.info("Sending modmail notification for new modmail thread about user {} in server {}.", member.getId(), serverId);
log.trace("Pinging {} roles to notify about modmail thread about user {} in server {}.", rolesToPing.size(), aUserInAServer.getMember().getId(), aUserInAServer.getMember().getGuild().getId()); AServer server = serverManagementService.loadServer(serverId);
List<ModMailRole> rolesToPing = modMailRoleManagementService.getRolesForServer(server);
log.trace("Pinging {} roles to notify about modmail thread about user {} in server {}.", rolesToPing.size(), member.getId(), serverId);
ModMailNotificationModel modMailNotificationModel = ModMailNotificationModel ModMailNotificationModel modMailNotificationModel = ModMailNotificationModel
.builder() .builder()
.threadUser(aUserInAServer) .member(member)
.roles(rolesToPing) .roles(rolesToPing)
.build(); .build();
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_notification_message", modMailNotificationModel); MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_notification_message", modMailNotificationModel);
List<CompletableFuture<Message>> modmailping = postTargetService.sendEmbedInPostTarget(messageToSend, ModMailPostTargets.MOD_MAIL_PING, aUserInAServer.getMember().getGuild().getIdLong()); List<CompletableFuture<Message>> modmailping = postTargetService.sendEmbedInPostTarget(messageToSend, ModMailPostTargets.MOD_MAIL_PING, serverId);
return CompletableFuture.allOf(modmailping.toArray(new CompletableFuture[0])); return CompletableFuture.allOf(modmailping.toArray(new CompletableFuture[0]));
} }
@@ -237,7 +240,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
if(!knownServers.isEmpty()) { if(!knownServers.isEmpty()) {
log.info("There are {} shared servers between user and abstracto.", knownServers.size()); log.info("There are {} shared servers between user and abstracto.", knownServers.size());
List<ServerChoice> availableGuilds = new ArrayList<>(); List<ServerChoice> availableGuilds = new ArrayList<>();
HashMap<String, AUserInAServer> choices = new HashMap<>(); HashMap<String, Long> choices = new HashMap<>();
for (int i = 0; i < knownServers.size(); i++) { for (int i = 0; i < knownServers.size(); i++) {
AUserInAServer aUserInAServer = knownServers.get(i); AUserInAServer aUserInAServer = knownServers.get(i);
// only take the servers in which mod mail is actually enabled, would not make much sense to make the // only take the servers in which mod mail is actually enabled, would not make much sense to make the
@@ -246,13 +249,13 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
AServer serverReference = aUserInAServer.getServerReference(); AServer serverReference = aUserInAServer.getServerReference();
FullGuild guild = FullGuild FullGuild guild = FullGuild
.builder() .builder()
.guild(botService.getGuildByIdNullable(serverReference.getId())) .guild(botService.getGuildById(serverReference.getId()))
.server(serverReference) .server(serverReference)
.build(); .build();
// TODO support more than this limited amount of servers // TODO support more than this limited amount of servers
String reactionEmote = NUMBER_EMOJI.get(i); String reactionEmote = NUMBER_EMOJI.get(i);
ServerChoice serverChoice = ServerChoice.builder().guild(guild).reactionEmote(reactionEmote).build(); ServerChoice serverChoice = ServerChoice.builder().guild(guild).reactionEmote(reactionEmote).build();
choices.put(reactionEmote, aUserInAServer); choices.put(reactionEmote, aUserInAServer.getServerReference().getId());
availableGuilds.add(serverChoice); availableGuilds.add(serverChoice);
} }
} }
@@ -269,28 +272,28 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.setEventWaiter(eventWaiter) .setEventWaiter(eventWaiter)
.setDescription(text) .setDescription(text)
.setAction(reactionEmote -> { .setAction(reactionEmote -> {
AUserInAServer chosenServer = choices.get(reactionEmote.getEmoji()); Long chosenServerId = choices.get(reactionEmote.getEmoji());
log.trace("Executing action for creationg a modmail thread in server {} for user {}.", chosenServer.getServerReference().getId(), chosenServer.getUserReference().getId()); log.trace("Executing action for creationg a modmail thread in server {} for user {}.", chosenServerId, initialMessage.getAuthor().getIdLong());
Member memberInServer = botService.getMemberInServer(chosenServer); botService.getMemberInServerAsync(chosenServerId, initialMessage.getAuthor().getIdLong()).thenCompose(member ->
FullUserInServer fullUser = FullUserInServer.builder().member(memberInServer).aUserInAServer(chosenServer).build(); self.createModMailThreadForUser(member, initialMessage, initialMessage.getChannel(), true, new ArrayList<>()).exceptionally(throwable -> {
self.createModMailThreadForUser(fullUser, initialMessage, initialMessage.getChannel(), true, new ArrayList<>()).exceptionally(throwable -> { log.error("Failed to setup thread correctly", throwable);
log.error("Failed to setup thread correctly", throwable); return null;
return null; })
}); );
}) })
.build(); .build();
log.trace("Displaying server choice message for user {} in channel {}.", user.getId(), initialMessage.getChannel().getId()); log.trace("Displaying server choice message for user {} in channel {}.", user.getId(), initialMessage.getChannel().getId());
menu.display(initialMessage.getChannel()); menu.display(initialMessage.getChannel());
} else if(availableGuilds.size() == 1) { } else if(availableGuilds.size() == 1) {
// if exactly one server is available, open the thread directly // if exactly one server is available, open the thread directly
AUserInAServer chosenServer = choices.get(availableGuilds.get(0).getReactionEmote()); Long chosenServerId = choices.get(availableGuilds.get(0).getReactionEmote());
log.info("Only one server available to modmail. Directly opening modmail thread for user {} in server {}.", chosenServer.getUserReference().getId(), chosenServer.getServerReference().getId()); log.info("Only one server available to modmail. Directly opening modmail thread for user {} in server {}.", initialMessage.getAuthor().getId(), chosenServerId);
Member memberInServer = botService.getMemberInServer(chosenServer); botService.getMemberInServerAsync(chosenServerId, initialMessage.getAuthor().getIdLong()).thenCompose(member ->
FullUserInServer fullUser = FullUserInServer.builder().member(memberInServer).aUserInAServer(chosenServer).build(); self.createModMailThreadForUser(member, initialMessage, initialMessage.getChannel(), true, new ArrayList<>()).exceptionally(throwable -> {
self.createModMailThreadForUser(fullUser, initialMessage, initialMessage.getChannel(), true, new ArrayList<>()).exceptionally(throwable -> { log.error("Failed to setup thread correctly", throwable);
log.error("Failed to setup thread correctly", throwable); return null;
return null; })
}); );
} else { } else {
log.info("No server available to open a modmail thread in."); log.info("No server available to open a modmail thread in.");
// in case there is no server available, send an error message // in case there is no server available, send an error message
@@ -306,15 +309,16 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* Method used to send the header of a newly created mod mail thread. This message contains information about * Method used to send the header of a newly created mod mail thread. This message contains information about
* the user which the thread is about * the user which the thread is about
* @param channel The {@link TextChannel} in which the mod mail thread is present in * @param channel The {@link TextChannel} in which the mod mail thread is present in
* @param aUserInAServer The {@link AUserInAServer} which the {@link ModMailThread} is about * @param member The {@link Member} which the {@link ModMailThread} is about
*/ */
private CompletableFuture<Void> sendModMailHeader(TextChannel channel, FullUserInServer aUserInAServer) { private CompletableFuture<Void> sendModMailHeader(TextChannel channel, Member member) {
log.trace("Sending modmail thread header for tread in channel {} on server {}.", channel.getIdLong(), channel.getGuild().getId()); log.trace("Sending modmail thread header for tread in channel {} on server {}.", channel.getIdLong(), channel.getGuild().getId());
ModMailThread latestThread = modMailThreadManagementService.getLatestModMailThread(aUserInAServer.getAUserInAServer()); AUserInAServer aUserInAServer = userInServerManagementService.loadUser(member);
List<ModMailThread> oldThreads = modMailThreadManagementService.getModMailThreadForUser(aUserInAServer.getAUserInAServer()); ModMailThread latestThread = modMailThreadManagementService.getLatestModMailThread(aUserInAServer);
List<ModMailThread> oldThreads = modMailThreadManagementService.getModMailThreadForUser(aUserInAServer);
ModMailThreaderHeader header = ModMailThreaderHeader ModMailThreaderHeader header = ModMailThreaderHeader
.builder() .builder()
.threadUser(aUserInAServer) .member(member)
.latestModMailThread(latestThread) .latestModMailThread(latestThread)
.pastModMailThreadCount((long)oldThreads.size()) .pastModMailThreadCount((long)oldThreads.size())
.build(); .build();
@@ -323,19 +327,25 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
} }
@Override @Override
public CompletableFuture<Void> relayMessageToModMailThread(ModMailThread modMailThread, Message message, List<UndoActionInstance> undoActions) { public CompletableFuture<Message> relayMessageToModMailThread(ModMailThread modMailThread, Message message, List<UndoActionInstance> undoActions) {
Long serverId = modMailThread.getServer().getId();
Long channelId = modMailThread.getChannel().getId();
Long modmailThreadId = modMailThread.getId();
log.trace("Relaying message {} to modmail thread {} for user {} to server {}.", message.getId(), modMailThread.getId(), message.getAuthor().getIdLong(), modMailThread.getServer().getId()); log.trace("Relaying message {} to modmail thread {} for user {} to server {}.", message.getId(), modMailThread.getId(), message.getAuthor().getIdLong(), modMailThread.getServer().getId());
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(modMailThread.getServer().getId(), modMailThread.getChannel().getId()); return botService.getMemberInServerAsync(modMailThread.getServer().getId(), message.getAuthor().getIdLong()).thenCompose(member -> {
if(textChannelFromServer.isPresent()) { Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(serverId, channelId);
TextChannel textChannel = textChannelFromServer.get(); if(textChannelFromServer.isPresent()) {
return self.sendUserReply(textChannel, modMailThread, message, modMailThread.getUser()); TextChannel textChannel = textChannelFromServer.get();
} else { return self.sendUserReply(textChannel, modmailThreadId, message, member, true);
log.warn("Closing modmail thread {}, because it seems the channel {} in server {} got deleted.", modMailThread.getId(), modMailThread.getChannel().getId(), modMailThread.getServer().getId()); } else {
// in this case there was no text channel on the server associated with the mod mail thread log.warn("Closing modmail thread {}, because it seems the channel {} in server {} got deleted.", modmailThreadId, channelId, serverId);
// close the existing one, so the user can start a new one // in this case there was no text channel on the server associated with the mod mail thread
self.closeModMailThreadInDb(modMailThread.getId()); // close the existing one, so the user can start a new one
return message.getChannel().sendMessage(templateService.renderTemplate("modmail_failed_to_forward_message", new Object())).submit().thenApply(message1 -> null); self.closeModMailThreadInDb(modmailThreadId);
} return message.getChannel().sendMessage(templateService.renderTemplate("modmail_failed_to_forward_message", new Object())).submit();
}
});
} }
/** /**
@@ -343,50 +353,50 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* the appropriate {@link ModMailThread} channel, the returned promise only returns if the message was dealt with on the user * the appropriate {@link ModMailThread} channel, the returned promise only returns if the message was dealt with on the user
* side. * side.
* @param textChannel The {@link TextChannel} in which the {@link ModMailThread} is being handled * @param textChannel The {@link TextChannel} in which the {@link ModMailThread} is being handled
* @param modMailThread The {@link ModMailThread} to which the received {@link Message} is a reply to, can be null, if it is null, its the initial message * @param modMailThreadId The id of the modmail thread to which the received {@link Message} is a reply to, can be null, if it is null, its the initial message
* @param message The received message from the user * @param message The received message from the user
* @param member The {@link Member} instance from the user the thread is about. It is used as author
* @param modMailThreadExists Whether or not the modmail thread already exists and is persisted.
* @return A {@link CompletableFuture} which resolves when the post processing of the message is completed (adding read notification, and storing messageIDs) * @return A {@link CompletableFuture} which resolves when the post processing of the message is completed (adding read notification, and storing messageIDs)
*/ */
public CompletableFuture<Void> sendUserReply(TextChannel textChannel, ModMailThread modMailThread, Message message, AUserInAServer userInServer) { public CompletableFuture<Message> sendUserReply(TextChannel textChannel, Long modMailThreadId, Message message, Member member, boolean modMailThreadExists) {
boolean modMailThreadExists = modMailThread != null; List<CompletableFuture<Member>> subscriberMemberFutures = new ArrayList<>();
FullUserInServer fullUser = FullUserInServer
.builder()
.aUserInAServer(userInServer)
.member(botService.getMemberInServer(userInServer))
.build();
List<FullUserInServer> subscribers = new ArrayList<>();
if(modMailThreadExists) { if(modMailThreadExists) {
ModMailThread modMailThread = modMailThreadManagementService.getById(modMailThreadId);
List<ModMailThreadSubscriber> subscriberList = modMailSubscriberManagementService.getSubscribersForThread(modMailThread); List<ModMailThreadSubscriber> subscriberList = modMailSubscriberManagementService.getSubscribersForThread(modMailThread);
subscriberList.forEach(modMailThreadSubscriber -> { subscriberList.forEach(modMailThreadSubscriber ->
FullUserInServer subscriber = FullUserInServer subscriberMemberFutures.add(botService.getMemberInServerAsync(modMailThreadSubscriber.getSubscriber()))
.builder() );
.aUserInAServer(modMailThreadSubscriber.getSubscriber()) if(subscriberList.isEmpty()) {
.member(botService.getMemberInServer(modMailThreadSubscriber.getSubscriber())) subscriberMemberFutures.add(CompletableFuture.completedFuture(null));
.build(); }
subscribers.add(subscriber); log.trace("Pinging {} subscribers for modmail thread {}.", subscriberList.size(), modMailThreadId);
}); } else {
log.trace("Pinging {} subscribers for modmail thread {}.", subscriberList.size(), modMailThread.getId()); subscriberMemberFutures.add(CompletableFuture.completedFuture(null));
} }
ModMailUserReplyModel modMailUserReplyModel = ModMailUserReplyModel return FutureUtils.toSingleFutureGeneric(subscriberMemberFutures).thenCompose(firstVoid -> {
.builder() List<FullUserInServer> subscribers = new ArrayList<>();
.modMailThread(modMailThread) ModMailUserReplyModel modMailUserReplyModel = ModMailUserReplyModel
.postedMessage(message) .builder()
.threadUser(fullUser) .postedMessage(message)
.subscribers(subscribers) .member(member)
.build(); .subscribers(subscribers)
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_user_message", modMailUserReplyModel); .build();
List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToSendToChannel(messageToSend, textChannel); MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_user_message", modMailUserReplyModel);
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])) List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToSendToChannel(messageToSend, textChannel);
.thenCompose(aVoid -> { return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0]))
log.trace("Adding read reaction to initial message for mod mail thread in channel {}.", textChannel.getGuild().getId()); .thenCompose(aVoid -> {
return messageService.addReactionToMessageWithFuture("readReaction", textChannel.getGuild().getIdLong(), message); log.trace("Adding read reaction to initial message for mod mail thread in channel {}.", textChannel.getGuild().getId());
}) return messageService.addReactionToMessageWithFuture("readReaction", textChannel.getGuild().getIdLong(), message);
.thenAccept(aVoid -> { })
if(modMailThreadExists) { .thenApply(aVoid -> {
self.postProcessSendMessages(textChannel, completableFutures.get(0).join()); if(modMailThreadExists) {
} self.postProcessSendMessages(textChannel, completableFutures.get(0).join());
}); }
return completableFutures.get(0).join();
});
});
} }
@@ -397,7 +407,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
*/ */
@Transactional @Transactional
public void postProcessSendMessages(TextChannel textChannel, Message message) { public void postProcessSendMessages(TextChannel textChannel, Message message) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getById(textChannel.getIdLong()); Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(textChannel.getIdLong());
if(modMailThreadOpt.isPresent()) { if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get(); ModMailThread modMailThread = modMailThreadOpt.get();
log.trace("Adding message {} sent from user to modmail thread {} and setting status to {}.", message.getId(), modMailThread.getId(), ModMailThreadState.USER_REPLIED); log.trace("Adding message {} sent from user to modmail thread {} and setting status to {}.", message.getId(), modMailThread.getId(), ModMailThreadState.USER_REPLIED);
@@ -410,39 +420,35 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
} }
@Override @Override
public CompletableFuture<Void> relayMessageToDm(ModMailThread modMailThread, String text, Message message, boolean anonymous, MessageChannel feedBack, List<UndoActionInstance> undoActions) { public CompletableFuture<Void> relayMessageToDm(Long modmailThreadId, String text, Message message, boolean anonymous, MessageChannel feedBack, List<UndoActionInstance> undoActions, Member targetMember) {
Long modMailThreadId = modMailThread.getId(); log.info("Relaying message {} to user {} in modmail thread {} on server {}.", message.getId(), targetMember.getId(), modmailThreadId, targetMember.getGuild().getId());
log.info("Relaying message {} to user {} in modmail thread {} on server {}.", message.getId(), modMailThread.getUser().getUserReference().getId(), modMailThread.getId(), modMailThread.getServer().getId()); AUserInAServer moderator = userInServerManagementService.loadUser(message.getMember());
User userById = botService.getInstance().getUserById(modMailThread.getUser().getUserReference().getId()); ModMailThread modMailThread = modMailThreadManagementService.getById(modmailThreadId);
if(userById != null) { FullUserInServer fullThreadUser = FullUserInServer
AUserInAServer moderator = userInServerManagementService.loadUser(message.getMember()); .builder()
Member userInGuild = botService.getMemberInServer(modMailThread.getUser()); .aUserInAServer(modMailThread.getUser())
FullUserInServer fullThreadUser = FullUserInServer .member(targetMember)
.builder() .build();
.aUserInAServer(modMailThread.getUser()) ModMailModeratorReplyModel.ModMailModeratorReplyModelBuilder modMailModeratorReplyModelBuilder = ModMailModeratorReplyModel
.member(userInGuild) .builder()
.build(); .text(text)
ModMailModeratorReplyModel.ModMailModeratorReplyModelBuilder modMailModeratorReplyModelBuilder = ModMailModeratorReplyModel .modMailThread(modMailThread)
.builder() .postedMessage(message)
.text(text) .anonymous(anonymous)
.modMailThread(modMailThread) .threadUser(fullThreadUser);
.postedMessage(message) if(anonymous) {
.anonymous(anonymous) log.trace("Message is sent anonymous.");
.threadUser(fullThreadUser); modMailModeratorReplyModelBuilder.moderator(botService.getBotInGuild(modMailThread.getServer()));
if(anonymous) { } else {
log.trace("Message is sent anonymous."); // should be loaded, because we are currently processing a command caused by the message
modMailModeratorReplyModelBuilder.moderator(botService.getBotInGuild(modMailThread.getServer())); Member moderatorMember = botService.getMemberInServer(moderator);
} else { modMailModeratorReplyModelBuilder.moderator(moderatorMember);
Member moderatorMember = botService.getMemberInServer(moderator);
modMailModeratorReplyModelBuilder.moderator(moderatorMember);
}
ModMailModeratorReplyModel modMailUserReplyModel = modMailModeratorReplyModelBuilder.build();
CompletableFuture<Message> future = messageService.sendEmbedToUserWithMessage(userById, "modmail_staff_message", modMailUserReplyModel);
return future.thenAccept(sendMessage ->
self.saveSendMessagesAndUpdateState(modMailThreadId, anonymous, moderator, sendMessage)
);
} }
throw new AbstractoRunTimeException("User in server not found."); ModMailModeratorReplyModel modMailUserReplyModel = modMailModeratorReplyModelBuilder.build();
CompletableFuture<Message> future = messageService.sendEmbedToUserWithMessage(targetMember.getUser(), "modmail_staff_message", modMailUserReplyModel);
return future.thenAccept(sendMessage ->
self.saveSendMessagesAndUpdateState(modmailThreadId, anonymous, moderator, sendMessage)
);
} }
@Override @Override
@@ -457,17 +463,21 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
Long modMailThreadId = modMailThread.getId(); Long modMailThreadId = modMailThread.getId();
log.info("Starting closing procedure for thread {}", modMailThread.getId()); log.info("Starting closing procedure for thread {}", modMailThread.getId());
List<ModMailMessage> modMailMessages = modMailThread.getMessages(); List<ModMailMessage> modMailMessages = modMailThread.getMessages();
Long userId = modMailThread.getUser().getUserReference().getId();
Long serverId = modMailThread.getServer().getId();
if(logThread) { if(logThread) {
List<CompletableFuture<Message>> messages = modMailMessageService.loadModMailMessages(modMailMessages); LoadedModmailThreadMessageList messages = modMailMessageService.loadModMailMessages(modMailMessages);
CompletableFuture<Void> messagesFuture = FutureUtils.toSingleFutureGeneric(messages); CompletableFuture<Void> messagesFuture = FutureUtils.toSingleFuture(messages.getAllFutures());
return messagesFuture.handle((aVoid, throwable) -> return messagesFuture.handle((aVoid, throwable) ->
self.logMessagesToModMailLog(note, notifyUser, modMailThreadId, undoActions, messages) self.logMessagesToModMailLog(note, notifyUser, modMailThreadId, undoActions, messages, serverId, userId)
).toCompletableFuture().thenCompose(o -> o); ).toCompletableFuture().thenCompose(o -> o);
} else { } else {
log.trace("Not logging modmail thread {}.", modMailThreadId); log.trace("Not logging modmail thread {}.", modMailThreadId);
return self.afterSuccessfulLog(modMailThreadId, notifyUser, undoActions); return botService.getMemberInServerAsync(modMailThread.getUser()).thenCompose(member ->
self.afterSuccessfulLog(modMailThreadId, notifyUser, member, undoActions)
);
} }
} }
@@ -479,10 +489,12 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* @param modMailThreadId The ID of the {@link ModMailThread} which is being closed * @param modMailThreadId The ID of the {@link ModMailThread} which is being closed
* @param undoActions The list of {@link UndoActionInstance} to execute in case of exceptions * @param undoActions The list of {@link UndoActionInstance} to execute in case of exceptions
* @param messages The list of loaded {@link Message} to log * @param messages The list of loaded {@link Message} to log
* @param serverId The ID of the {@link Guild} the {@link ModMailThread} is in
* @param userId The ID of the user the {@link ModMailThread} is about
*/ */
@Transactional @Transactional
public CompletableFuture<Void> logMessagesToModMailLog(String note, Boolean notifyUser, Long modMailThreadId, List<UndoActionInstance> undoActions, List<CompletableFuture<Message>> messages) { public CompletableFuture<Void> logMessagesToModMailLog(String note, Boolean notifyUser, Long modMailThreadId, List<UndoActionInstance> undoActions, LoadedModmailThreadMessageList messages, Long serverId, Long userId) {
log.trace("Logging {} modmail messages for modmail thread {}.", messages.size(), modMailThreadId); log.trace("Logging {} modmail messages for modmail thread {}.", messages.getMessageList().size(), modMailThreadId);
try { try {
CompletableFutureList<Message> list = self.logModMailThread(modMailThreadId, messages, note, undoActions); CompletableFutureList<Message> list = self.logModMailThread(modMailThreadId, messages, note, undoActions);
return list.getMainFuture().thenCompose(avoid -> { return list.getMainFuture().thenCompose(avoid -> {
@@ -490,7 +502,9 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
Message message = messageCompletableFuture.join(); Message message = messageCompletableFuture.join();
undoActions.add(UndoActionInstance.getMessageDeleteAction(messageCompletableFuture.join().getChannel().getIdLong(), message.getIdLong())); undoActions.add(UndoActionInstance.getMessageDeleteAction(messageCompletableFuture.join().getChannel().getIdLong(), message.getIdLong()));
}); });
return self.afterSuccessfulLog(modMailThreadId, notifyUser, undoActions); return botService.getMemberInServerAsync(serverId, userId).thenCompose(member ->
self.afterSuccessfulLog(modMailThreadId, notifyUser, member, undoActions)
);
}); });
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to log mod mail messages", e); log.error("Failed to log mod mail messages", e);
@@ -507,22 +521,21 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* @throws ModMailThreadNotFoundException in case the {@link ModMailThread} is not found by the ID * @throws ModMailThreadNotFoundException in case the {@link ModMailThread} is not found by the ID
*/ */
@Transactional @Transactional
public CompletableFuture<Void> afterSuccessfulLog(Long modMailThreadId, Boolean notifyUser, List<UndoActionInstance> undoActions) { public CompletableFuture<Void> afterSuccessfulLog(Long modMailThreadId, Boolean notifyUser, Member modMailThreaduser, List<UndoActionInstance> undoActions) {
log.trace("Mod mail logging for thread {} has completed. Starting post logging activities.", modMailThreadId); log.trace("Mod mail logging for thread {} has completed. Starting post logging activities.", modMailThreadId);
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getById(modMailThreadId); Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
if(modMailThreadOpt.isPresent()) { if(modMailThreadOpt.isPresent()) {
if(notifyUser) { if(notifyUser) {
log.trace("Notifying user about the closed modmail thread {}.", modMailThreadId); log.info("Notifying user about the closed modmail thread {}.", modMailThreadId);
ModMailThread modMailThread = modMailThreadOpt.get(); ModMailThread modMailThread = modMailThreadOpt.get();
User user = botService.getMemberInServer(modMailThread.getUser()).getUser();
HashMap<String, String> closingMessage = new HashMap<>(); HashMap<String, String> closingMessage = new HashMap<>();
String defaultValue = templateService.renderSimpleTemplate("modmail_closing_user_message_description"); String defaultValue = templateService.renderSimpleTemplate("modmail_closing_user_message_description");
closingMessage.put("closingMessage", configService.getStringValue(MODMAIL_CLOSING_MESSAGE_TEXT, modMailThread.getServer().getId(), defaultValue)); closingMessage.put("closingMessage", configService.getStringValue(MODMAIL_CLOSING_MESSAGE_TEXT, modMailThread.getServer().getId(), defaultValue));
return messageService.sendEmbedToUser(user, "modmail_closing_user_message", closingMessage).thenAccept(message -> return messageService.sendEmbedToUser(modMailThreaduser.getUser(), "modmail_closing_user_message", closingMessage).thenAccept(message ->
self.deleteChannelAndClose(modMailThreadId, undoActions) self.deleteChannelAndClose(modMailThreadId, undoActions)
); );
} else { } else {
log.trace("NOT Notifying user about the closed modmail thread {}.", modMailThreadId); log.info("NOT Notifying user about the closed modmail thread {}.", modMailThreadId);
return deleteChannelAndClose(modMailThreadId, undoActions); return deleteChannelAndClose(modMailThreadId, undoActions);
} }
} else { } else {
@@ -539,7 +552,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
*/ */
@Transactional @Transactional
public CompletableFuture<Void> deleteChannelAndClose(Long modMailThreadId, List<UndoActionInstance> undoActions) { public CompletableFuture<Void> deleteChannelAndClose(Long modMailThreadId, List<UndoActionInstance> undoActions) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getById(modMailThreadId); Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
if(modMailThreadOpt.isPresent()) { if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get(); ModMailThread modMailThread = modMailThreadOpt.get();
String failureMessage = "Failed to delete text channel containing mod mail thread {}"; String failureMessage = "Failed to delete text channel containing mod mail thread {}";
@@ -575,14 +588,15 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* the result values of the individual futures after they are done. * the result values of the individual futures after they are done.
*/ */
@Transactional @Transactional
public CompletableFutureList<Message> logModMailThread(Long modMailThreadId, List<CompletableFuture<Message>> messages, String note, List<UndoActionInstance> undoActions) { public CompletableFutureList<Message> logModMailThread(Long modMailThreadId, LoadedModmailThreadMessageList messages, String note, List<UndoActionInstance> undoActions) {
log.info("Logging mod mail thread {} with {} messages.", modMailThreadId, messages.size()); log.info("Logging mod mail thread {} with {} messages.", modMailThreadId, messages.getMessageList().size());
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getById(modMailThreadId); Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
if(modMailThreadOpt.isPresent()) { if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get(); ModMailThread modMailThread = modMailThreadOpt.get();
List<ModMailLoggedMessageModel> loggedMessages = new ArrayList<>(); List<ModMailLoggedMessageModel> loggedMessages = new ArrayList<>();
messages.forEach(future -> { messages.getMessageList().forEach(futures -> {
try { try {
CompletableFuture<Message> future = futures.getMessageFuture();
if(!future.isCompletedExceptionally()) { if(!future.isCompletedExceptionally()) {
Message loadedMessage = future.join(); Message loadedMessage = future.join();
if(loadedMessage != null) { if(loadedMessage != null) {
@@ -591,12 +605,13 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.stream() .stream()
.filter(modMailMessage -> modMailMessage.getMessageId().equals(loadedMessage.getIdLong())) .filter(modMailMessage -> modMailMessage.getMessageId().equals(loadedMessage.getIdLong()))
.findFirst().orElseThrow(() -> new AbstractoRunTimeException("Could not find desired message in list of messages in thread. This should not happen, as we just retrieved them from the same place.")); .findFirst().orElseThrow(() -> new AbstractoRunTimeException("Could not find desired message in list of messages in thread. This should not happen, as we just retrieved them from the same place."));
Member author = futures.getMemberFuture().join();
ModMailLoggedMessageModel modMailLoggedMessageModel = ModMailLoggedMessageModel modMailLoggedMessageModel =
ModMailLoggedMessageModel ModMailLoggedMessageModel
.builder() .builder()
.message(loadedMessage) .message(loadedMessage)
.modMailMessage(modmailMessage) .modMailMessage(modmailMessage)
.author(userInServerService.getFullUser(modmailMessage.getAuthor())) .author(author) // doesnt work for the messages from the DM channel
.build(); .build();
loggedMessages.add(modMailLoggedMessageModel); loggedMessages.add(modMailLoggedMessageModel);
} }
@@ -634,7 +649,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
*/ */
@Transactional @Transactional
public void closeModMailThreadInDb(Long modMailThreadId) { public void closeModMailThreadInDb(Long modMailThreadId) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getById(modMailThreadId); Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
if(modMailThreadOpt.isPresent()) { if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get(); ModMailThread modMailThread = modMailThreadOpt.get();
log.info("Setting thread {} to closed in db.", modMailThread.getId()); log.info("Setting thread {} to closed in db.", modMailThread.getId());
@@ -654,6 +669,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
*/ */
public List<CompletableFuture<Message>> sendMessagesToPostTarget(ModMailThread modMailThread, List<ModMailLoggedMessageModel> loadedMessages) { public List<CompletableFuture<Message>> sendMessagesToPostTarget(ModMailThread modMailThread, List<ModMailLoggedMessageModel> loadedMessages) {
List<CompletableFuture<Message>> messageFutures = new ArrayList<>(); List<CompletableFuture<Message>> messageFutures = new ArrayList<>();
// TODO order messages
loadedMessages.forEach(message -> { loadedMessages.forEach(message -> {
log.trace("Sending message {} of modmail thread {} to modmail log post target.", modMailThread.getId(), message.getMessage().getId()); log.trace("Sending message {} of modmail thread {} to modmail log post target.", modMailThread.getId(), message.getMessage().getId());
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_close_logged_message", message); MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_close_logged_message", message);
@@ -673,7 +689,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
*/ */
@Transactional @Transactional
public void saveSendMessagesAndUpdateState(Long modMailThreadId, Boolean anonymous, AUserInAServer moderator, Message message) { public void saveSendMessagesAndUpdateState(Long modMailThreadId, Boolean anonymous, AUserInAServer moderator, Message message) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getById(modMailThreadId); Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
if(modMailThreadOpt.isPresent()) { if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get(); ModMailThread modMailThread = modMailThreadOpt.get();
log.trace("Adding (anonymous: {}) message {} of moderator to modmail thread {} and setting state to {}.", anonymous, message.getId(), modMailThreadId, ModMailThreadState.MOD_REPLIED); log.trace("Adding (anonymous: {}) message {} of moderator to modmail thread {} and setting state to {}.", anonymous, message.getId(), modMailThreadId, ModMailThreadState.MOD_REPLIED);

View File

@@ -4,6 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUser; import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService; import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.modmail.exception.ModMailThreadNotFoundException;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread; import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
import dev.sheldan.abstracto.modmail.models.database.ModMailThreadState; import dev.sheldan.abstracto.modmail.models.database.ModMailThreadState;
import dev.sheldan.abstracto.modmail.repository.ModMailThreadRepository; import dev.sheldan.abstracto.modmail.repository.ModMailThreadRepository;
@@ -32,10 +33,15 @@ public class ModMailThreadManagementServiceBean implements ModMailThreadManageme
} }
@Override @Override
public Optional<ModMailThread> getById(Long modMailThreadId) { public Optional<ModMailThread> getByIdOptional(Long modMailThreadId) {
return modMailThreadRepository.findById(modMailThreadId); return modMailThreadRepository.findById(modMailThreadId);
} }
@Override
public ModMailThread getById(Long modMailThreadId) {
return getByIdOptional(modMailThreadId).orElseThrow(() -> new ModMailThreadNotFoundException(modMailThreadId));
}
@Override @Override
public ModMailThread getByChannel(AChannel channel) { public ModMailThread getByChannel(AChannel channel) {
return modMailThreadRepository.findByChannel(channel); return modMailThreadRepository.findByChannel(channel);

View File

@@ -78,7 +78,7 @@ public class ModMailCategorySetupBean implements ModMailCategorySetup {
.builder() .builder()
.build(); .build();
if(configManagementService.configExists(user.getGuildId(), ModMailThreadServiceBean.MODMAIL_CATEGORY)) { if(configManagementService.configExists(user.getGuildId(), ModMailThreadServiceBean.MODMAIL_CATEGORY)) {
Guild guild = botService.getGuildByIdNullable(user.getGuildId()); Guild guild = botService.getGuildById(user.getGuildId());
Long categoryId = configService.getLongValue(ModMailThreadServiceBean.MODMAIL_CATEGORY, user.getGuildId()); Long categoryId = configService.getLongValue(ModMailThreadServiceBean.MODMAIL_CATEGORY, user.getGuildId());
log.trace("Previous modmail category exists for server {}. Loading value {}.", guild.getId(), categoryId); log.trace("Previous modmail category exists for server {}. Loading value {}.", guild.getId(), categoryId);
Category category = guild.getCategoryById(categoryId); Category category = guild.getCategoryById(categoryId);
@@ -104,7 +104,7 @@ public class ModMailCategorySetupBean implements ModMailCategorySetup {
String messageContent = event.getMessage().getContentRaw(); String messageContent = event.getMessage().getContentRaw();
// directly parse the long from the message, for *now*, only the category ID is supported // directly parse the long from the message, for *now*, only the category ID is supported
Long categoryId = Long.parseLong(messageContent); Long categoryId = Long.parseLong(messageContent);
Guild guild = botService.getGuildByIdNullable(user.getGuildId()); Guild guild = botService.getGuildById(user.getGuildId());
FeatureValidationResult featureValidationResult = FeatureValidationResult.builder().validationResult(true).build(); FeatureValidationResult featureValidationResult = FeatureValidationResult.builder().validationResult(true).build();
// directly validate whether or not the given category ID is a valid value // directly validate whether or not the given category ID is a valid value
modMailFeatureValidator.validateModMailCategory(featureValidationResult, guild, categoryId); modMailFeatureValidator.validateModMailCategory(featureValidationResult, guild, categoryId);

View File

@@ -42,7 +42,7 @@ public class ModMailFeatureValidatorBean implements ModMailFeatureValidator {
*/ */
@Override @Override
public void featureIsSetup(FeatureConfig featureConfig, AServer server, FeatureValidationResult validationResult) { public void featureIsSetup(FeatureConfig featureConfig, AServer server, FeatureValidationResult validationResult) {
Optional<Guild> guildById = botService.getGuildById(server.getId()); Optional<Guild> guildById = botService.getGuildByIdOptional(server.getId());
if(guildById.isPresent()) { if(guildById.isPresent()) {
Guild guild = guildById.get(); Guild guild = guildById.get();
boolean checkSucceeded = featureValidatorService.checkSystemConfig(ModMailThreadServiceBean.MODMAIL_CATEGORY, server, validationResult); boolean checkSucceeded = featureValidatorService.checkSystemConfig(ModMailThreadServiceBean.MODMAIL_CATEGORY, server, validationResult);

View File

@@ -0,0 +1,17 @@
package dev.sheldan.abstracto.modmail.models.dto;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import java.util.concurrent.CompletableFuture;
@Getter
@Setter
@Builder
public class LoadedModmailThreadMessage {
private CompletableFuture<Message> messageFuture;
private CompletableFuture<Member> memberFuture;
}

View File

@@ -0,0 +1,24 @@
package dev.sheldan.abstracto.modmail.models.dto;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Getter
@Setter
@Builder
public class LoadedModmailThreadMessageList {
private List<LoadedModmailThreadMessage> messageList;
public List<CompletableFuture> getAllFutures() {
List<CompletableFuture> futures = new ArrayList<>();
messageList.forEach(loadedModmailThreadMessage -> {
futures.add(loadedModmailThreadMessage.getMemberFuture());
futures.add(loadedModmailThreadMessage.getMessageFuture());
});
return futures;
}
}

View File

@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.modmail.models.database.ModMailMessage;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
/** /**
@@ -26,7 +27,7 @@ public class ModMailLoggedMessageModel {
/** /**
* A reference to the {@link FullUserInServer} which is the author. The member part is null, if the member left the guild. * A reference to the {@link FullUserInServer} which is the author. The member part is null, if the member left the guild.
*/ */
private FullUserInServer author; private Member author;
} }

View File

@@ -7,6 +7,7 @@ import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import java.util.List; import java.util.List;
@@ -25,7 +26,7 @@ public class ModMailNotificationModel extends ServerContext {
/** /**
* The {@link FullUserInServer} for which this thread is about * The {@link FullUserInServer} for which this thread is about
*/ */
private FullUserInServer threadUser; private Member member;
/** /**
* A list of roles which will be notified upon creation of the mod mail thread. * A list of roles which will be notified upon creation of the mod mail thread.
*/ */

View File

@@ -1,10 +1,10 @@
package dev.sheldan.abstracto.modmail.models.template; package dev.sheldan.abstracto.modmail.models.template;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread; import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
/** /**
* This is the model used when a new mod mail thread is opened and a message containing some information about the user * This is the model used when a new mod mail thread is opened and a message containing some information about the user
@@ -15,9 +15,9 @@ import lombok.Setter;
@Builder @Builder
public class ModMailThreaderHeader { public class ModMailThreaderHeader {
/** /**
* A {@link FullUserInServer} instance to retrieve information from * A {@link Member} instance to retrieve information from
*/ */
private FullUserInServer threadUser; private Member member;
/** /**
* The latest {@link ModMailThread}, before the current opened one. This is null if there is no closed mod mail thread * The latest {@link ModMailThread}, before the current opened one. This is null if there is no closed mod mail thread
* for the user * for the user

View File

@@ -1,10 +1,10 @@
package dev.sheldan.abstracto.modmail.models.template; package dev.sheldan.abstracto.modmail.models.template;
import dev.sheldan.abstracto.core.models.FullUserInServer; import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import java.util.List; import java.util.List;
@@ -17,17 +17,13 @@ import java.util.List;
@Builder @Builder
public class ModMailUserReplyModel { public class ModMailUserReplyModel {
/** /**
* The {@link FullUserInServer} from which the message is and whose mod mail thread it is * The {@link Member} from which the message is and whose mod mail thread it is
*/ */
private FullUserInServer threadUser; private Member member;
/** /**
* The {@link Message} which was posted, which contains all the possible information * The {@link Message} which was posted, which contains all the possible information
*/ */
private Message postedMessage; private Message postedMessage;
/**
* The {@link ModMailThread} for which the {@link Message} was a reply for
*/
private ModMailThread modMailThread;
/** /**
* List of {@link FullUserInServer} which are registered as subscribers for a particular mod mail thread and will be pinged * List of {@link FullUserInServer} which are registered as subscribers for a particular mod mail thread and will be pinged
* when the user sends a new message * when the user sends a new message

View File

@@ -1,10 +1,10 @@
package dev.sheldan.abstracto.modmail.service; package dev.sheldan.abstracto.modmail.service;
import dev.sheldan.abstracto.modmail.models.database.ModMailMessage; import dev.sheldan.abstracto.modmail.models.database.ModMailMessage;
import dev.sheldan.abstracto.modmail.models.dto.LoadedModmailThreadMessageList;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
/** /**
* Service to handle the messages of a {@link dev.sheldan.abstracto.modmail.models.database.ModMailThread} * Service to handle the messages of a {@link dev.sheldan.abstracto.modmail.models.database.ModMailThread}
@@ -13,7 +13,7 @@ public interface ModMailMessageService {
/** /**
* Loads the given mod mail messages in the form of {@link Message} from Discord and returns the created promises, some of which might fail, if the message was already deleted * Loads the given mod mail messages in the form of {@link Message} from Discord and returns the created promises, some of which might fail, if the message was already deleted
* @param modMailMessages The list of {@link ModMailMessage} to load * @param modMailMessages The list of {@link ModMailMessage} to load
* @return A list of futures which contain the individual results of actively loading the {@link Message} * @return A instance of {@link LoadedModmailThreadMessageList} which contain the individual results of actively loading the {@link Message} and the {@link net.dv8tion.jda.api.entities.Member}
*/ */
List<CompletableFuture<Message>> loadModMailMessages(List<ModMailMessage> modMailMessages); LoadedModmailThreadMessageList loadModMailMessages(List<ModMailMessage> modMailMessages);
} }

View File

@@ -1,12 +1,12 @@
package dev.sheldan.abstracto.modmail.service; package dev.sheldan.abstracto.modmail.service;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.UndoActionInstance; import dev.sheldan.abstracto.core.models.UndoActionInstance;
import dev.sheldan.abstracto.core.models.database.AUser; import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread; import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.entities.MessageChannel;
@@ -21,12 +21,12 @@ public interface ModMailThreadService {
* Creates a new mod mail thread for the given user. including: the {@link net.dv8tion.jda.api.entities.TextChannel} * Creates a new mod mail thread for the given user. including: the {@link net.dv8tion.jda.api.entities.TextChannel}
* in the appropriate {@link net.dv8tion.jda.api.entities.Category} and calls the methods responsible for storing * in the appropriate {@link net.dv8tion.jda.api.entities.Category} and calls the methods responsible for storing
* the necessary data in the database, notifying the users and sending messages related to the creation of the {@link ModMailThread} * the necessary data in the database, notifying the users and sending messages related to the creation of the {@link ModMailThread}
* @param userInAServer The {@link AUserInAServer} to create the mod mail thread for * @param member The {@link AUserInAServer} to create the mod mail thread for
* @param initialMessage The initial message sparking this mod mail thread, null in case it was created by a command * @param initialMessage The initial message sparking this mod mail thread, null in case it was created by a command
* @param feedBackChannel The {@link MessageChannel} in which feedback about exceptions should be posted to * @param feedBackChannel The {@link MessageChannel} in which feedback about exceptions should be posted to
* @param userInitiated Whether or not the mod mail thread was initiated by a user * @param userInitiated Whether or not the mod mail thread was initiated by a user
*/ */
CompletableFuture<Void> createModMailThreadForUser(FullUserInServer userInAServer, Message initialMessage, MessageChannel feedBackChannel, boolean userInitiated, List<UndoActionInstance> undoActions); CompletableFuture<Void> createModMailThreadForUser(Member member, Message initialMessage, MessageChannel feedBackChannel, boolean userInitiated, List<UndoActionInstance> undoActions);
/** /**
* Changes the configuration value of the category used to create mod mail threads to the given ID. * Changes the configuration value of the category used to create mod mail threads to the given ID.
@@ -52,18 +52,20 @@ public interface ModMailThreadService {
* @param modMailThread The {@link ModMailThread} on which the user answered * @param modMailThread The {@link ModMailThread} on which the user answered
* @param message The {@link Message} object which was sent by the user to answer with * @param message The {@link Message} object which was sent by the user to answer with
*/ */
CompletableFuture<Void> relayMessageToModMailThread(ModMailThread modMailThread, Message message, List<UndoActionInstance> undoActions); CompletableFuture<Message> relayMessageToModMailThread(ModMailThread modMailThread, Message message, List<UndoActionInstance> undoActions);
/** /**
* Forwards a message send by a moderator to the direct message channel opened with the user. If the message is * Forwards a message send by a moderator to the direct message channel opened with the user. If the message is
* marked as anonymous, the bot will take the place of the author, in other case the author is shown in the embed. * marked as anonymous, the bot will take the place of the author, in other case the author is shown in the embed.
* @param modMailThread The {@link ModMailThread} to which the reply was sent to * @param threadId The id of the {@link ModMailThread} to which the reply was sent to
* @param text The parsed text of the reply * @param text The parsed text of the reply
* @param message The pure {@link Message} containing the command which caused the reply * @param message The pure {@link Message} containing the command which caused the reply
* @param anonymous Whether or nor the message should be send anonymous * @param anonymous Whether or nor the message should be send anonymous
* @param feedBack The {@link MessageChannel} in which feedback about possible exceptions should be sent to * @param feedBack The {@link MessageChannel} in which feedback about possible exceptions should be sent to
* @param undoActions list of {@link UndoActionInstance} to execute in case this fails
* @param targetMember The {@link Member} the {@link ModMailThread} is about.
*/ */
CompletableFuture<Void> relayMessageToDm(ModMailThread modMailThread, String text, Message message, boolean anonymous, MessageChannel feedBack, List<UndoActionInstance> undoActions); CompletableFuture<Void> relayMessageToDm(Long threadId, String text, Message message, boolean anonymous, MessageChannel feedBack, List<UndoActionInstance> undoActions, Member targetMember);
/** /**
* Closes the mod mail thread which means: deletes the {@link net.dv8tion.jda.api.entities.TextChannel} associated with the mod mail thread, * Closes the mod mail thread which means: deletes the {@link net.dv8tion.jda.api.entities.TextChannel} associated with the mod mail thread,

View File

@@ -29,7 +29,14 @@ public interface ModMailThreadManagementService {
* @param modMailThreadId The ID of the mod mail to search for * @param modMailThreadId The ID of the mod mail to search for
* @return An {@link Optional} containing the mod mail thread or empty * @return An {@link Optional} containing the mod mail thread or empty
*/ */
Optional<ModMailThread> getById(Long modMailThreadId); Optional<ModMailThread> getByIdOptional(Long modMailThreadId);
/**
* Retrieves the {@link ModMailThread} by the given ID in an optional, if it exists, and an {@literal Optional#empty()} otherwise
* @param modMailThreadId The ID of the mod mail to search for
* @return An {@link Optional} containing the mod mail thread or empty
*/
ModMailThread getById(Long modMailThreadId);
/** /**
* Retrieves a {@link ModMailThread} found in the {@link net.dv8tion.jda.api.entities.MessageChannel} given by the * Retrieves a {@link ModMailThread} found in the {@link net.dv8tion.jda.api.entities.MessageChannel} given by the

View File

@@ -11,7 +11,6 @@ import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils; import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.utility.config.features.UtilityFeature; import dev.sheldan.abstracto.utility.config.features.UtilityFeature;
import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsModel;
import dev.sheldan.abstracto.utility.service.StarboardService; import dev.sheldan.abstracto.utility.service.StarboardService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -33,9 +32,10 @@ public class StarStats extends AbstractConditionableCommand {
@Override @Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
StarStatsModel result = starboardService.retrieveStarStats(commandContext.getGuild().getIdLong()); return starboardService.retrieveStarStats(commandContext.getGuild().getIdLong())
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(STARSTATS_RESPONSE_TEMPLATE, result, commandContext.getChannel())) .thenCompose(starStatsModel ->
.thenApply(aVoid -> CommandResult.fromSuccess()); FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(STARSTATS_RESPONSE_TEMPLATE, starStatsModel, commandContext.getChannel()))
).thenApply(o -> CommandResult.fromSuccess());
} }
@Override @Override

View File

@@ -47,7 +47,7 @@ public class MessageEmbedListener implements MessageReceivedListener {
continue; continue;
} }
messageRaw = messageRaw.replace(messageEmbedLink.getWholeUrl(), ""); messageRaw = messageRaw.replace(messageEmbedLink.getWholeUrl(), "");
Consumer<CachedMessage> cachedMessageConsumer = cachedMessage ->self.loadUserAndEmbed(message, userEmbeddingUserInServerId, cachedMessage); Consumer<CachedMessage> cachedMessageConsumer = cachedMessage -> self.embedSingleLink(message, userEmbeddingUserInServerId, cachedMessage);
messageCache.getMessageFromCache(messageEmbedLink.getServerId(), messageEmbedLink.getChannelId(), messageEmbedLink.getMessageId()) messageCache.getMessageFromCache(messageEmbedLink.getServerId(), messageEmbedLink.getChannelId(), messageEmbedLink.getMessageId())
.thenAccept(cachedMessageConsumer) .thenAccept(cachedMessageConsumer)
.exceptionally(throwable -> { .exceptionally(throwable -> {
@@ -62,10 +62,14 @@ public class MessageEmbedListener implements MessageReceivedListener {
} }
@Transactional @Transactional
public void loadUserAndEmbed(Message message, Long cause, CachedMessage cachedMessage) { public void embedSingleLink(Message message, Long cause, CachedMessage cachedMessage) {
log.info("Embedding link to message {} in channel {} in server {} to channel {} and server {}.", log.info("Embedding link to message {} in channel {} in server {} to channel {} and server {}.",
cachedMessage.getMessageId(), cachedMessage.getChannelId(), cachedMessage.getServerId(), message.getChannel().getId(), message.getGuild().getId()); cachedMessage.getMessageId(), cachedMessage.getChannelId(), cachedMessage.getServerId(), message.getChannel().getId(), message.getGuild().getId());
messageEmbedService.embedLink(cachedMessage, message.getTextChannel(), cause , message); messageEmbedService.embedLink(cachedMessage, message.getTextChannel(), cause , message).exceptionally(throwable -> {
log.error("Failed to embed link towards message {} in channel {} in sever {} linked from message {} in channel {} in server {}.", cachedMessage.getMessageId(), cachedMessage.getChannelId(), cachedMessage.getServerId(),
message.getId(), message.getChannel().getId(), message.getGuild().getId(), throwable);
return null;
});
} }
@Override @Override

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.utility.repository; package dev.sheldan.abstracto.utility.repository;
public interface StarStatsUserResult { public interface StarStatsUserResult {
// this is the User in Server Id
Long getUserId(); Long getUserId();
Integer getStarCount(); Integer getStarCount();
} }

View File

@@ -1,7 +1,9 @@
package dev.sheldan.abstracto.utility.repository.converter; package dev.sheldan.abstracto.utility.repository.converter;
import dev.sheldan.abstracto.core.models.database.AUser; import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsUser; import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsUser;
import dev.sheldan.abstracto.utility.repository.StarStatsUserResult; import dev.sheldan.abstracto.utility.repository.StarStatsUserResult;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -9,6 +11,7 @@ import org.springframework.stereotype.Component;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component @Component
public class StarStatsUserConverter { public class StarStatsUserConverter {
@@ -16,17 +19,26 @@ public class StarStatsUserConverter {
@Autowired @Autowired
private BotService botService; private BotService botService;
public List<StarStatsUser> convertToStarStatsUser(List<StarStatsUserResult> users, Long serverId) { @Autowired
List<StarStatsUser> result = new ArrayList<>(); private UserInServerManagementService userInServerManagementService;
users.forEach(starStatsUserResult -> {
StarStatsUser newUser = StarStatsUser public List<CompletableFuture<StarStatsUser>> convertToStarStatsUser(List<StarStatsUserResult> users, Long serverId) {
.builder() List<CompletableFuture<StarStatsUser>> result = new ArrayList<>();
.starCount(starStatsUserResult.getStarCount()) users.forEach(starStatsUserResult ->
.member(botService.getMemberInServer(serverId, starStatsUserResult.getUserId())) result.add(createStarStatsUser(serverId, starStatsUserResult))
.user(AUser.builder().id(starStatsUserResult.getUserId()).build()) );
.build();
result.add(newUser);
});
return result; return result;
} }
private CompletableFuture<StarStatsUser> createStarStatsUser(Long serverId, StarStatsUserResult starStatsUserResult) {
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(starStatsUserResult.getUserId());
return botService.getMemberInServerAsync(serverId, aUserInAServer.getUserReference().getId()).thenApply(member ->
StarStatsUser
.builder()
.starCount(starStatsUserResult.getStarCount())
.member(member)
.user(AUser.builder().id(starStatsUserResult.getUserId()).build())
.build()
);
}
} }

View File

@@ -28,7 +28,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.CompletionStage;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -60,7 +60,7 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
private ChannelService channelService; private ChannelService channelService;
@Autowired @Autowired
private MessageEmbedService self; private MessageEmbedServiceBean self;
@Autowired @Autowired
private MessageCache messageCache; private MessageCache messageCache;
@@ -102,7 +102,7 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
.thenAccept(cachedMessage -> self.embedLink(cachedMessage, target, userEmbeddingUserInServerId, embeddingMessage) .thenAccept(cachedMessage -> self.embedLink(cachedMessage, target, userEmbeddingUserInServerId, embeddingMessage)
).exceptionally(throwable -> { ).exceptionally(throwable -> {
log.error("Message retrieval from cache failed for message {}.", messageEmbedLink.getMessageId(), throwable); log.error("Message embedding from cache failed for message {}.", messageEmbedLink.getMessageId(), throwable);
return null; return null;
}) })
); );
@@ -110,42 +110,50 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
@Override @Override
@Transactional @Transactional
public void embedLink(CachedMessage cachedMessage, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage) { public CompletableFuture<Void> embedLink(CachedMessage cachedMessage, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage) {
Optional<AUserInAServer> causeOpt = userInServerManagementService.loadUserConditional(userEmbeddingUserInServerId); Optional<AUserInAServer> causeOpt = userInServerManagementService.loadUserConditional(userEmbeddingUserInServerId);
if(causeOpt.isPresent()) { if(causeOpt.isPresent()) {
AUserInAServer cause = causeOpt.get(); return buildTemplateParameter(embeddingMessage, cachedMessage).thenCompose(messageEmbeddedModel ->
MessageEmbeddedModel messageEmbeddedModel = buildTemplateParameter(embeddingMessage, cachedMessage); self.sendEmbeddingMessage(cachedMessage, target, userEmbeddingUserInServerId, messageEmbeddedModel)
MessageToSend embed = templateService.renderEmbedTemplate(MESSAGE_EMBED_TEMPLATE, messageEmbeddedModel); );
List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToSendToChannel(embed, target);
log.trace("Embedding message {} from channel {} from server {}, because of user {}", cachedMessage.getMessageId(),
cachedMessage.getChannelId(), cachedMessage.getServerId(), cause.getUserReference().getId());
Long userInServerId = cause.getUserInServerId();
CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenAccept(aVoid -> {
try {
Message createdMessage = completableFutures.get(0).get();
Optional<AUserInAServer> innerCauseOpt = userInServerManagementService.loadUserConditional(userInServerId);
innerCauseOpt.ifPresent(aUserInAServer -> {
messageEmbedPostManagementService.createMessageEmbed(cachedMessage, createdMessage, aUserInAServer);
messageService.addReactionToMessage(REMOVAL_EMOTE, cachedMessage.getServerId(), createdMessage);
});
} catch (InterruptedException | ExecutionException e) {
log.error("Failed to post message embed.", e);
Thread.currentThread().interrupt();
}
}).exceptionally(throwable -> {
log.error("Failed to send message for embedding the link for message {} in channel {} in server {}",
cachedMessage.getMessageId(), cachedMessage.getChannelId(), cachedMessage.getServerId(), throwable);
return null;
});
} else { } else {
log.warn("User {} which was not found in database wanted to embed link in message {}.", userEmbeddingUserInServerId, embeddingMessage.getJumpUrl()); log.warn("User {} which was not found in database wanted to embed link in message {}.", userEmbeddingUserInServerId, embeddingMessage.getJumpUrl());
return CompletableFuture.completedFuture(null);
} }
} }
private MessageEmbeddedModel buildTemplateParameter(Message message, CachedMessage embeddedMessage) { @Transactional
public CompletionStage<Void> sendEmbeddingMessage(CachedMessage cachedMessage, TextChannel target, Long userEmbeddingUserInServerId, MessageEmbeddedModel messageEmbeddedModel) {
MessageToSend embed = templateService.renderEmbedTemplate(MESSAGE_EMBED_TEMPLATE, messageEmbeddedModel);
AUserInAServer cause = userInServerManagementService.loadUser(userEmbeddingUserInServerId);
List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToSendToChannel(embed, target);
log.trace("Embedding message {} from channel {} from server {}, because of user {}", cachedMessage.getMessageId(),
cachedMessage.getChannelId(), cachedMessage.getServerId(), cause.getUserReference().getId());
Long userInServerId = cause.getUserInServerId();
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenCompose(aVoid -> {
Message createdMessage = completableFutures.get(0).join();
return messageService.addReactionToMessageWithFuture(REMOVAL_EMOTE, cachedMessage.getServerId(), createdMessage).thenAccept(aVoid1 ->
self.loadUserAndPersistMessage(cachedMessage, userInServerId, createdMessage)
);
});
}
@Transactional
public void loadUserAndPersistMessage(CachedMessage cachedMessage, Long userInServerId, Message createdMessage) {
AUserInAServer innerCause = userInServerManagementService.loadUser(userInServerId);
messageEmbedPostManagementService.createMessageEmbed(cachedMessage, createdMessage, innerCause);
}
private CompletableFuture<MessageEmbeddedModel> buildTemplateParameter(Message message, CachedMessage embeddedMessage) {
return botService.getMemberInServerAsync(embeddedMessage.getServerId(), embeddedMessage.getAuthorId()).thenApply(member ->
self.loadMessageEmbedModel(message, embeddedMessage, member)
);
}
@Transactional
public MessageEmbeddedModel loadMessageEmbedModel(Message message, CachedMessage embeddedMessage, Member member) {
AServer server = serverManagementService.loadOrCreate(message.getGuild().getIdLong()); AServer server = serverManagementService.loadOrCreate(message.getGuild().getIdLong());
AUserInAServer user = userInServerManagementService.loadUser(message.getMember()); AUserInAServer user = userInServerManagementService.loadUser(message.getMember());
Member author = botService.getMemberInServer(embeddedMessage.getServerId(), embeddedMessage.getAuthorId());
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(embeddedMessage.getServerId(), embeddedMessage.getChannelId()); Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(embeddedMessage.getServerId(), embeddedMessage.getChannelId());
TextChannel sourceChannel = textChannelFromServer.orElse(null); TextChannel sourceChannel = textChannelFromServer.orElse(null);
AChannel channel = channelManagementService.loadChannel(message.getChannel().getIdLong()); AChannel channel = channelManagementService.loadChannel(message.getChannel().getIdLong());
@@ -155,7 +163,7 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
.server(server) .server(server)
.member(message.getMember()) .member(message.getMember())
.aUserInAServer(user) .aUserInAServer(user)
.author(author) .author(member)
.sourceChannel(sourceChannel) .sourceChannel(sourceChannel)
.embeddingUser(message.getMember()) .embeddingUser(message.getMember())
.user(user.getUserReference()) .user(user.getUserReference())

View File

@@ -5,8 +5,8 @@ import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser; import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.templating.model.MessageToSend; import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.scheduling.service.SchedulerService; import dev.sheldan.abstracto.scheduling.service.SchedulerService;
@@ -36,6 +36,7 @@ import java.util.concurrent.*;
@Slf4j @Slf4j
public class RemindServiceBean implements ReminderService { public class RemindServiceBean implements ReminderService {
public static final String REMINDER_TEMPLATE_TEXT = "remind_reminder";
@Autowired @Autowired
private ReminderManagementService reminderManagementService; private ReminderManagementService reminderManagementService;
@@ -52,7 +53,7 @@ public class RemindServiceBean implements ReminderService {
private BotService botService; private BotService botService;
@Autowired @Autowired
private ReminderService self; private RemindServiceBean self;
@Autowired @Autowired
private ChannelService channelService; private ChannelService channelService;
@@ -61,6 +62,7 @@ public class RemindServiceBean implements ReminderService {
@Qualifier("reminderScheduler") @Qualifier("reminderScheduler")
private ScheduledExecutorService instantReminderScheduler; private ScheduledExecutorService instantReminderScheduler;
@Override @Override
public Reminder createReminderInForUser(AUserInAServer user, String remindText, Duration remindIn, Message message) { public Reminder createReminderInForUser(AUserInAServer user, String remindText, Duration remindIn, Message message) {
AChannel channel = channelManagementService.loadChannel(message.getChannel().getIdLong()); AChannel channel = channelManagementService.loadChannel(message.getChannel().getIdLong());
@@ -99,7 +101,7 @@ public class RemindServiceBean implements ReminderService {
@Override @Override
@Transactional @Transactional
public void executeReminder(Long reminderId) { public void executeReminder(Long reminderId) {
Reminder reminderToRemindFor = reminderManagementService.loadReminder(reminderId).orElseThrow(() -> new ReminderNotFoundException(reminderId)); Reminder reminderToRemindFor = reminderManagementService.loadReminder(reminderId);
if(reminderToRemindFor.isReminded()) { if(reminderToRemindFor.isReminded()) {
return; return;
} }
@@ -107,22 +109,15 @@ public class RemindServiceBean implements ReminderService {
AChannel channel = reminderToRemindFor.getChannel(); AChannel channel = reminderToRemindFor.getChannel();
log.info("Executing reminder {} in channel {} in server {} for user {}.", log.info("Executing reminder {} in channel {} in server {} for user {}.",
reminderId, channel.getId(), server.getId(), reminderToRemindFor.getRemindedUser().getUserReference().getId()); reminderId, channel.getId(), server.getId(), reminderToRemindFor.getRemindedUser().getUserReference().getId());
Optional<Guild> guildToAnswerIn = botService.getGuildById(server.getId()); Optional<Guild> guildToAnswerIn = botService.getGuildByIdOptional(server.getId());
if(guildToAnswerIn.isPresent()) { if(guildToAnswerIn.isPresent()) {
Optional<TextChannel> channelToAnswerIn = botService.getTextChannelFromServerOptional(server.getId(), channel.getId()); Optional<TextChannel> channelToAnswerIn = botService.getTextChannelFromServerOptional(server.getId(), channel.getId());
// only send the message if the channel still exists, if not, only set the reminder to reminded. // only send the message if the channel still exists, if not, only set the reminder to reminded.
if(channelToAnswerIn.isPresent()) { if(channelToAnswerIn.isPresent()) {
AUser userReference = reminderToRemindFor.getRemindedUser().getUserReference(); botService.getMemberInServerAsync(server.getId(), reminderToRemindFor.getRemindedUser().getUserReference().getId()).thenAccept(member ->
Member memberInServer = botService.getMemberInServer(server.getId(), userReference.getId()); self.sendReminderText(reminderId, channelToAnswerIn.get(), member)
log.trace("Reminding user {}", userReference.getId()); );
ExecutedReminderModel build = ExecutedReminderModel
.builder()
.reminder(reminderToRemindFor)
.member(memberInServer)
.duration(Duration.between(reminderToRemindFor.getReminderDate(), reminderToRemindFor.getTargetDate()))
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate("remind_reminder", build);
channelService.sendMessageToSendToChannel(messageToSend, channelToAnswerIn.get());
} else { } else {
log.warn("Channel {} in server {} to remind user did not exist anymore. Ignoring reminder {}", channel.getId(), server.getId(), reminderId); log.warn("Channel {} in server {} to remind user did not exist anymore. Ignoring reminder {}", channel.getId(), server.getId(), reminderId);
} }
@@ -132,6 +127,20 @@ public class RemindServiceBean implements ReminderService {
reminderManagementService.setReminded(reminderToRemindFor); reminderManagementService.setReminded(reminderToRemindFor);
} }
@Transactional
public CompletableFuture<Void> sendReminderText(Long reminderId, TextChannel channelToAnswerIn, Member member) {
Reminder reminder = reminderManagementService.loadReminder(reminderId);
log.trace("Sending remind message for reminder {} to user user {} in server {}.", reminderId, member.getIdLong(), member.getGuild().getIdLong());
ExecutedReminderModel build = ExecutedReminderModel
.builder()
.reminder(reminder)
.member(member)
.duration(Duration.between(reminder.getReminderDate(), reminder.getTargetDate()))
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(REMINDER_TEMPLATE_TEXT, build);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, channelToAnswerIn));
}
@Override @Override
public void unRemind(Long reminderId, AUserInAServer aUserInAServer) { public void unRemind(Long reminderId, AUserInAServer aUserInAServer) {
log.info("Trying to end reminder {} for user {} in server {}.", reminderId, aUserInAServer.getUserReference().getId(),aUserInAServer.getServerReference().getId()); log.info("Trying to end reminder {} for user {} in server {}.", reminderId, aUserInAServer.getUserReference().getId(),aUserInAServer.getServerReference().getId());

View File

@@ -8,6 +8,7 @@ import dev.sheldan.abstracto.core.models.AServerAChannelMessage;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.*; import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.templating.model.MessageToSend; import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.ConfigService; import dev.sheldan.abstracto.core.service.ConfigService;
@@ -32,6 +33,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -80,22 +82,24 @@ public class StarboardServiceBean implements StarboardService {
private StarboardServiceBean self; private StarboardServiceBean self;
@Override @Override
public void createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser) { public CompletableFuture<Void> createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser) {
StarboardPostModel starboardPostModel = buildStarboardPostModel(message, userExceptAuthor.size()); Long starredUserId = starredUser.getUserInServerId();
List<Long> userExceptAuthorIds = new ArrayList<>(); List<Long> userExceptAuthorIds = userExceptAuthor.stream().map(AUserInAServer::getUserInServerId).collect(Collectors.toList());
userExceptAuthor.forEach(aUserInAServer -> userExceptAuthorIds.add(aUserInAServer.getUserInServerId())); return buildStarboardPostModel(message, userExceptAuthor.size()).thenCompose(starboardPostModel ->
self.sendStarboardPostAndStore(message, starredUserId, userExceptAuthorIds, starboardPostModel)
);
}
@Transactional
public CompletionStage<Void> sendStarboardPostAndStore(CachedMessage message, Long starredUserId, List<Long> userExceptAuthorIds, StarboardPostModel starboardPostModel) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(STARBOARD_POST_TEMPLATE, starboardPostModel); MessageToSend messageToSend = templateService.renderEmbedTemplate(STARBOARD_POST_TEMPLATE, starboardPostModel);
PostTarget starboard = postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), message.getServerId()); PostTarget starboard = postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), message.getServerId());
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, StarboardPostTarget.STARBOARD, message.getServerId()); List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, StarboardPostTarget.STARBOARD, message.getServerId());
Long starboardChannelId = starboard.getChannelReference().getId(); Long starboardChannelId = starboard.getChannelReference().getId();
Long starredUserId = starredUser.getUserInServerId(); return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenAccept(aVoid ->
CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenAccept(aVoid -> self.persistPost(message, userExceptAuthorIds, completableFutures, starboardChannelId, starredUserId)
self.persistPost(message, userExceptAuthorIds, completableFutures, starboardChannelId, starredUserId) );
) .exceptionally(throwable -> {
log.error("Failed to create starboard post for message {} in channel {} in server {}", message.getMessageId(), message.getChannelId(), message.getServerId(), throwable);
return null;
});
} }
@Transactional @Transactional
@@ -120,41 +124,41 @@ public class StarboardServiceBean implements StarboardService {
}); });
} }
private StarboardPostModel buildStarboardPostModel(CachedMessage message, Integer starCount) { private CompletableFuture<StarboardPostModel> buildStarboardPostModel(CachedMessage message, Integer starCount) {
Member member = botService.getMemberInServer(message.getServerId(), message.getAuthorId()); return botService.getMemberInServerAsync(message.getServerId(), message.getAuthorId()).thenApply(member -> {
Optional<TextChannel> channel = botService.getTextChannelFromServerOptional(message.getServerId(), message.getChannelId()); Optional<TextChannel> channel = botService.getTextChannelFromServerOptional(message.getServerId(), message.getChannelId());
Optional<Guild> guild = botService.getGuildById(message.getServerId()); Optional<Guild> guild = botService.getGuildByIdOptional(message.getServerId());
AChannel aChannel = AChannel.builder().id(message.getChannelId()).build(); AChannel aChannel = AChannel.builder().id(message.getChannelId()).build();
AUser user = AUser.builder().id(message.getAuthorId()).build(); AUser user = AUser.builder().id(message.getAuthorId()).build();
AServer server = AServer.builder().id(message.getServerId()).build(); AServer server = AServer.builder().id(message.getServerId()).build();
String starLevelEmote = getAppropriateEmote(message.getServerId(), starCount); String starLevelEmote = getAppropriateEmote(message.getServerId(), starCount);
return StarboardPostModel return StarboardPostModel
.builder() .builder()
.message(message) .message(message)
.author(member) .author(member)
.channel(channel.orElse(null)) .channel(channel.orElse(null))
.aChannel(aChannel) .aChannel(aChannel)
.starCount(starCount) .starCount(starCount)
.guild(guild.orElse(null)) .guild(guild.orElse(null))
.user(user) .user(user)
.server(server) .server(server)
.starLevelEmote(starLevelEmote) .starLevelEmote(starLevelEmote)
.build(); .build();
});
} }
@Override @Override
public void updateStarboardPost(StarboardPost post, CachedMessage message, List<AUserInAServer> userExceptAuthor) { public CompletableFuture<Void> updateStarboardPost(StarboardPost post, CachedMessage message, List<AUserInAServer> userExceptAuthor) {
log.info("Updating starboard post {} in server {} with reactors {}.", post.getId(), post.getSourceChanel().getServer().getId(), userExceptAuthor.size()); int starCount = userExceptAuthor.size();
StarboardPostModel starboardPostModel = buildStarboardPostModel(message, userExceptAuthor.size()); log.info("Updating starboard post {} in server {} with reactors {}.", post.getId(), post.getSourceChanel().getServer().getId(), starCount);
MessageToSend messageToSend = templateService.renderEmbedTemplate(STARBOARD_POST_TEMPLATE, starboardPostModel); return buildStarboardPostModel(message, starCount).thenCompose(starboardPostModel -> {
List<CompletableFuture<Message>> futures = postTargetService.editOrCreatedInPostTarget(post.getStarboardMessageId(), messageToSend, StarboardPostTarget.STARBOARD, message.getServerId()); MessageToSend messageToSend = templateService.renderEmbedTemplate(STARBOARD_POST_TEMPLATE, starboardPostModel);
Long starboardPostId = post.getId(); List<CompletableFuture<Message>> futures = postTargetService.editOrCreatedInPostTarget(post.getStarboardMessageId(), messageToSend, StarboardPostTarget.STARBOARD, message.getServerId());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(aVoid -> { Long starboardPostId = post.getId();
Optional<StarboardPost> innerPost = starboardPostManagementService.findByStarboardPostId(starboardPostId); return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(aVoid -> {
innerPost.ifPresent(starboardPost -> starboardPostManagementService.setStarboardPostMessageId(starboardPost, futures.get(0).join().getIdLong())); Optional<StarboardPost> innerPost = starboardPostManagementService.findByStarboardPostId(starboardPostId);
}).exceptionally(throwable -> { innerPost.ifPresent(starboardPost -> starboardPostManagementService.setStarboardPostMessageId(starboardPost, futures.get(0).join().getIdLong()));
log.error("Failed to update starboard post {}.", post.getId(), throwable); });
return null;
}); });
} }
@@ -166,28 +170,35 @@ public class StarboardServiceBean implements StarboardService {
} }
@Override @Override
public StarStatsModel retrieveStarStats(Long serverId) { public CompletableFuture<StarStatsModel> retrieveStarStats(Long serverId) {
int count = 3; int count = 3;
List<StarboardPost> starboardPosts = starboardPostManagementService.retrieveTopPosts(serverId, count); List<CompletableFuture<StarStatsUser>> topStarGiverFutures = starboardPostReactorManagementService.retrieveTopStarGiver(serverId, count);
List<StarStatsUser> topStarGivers = starboardPostReactorManagementService.retrieveTopStarGiver(serverId, count); List<CompletableFuture<StarStatsUser>> topStarReceiverFutures = starboardPostReactorManagementService.retrieveTopStarReceiver(serverId, count);
List<StarStatsPost> starStatsPosts = starboardPosts.stream().map(this::fromStarboardPost).collect(Collectors.toList()); List<CompletableFuture> allFutures = new ArrayList<>();
List<StarStatsUser> topStarReceiver = starboardPostReactorManagementService.retrieveTopStarReceiver(serverId, count); allFutures.addAll(topStarGiverFutures);
Integer postCount = starboardPostManagementService.getPostCount(serverId); allFutures.addAll(topStarReceiverFutures);
Integer reactionCount = starboardPostReactorManagementService.getStarCount(serverId); return FutureUtils.toSingleFuture(allFutures).thenApply(aVoid -> {
List<String> emotes = new ArrayList<>(); List<StarboardPost> starboardPosts = starboardPostManagementService.retrieveTopPosts(serverId, count);
for (int i = 1; i < count + 1; i++) { List<StarStatsPost> starStatsPosts = starboardPosts.stream().map(this::fromStarboardPost).collect(Collectors.toList());
emotes.add(getStarboardRankingEmote(serverId, i)); Integer postCount = starboardPostManagementService.getPostCount(serverId);
} Integer reactionCount = starboardPostReactorManagementService.getStarCount(serverId);
List<String> emotes = new ArrayList<>();
for (int i = 1; i < count + 1; i++) {
emotes.add(getStarboardRankingEmote(serverId, i));
}
List<StarStatsUser> topStarGivers = topStarGiverFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
List<StarStatsUser> topStarReceiver = topStarReceiverFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
return StarStatsModel
.builder()
.badgeEmotes(emotes)
.starGiver(topStarGivers)
.starReceiver(topStarReceiver)
.topPosts(starStatsPosts)
.starredMessages(postCount)
.totalStars(reactionCount)
.build();
});
return StarStatsModel
.builder()
.badgeEmotes(emotes)
.starGiver(topStarGivers)
.starReceiver(topStarReceiver)
.topPosts(starStatsPosts)
.starredMessages(postCount)
.totalStars(reactionCount)
.build();
} }
public StarStatsPost fromStarboardPost(StarboardPost starboardPost) { public StarStatsPost fromStarboardPost(StarboardPost starboardPost) {

View File

@@ -1,10 +1,9 @@
package dev.sheldan.abstracto.utility.service; package dev.sheldan.abstracto.utility.service;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.CounterService; import dev.sheldan.abstracto.core.service.CounterService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.templating.model.MessageToSend; import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
@@ -73,7 +72,7 @@ public class SuggestionServiceBean implements SuggestionService {
long guildId = member.getGuild().getIdLong(); long guildId = member.getGuild().getIdLong();
log.info("Creating suggestion with id {} in server {} from member {}.", newSuggestionId, member.getGuild().getId(), member.getId()); log.info("Creating suggestion with id {} in server {} from member {}.", newSuggestionId, member.getGuild().getId(), member.getId());
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, SuggestionPostTarget.SUGGESTION, guildId); List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, SuggestionPostTarget.SUGGESTION, guildId);
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenCompose(aVoid -> { return FutureUtils.toSingleFutureGeneric(completableFutures).thenCompose(aVoid -> {
Message message = completableFutures.get(0).join(); Message message = completableFutures.get(0).join();
log.trace("Posted message, adding reaction for suggestion {} to message {}.", newSuggestionId, message.getId()); log.trace("Posted message, adding reaction for suggestion {} to message {}.", newSuggestionId, message.getId());
CompletableFuture<Void> firstReaction = messageService.addReactionToMessageWithFuture(SUGGESTION_YES_EMOTE, guildId, message); CompletableFuture<Void> firstReaction = messageService.addReactionToMessageWithFuture(SUGGESTION_YES_EMOTE, guildId, message);
@@ -110,26 +109,22 @@ public class SuggestionServiceBean implements SuggestionService {
suggestionLog.setOriginalMessageId(originalMessageId); suggestionLog.setOriginalMessageId(originalMessageId);
suggestionLog.setOriginalMessageUrl(MessageUtils.buildMessageUrl(serverId, channelId, originalMessageId)); suggestionLog.setOriginalMessageUrl(MessageUtils.buildMessageUrl(serverId, channelId, originalMessageId));
AUserInAServer suggester = suggestion.getSuggester(); AUserInAServer suggester = suggestion.getSuggester();
Optional<Guild> guildByIdOptional = botService.getGuildById(serverId); TextChannel textChannelById = botService.getTextChannelFromServer(serverId, channelId);
if(guildByIdOptional.isPresent()) { CompletableFuture<Member> memberById = botService.getMemberInServerAsync(serverId, suggester.getUserReference().getId());
Guild guildById = guildByIdOptional.get(); suggestionLog.setState(suggestion.getState());
Member memberById = guildById.getMemberById(suggester.getUserReference().getId()); suggestionLog.setSuggestionId(suggestion.getId());
suggestionLog.setSuggester(memberById); CompletableFuture<Void> finalFuture = new CompletableFuture<>();
suggestionLog.setState(suggestion.getState()); memberById.whenComplete((member, throwable) -> {
suggestionLog.setSuggestionId(suggestion.getId()); if(throwable == null) {
TextChannel textChannelById = guildById.getTextChannelById(channelId); suggestionLog.setSuggester(member);
if(textChannelById != null) {
return textChannelById.retrieveMessageById(originalMessageId).submit().thenCompose(message ->
self.updateSuggestionMessageText(text, suggestionLog, message)
);
} else {
log.warn("Not possible to update suggestion {}, because text channel {} was not found in guild {}.", suggestion.getId(), channelId, serverId);
throw new ChannelNotFoundException(channelId);
} }
} else { textChannelById.retrieveMessageById(originalMessageId).submit().thenCompose(message ->
log.warn("Not possible to update suggestion {}, because guild {} was not found.", suggestion.getId(), serverId); self.updateSuggestionMessageText(text, suggestionLog, message)
throw new GuildNotFoundException(serverId); ).thenAccept(aVoid -> finalFuture.complete(null));
} });
return finalFuture;
} }
@Transactional(propagation = Propagation.REQUIRES_NEW) @Transactional(propagation = Propagation.REQUIRES_NEW)

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.utility.service.management;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser; import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.utility.exception.ReminderNotFoundException;
import dev.sheldan.abstracto.utility.models.database.Reminder; import dev.sheldan.abstracto.utility.models.database.Reminder;
import dev.sheldan.abstracto.utility.repository.ReminderRepository; import dev.sheldan.abstracto.utility.repository.ReminderRepository;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -39,10 +40,15 @@ public class ReminderManagementServiceBean implements ReminderManagementService
} }
@Override @Override
public Optional<Reminder> loadReminder(Long reminderId) { public Optional<Reminder> loadReminderOptional(Long reminderId) {
return reminderRepository.findById(reminderId); return reminderRepository.findById(reminderId);
} }
@Override
public Reminder loadReminder(Long reminderId) {
return loadReminderOptional(reminderId).orElseThrow(() -> new ReminderNotFoundException(reminderId));
}
@Override @Override
public void setReminded(Reminder reminder) { public void setReminded(Reminder reminder) {
reminder.setReminded(true); reminder.setReminded(true);

View File

@@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component @Component
@Slf4j @Slf4j
@@ -52,13 +53,13 @@ public class StarboardPostReactorManagementServiceBean implements StarboardPostR
} }
@Override @Override
public List<StarStatsUser> retrieveTopStarGiver(Long serverId, Integer count) { public List<CompletableFuture<StarStatsUser>> retrieveTopStarGiver(Long serverId, Integer count) {
List<StarStatsUserResult> starGivers = repository.findTopStarGiverInServer(serverId, count); List<StarStatsUserResult> starGivers = repository.findTopStarGiverInServer(serverId, count);
return converter.convertToStarStatsUser(starGivers, serverId); return converter.convertToStarStatsUser(starGivers, serverId);
} }
@Override @Override
public List<StarStatsUser> retrieveTopStarReceiver(Long serverId, Integer count) { public List<CompletableFuture<StarStatsUser>> retrieveTopStarReceiver(Long serverId, Integer count) {
List<StarStatsUserResult> starReceivers = repository.retrieveTopStarReceiverInServer(serverId, count); List<StarStatsUserResult> starReceivers = repository.retrieveTopStarReceiverInServer(serverId, count);
return converter.convertToStarStatsUser(starReceivers, serverId); return converter.convertToStarStatsUser(starReceivers, serverId);
} }

View File

@@ -34,7 +34,7 @@ public class StarStatsTest {
CommandContext noParameters = CommandTestUtilities.getNoParameters(); CommandContext noParameters = CommandTestUtilities.getNoParameters();
when(noParameters.getGuild().getIdLong()).thenReturn(noParameters.getUserInitiatedContext().getChannel().getId()); when(noParameters.getGuild().getIdLong()).thenReturn(noParameters.getUserInitiatedContext().getChannel().getId());
StarStatsModel starStatsModel = StarStatsModel.builder().build(); StarStatsModel starStatsModel = StarStatsModel.builder().build();
when(starboardService.retrieveStarStats(noParameters.getGuild().getIdLong())).thenReturn(starStatsModel); when(starboardService.retrieveStarStats(noParameters.getGuild().getIdLong())).thenReturn(CompletableFuture.completedFuture(starStatsModel));
CompletableFuture<CommandResult> result = testUnit.executeAsync(noParameters); CompletableFuture<CommandResult> result = testUnit.executeAsync(noParameters);
verify(channelService, times(1)).sendEmbedTemplateInChannel(StarStats.STARSTATS_RESPONSE_TEMPLATE, starStatsModel, noParameters.getChannel()); verify(channelService, times(1)).sendEmbedTemplateInChannel(StarStats.STARSTATS_RESPONSE_TEMPLATE, starStatsModel, noParameters.getChannel());
CommandTestUtilities.checkSuccessfulCompletionAsync(result); CommandTestUtilities.checkSuccessfulCompletionAsync(result);

View File

@@ -83,7 +83,7 @@ public class MessageEmbedListenerTest {
String linkText = "link"; String linkText = "link";
String text = linkText; String text = linkText;
executeLinkTestForOneLink(text, linkText, ORIGIN_GUILD_ID, ORIGIN_GUILD_ID); executeLinkTestForOneLink(text, linkText, ORIGIN_GUILD_ID, ORIGIN_GUILD_ID);
verify(self, times(1)).loadUserAndEmbed(eq(message), anyLong(), any(CachedMessage.class)); verify(self, times(1)).embedSingleLink(eq(message), anyLong(), any(CachedMessage.class));
verify(message, times(1)).delete(); verify(message, times(1)).delete();
verify(deletionRestAction, times(1)).queue(); verify(deletionRestAction, times(1)).queue();
} }
@@ -93,7 +93,7 @@ public class MessageEmbedListenerTest {
String linkText = "link"; String linkText = "link";
String text = linkText + "more text"; String text = linkText + "more text";
executeLinkTestForOneLink(text, linkText, ORIGIN_GUILD_ID, ORIGIN_GUILD_ID); executeLinkTestForOneLink(text, linkText, ORIGIN_GUILD_ID, ORIGIN_GUILD_ID);
verify(self, times(1)).loadUserAndEmbed(eq(message), anyLong(), any(CachedMessage.class)); verify(self, times(1)).embedSingleLink(eq(message), anyLong(), any(CachedMessage.class));
verify(message, times(0)).delete(); verify(message, times(0)).delete();
verify(deletionRestAction, times(0)).queue(); verify(deletionRestAction, times(0)).queue();
} }
@@ -105,7 +105,7 @@ public class MessageEmbedListenerTest {
executeLinkTestForOneLink(text, linkText, ORIGIN_GUILD_ID, EMBEDDING_GUILD_ID); executeLinkTestForOneLink(text, linkText, ORIGIN_GUILD_ID, EMBEDDING_GUILD_ID);
verify(message, times(0)).delete(); verify(message, times(0)).delete();
verify(deletionRestAction, times(0)).queue(); verify(deletionRestAction, times(0)).queue();
verify(self, times(0)).loadUserAndEmbed(eq(message), anyLong(), any(CachedMessage.class)); verify(self, times(0)).embedSingleLink(eq(message), anyLong(), any(CachedMessage.class));
verify(messageCache, times(0)).getMessageFromCache(anyLong(), anyLong(), anyLong()); verify(messageCache, times(0)).getMessageFromCache(anyLong(), anyLong(), anyLong());
} }
@@ -149,7 +149,7 @@ public class MessageEmbedListenerTest {
testUnit.execute(message); testUnit.execute(message);
verify(message, times(0)).delete(); verify(message, times(0)).delete();
verify(deletionRestAction, times(0)).queue(); verify(deletionRestAction, times(0)).queue();
verify(self, times(1)).loadUserAndEmbed(message, embeddingUser.getUserInServerId(), cachedMessage); verify(self, times(1)).embedSingleLink(message, embeddingUser.getUserInServerId(), cachedMessage);
} }
@Test @Test
@@ -189,8 +189,8 @@ public class MessageEmbedListenerTest {
testUnit.execute(message); testUnit.execute(message);
verify(message, times(1)).delete(); verify(message, times(1)).delete();
verify(deletionRestAction, times(1)).queue(); verify(deletionRestAction, times(1)).queue();
verify(self, times(1)).loadUserAndEmbed(message, userInAServer.getUserInServerId(), cachedMessage); verify(self, times(1)).embedSingleLink(message, userInAServer.getUserInServerId(), cachedMessage);
verify(self, times(1)).loadUserAndEmbed(message, userInAServer.getUserInServerId(), secondCachedMessage); verify(self, times(1)).embedSingleLink(message, userInAServer.getUserInServerId(), secondCachedMessage);
} }
@Test @Test
@@ -198,8 +198,8 @@ public class MessageEmbedListenerTest {
CachedMessage cachedMessage = CachedMessage.builder().build(); CachedMessage cachedMessage = CachedMessage.builder().build();
long userId = 3L; long userId = 3L;
when(message.getTextChannel()).thenReturn(textChannel); when(message.getTextChannel()).thenReturn(textChannel);
testUnit.loadUserAndEmbed(message, userId, cachedMessage); when(messageEmbedService.embedLink(cachedMessage, textChannel, userId, message)).thenReturn(CompletableFuture.completedFuture(null));
verify(messageEmbedService, times(1)).embedLink(cachedMessage, textChannel, userId, message); testUnit.embedSingleLink(message, userId, cachedMessage);
} }
private void executeLinkTestForOneLink(String text, String linkText, Long originServerId, Long embeddingServerId) { private void executeLinkTestForOneLink(String text, String linkText, Long originServerId, Long embeddingServerId) {

View File

@@ -1,6 +1,9 @@
package dev.sheldan.abstracto.utility.repository.converter; package dev.sheldan.abstracto.utility.repository.converter;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsUser; import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsUser;
import dev.sheldan.abstracto.utility.repository.StarStatsUserResult; import dev.sheldan.abstracto.utility.repository.StarStatsUserResult;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
@@ -14,6 +17,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@@ -26,6 +30,8 @@ public class StarStatsUserConverterTest {
@Mock @Mock
private BotService botService; private BotService botService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Test @Test
public void testConversionOfMultipleItems() { public void testConversionOfMultipleItems() {
@@ -37,24 +43,34 @@ public class StarStatsUserConverterTest {
List<StarStatsUserResult> results = new ArrayList<>(); List<StarStatsUserResult> results = new ArrayList<>();
StarStatsUserResult firstResult = Mockito.mock(StarStatsUserResult.class); StarStatsUserResult firstResult = Mockito.mock(StarStatsUserResult.class);
Member firstMember = Mockito.mock(Member.class); Member firstMember = Mockito.mock(Member.class);
when(botService.getMemberInServer(serverId, firstUserId)).thenReturn(firstMember); AUserInAServer firstUser = Mockito.mock(AUserInAServer.class);
AUser firstAUser = Mockito.mock(AUser.class);
when(firstAUser.getId()).thenReturn(firstUserId);
when(firstUser.getUserReference()).thenReturn(firstAUser);
when(userInServerManagementService.loadUser(firstUserId)).thenReturn(firstUser);
when(botService.getMemberInServerAsync(serverId, firstUserId)).thenReturn(CompletableFuture.completedFuture(firstMember));
when(firstResult.getUserId()).thenReturn(firstUserId); when(firstResult.getUserId()).thenReturn(firstUserId);
when(firstResult.getStarCount()).thenReturn(firstStarCount); when(firstResult.getStarCount()).thenReturn(firstStarCount);
results.add(firstResult); results.add(firstResult);
StarStatsUserResult secondResult = Mockito.mock(StarStatsUserResult.class); StarStatsUserResult secondResult = Mockito.mock(StarStatsUserResult.class);
Member secondMember = Mockito.mock(Member.class); Member secondMember = Mockito.mock(Member.class);
when(botService.getMemberInServer(serverId, secondUserId)).thenReturn(secondMember); AUserInAServer secondUser = Mockito.mock(AUserInAServer.class);
AUser secondAUser = Mockito.mock(AUser.class);
when(secondAUser.getId()).thenReturn(secondUserId);
when(secondUser.getUserReference()).thenReturn(secondAUser);
when(userInServerManagementService.loadUser(secondUserId)).thenReturn(secondUser);
when(botService.getMemberInServerAsync(serverId, secondUserId)).thenReturn(CompletableFuture.completedFuture(secondMember));
when(secondResult.getUserId()).thenReturn(secondUserId); when(secondResult.getUserId()).thenReturn(secondUserId);
when(secondResult.getStarCount()).thenReturn(secondStarCount); when(secondResult.getStarCount()).thenReturn(secondStarCount);
results.add(secondResult); results.add(secondResult);
List<StarStatsUser> starStatsUsers = testUnit.convertToStarStatsUser(results, serverId); List<CompletableFuture<StarStatsUser>> starStatsUsers = testUnit.convertToStarStatsUser(results, serverId);
StarStatsUser firstConverted = starStatsUsers.get(0); StarStatsUser firstConverted = starStatsUsers.get(0).join();
Assert.assertEquals(firstStarCount, firstConverted.getStarCount()); Assert.assertEquals(firstStarCount, firstConverted.getStarCount());
Assert.assertEquals(firstMember, firstConverted.getMember()); Assert.assertEquals(firstMember, firstConverted.getMember());
Assert.assertEquals(firstUserId, firstConverted.getUser().getId()); Assert.assertEquals(firstUserId, firstConverted.getUser().getId());
StarStatsUser secondConverted = starStatsUsers.get(1); StarStatsUser secondConverted = starStatsUsers.get(1).join();
Assert.assertEquals(secondStarCount, secondConverted.getStarCount()); Assert.assertEquals(secondStarCount, secondConverted.getStarCount());
Assert.assertEquals(secondMember, secondConverted.getMember()); Assert.assertEquals(secondMember, secondConverted.getMember());
Assert.assertEquals(secondUserId, secondConverted.getUser().getId()); Assert.assertEquals(secondUserId, secondConverted.getUser().getId());
@@ -66,7 +82,7 @@ public class StarStatsUserConverterTest {
Long serverId = 5L; Long serverId = 5L;
List<StarStatsUserResult> results = new ArrayList<>(); List<StarStatsUserResult> results = new ArrayList<>();
List<StarStatsUser> starStatsUsers = testUnit.convertToStarStatsUser(results, serverId); List<CompletableFuture<StarStatsUser>> starStatsUsers = testUnit.convertToStarStatsUser(results, serverId);
verify(botService, times(0)).getMemberInServer(eq(serverId), anyLong()); verify(botService, times(0)).getMemberInServer(eq(serverId), anyLong());
Assert.assertEquals(0, starStatsUsers.size()); Assert.assertEquals(0, starStatsUsers.size());

View File

@@ -1,8 +1,6 @@
package dev.sheldan.abstracto.utility.service; package dev.sheldan.abstracto.utility.service;
import dev.sheldan.abstracto.core.models.cache.CachedMessage; import dev.sheldan.abstracto.core.models.cache.CachedMessage;
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.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.listener.MessageEmbeddedModel; import dev.sheldan.abstracto.core.models.template.listener.MessageEmbeddedModel;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
@@ -12,12 +10,9 @@ import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService; import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService; import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService; import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.test.MockUtils;
import dev.sheldan.abstracto.utility.models.MessageEmbedLink; import dev.sheldan.abstracto.utility.models.MessageEmbedLink;
import dev.sheldan.abstracto.utility.service.management.MessageEmbedPostManagementService; import dev.sheldan.abstracto.utility.service.management.MessageEmbedPostManagementService;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.TextChannel;
@@ -28,7 +23,6 @@ import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -60,7 +54,7 @@ public class MessageEmbedServiceBeanTest {
private ChannelService channelService; private ChannelService channelService;
@Mock @Mock
private MessageEmbedService self; private MessageEmbedServiceBean self;
@Mock @Mock
private MessageCache messageCache; private MessageCache messageCache;
@@ -84,6 +78,16 @@ public class MessageEmbedServiceBeanTest {
private static final String SHORTER_LINK_TEXT = "https://discord.com/channels/1/2/3"; private static final String SHORTER_LINK_TEXT = "https://discord.com/channels/1/2/3";
private static final String SECOND_LINK_TEXT = "https://discordapp.com/channels/2/3/4"; private static final String SECOND_LINK_TEXT = "https://discordapp.com/channels/2/3/4";
private static final Long EMBEDDING_USER_IN_SERVER_ID = 8L;
private static final Long SERVER_ID = 8L;
private static final Long USER_ID = 9L;
@Mock
private AUserInAServer embeddingUser;
@Mock
private Member embeddingMember;
@Test @Test
public void testNoLinkInString(){ public void testNoLinkInString(){
String message = "test"; String message = "test";
@@ -182,38 +186,16 @@ public class MessageEmbedServiceBeanTest {
} }
@Test @Test
public void testEmbeddingLink() { public void testLoadingEmbeddingModel() {
Long channelId = 6L; CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
Long serverId = 4L; when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
AServer server = MockUtils.getServer(serverId); when(cachedMessage.getAuthorId()).thenReturn(USER_ID);
AChannel aChannel = MockUtils.getTextChannel(server, channelId); when(userInServerManagementService.loadUserConditional(EMBEDDING_USER_IN_SERVER_ID)).thenReturn(Optional.of(embeddingUser));
Long userEmbeddingUserInServerId = 5L; when(botService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(embeddingMember));
AUserInAServer embeddingUser = MockUtils.getUserObject(userEmbeddingUserInServerId, server); MessageEmbeddedModel model = Mockito.mock(MessageEmbeddedModel.class);
Long authorId = 7L; when(self.loadMessageEmbedModel(embeddingMessage, cachedMessage, embeddingMember)).thenReturn(model);
AUserInAServer authorUser = MockUtils.getUserObject(authorId, server); when(self.sendEmbeddingMessage(cachedMessage, textChannel, EMBEDDING_USER_IN_SERVER_ID, model)).thenReturn(CompletableFuture.completedFuture(null));
Long firstMessageId = 6L; testUnit.embedLink(cachedMessage, textChannel, EMBEDDING_USER_IN_SERVER_ID, embeddingMessage).join();
CachedMessage cachedMessage = CachedMessage.builder().serverId(serverId).channelId(channelId).messageId(firstMessageId).authorId(authorUser.getUserReference().getId()).build();
Member embeddingMember = Mockito.mock(Member.class);
Member author = Mockito.mock(Member.class);
Guild guild = Mockito.mock(Guild.class);
when(embeddingMessage.getMember()).thenReturn(embeddingMember);
when(textChannel.getIdLong()).thenReturn(channelId);
when(embeddingMessage.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(serverId);
when(embeddingMessage.getChannel()).thenReturn(textChannel);
when(userInServerManagementService.loadUser(embeddingMember)).thenReturn(embeddingUser);
when(userInServerManagementService.loadUserConditional(userEmbeddingUserInServerId)).thenReturn(Optional.of(embeddingUser));
when(channelManagementService.loadChannel(channelId)).thenReturn(aChannel);
when(serverManagementService.loadOrCreate(serverId)).thenReturn(server);
when(botService.getMemberInServer(cachedMessage.getServerId(), cachedMessage.getAuthorId())).thenReturn(author);
when(botService.getTextChannelFromServerOptional(cachedMessage.getServerId(), cachedMessage.getChannelId())).thenReturn(Optional.of(textChannel));
MessageToSend messageToSend = MessageToSend.builder().build();
when(templateService.renderEmbedTemplate(eq(MessageEmbedServiceBean.MESSAGE_EMBED_TEMPLATE), any(MessageEmbeddedModel.class))).thenReturn(messageToSend);
Message messageContainingEmbed = Mockito.mock(Message.class);
when(channelService.sendMessageToSendToChannel(messageToSend, textChannel)).thenReturn(Arrays.asList(CompletableFuture.completedFuture(messageContainingEmbed)));
testUnit.embedLink(cachedMessage, textChannel, userEmbeddingUserInServerId, embeddingMessage);
verify(messageEmbedPostManagementService, times(1)).createMessageEmbed(cachedMessage, messageContainingEmbed, embeddingUser);
verify(messageService, times(1)).addReactionToMessage(MessageEmbedServiceBean.REMOVAL_EMOTE, cachedMessage.getServerId(), messageContainingEmbed);
} }
@Test @Test

View File

@@ -14,7 +14,6 @@ import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.test.MockUtils; import dev.sheldan.abstracto.test.MockUtils;
import dev.sheldan.abstracto.utility.exception.ReminderNotFoundException; import dev.sheldan.abstracto.utility.exception.ReminderNotFoundException;
import dev.sheldan.abstracto.utility.models.database.Reminder; import dev.sheldan.abstracto.utility.models.database.Reminder;
import dev.sheldan.abstracto.utility.models.template.commands.reminder.ExecutedReminderModel;
import dev.sheldan.abstracto.utility.service.management.ReminderManagementService; import dev.sheldan.abstracto.utility.service.management.ReminderManagementService;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
@@ -32,10 +31,13 @@ import org.quartz.JobDataMap;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import static dev.sheldan.abstracto.utility.service.RemindServiceBean.REMINDER_TEMPLATE_TEXT;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
@@ -74,6 +76,8 @@ public class RemindServiceBeanTest {
@Mock @Mock
private ScheduledExecutorService instantReminderScheduler; private ScheduledExecutorService instantReminderScheduler;
private static final Long REMINDER_ID = 5L;
@Before @Before
public void setup() { public void setup() {
when(message.getIdLong()).thenReturn(5L); when(message.getIdLong()).thenReturn(5L);
@@ -89,8 +93,7 @@ public class RemindServiceBeanTest {
String remindText = "text"; String remindText = "text";
Duration duration = Duration.ofSeconds(62); Duration duration = Duration.ofSeconds(62);
when(channelManagementService.loadChannel(channel.getIdLong())).thenReturn(aChannel); when(channelManagementService.loadChannel(channel.getIdLong())).thenReturn(aChannel);
Long reminderId = 5L; Reminder createdReminder = Reminder.builder().targetDate(Instant.now().plus(duration)).text(remindText).id(REMINDER_ID).build();
Reminder createdReminder = Reminder.builder().targetDate(Instant.now().plus(duration)).text(remindText).id(reminderId).build();
Long messageId = 5L; Long messageId = 5L;
when(reminderManagementService.createReminder(any(AServerAChannelAUser.class), eq(remindText), any(Instant.class), eq(messageId))).thenReturn(createdReminder); when(reminderManagementService.createReminder(any(AServerAChannelAUser.class), eq(remindText), any(Instant.class), eq(messageId))).thenReturn(createdReminder);
String triggerKey = "trigger"; String triggerKey = "trigger";
@@ -111,8 +114,7 @@ public class RemindServiceBeanTest {
String remindText = "text"; String remindText = "text";
Duration duration = Duration.ofSeconds(50); Duration duration = Duration.ofSeconds(50);
when(channelManagementService.loadChannel(channel.getIdLong())).thenReturn(aChannel); when(channelManagementService.loadChannel(channel.getIdLong())).thenReturn(aChannel);
Long reminderId = 5L; Reminder createdReminder = Reminder.builder().targetDate(Instant.now().plus(duration)).text(remindText).id(REMINDER_ID).build();
Reminder createdReminder = Reminder.builder().targetDate(Instant.now().plus(duration)).text(remindText).id(reminderId).build();
Long messageId = 5L; Long messageId = 5L;
when(reminderManagementService.createReminder(any(AServerAChannelAUser.class), eq(remindText), any(Instant.class), eq(messageId))).thenReturn(createdReminder); when(reminderManagementService.createReminder(any(AServerAChannelAUser.class), eq(remindText), any(Instant.class), eq(messageId))).thenReturn(createdReminder);
Reminder returnedReminder = testUnit.createReminderInForUser(user, remindText, duration, message); Reminder returnedReminder = testUnit.createReminderInForUser(user, remindText, duration, message);
@@ -126,36 +128,31 @@ public class RemindServiceBeanTest {
AServer server = MockUtils.getServer(); AServer server = MockUtils.getServer();
AChannel aChannel = MockUtils.getTextChannel(server, 4L); AChannel aChannel = MockUtils.getTextChannel(server, 4L);
AUserInAServer remindedUser = MockUtils.getUserObject(5L, server); AUserInAServer remindedUser = MockUtils.getUserObject(5L, server);
Long reminderId = 5L; Reminder remindedReminder = Reminder.builder().reminded(false).remindedUser(remindedUser).reminderDate(Instant.now()).targetDate(Instant.now()).server(server).channel(aChannel).id(REMINDER_ID).build();
Reminder remindedReminder = Reminder.builder().reminded(false).remindedUser(remindedUser).reminderDate(Instant.now()).targetDate(Instant.now()).server(server).channel(aChannel).id(reminderId).build(); when(reminderManagementService.loadReminder(REMINDER_ID)).thenReturn(remindedReminder);
when(reminderManagementService.loadReminder(reminderId)).thenReturn(Optional.of(remindedReminder));
Guild guildMock = Mockito.mock(Guild.class); Guild guildMock = Mockito.mock(Guild.class);
when(botService.getGuildById(server.getId())).thenReturn(Optional.of(guildMock)); when(botService.getGuildByIdOptional(server.getId())).thenReturn(Optional.of(guildMock));
when(botService.getTextChannelFromServerOptional(server.getId(), aChannel.getId())).thenReturn(Optional.of(channel)); when(botService.getTextChannelFromServerOptional(server.getId(), aChannel.getId())).thenReturn(Optional.of(channel));
Member mockedMember = Mockito.mock(Member.class); Member mockedMember = Mockito.mock(Member.class);
when(botService.getMemberInServer(server.getId(), remindedUser.getUserReference().getId())).thenReturn(mockedMember); when(botService.getMemberInServerAsync(server.getId(), remindedUser.getUserReference().getId())).thenReturn(CompletableFuture.completedFuture(mockedMember));
MessageToSend messageToSend = MessageToSend.builder().build(); testUnit.executeReminder(REMINDER_ID);
when(templateService.renderEmbedTemplate(eq("remind_reminder"), any(ExecutedReminderModel.class))).thenReturn(messageToSend);
testUnit.executeReminder(reminderId);
verify(reminderManagementService, times(1)).setReminded(remindedReminder); verify(reminderManagementService, times(1)).setReminded(remindedReminder);
verify(channelService, times(1)).sendMessageToSendToChannel(messageToSend, channel);
} }
@Test @Test
public void testExecuteReminderFromNotFoundChannel() { public void testExecuteReminderFromNotFoundChannel() {
AServer server = MockUtils.getServer(); AServer server = MockUtils.getServer();
AChannel aChannel = MockUtils.getTextChannel(server, 4L); AChannel aChannel = MockUtils.getTextChannel(server, 4L);
Long reminderId = 5L;
AUserInAServer remindedUser = Mockito.mock(AUserInAServer.class); AUserInAServer remindedUser = Mockito.mock(AUserInAServer.class);
AUser user = Mockito.mock(AUser.class); AUser user = Mockito.mock(AUser.class);
when(user.getId()).thenReturn(6L); when(user.getId()).thenReturn(6L);
when(remindedUser.getUserReference()).thenReturn(user); when(remindedUser.getUserReference()).thenReturn(user);
Reminder remindedReminder = Reminder.builder().reminded(false).server(server).remindedUser(remindedUser).channel(aChannel).id(reminderId).build(); Reminder remindedReminder = Reminder.builder().reminded(false).server(server).remindedUser(remindedUser).channel(aChannel).id(REMINDER_ID).build();
when(reminderManagementService.loadReminder(reminderId)).thenReturn(Optional.of(remindedReminder)); when(reminderManagementService.loadReminder(REMINDER_ID)).thenReturn(remindedReminder);
Guild guildMock = Mockito.mock(Guild.class); Guild guildMock = Mockito.mock(Guild.class);
when(botService.getGuildById(server.getId())).thenReturn(Optional.of(guildMock)); when(botService.getGuildByIdOptional(server.getId())).thenReturn(Optional.of(guildMock));
when(botService.getTextChannelFromServerOptional(server.getId(), aChannel.getId())).thenReturn(Optional.empty()); when(botService.getTextChannelFromServerOptional(server.getId(), aChannel.getId())).thenReturn(Optional.empty());
testUnit.executeReminder(reminderId); testUnit.executeReminder(REMINDER_ID);
verify(reminderManagementService, times(1)).setReminded(remindedReminder); verify(reminderManagementService, times(1)).setReminded(remindedReminder);
} }
@@ -170,8 +167,8 @@ public class RemindServiceBeanTest {
when(user.getId()).thenReturn(6L); when(user.getId()).thenReturn(6L);
when(remindedUser.getUserReference()).thenReturn(user); when(remindedUser.getUserReference()).thenReturn(user);
Reminder remindedReminder = Reminder.builder().reminded(false).server(server).channel(aChannel).remindedUser(remindedUser).id(reminderId).build(); Reminder remindedReminder = Reminder.builder().reminded(false).server(server).channel(aChannel).remindedUser(remindedUser).id(reminderId).build();
when(reminderManagementService.loadReminder(reminderId)).thenReturn(Optional.of(remindedReminder)); when(reminderManagementService.loadReminder(reminderId)).thenReturn(remindedReminder);
when(botService.getGuildById(server.getId())).thenReturn(Optional.empty()); when(botService.getGuildByIdOptional(server.getId())).thenReturn(Optional.empty());
testUnit.executeReminder(reminderId); testUnit.executeReminder(reminderId);
verify(reminderManagementService, times(1)).setReminded(remindedReminder); verify(reminderManagementService, times(1)).setReminded(remindedReminder);
} }
@@ -180,17 +177,9 @@ public class RemindServiceBeanTest {
public void testExecuteRemindedReminder() { public void testExecuteRemindedReminder() {
Long reminderId = 5L; Long reminderId = 5L;
Reminder remindedReminder = Reminder.builder().reminded(true).build(); Reminder remindedReminder = Reminder.builder().reminded(true).build();
when(reminderManagementService.loadReminder(reminderId)).thenReturn(Optional.of(remindedReminder)); when(reminderManagementService.loadReminder(reminderId)).thenReturn(remindedReminder);
testUnit.executeReminder(reminderId); testUnit.executeReminder(reminderId);
verify(botService, times(0)).getGuildById(anyLong()); verify(botService, times(0)).getGuildByIdOptional(anyLong());
}
@Test(expected = ReminderNotFoundException.class)
public void testExecuteIllegalReminderId() {
Long reminderId = 5L;
when(reminderManagementService.loadReminder(reminderId)).thenReturn(Optional.empty());
testUnit.executeReminder(reminderId);
verify(botService, times(0)).getGuildById(anyLong());
} }
@Test @Test
@@ -225,4 +214,21 @@ public class RemindServiceBeanTest {
testUnit.unRemind(reminderId, remindedUser); testUnit.unRemind(reminderId, remindedUser);
} }
@Test
public void testSendReminderText() {
Member remindedMember = Mockito.mock(Member.class);
Guild guild = Mockito.mock(Guild.class);
when(remindedMember.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(8L);
when(remindedMember.getIdLong()).thenReturn(9L);
Reminder remindedReminder = Mockito.mock(Reminder.class);
when(remindedReminder.getTargetDate()).thenReturn(Instant.now());
when(remindedReminder.getReminderDate()).thenReturn(Instant.now());
when(reminderManagementService.loadReminder(REMINDER_ID)).thenReturn(remindedReminder);
MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
when(templateService.renderEmbedTemplate(eq(REMINDER_TEMPLATE_TEXT), any())).thenReturn(messageToSend);
when(channelService.sendMessageToSendToChannel(messageToSend, channel)).thenReturn(Arrays.asList(CompletableFuture.completedFuture(null)));
testUnit.sendReminderText(REMINDER_ID, channel, remindedMember).join();
}
} }

View File

@@ -40,6 +40,7 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.utility.service.StarboardServiceBean.STARBOARD_POST_TEMPLATE;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
@@ -93,6 +94,19 @@ public class StarboardServiceBeanTest {
@Mock @Mock
private TextChannel mockedTextChannel; private TextChannel mockedTextChannel;
@Mock
private Member starredMember;
@Mock
private AChannel starboardChannel;
@Mock
private MessageToSend messageToSend;
private static final Long STARRED_USER_ID = 5L;
private static final Long SERVER_ID = 6L;
private static final Long STARBOARD_CHANNEL_ID = 8L;
@Captor @Captor
private ArgumentCaptor<AUserInAServer> userInAServerArgumentCaptor; private ArgumentCaptor<AUserInAServer> userInAServerArgumentCaptor;
@@ -108,34 +122,40 @@ public class StarboardServiceBeanTest {
AUserInAServer userReacting = MockUtils.getUserObject(4L, server); AUserInAServer userReacting = MockUtils.getUserObject(4L, server);
AUserInAServer starredUser = MockUtils.getUserObject(5L, server); AUserInAServer starredUser = MockUtils.getUserObject(5L, server);
Long channelId = 10L; Long channelId = 10L;
Long starredUserId = starredUser.getUserReference().getId();
CachedMessage message = CachedMessage CachedMessage message = CachedMessage
.builder() .builder()
.authorId(starredUser.getUserReference().getId()) .authorId(starredUserId)
.serverId(server.getId()) .serverId(server.getId())
.channelId(channelId) .channelId(channelId)
.build(); .build();
Member authorMember = Mockito.mock(Member.class); Member authorMember = Mockito.mock(Member.class);
when(botService.getMemberInServer(message.getServerId(), message.getAuthorId())).thenReturn(authorMember); when(botService.getMemberInServerAsync(message.getServerId(), message.getAuthorId())).thenReturn(CompletableFuture.completedFuture(authorMember));
when(botService.getTextChannelFromServerOptional(server.getId(), channelId)).thenReturn(Optional.of(mockedTextChannel)); when(botService.getTextChannelFromServerOptional(server.getId(), channelId)).thenReturn(Optional.of(mockedTextChannel));
when(botService.getGuildById(server.getId())).thenReturn(Optional.of(guild)); when(botService.getGuildByIdOptional(server.getId())).thenReturn(Optional.of(guild));
MessageToSend postMessage = MessageToSend.builder().build();
when(templateService.renderEmbedTemplate(eq(StarboardServiceBean.STARBOARD_POST_TEMPLATE), starboardPostModelArgumentCaptor.capture())).thenReturn(postMessage);
AChannel channel = MockUtils.getTextChannel(server, channelId);
PostTarget postTarget = PostTarget.builder().channelReference(channel).build();
when(postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), server.getId())).thenReturn(postTarget);
List<CompletableFuture<Message>> futures = Arrays.asList(CompletableFuture.completedFuture(sendPost));
when(postTargetService.sendEmbedInPostTarget(postMessage, StarboardPostTarget.STARBOARD, server.getId())).thenReturn(futures);
ADefaultConfig config = ADefaultConfig.builder().longValue(3L).build(); ADefaultConfig config = ADefaultConfig.builder().longValue(3L).build();
when(defaultConfigManagementService.getDefaultConfig(StarboardServiceBean.STAR_LEVELS_CONFIG_KEY)).thenReturn(config); when(defaultConfigManagementService.getDefaultConfig(StarboardServiceBean.STAR_LEVELS_CONFIG_KEY)).thenReturn(config);
when(configService.getLongValue("starLvl3", server.getId())).thenReturn(3L); when(configService.getLongValue("starLvl3", server.getId())).thenReturn(3L);
when(configService.getLongValue("starLvl2", server.getId())).thenReturn(2L); when(configService.getLongValue("starLvl2", server.getId())).thenReturn(2L);
when(emoteService.getUsableEmoteOrDefault(server.getId(), "star2")).thenReturn("b"); when(emoteService.getUsableEmoteOrDefault(server.getId(), "star2")).thenReturn("b");
testUnit.createStarboardPost(message, userExceptAuthor, userReacting, starredUser); when(self.sendStarboardPostAndStore(eq(message), eq(starredUserId), anyList(), any())).thenReturn(CompletableFuture.completedFuture(null));
verify(self, times(1)).persistPost(eq(message), anyList(), eq(futures), eq(channelId), eq(starredUser.getUserInServerId())); testUnit.createStarboardPost(message, userExceptAuthor, userReacting, starredUser).join();
List<StarboardPostModel> starboardPostModels = starboardPostModelArgumentCaptor.getAllValues(); }
Assert.assertEquals(1, starboardPostModels.size());
StarboardPostModel usedModel = starboardPostModels.get(0); @Test
Assert.assertEquals("b", usedModel.getStarLevelEmote()); public void testSendStarboard() {
CachedMessage message = Mockito.mock(CachedMessage.class);
when(message.getServerId()).thenReturn(SERVER_ID);
StarboardPostModel model = Mockito.mock(StarboardPostModel.class);
when(templateService.renderEmbedTemplate(STARBOARD_POST_TEMPLATE, model)).thenReturn(messageToSend);
PostTarget postTarget = Mockito.mock(PostTarget.class);
when(postTarget.getChannelReference()).thenReturn(starboardChannel);
when(starboardChannel.getId()).thenReturn(STARBOARD_CHANNEL_ID);
when(postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), SERVER_ID)).thenReturn(postTarget);
when(postTargetService.sendEmbedInPostTarget(messageToSend, StarboardPostTarget.STARBOARD, SERVER_ID)).thenReturn(Arrays.asList(CompletableFuture.completedFuture(null)));
ArrayList<Long> userExceptAuthorIds = new ArrayList<>();
testUnit.sendStarboardPostAndStore(message, STARRED_USER_ID, userExceptAuthorIds, model);
verify(self, times(1)).persistPost(eq(message), eq(userExceptAuthorIds), any(), eq(STARBOARD_CHANNEL_ID), eq(STARRED_USER_ID));
} }
@Test @Test
@@ -189,12 +209,13 @@ public class StarboardServiceBeanTest {
Long starboardPostId = 47L; Long starboardPostId = 47L;
StarboardPost post = StarboardPost.builder().postMessageId(postMessageId).starboardMessageId(oldPostId).sourceChanel(sourceChannel).id(starboardPostId).build(); StarboardPost post = StarboardPost.builder().postMessageId(postMessageId).starboardMessageId(oldPostId).sourceChanel(sourceChannel).id(starboardPostId).build();
MessageToSend postMessage = MessageToSend.builder().build(); MessageToSend postMessage = MessageToSend.builder().build();
when(templateService.renderEmbedTemplate(eq(StarboardServiceBean.STARBOARD_POST_TEMPLATE), starboardPostModelArgumentCaptor.capture())).thenReturn(postMessage); when(templateService.renderEmbedTemplate(eq(STARBOARD_POST_TEMPLATE), starboardPostModelArgumentCaptor.capture())).thenReturn(postMessage);
when(postTargetService.editOrCreatedInPostTarget(oldPostId, postMessage, StarboardPostTarget.STARBOARD, server.getId())).thenReturn(Arrays.asList(CompletableFuture.completedFuture(sendPost))); when(postTargetService.editOrCreatedInPostTarget(oldPostId, postMessage, StarboardPostTarget.STARBOARD, server.getId())).thenReturn(Arrays.asList(CompletableFuture.completedFuture(sendPost)));
when(sendPost.getIdLong()).thenReturn(newPostId); when(sendPost.getIdLong()).thenReturn(newPostId);
ADefaultConfig config = ADefaultConfig.builder().longValue(4L).build(); ADefaultConfig config = ADefaultConfig.builder().longValue(4L).build();
when(defaultConfigManagementService.getDefaultConfig(StarboardServiceBean.STAR_LEVELS_CONFIG_KEY)).thenReturn(config); when(defaultConfigManagementService.getDefaultConfig(StarboardServiceBean.STAR_LEVELS_CONFIG_KEY)).thenReturn(config);
when(starboardPostManagementService.findByStarboardPostId(starboardPostId)).thenReturn(Optional.of(post)); when(starboardPostManagementService.findByStarboardPostId(starboardPostId)).thenReturn(Optional.of(post));
when(botService.getMemberInServerAsync(server.getId(), starredUser.getUserReference().getId())).thenReturn(CompletableFuture.completedFuture(starredMember));
List<AUserInAServer > userExceptAuthor = new ArrayList<>(); List<AUserInAServer > userExceptAuthor = new ArrayList<>();
testUnit.updateStarboardPost(post, message, userExceptAuthor); testUnit.updateStarboardPost(post, message, userExceptAuthor);
verify(postTargetService, times(1)).editOrCreatedInPostTarget(oldPostId, postMessage, StarboardPostTarget.STARBOARD, server.getId()); verify(postTargetService, times(1)).editOrCreatedInPostTarget(oldPostId, postMessage, StarboardPostTarget.STARBOARD, server.getId());
@@ -237,9 +258,9 @@ public class StarboardServiceBeanTest {
StarboardPost post2 = StarboardPost.builder().starboardChannel(channel).postMessageId(secondPostMessageId).reactions(new ArrayList<>()).build(); StarboardPost post2 = StarboardPost.builder().starboardChannel(channel).postMessageId(secondPostMessageId).reactions(new ArrayList<>()).build();
List<StarboardPost> topPosts = Arrays.asList(post1, post2); List<StarboardPost> topPosts = Arrays.asList(post1, post2);
when(starboardPostManagementService.retrieveTopPosts(server.getId(), limit)).thenReturn(topPosts); when(starboardPostManagementService.retrieveTopPosts(server.getId(), limit)).thenReturn(topPosts);
StarStatsUser statsUser = StarStatsUser.builder().build(); CompletableFuture<StarStatsUser> statsUser = CompletableFuture.completedFuture(Mockito.mock(StarStatsUser.class));
StarStatsUser statsUser2 = StarStatsUser.builder().build(); CompletableFuture<StarStatsUser> statsUser2 = CompletableFuture.completedFuture(Mockito.mock(StarStatsUser.class));
List<StarStatsUser> topGiver = Arrays.asList(statsUser, statsUser2); List<CompletableFuture<StarStatsUser>> topGiver = Arrays.asList(statsUser, statsUser2);
when(starboardPostReactorManagementService.retrieveTopStarGiver(server.getId(), limit)).thenReturn(topGiver); when(starboardPostReactorManagementService.retrieveTopStarGiver(server.getId(), limit)).thenReturn(topGiver);
when(starboardPostReactorManagementService.retrieveTopStarReceiver(server.getId(), limit)).thenReturn(topGiver); when(starboardPostReactorManagementService.retrieveTopStarReceiver(server.getId(), limit)).thenReturn(topGiver);
when(starboardPostManagementService.getPostCount(server.getId())).thenReturn(50); when(starboardPostManagementService.getPostCount(server.getId())).thenReturn(50);
@@ -247,7 +268,8 @@ public class StarboardServiceBeanTest {
when(emoteService.getUsableEmoteOrDefault(server.getId(), "starboardBadge1")).thenReturn("1"); when(emoteService.getUsableEmoteOrDefault(server.getId(), "starboardBadge1")).thenReturn("1");
when(emoteService.getUsableEmoteOrDefault(server.getId(), "starboardBadge2")).thenReturn("2"); when(emoteService.getUsableEmoteOrDefault(server.getId(), "starboardBadge2")).thenReturn("2");
when(emoteService.getUsableEmoteOrDefault(server.getId(), "starboardBadge3")).thenReturn("3"); when(emoteService.getUsableEmoteOrDefault(server.getId(), "starboardBadge3")).thenReturn("3");
StarStatsModel model = testUnit.retrieveStarStats(server.getId()); CompletableFuture<StarStatsModel> modelFuture = testUnit.retrieveStarStats(server.getId());
StarStatsModel model = modelFuture.join();
List<String> badgeEmotes = model.getBadgeEmotes(); List<String> badgeEmotes = model.getBadgeEmotes();
Assert.assertEquals(limit.intValue(), badgeEmotes.size()); Assert.assertEquals(limit.intValue(), badgeEmotes.size());
Assert.assertEquals("1", badgeEmotes.get(0)); Assert.assertEquals("1", badgeEmotes.get(0));

View File

@@ -1,9 +1,9 @@
package dev.sheldan.abstracto.utility.service; package dev.sheldan.abstracto.utility.service;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException; import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService; import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.CounterService; import dev.sheldan.abstracto.core.service.CounterService;
@@ -11,7 +11,6 @@ import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.templating.model.MessageToSend; import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService; import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.test.MockUtils;
import dev.sheldan.abstracto.utility.config.posttargets.SuggestionPostTarget; import dev.sheldan.abstracto.utility.config.posttargets.SuggestionPostTarget;
import dev.sheldan.abstracto.utility.exception.SuggestionNotFoundException; import dev.sheldan.abstracto.utility.exception.SuggestionNotFoundException;
import dev.sheldan.abstracto.utility.exception.SuggestionUpdateException; import dev.sheldan.abstracto.utility.exception.SuggestionUpdateException;
@@ -74,49 +73,64 @@ public class SuggestionServiceBeanTest {
@Mock @Mock
private CounterService counterService; private CounterService counterService;
@Mock
private AServer server;
@Mock
private AChannel channel;
@Mock
private AUserInAServer suggester;
@Mock
private AUser suggesterUser;
@Mock
private Member suggesterMember;
private static final Long SUGGESTER_ID = 8L;
private static final Long SERVER_ID = 3L;
private static final Long CHANNEL_ID = 7L;
private static final Long SUGGESTION_ID = 5L;
@Test @Test
public void testCreateSuggestionMessage() { public void testCreateSuggestionMessage() {
String suggestionText = "text"; String suggestionText = "text";
AServer server = MockUtils.getServer();
SuggestionLog log = Mockito.mock(SuggestionLog.class); SuggestionLog log = Mockito.mock(SuggestionLog.class);
when(log.getServer()).thenReturn(server); when(log.getServer()).thenReturn(server);
Long suggestionId = 5L;
when(suggestionCreator.getGuild()).thenReturn(guild); when(suggestionCreator.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(server.getId()); when(guild.getIdLong()).thenReturn(SERVER_ID);
when(guild.getIdLong()).thenReturn(SERVER_ID);
MessageToSend messageToSend = MessageToSend.builder().build(); MessageToSend messageToSend = MessageToSend.builder().build();
when(templateService.renderEmbedTemplate(eq(SuggestionServiceBean.SUGGESTION_LOG_TEMPLATE), any(SuggestionLog.class))).thenReturn(messageToSend); when(templateService.renderEmbedTemplate(eq(SuggestionServiceBean.SUGGESTION_LOG_TEMPLATE), any(SuggestionLog.class))).thenReturn(messageToSend);
Message suggestionMessage = Mockito.mock(Message.class); Message suggestionMessage = Mockito.mock(Message.class);
when(counterService.getNextCounterValue(server, SUGGESTION_COUNTER_KEY)).thenReturn(suggestionId); when(counterService.getNextCounterValue(server, SUGGESTION_COUNTER_KEY)).thenReturn(SUGGESTION_ID);
List<CompletableFuture<Message>> postingFutures = Arrays.asList(CompletableFuture.completedFuture(suggestionMessage)); List<CompletableFuture<Message>> postingFutures = Arrays.asList(CompletableFuture.completedFuture(suggestionMessage));
when(postTargetService.sendEmbedInPostTarget(messageToSend, SuggestionPostTarget.SUGGESTION, server.getId())).thenReturn(postingFutures); when(postTargetService.sendEmbedInPostTarget(messageToSend, SuggestionPostTarget.SUGGESTION, SERVER_ID)).thenReturn(postingFutures);
testUnit.createSuggestionMessage(suggestionCreator, suggestionText, log); testUnit.createSuggestionMessage(suggestionCreator, suggestionText, log);
verify( messageService, times(1)).addReactionToMessageWithFuture(SuggestionServiceBean.SUGGESTION_YES_EMOTE, server.getId(), suggestionMessage); verify( messageService, times(1)).addReactionToMessageWithFuture(SuggestionServiceBean.SUGGESTION_YES_EMOTE, SERVER_ID, suggestionMessage);
verify( messageService, times(1)).addReactionToMessageWithFuture(SuggestionServiceBean.SUGGESTION_NO_EMOTE, server.getId(), suggestionMessage); verify( messageService, times(1)).addReactionToMessageWithFuture(SuggestionServiceBean.SUGGESTION_NO_EMOTE, SERVER_ID, suggestionMessage);
} }
@Test @Test
public void testCreateSuggestion() { public void testCreateSuggestion() {
Member member = Mockito.mock(Member.class); when(suggesterMember.getGuild()).thenReturn(guild);
when(member.getGuild()).thenReturn(guild);
when(guild.getId()).thenReturn("5"); when(guild.getId()).thenReturn("5");
String text = "text"; String text = "text";
Message message = Mockito.mock(Message.class); Message message = Mockito.mock(Message.class);
Long suggestionId = 3L; testUnit.persistSuggestionInDatabase(suggesterMember, text, message, SUGGESTION_ID);
testUnit.persistSuggestionInDatabase(member, text, message, suggestionId); verify(suggestionManagementService, times(1)).createSuggestion(suggesterMember, text, message, SUGGESTION_ID);
verify(suggestionManagementService, times(1)).createSuggestion(member, text, message, suggestionId);
} }
@Test @Test
public void testAcceptExistingSuggestion() { public void testAcceptExistingSuggestion() {
Member suggesterMember = Mockito.mock(Member.class);
executeAcceptWithMember(suggesterMember); executeAcceptWithMember(suggesterMember);
} }
@Test(expected = SuggestionNotFoundException.class) @Test(expected = SuggestionNotFoundException.class)
public void testAcceptNotExistingSuggestion() { public void testAcceptNotExistingSuggestion() {
Long suggestionId = 5L; when(suggestionManagementService.getSuggestion(SUGGESTION_ID)).thenReturn(Optional.empty());
when(suggestionManagementService.getSuggestion(suggestionId)).thenReturn(Optional.empty()); testUnit.acceptSuggestion(SUGGESTION_ID, CLOSING_TEXT, SuggestionLog.builder().build());
testUnit.acceptSuggestion(suggestionId, CLOSING_TEXT, SuggestionLog.builder().build());
} }
@Test @Test
@@ -126,38 +140,24 @@ public class SuggestionServiceBeanTest {
@Test(expected = ChannelNotFoundException.class) @Test(expected = ChannelNotFoundException.class)
public void testAcceptSuggestionInNoTextChannel() { public void testAcceptSuggestionInNoTextChannel() {
Long suggestionId = 5L; setupForNoTextChannel();
setupForNoTextChannel(suggestionId); testUnit.acceptSuggestion(SUGGESTION_ID, CLOSING_TEXT, SuggestionLog.builder().build());
testUnit.acceptSuggestion(suggestionId, CLOSING_TEXT, SuggestionLog.builder().build());
} }
private void setupForNoTextChannel(Long suggestionId) { private void setupForNoTextChannel() {
AServer server = MockUtils.getServer();
Long channelId = 5L;
Long messageId = 7L; Long messageId = 7L;
AChannel channel = MockUtils.getTextChannel(server, channelId);
AUserInAServer suggester = MockUtils.getUserObject(4L, server);
Suggestion suggestionToAccept = Suggestion Suggestion suggestionToAccept = Suggestion
.builder() .builder()
.channel(channel) .channel(channel)
.server(server) .server(server)
.id(suggestionId) .id(SUGGESTION_ID)
.suggester(suggester) .suggester(suggester)
.messageId(messageId) .messageId(messageId)
.build(); .build();
when(server.getId()).thenReturn(SERVER_ID);
when(suggestionManagementService.getSuggestion(suggestionId)).thenReturn(Optional.of(suggestionToAccept)); when(channel.getId()).thenReturn(CHANNEL_ID);
when(botService.getGuildById(server.getId())).thenReturn(Optional.of(guild)); when(botService.getTextChannelFromServer(SERVER_ID, CHANNEL_ID)).thenThrow(new ChannelNotFoundException(CHANNEL_ID));
Member suggesterMember = Mockito.mock(Member.class); when(suggestionManagementService.getSuggestion(SUGGESTION_ID)).thenReturn(Optional.of(suggestionToAccept));
when(guild.getMemberById(suggester.getUserReference().getId())).thenReturn(suggesterMember);
when(guild.getTextChannelById(channelId)).thenReturn(null);
}
@Test(expected = GuildNotFoundException.class)
public void testAcceptSuggestionInNoGuild() {
Long suggestionId = 5L;
setupForNoGuild(suggestionId);
testUnit.acceptSuggestion(suggestionId, CLOSING_TEXT, SuggestionLog.builder().build());
} }
@Test(expected = SuggestionUpdateException.class) @Test(expected = SuggestionUpdateException.class)
@@ -169,7 +169,6 @@ public class SuggestionServiceBeanTest {
@Test @Test
public void testUpdateSuggestionMessageWithEmbed() { public void testUpdateSuggestionMessageWithEmbed() {
AServer server = MockUtils.getServer();
SuggestionLog log = SuggestionLog.builder().server(server).build(); SuggestionLog log = SuggestionLog.builder().server(server).build();
MessageEmbed embed = Mockito.mock(MessageEmbed.class); MessageEmbed embed = Mockito.mock(MessageEmbed.class);
when(embed.getDescription()).thenReturn("description"); when(embed.getDescription()).thenReturn("description");
@@ -185,15 +184,13 @@ public class SuggestionServiceBeanTest {
@Test @Test
public void testRejectExistingSuggestion() { public void testRejectExistingSuggestion() {
Member suggesterMember = Mockito.mock(Member.class);
executeRejectWithMember(suggesterMember); executeRejectWithMember(suggesterMember);
} }
@Test(expected = SuggestionNotFoundException.class) @Test(expected = SuggestionNotFoundException.class)
public void testRejectNotExistingSuggestion() { public void testRejectNotExistingSuggestion() {
Long suggestionId = 5L; when(suggestionManagementService.getSuggestion(SUGGESTION_ID)).thenReturn(Optional.empty());
when(suggestionManagementService.getSuggestion(suggestionId)).thenReturn(Optional.empty()); testUnit.rejectSuggestion(SUGGESTION_ID, CLOSING_TEXT, SuggestionLog.builder().build());
testUnit.rejectSuggestion(suggestionId, CLOSING_TEXT, SuggestionLog.builder().build());
} }
@Test @Test
@@ -203,82 +200,51 @@ public class SuggestionServiceBeanTest {
@Test(expected = ChannelNotFoundException.class) @Test(expected = ChannelNotFoundException.class)
public void testRejectSuggestionInNoTextChannel() { public void testRejectSuggestionInNoTextChannel() {
Long suggestionId = setupForNoTextChannel(); setupForNoTextChannel();
testUnit.rejectSuggestion(suggestionId, CLOSING_TEXT, SuggestionLog.builder().build()); testUnit.rejectSuggestion(SUGGESTION_ID, CLOSING_TEXT, SuggestionLog.builder().build());
} }
@Test(expected = GuildNotFoundException.class) private void executeAcceptWithMember(Member actualMember) {
public void testRejectSuggestionInNoGuild() {
Long suggestionId = 5L;
setupForNoGuild(suggestionId);
testUnit.rejectSuggestion(suggestionId, CLOSING_TEXT, SuggestionLog.builder().build());
}
private Long setupForNoTextChannel() {
Long suggestionId = 5L;
setupForNoTextChannel(suggestionId);
return suggestionId;
}
private Long setupForNoGuild(Long suggestionId) {
AServer server = MockUtils.getServer();
Long channelId = 5L;
AChannel channel = MockUtils.getTextChannel(server, channelId);
Suggestion suggestionToAccept = Suggestion
.builder()
.server(server)
.id(suggestionId)
.channel(channel)
.build();
when(suggestionManagementService.getSuggestion(suggestionId)).thenReturn(Optional.of(suggestionToAccept));
when(botService.getGuildById(server.getId())).thenReturn(Optional.empty());
return suggestionId;
}
private void executeAcceptWithMember(Member suggesterMember) {
Long suggestionId = 5L;
Long channelId = 5L;
Long messageId = 7L; Long messageId = 7L;
SuggestionLog logParameter = SuggestionLog.builder().build(); SuggestionLog logParameter = SuggestionLog.builder().build();
Suggestion suggestionToAccept = setupClosing(suggesterMember, suggestionId, channelId, messageId); Suggestion suggestionToAccept = setupClosing(messageId);
RestAction<Message> retrievalAction = Mockito.mock(RestAction.class); RestAction<Message> retrievalAction = Mockito.mock(RestAction.class);
when(textChannel.retrieveMessageById(messageId)).thenReturn(retrievalAction); when(textChannel.retrieveMessageById(messageId)).thenReturn(retrievalAction);
Message suggestionMessage = Mockito.mock(Message.class); Message suggestionMessage = Mockito.mock(Message.class);
when(retrievalAction.submit()).thenReturn(CompletableFuture.completedFuture(suggestionMessage)); when(retrievalAction.submit()).thenReturn(CompletableFuture.completedFuture(suggestionMessage));
testUnit.acceptSuggestion(suggestionId, CLOSING_TEXT, logParameter); when(botService.getMemberInServerAsync(SERVER_ID, SUGGESTER_ID)).thenReturn(CompletableFuture.completedFuture(actualMember));
testUnit.acceptSuggestion(SUGGESTION_ID, CLOSING_TEXT, logParameter);
verify(suggestionManagementService, times(1)).setSuggestionState(suggestionToAccept, SuggestionState.ACCEPTED); verify(suggestionManagementService, times(1)).setSuggestionState(suggestionToAccept, SuggestionState.ACCEPTED);
} }
private void executeRejectWithMember(Member suggesterMember) { private void executeRejectWithMember(Member actualMember) {
Long suggestionId = 5L;
Long channelId = 5L;
Long messageId = 7L; Long messageId = 7L;
SuggestionLog logParameter = SuggestionLog.builder().build(); SuggestionLog logParameter = SuggestionLog.builder().build();
Suggestion suggestionToAccept = setupClosing(suggesterMember, suggestionId, channelId, messageId); Suggestion suggestionToAccept = setupClosing(messageId);
RestAction<Message> retrievalAction = Mockito.mock(RestAction.class); RestAction<Message> retrievalAction = Mockito.mock(RestAction.class);
when(textChannel.retrieveMessageById(messageId)).thenReturn(retrievalAction); when(textChannel.retrieveMessageById(messageId)).thenReturn(retrievalAction);
Message suggestionMessage = Mockito.mock(Message.class); Message suggestionMessage = Mockito.mock(Message.class);
when(retrievalAction.submit()).thenReturn(CompletableFuture.completedFuture(suggestionMessage)); when(retrievalAction.submit()).thenReturn(CompletableFuture.completedFuture(suggestionMessage));
testUnit.rejectSuggestion(suggestionId, CLOSING_TEXT, logParameter); when(botService.getMemberInServerAsync(SERVER_ID, SUGGESTER_ID)).thenReturn(CompletableFuture.completedFuture(actualMember));
testUnit.rejectSuggestion(SUGGESTION_ID, CLOSING_TEXT, logParameter);
verify(suggestionManagementService, times(1)).setSuggestionState(suggestionToAccept, SuggestionState.REJECTED); verify(suggestionManagementService, times(1)).setSuggestionState(suggestionToAccept, SuggestionState.REJECTED);
} }
private Suggestion setupClosing(Member suggesterMember, Long suggestionId, Long channelId, Long messageId) { private Suggestion setupClosing(Long messageId) {
AServer server = MockUtils.getServer();
AChannel channel = MockUtils.getTextChannel(server, channelId);
AUserInAServer suggester = MockUtils.getUserObject(4L, server);
Suggestion suggestionToAccept = Suggestion Suggestion suggestionToAccept = Suggestion
.builder() .builder()
.channel(channel) .channel(channel)
.server(server) .server(server)
.id(suggestionId) .id(SUGGESTION_ID)
.suggester(suggester) .suggester(suggester)
.messageId(messageId) .messageId(messageId)
.build(); .build();
when(suggestionManagementService.getSuggestion(suggestionId)).thenReturn(Optional.of(suggestionToAccept)); when(server.getId()).thenReturn(SERVER_ID);
when(botService.getGuildById(server.getId())).thenReturn(Optional.of(guild)); when(channel.getId()).thenReturn(CHANNEL_ID);
when(guild.getTextChannelById(channelId)).thenReturn(textChannel); when(suggester.getUserReference()).thenReturn(suggesterUser);
when(guild.getMemberById(suggester.getUserReference().getId())).thenReturn(suggesterMember); when(suggesterUser.getId()).thenReturn(SUGGESTER_ID);
when(suggestionManagementService.getSuggestion(SUGGESTION_ID)).thenReturn(Optional.of(suggestionToAccept));
when(botService.getTextChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(textChannel);
return suggestionToAccept; return suggestionToAccept;
} }
} }

View File

@@ -116,7 +116,7 @@ public class ReminderManagementServiceBeanTest {
Long reminderId = 5L; Long reminderId = 5L;
Reminder reminderToLoad = Reminder.builder().build(); Reminder reminderToLoad = Reminder.builder().build();
when(reminderRepository.findById(reminderId)).thenReturn(Optional.of(reminderToLoad)); when(reminderRepository.findById(reminderId)).thenReturn(Optional.of(reminderToLoad));
Optional<Reminder> returned = testUnit.loadReminder(reminderId); Optional<Reminder> returned = testUnit.loadReminderOptional(reminderId);
Assert.assertTrue(returned.isPresent()); Assert.assertTrue(returned.isPresent());
returned.ifPresent(returnedReminder -> Assert.assertEquals(reminderToLoad, returnedReminder)); returned.ifPresent(returnedReminder -> Assert.assertEquals(reminderToLoad, returnedReminder));
} }
@@ -125,7 +125,7 @@ public class ReminderManagementServiceBeanTest {
public void testLoadingReminderNotFound() { public void testLoadingReminderNotFound() {
Long reminderId = 5L; Long reminderId = 5L;
when(reminderRepository.findById(reminderId)).thenReturn(Optional.empty()); when(reminderRepository.findById(reminderId)).thenReturn(Optional.empty());
Optional<Reminder> returned = testUnit.loadReminder(reminderId); Optional<Reminder> returned = testUnit.loadReminderOptional(reminderId);
Assert.assertFalse(returned.isPresent()); Assert.assertFalse(returned.isPresent());
} }

View File

@@ -18,6 +18,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@@ -108,11 +109,11 @@ public class StarboardPostReactorManagementServiceBeanTest {
StarStatsUser user1 = Mockito.mock(StarStatsUser.class); StarStatsUser user1 = Mockito.mock(StarStatsUser.class);
StarStatsUser user2 = Mockito.mock(StarStatsUser.class); StarStatsUser user2 = Mockito.mock(StarStatsUser.class);
setupStarStatsReceiverResult(amountToRetrieve, serverId, user1, user2); setupStarStatsReceiverResult(amountToRetrieve, serverId, user1, user2);
List<StarStatsUser> starStatsUsers = testUnit.retrieveTopStarReceiver(serverId, amountToRetrieve); List<CompletableFuture<StarStatsUser>> starStatsUsers = testUnit.retrieveTopStarReceiver(serverId, amountToRetrieve);
Assert.assertEquals(expectedAmount, starStatsUsers.size()); Assert.assertEquals(expectedAmount, starStatsUsers.size());
Assert.assertEquals(user1, starStatsUsers.get(0)); Assert.assertEquals(user1, starStatsUsers.get(0).join());
if(amountToRetrieve > 1) { if(amountToRetrieve > 1) {
Assert.assertEquals(user2, starStatsUsers.get(1)); Assert.assertEquals(user2, starStatsUsers.get(1).join());
} }
} }
@@ -121,11 +122,11 @@ public class StarboardPostReactorManagementServiceBeanTest {
StarStatsUser user1 = Mockito.mock(StarStatsUser.class); StarStatsUser user1 = Mockito.mock(StarStatsUser.class);
StarStatsUser user2 = Mockito.mock(StarStatsUser.class); StarStatsUser user2 = Mockito.mock(StarStatsUser.class);
setupStarStatsGiverResult(amountToRetrieve, serverId, user1, user2); setupStarStatsGiverResult(amountToRetrieve, serverId, user1, user2);
List<StarStatsUser> starStatsUsers = testUnit.retrieveTopStarGiver(serverId, amountToRetrieve); List<CompletableFuture<StarStatsUser>> starStatsUsers = testUnit.retrieveTopStarGiver(serverId, amountToRetrieve);
Assert.assertEquals(expectedAmount, starStatsUsers.size()); Assert.assertEquals(expectedAmount, starStatsUsers.size());
Assert.assertEquals(user1, starStatsUsers.get(0)); Assert.assertEquals(user1, starStatsUsers.get(0).join());
if(amountToRetrieve > 1) { if(amountToRetrieve > 1) {
Assert.assertEquals(user2, starStatsUsers.get(1)); Assert.assertEquals(user2, starStatsUsers.get(1).join());
} }
} }
@@ -134,10 +135,10 @@ public class StarboardPostReactorManagementServiceBeanTest {
StarStatsUserResult result2 = Mockito.mock(StarStatsUserResult.class); StarStatsUserResult result2 = Mockito.mock(StarStatsUserResult.class);
List<StarStatsUserResult> results = Arrays.asList(result1, result2); List<StarStatsUserResult> results = Arrays.asList(result1, result2);
when(repository.findTopStarGiverInServer(serverId, amountToRetrieve)).thenReturn(results); when(repository.findTopStarGiverInServer(serverId, amountToRetrieve)).thenReturn(results);
List<StarStatsUser> statsUser = new ArrayList<>(); List<CompletableFuture<StarStatsUser>> statsUser = new ArrayList<>();
statsUser.add(user1); statsUser.add(CompletableFuture.completedFuture(user1));
if (amountToRetrieve > 1) { if (amountToRetrieve > 1) {
statsUser.add(user2); statsUser.add(CompletableFuture.completedFuture(user2));
} }
when(converter.convertToStarStatsUser(results, serverId)).thenReturn(statsUser); when(converter.convertToStarStatsUser(results, serverId)).thenReturn(statsUser);
} }
@@ -147,10 +148,10 @@ public class StarboardPostReactorManagementServiceBeanTest {
StarStatsUserResult result2 = Mockito.mock(StarStatsUserResult.class); StarStatsUserResult result2 = Mockito.mock(StarStatsUserResult.class);
List<StarStatsUserResult> results = Arrays.asList(result1, result2); List<StarStatsUserResult> results = Arrays.asList(result1, result2);
when(repository.retrieveTopStarReceiverInServer(serverId, amountToRetrieve)).thenReturn(results); when(repository.retrieveTopStarReceiverInServer(serverId, amountToRetrieve)).thenReturn(results);
List<StarStatsUser> statsUser = new ArrayList<>(); List<CompletableFuture<StarStatsUser>> statsUser = new ArrayList<>();
statsUser.add(user1); statsUser.add(CompletableFuture.completedFuture(user1));
if (amountToRetrieve > 1) { if (amountToRetrieve > 1) {
statsUser.add(user2); statsUser.add(CompletableFuture.completedFuture(user2));
} }
when(converter.convertToStarStatsUser(results, serverId)).thenReturn(statsUser); when(converter.convertToStarStatsUser(results, serverId)).thenReturn(statsUser);
} }

View File

@@ -6,9 +6,10 @@ import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.TextChannel;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
public interface MessageEmbedService { public interface MessageEmbedService {
List<MessageEmbedLink> getLinksInMessage(String message); List<MessageEmbedLink> getLinksInMessage(String message);
void embedLinks(List<MessageEmbedLink> linksToEmbed, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage); void embedLinks(List<MessageEmbedLink> linksToEmbed, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage);
void embedLink(CachedMessage cachedMessage, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage); CompletableFuture<Void> embedLink(CachedMessage cachedMessage, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage);
} }

View File

@@ -7,11 +7,12 @@ import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStat
import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsPost; import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsPost;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
public interface StarboardService { public interface StarboardService {
void createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser); CompletableFuture<Void> createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser);
void updateStarboardPost(StarboardPost post, CachedMessage message, List<AUserInAServer> userExceptAuthor); CompletableFuture<Void> updateStarboardPost(StarboardPost post, CachedMessage message, List<AUserInAServer> userExceptAuthor);
void deleteStarboardMessagePost(StarboardPost message); void deleteStarboardMessagePost(StarboardPost message);
StarStatsModel retrieveStarStats(Long serverId); CompletableFuture<StarStatsModel> retrieveStarStats(Long serverId);
StarStatsPost fromStarboardPost(StarboardPost starboardPost); StarStatsPost fromStarboardPost(StarboardPost starboardPost);
} }

View File

@@ -10,7 +10,8 @@ import java.util.Optional;
public interface ReminderManagementService { public interface ReminderManagementService {
Reminder createReminder(AServerAChannelAUser userToBeReminded, String text, Instant timeToBeRemindedAt, Long messageId); Reminder createReminder(AServerAChannelAUser userToBeReminded, String text, Instant timeToBeRemindedAt, Long messageId);
Optional<Reminder> loadReminder(Long reminderId); Optional<Reminder> loadReminderOptional(Long reminderId);
Reminder loadReminder(Long reminderId);
void setReminded(Reminder reminder); void setReminded(Reminder reminder);
Reminder saveReminder(Reminder reminder); Reminder saveReminder(Reminder reminder);
List<Reminder> getActiveRemindersForUser(AUserInAServer aUserInAServer); List<Reminder> getActiveRemindersForUser(AUserInAServer aUserInAServer);

View File

@@ -5,12 +5,13 @@ import dev.sheldan.abstracto.utility.models.database.StarboardPost;
import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsUser; import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsUser;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
public interface StarboardPostReactorManagementService { public interface StarboardPostReactorManagementService {
void addReactor(StarboardPost post, AUserInAServer user); void addReactor(StarboardPost post, AUserInAServer user);
void removeReactor(StarboardPost post, AUserInAServer user); void removeReactor(StarboardPost post, AUserInAServer user);
void removeReactors(StarboardPost post); void removeReactors(StarboardPost post);
Integer getStarCount(Long serverId); Integer getStarCount(Long serverId);
List<StarStatsUser> retrieveTopStarGiver(Long serverId, Integer count); List<CompletableFuture<StarStatsUser>> retrieveTopStarGiver(Long serverId, Integer count);
List<StarStatsUser> retrieveTopStarReceiver(Long serverId, Integer count); List<CompletableFuture<StarStatsUser>> retrieveTopStarReceiver(Long serverId, Integer count);
} }

View File

@@ -19,6 +19,7 @@ import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.RoleService; import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.service.management.*; import dev.sheldan.abstracto.core.service.management.*;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext; import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
@@ -30,6 +31,7 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static java.util.Comparator.comparing; import static java.util.Comparator.comparing;
@@ -106,27 +108,24 @@ public class CommandReceivedHandler extends ListenerAdapter {
tryToExecuteFoundCommand(event, commandContextBuilder, foundCommand, unParsedParameter); tryToExecuteFoundCommand(event, commandContextBuilder, foundCommand, unParsedParameter);
} catch (Exception e) { } catch (Exception e) {
log.error("Exception when executing command.", e); reportException(commandContextBuilder, null, e, "Exception when executing command.");
CommandResult commandResult = CommandResult.fromError(e.getMessage(), e);
CommandContext commandContext = commandContextBuilder.build();
self.executePostCommandListener(null, commandContext, commandResult);
} }
} }
private void tryToExecuteFoundCommand(@Nonnull MessageReceivedEvent event, CommandContext.CommandContextBuilder commandContextBuilder, Command foundCommand, UnParsedCommandParameter unParsedParameter) { private void tryToExecuteFoundCommand(@Nonnull MessageReceivedEvent event, CommandContext.CommandContextBuilder commandContextBuilder, Command foundCommand, UnParsedCommandParameter unParsedParameter) {
try { CompletableFuture<Parameters> parsingFuture = getParsedParameters(unParsedParameter, foundCommand, event.getMessage());
Parameters parsedParameters = getParsedParameters(unParsedParameter, foundCommand, event.getMessage()); parsingFuture.thenAccept(parsedParameters -> {
validateCommandParameters(parsedParameters, foundCommand); validateCommandParameters(parsedParameters, foundCommand);
CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build(); CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build();
ConditionResult conditionResult = commandService.isCommandExecutable(foundCommand, commandContext); ConditionResult conditionResult = commandService.isCommandExecutable(foundCommand, commandContext);
CommandResult commandResult = null; CommandResult commandResult = null;
if(conditionResult.isResult()) { if(conditionResult.isResult()) {
if(foundCommand.getConfiguration().isAsync()) { if(foundCommand.getConfiguration().isAsync()) {
log.info("Executing async command {} for server {} in channel {} based on message {} by user {}.", log.info("Executing async command {} for server {} in channel {} based on message {} by user {}.",
foundCommand.getConfiguration().getName(), commandContext.getGuild().getId(), commandContext.getChannel().getId(), commandContext.getMessage().getId(), commandContext.getAuthor().getId()); foundCommand.getConfiguration().getName(), commandContext.getGuild().getId(), commandContext.getChannel().getId(), commandContext.getMessage().getId(), commandContext.getAuthor().getId());
foundCommand.executeAsync(commandContext).thenAccept(result -> foundCommand.executeAsync(commandContext).thenAccept(result ->
executePostCommandListener(foundCommand, commandContext, result) executePostCommandListener(foundCommand, commandContext, result)
).exceptionally(throwable -> { ).exceptionally(throwable -> {
log.error("Asynchronous command {} failed.", foundCommand.getConfiguration().getName(), throwable); log.error("Asynchronous command {} failed.", foundCommand.getConfiguration().getName(), throwable);
UserInitiatedServerContext rebuildUserContext = buildTemplateParameter(event); UserInitiatedServerContext rebuildUserContext = buildTemplateParameter(event);
@@ -155,12 +154,21 @@ public class CommandReceivedHandler extends ListenerAdapter {
if(commandResult != null) { if(commandResult != null) {
self.executePostCommandListener(foundCommand, commandContext, commandResult); self.executePostCommandListener(foundCommand, commandContext, commandResult);
} }
} catch (Exception e) { }).exceptionally(throwable -> {
log.error("Exception when executing command.", e); reportException(commandContextBuilder, foundCommand, throwable, "Exception when executing command.");
CommandResult commandResult = CommandResult.fromError(e.getMessage(), e); return null;
CommandContext commandContext = commandContextBuilder.build(); });
self.executePostCommandListener(foundCommand, commandContext, commandResult); parsingFuture.exceptionally(throwable -> {
} reportException(commandContextBuilder, foundCommand, throwable, "Exception when parsing command.");
return null;
});
}
private void reportException(CommandContext.CommandContextBuilder commandContextBuilder, Command foundCommand, Throwable throwable, String s) {
log.error(s, throwable);
CommandResult commandResult = CommandResult.fromError(throwable.getMessage(), throwable);
CommandContext commandContext = commandContextBuilder.build();
self.executePostCommandListener(foundCommand, commandContext, commandResult);
} }
private void validateCommandParameters(Parameters parameters, Command foundCommand) { private void validateCommandParameters(Parameters parameters, Command foundCommand) {
@@ -210,10 +218,10 @@ public class CommandReceivedHandler extends ListenerAdapter {
.build(); .build();
} }
public Parameters getParsedParameters(UnParsedCommandParameter unParsedCommandParameter, Command command, Message message){ public CompletableFuture<Parameters> getParsedParameters(UnParsedCommandParameter unParsedCommandParameter, Command command, Message message){
List<Object> parsedParameters = new ArrayList<>(); List<Object> parsedParameters = new ArrayList<>();
if(command.getConfiguration().getParameters() == null || command.getConfiguration().getParameters().isEmpty()) { if(command.getConfiguration().getParameters() == null || command.getConfiguration().getParameters().isEmpty()) {
return Parameters.builder().parameters(parsedParameters).build(); return CompletableFuture.completedFuture(Parameters.builder().parameters(parsedParameters).build());
} }
log.trace("Parsing parameters for command {} based on message {}.", command.getConfiguration().getName(), message.getId()); log.trace("Parsing parameters for command {} based on message {}.", command.getConfiguration().getName(), message.getId());
Iterator<TextChannel> channelIterator = message.getMentionedChannels().iterator(); Iterator<TextChannel> channelIterator = message.getMentionedChannels().iterator();
@@ -224,6 +232,7 @@ public class CommandReceivedHandler extends ListenerAdapter {
CommandParameterIterators iterators = new CommandParameterIterators(channelIterator, emoteIterator, memberIterator, roleIterator); CommandParameterIterators iterators = new CommandParameterIterators(channelIterator, emoteIterator, memberIterator, roleIterator);
boolean reminderActive = false; boolean reminderActive = false;
List<CommandParameterHandler> orderedHandlers = parameterHandlers.stream().sorted(comparing(CommandParameterHandler::getPriority)).collect(Collectors.toList()); List<CommandParameterHandler> orderedHandlers = parameterHandlers.stream().sorted(comparing(CommandParameterHandler::getPriority)).collect(Collectors.toList());
List<CompletableFuture> futures = new ArrayList<>();
for (int i = 0; i < unParsedCommandParameter.getParameters().size(); i++) { for (int i = 0; i < unParsedCommandParameter.getParameters().size(); i++) {
if(i < command.getConfiguration().getParameters().size() && !param.isRemainder()) { if(i < command.getConfiguration().getParameters().size() && !param.isRemainder()) {
param = command.getConfiguration().getParameters().get(i); param = command.getConfiguration().getParameters().get(i);
@@ -236,7 +245,13 @@ public class CommandReceivedHandler extends ListenerAdapter {
try { try {
if(handler.handles(param.getType())) { if(handler.handles(param.getType())) {
handlerMatched = true; handlerMatched = true;
parsedParameters.add(handler.handle(value, iterators, param.getType(), message)); if(handler.async()) {
CompletableFuture future = handler.handleAsync(value, iterators, param.getType(), message);
futures.add(future);
parsedParameters.add(future);
} else {
parsedParameters.add(handler.handle(value, iterators, param.getType(), message));
}
break; break;
} }
} catch (Exception e) { } catch (Exception e) {
@@ -256,9 +271,22 @@ public class CommandReceivedHandler extends ListenerAdapter {
} }
} }
} }
} }
return Parameters.builder().parameters(parsedParameters).build(); if(!futures.isEmpty()) {
return FutureUtils.toSingleFuture(futures).thenApply(aVoid -> {
List<Object> usableParameters = parsedParameters.stream().map(o -> {
if(o instanceof CompletableFuture) {
return ((CompletableFuture) o).join();
} else {
return o;
}
}).collect(Collectors.toList());
return Parameters.builder().parameters(usableParameters).build();
});
} else {
Parameters parameters = Parameters.builder().parameters(parsedParameters).build();
return CompletableFuture.completedFuture(parameters);
}
} }
} }

View File

@@ -5,6 +5,7 @@ import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@Component @Component
@@ -15,14 +16,19 @@ public class MemberParameterHandler implements CommandParameterHandler {
} }
@Override @Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) { public boolean async() {
return true;
}
@Override
public CompletableFuture<Object> handleAsync(String input, CommandParameterIterators iterators, Class clazz, Message context) {
Matcher matcher = Message.MentionType.USER.getPattern().matcher(input); Matcher matcher = Message.MentionType.USER.getPattern().matcher(input);
if(matcher.matches()) { if(matcher.matches()) {
return iterators.getMemberIterator().next(); return CompletableFuture.completedFuture(iterators.getMemberIterator().next());
} else { } else {
// TODO add handling for names // TODO add handling for names
long emoteId = Long.parseLong(input); long userId = Long.parseLong(input);
return context.getGuild().getMemberById(emoteId); return context.getGuild().retrieveMemberById(userId).submit().thenApply(member -> member);
} }
} }

View File

@@ -76,24 +76,29 @@ public class MessageDeletedListenerBean extends ListenerAdapter {
.channel(channelManagementService.loadChannel(cachedMessage.getChannelId())) .channel(channelManagementService.loadChannel(cachedMessage.getChannelId()))
.aUserInAServer(userInServerManagementService.loadUser(cachedMessage.getServerId(), cachedMessage.getAuthorId())) .aUserInAServer(userInServerManagementService.loadUser(cachedMessage.getServerId(), cachedMessage.getAuthorId()))
.build(); .build();
botService.getMemberInServerAsync(cachedMessage.getServerId(), cachedMessage.getAuthorId()).thenAccept(member -> {
GuildChannelMember authorMember = GuildChannelMember GuildChannelMember authorMember = GuildChannelMember
.builder() .builder()
.guild(botService.getGuildByIdNullable(cachedMessage.getServerId())) .guild(botService.getGuildById(cachedMessage.getServerId()))
.textChannel(botService.getTextChannelFromServerOptional(cachedMessage.getServerId(), cachedMessage.getChannelId()).orElseThrow(() -> new ChannelNotFoundException(cachedMessage.getChannelId()))) .textChannel(botService.getTextChannelFromServerOptional(cachedMessage.getServerId(), cachedMessage.getChannelId()).orElseThrow(() -> new ChannelNotFoundException(cachedMessage.getChannelId())))
.member(botService.getMemberInServer(cachedMessage.getServerId(), cachedMessage.getAuthorId())) .member(botService.getMemberInServer(cachedMessage.getServerId(), cachedMessage.getAuthorId()))
.build(); .build();
listener.forEach(messageDeletedListener -> { listener.forEach(messageDeletedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageDeletedListener.getFeature()); FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageDeletedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, cachedMessage.getServerId())) { if(!featureFlagService.isFeatureEnabled(feature, cachedMessage.getServerId())) {
return; return;
} }
try { try {
self.executeIndividualMessageDeletedListener(cachedMessage, authorUser, authorMember, messageDeletedListener); self.executeIndividualMessageDeletedListener(cachedMessage, authorUser, authorMember, messageDeletedListener);
} catch (AbstractoRunTimeException e) { } catch (AbstractoRunTimeException e) {
log.error("Listener {} failed with exception:", messageDeletedListener.getClass().getName(), e); log.error("Listener {} failed with exception:", messageDeletedListener.getClass().getName(), e);
} }
});
}).exceptionally(throwable -> {
log.error("Message deleted listener failed.", throwable);
return null;
}); });
} }
@Transactional(propagation = Propagation.REQUIRES_NEW) @Transactional(propagation = Propagation.REQUIRES_NEW)

View File

@@ -50,9 +50,7 @@ public class BotServiceBean implements BotService {
@Override @Override
public GuildChannelMember getServerChannelUser(Long serverId, Long channelId, Long userId) { public GuildChannelMember getServerChannelUser(Long serverId, Long channelId, Long userId) {
log.trace("Trying to retrieve member {}, channel {} in server {} from cache.", userId, channelId, serverId); log.trace("Trying to retrieve member {}, channel {} in server {} from cache.", userId, channelId, serverId);
Optional<Guild> guildOptional = getGuildById(serverId); Guild guild = getGuildById(serverId);
if(guildOptional.isPresent()) {
Guild guild = guildOptional.get();
Optional<TextChannel> textChannelOptional = this.getTextChannelFromServerOptional(guild, channelId); Optional<TextChannel> textChannelOptional = this.getTextChannelFromServerOptional(guild, channelId);
if(textChannelOptional.isPresent()) { if(textChannelOptional.isPresent()) {
TextChannel textChannel = textChannelOptional.get(); TextChannel textChannel = textChannelOptional.get();
@@ -61,10 +59,18 @@ public class BotServiceBean implements BotService {
} else { } else {
throw new ChannelNotFoundException(channelId); throw new ChannelNotFoundException(channelId);
} }
} }
else {
throw new GuildNotFoundException(serverId); @Override
} public CompletableFuture<GuildChannelMember> getServerChannelUserAsync(Long serverId, Long channelId, Long userId) {
log.trace("Trying to retrieve member {}, channel {} in server {} async.", userId, channelId, serverId);
CompletableFuture<Member> memberFuture = getMemberInServerAsync(serverId, userId);
Guild guild = getGuildById(serverId);
TextChannel textChannel = this.getTextChannelFromServer(guild, channelId);
return memberFuture.thenApply(member ->
GuildChannelMember.builder().guild(guild).textChannel(textChannel).member(member).build()
);
} }
@Override @Override
@@ -78,6 +84,17 @@ public class BotServiceBean implements BotService {
} }
} }
@Override
public CompletableFuture<Member> getMemberInServerAsync(Long serverId, Long memberId) {
log.trace("Retrieving member {} in server {} from cache.", memberId, serverId);
Guild guildById = instance.getGuildById(serverId);
if(guildById != null) {
return guildById.retrieveMemberById(memberId).submit();
} else {
throw new GuildNotFoundException(serverId);
}
}
@Override @Override
public boolean isUserInGuild(AUserInAServer aUserInAServer) { public boolean isUserInGuild(AUserInAServer aUserInAServer) {
Guild guildById = instance.getGuildById(aUserInAServer.getServerReference().getId()); Guild guildById = instance.getGuildById(aUserInAServer.getServerReference().getId());
@@ -98,6 +115,11 @@ public class BotServiceBean implements BotService {
return getMemberInServer(aUserInAServer.getServerReference().getId(), aUserInAServer.getUserReference().getId()); return getMemberInServer(aUserInAServer.getServerReference().getId(), aUserInAServer.getUserReference().getId());
} }
@Override
public CompletableFuture<Member> getMemberInServerAsync(AUserInAServer aUserInAServer) {
return getMemberInServerAsync(aUserInAServer.getServerReference().getId(), aUserInAServer.getUserReference().getId());
}
@Override @Override
public Member getMemberInServer(AServer server, AUser member) { public Member getMemberInServer(AServer server, AUser member) {
return getMemberInServer(server.getId(), member.getId()); return getMemberInServer(server.getId(), member.getId());
@@ -139,7 +161,7 @@ public class BotServiceBean implements BotService {
if(Boolean.FALSE.equals(emote.getCustom())) { if(Boolean.FALSE.equals(emote.getCustom())) {
return Optional.empty(); return Optional.empty();
} }
Optional<Guild> guildById = getGuildById(serverId); Optional<Guild> guildById = getGuildByIdOptional(serverId);
if(guildById.isPresent()) { if(guildById.isPresent()) {
Guild guild = guildById.get(); Guild guild = guildById.get();
Emote emoteById = guild.getEmoteById(emote.getEmoteId()); Emote emoteById = guild.getEmoteById(emote.getEmoteId());
@@ -168,7 +190,7 @@ public class BotServiceBean implements BotService {
@Override @Override
public Optional<TextChannel> getTextChannelFromServerOptional(Long serverId, Long textChannelId) { public Optional<TextChannel> getTextChannelFromServerOptional(Long serverId, Long textChannelId) {
Optional<Guild> guildOptional = getGuildById(serverId); Optional<Guild> guildOptional = getGuildByIdOptional(serverId);
if(guildOptional.isPresent()) { if(guildOptional.isPresent()) {
Guild guild = guildOptional.get(); Guild guild = guildOptional.get();
return Optional.ofNullable(guild.getTextChannelById(textChannelId)); return Optional.ofNullable(guild.getTextChannelById(textChannelId));
@@ -182,22 +204,22 @@ public class BotServiceBean implements BotService {
} }
@Override @Override
public Optional<Guild> getGuildById(Long serverId) { public Optional<Guild> getGuildByIdOptional(Long serverId) {
return Optional.ofNullable(instance.getGuildById(serverId)); return Optional.ofNullable(instance.getGuildById(serverId));
} }
@Override @Override
public Guild getGuildByIdNullable(Long serverId) { public Guild getGuildById(Long serverId) {
return instance.getGuildById(serverId); Guild guildById = instance.getGuildById(serverId);
if(guildById == null) {
throw new GuildNotFoundException(serverId);
}
return guildById;
} }
@Override @Override
public Member getBotInGuild(AServer server) { public Member getBotInGuild(AServer server) {
Optional<Guild> guildOptional = getGuildById(server.getId()); Guild guild = getGuildById(server.getId());
if(guildOptional.isPresent()) { return guild.getMemberById(instance.getSelfUser().getId());
Guild guild = guildOptional.get();
return guild.getMemberById(instance.getSelfUser().getId());
}
return null;
} }
} }

View File

@@ -252,7 +252,7 @@ public class ChannelServiceBean implements ChannelService {
@Override @Override
public CompletableFuture<TextChannel> createTextChannel(String name, AServer server, Long categoryId) { public CompletableFuture<TextChannel> createTextChannel(String name, AServer server, Long categoryId) {
Optional<Guild> guildById = botService.getGuildById(server.getId()); Optional<Guild> guildById = botService.getGuildByIdOptional(server.getId());
if(guildById.isPresent()) { if(guildById.isPresent()) {
Guild guild = guildById.get(); Guild guild = guildById.get();
Category categoryById = guild.getCategoryById(categoryId); Category categoryById = guild.getCategoryById(categoryId);

View File

@@ -76,7 +76,7 @@ public class MessageCacheBean implements MessageCache {
@Override @Override
public CompletableFuture<CachedMessage> loadMessage(Long guildId, Long textChannelId, Long messageId) { public CompletableFuture<CachedMessage> loadMessage(Long guildId, Long textChannelId, Long messageId) {
CompletableFuture<CachedMessage> future = new CompletableFuture<>(); CompletableFuture<CachedMessage> future = new CompletableFuture<>();
Optional<Guild> guildOptional = botService.getGuildById(guildId); Optional<Guild> guildOptional = botService.getGuildByIdOptional(guildId);
if(guildOptional.isPresent()) { if(guildOptional.isPresent()) {
Optional<TextChannel> textChannelByIdOptional = botService.getTextChannelFromServerOptional(guildOptional.get(), textChannelId); Optional<TextChannel> textChannelByIdOptional = botService.getTextChannelFromServerOptional(guildOptional.get(), textChannelId);
if(textChannelByIdOptional.isPresent()) { if(textChannelByIdOptional.isPresent()) {

View File

@@ -13,6 +13,7 @@ import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -47,7 +48,7 @@ public class MessageServiceBean implements MessageService {
@Override @Override
public CompletableFuture<Void> addReactionToMessageWithFuture(String emoteKey, Long serverId, Message message) { public CompletableFuture<Void> addReactionToMessageWithFuture(String emoteKey, Long serverId, Message message) {
Guild guild = botService.getGuildByIdNullable(serverId); Guild guild = botService.getGuildById(serverId);
return addReactionToMessageWithFuture(emoteKey, guild, message); return addReactionToMessageWithFuture(emoteKey, guild, message);
} }
@@ -93,7 +94,7 @@ public class MessageServiceBean implements MessageService {
} }
@Override @Override
public CompletableFuture<Void> removeReactionFromMessageWithFuture(AEmote emote, Long serverId, Message message) { public CompletableFuture<Void> removeReactionFromMessageWithFuture(AEmote emote, Message message) {
if(Boolean.TRUE.equals(emote.getCustom())) { if(Boolean.TRUE.equals(emote.getCustom())) {
Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId()); Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId());
if(emoteById == null) { if(emoteById == null) {
@@ -123,13 +124,13 @@ public class MessageServiceBean implements MessageService {
} }
@Override @Override
public CompletableFuture<Void> removeReactionFromMessageWithFuture(Integer emoteId, Long serverId, Message message) { public CompletableFuture<Void> removeReactionFromMessageWithFuture(Integer emoteId, Message message) {
AEmote emote = emoteManagementService.loadEmote(emoteId); AEmote emote = emoteManagementService.loadEmote(emoteId);
return removeReactionFromMessageWithFuture(emote, serverId, message); return removeReactionFromMessageWithFuture(emote, message);
} }
@Override @Override
public CompletableFuture<Void> clearReactionFromMessageWithFuture(Integer emoteId, Long serverId, Message message) { public CompletableFuture<Void> clearReactionFromMessageWithFuture(Integer emoteId, Message message) {
AEmote emote = emoteManagementService.loadEmote(emoteId); AEmote emote = emoteManagementService.loadEmote(emoteId);
return clearReactionFromMessageWithFuture(emote, message); return clearReactionFromMessageWithFuture(emote, message);
} }
@@ -139,14 +140,22 @@ public class MessageServiceBean implements MessageService {
TextChannel channel = botService.getTextChannelFromServer(serverId, channelId); TextChannel channel = botService.getTextChannelFromServer(serverId, channelId);
Integer emoteId = emote.getId(); Integer emoteId = emote.getId();
return channel.retrieveMessageById(messageId).submit() return channel.retrieveMessageById(messageId).submit()
.thenCompose(message1 -> removeReactionFromMessageWithFuture(emoteId, serverId, message1)); .thenCompose(message -> self.removeReactionFromMessageWithFuture(emoteId, message));
} }
@Override @Override
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Long userId) { public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Long userId) {
Guild guild = botService.getGuildByIdNullable(serverId); Guild guild = botService.getGuildById(serverId);
Member memberById = guild.getMemberById(userId); Integer emoteId = emote.getId();
return removeReactionOfUserFromMessageWithFuture(emote, serverId, channelId, messageId, memberById); TextChannel textChannel = botService.getTextChannelFromServer(serverId, channelId);
CompletableFuture<Member> memberFuture = guild.retrieveMemberById(userId).submit();
CompletableFuture<Message> messageFuture = textChannel.retrieveMessageById(messageId).submit();
return CompletableFuture.allOf(memberFuture, messageFuture).thenCompose(aVoid ->
memberFuture.thenCompose(member ->
self.removeReactionOfUserFromMessageWithFuture(emoteId, messageFuture.join(), memberFuture.join())
)
);
} }
@Override @Override
@@ -154,7 +163,7 @@ public class MessageServiceBean implements MessageService {
TextChannel channel = botService.getTextChannelFromServer(serverId, channelId); TextChannel channel = botService.getTextChannelFromServer(serverId, channelId);
Integer emoteId = emote.getId(); Integer emoteId = emote.getId();
return channel.retrieveMessageById(messageId).submit() return channel.retrieveMessageById(messageId).submit()
.thenCompose(message1 -> removeReactionOfUserFromMessageWithFuture(emoteId, message1, member)); .thenCompose(message -> self.removeReactionOfUserFromMessageWithFuture(emoteId, message, member));
} }
@Override @Override
@@ -173,6 +182,7 @@ public class MessageServiceBean implements MessageService {
} }
@Override @Override
@Transactional
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Member member) { public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Member member) {
AEmote emote = emoteManagementService.loadEmote(emoteId); AEmote emote = emoteManagementService.loadEmote(emoteId);
return removeReactionOfUserFromMessageWithFuture(emote, message, member); return removeReactionOfUserFromMessageWithFuture(emote, message, member);
@@ -180,15 +190,17 @@ public class MessageServiceBean implements MessageService {
@Override @Override
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Message message, Long userId) { public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Message message, Long userId) {
Member memberById = message.getGuild().getMemberById(userId); Integer emoteId = emote.getId();
return removeReactionOfUserFromMessageWithFuture(emote, message, memberById); return message.getGuild().retrieveMemberById(userId).submit().thenCompose(member ->
self.removeReactionOfUserFromMessageWithFuture(emoteId, message, member)
);
} }
@Override @Override
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Long userId) { public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Long userId) {
Member memberById = message.getGuild().getMemberById(userId); return message.getGuild().retrieveMemberById(userId).submit().thenCompose(member ->
AEmote emote = emoteManagementService.loadEmote(emoteId); self.removeReactionOfUserFromMessageWithFuture(emoteId, message, member)
return removeReactionOfUserFromMessageWithFuture(emote, message, memberById); );
} }
@Override @Override
@@ -196,7 +208,7 @@ public class MessageServiceBean implements MessageService {
TextChannel channel = botService.getTextChannelFromServer(serverId, channelId); TextChannel channel = botService.getTextChannelFromServer(serverId, channelId);
Integer emoteId = emote.getId(); Integer emoteId = emote.getId();
return channel.retrieveMessageById(messageId).submit() return channel.retrieveMessageById(messageId).submit()
.thenCompose(message1 -> clearReactionFromMessageWithFuture(emoteId, serverId, message1)); .thenCompose(message1 -> clearReactionFromMessageWithFuture(emoteId, message1));
} }
@Override @Override
@@ -239,8 +251,9 @@ public class MessageServiceBean implements MessageService {
@Override @Override
public CompletableFuture<Message> sendMessageToUser(AUserInAServer userInAServer, String text) { public CompletableFuture<Message> sendMessageToUser(AUserInAServer userInAServer, String text) {
Member memberInServer = botService.getMemberInServer(userInAServer); return botService.getMemberInServerAsync(userInAServer).thenCompose(member ->
return sendMessageToUser(memberInServer.getUser(), text); sendMessageToUser(member.getUser(), text)
);
} }
@Override @Override

View File

@@ -32,7 +32,7 @@ public class RoleServiceBean implements RoleService {
@Override @Override
public void addRoleToUser(AUserInAServer aUserInAServer, ARole role) { public void addRoleToUser(AUserInAServer aUserInAServer, ARole role) {
Optional<Guild> guildById = botService.getGuildById(aUserInAServer.getServerReference().getId()); Optional<Guild> guildById = botService.getGuildByIdOptional(aUserInAServer.getServerReference().getId());
if(guildById.isPresent()) { if(guildById.isPresent()) {
addRoleToUser(guildById.get(), role, aUserInAServer.getUserReference().getId()); addRoleToUser(guildById.get(), role, aUserInAServer.getUserReference().getId());
} else { } else {
@@ -42,7 +42,7 @@ public class RoleServiceBean implements RoleService {
@Override @Override
public CompletableFuture<Void> addRoleToUserFuture(AUserInAServer aUserInAServer, ARole role) { public CompletableFuture<Void> addRoleToUserFuture(AUserInAServer aUserInAServer, ARole role) {
Optional<Guild> guildById = botService.getGuildById(aUserInAServer.getServerReference().getId()); Optional<Guild> guildById = botService.getGuildByIdOptional(aUserInAServer.getServerReference().getId());
if(guildById.isPresent()) { if(guildById.isPresent()) {
return addRoleToUserFuture(guildById.get(), role, aUserInAServer.getUserReference().getId()); return addRoleToUserFuture(guildById.get(), role, aUserInAServer.getUserReference().getId());
} else { } else {
@@ -50,30 +50,46 @@ public class RoleServiceBean implements RoleService {
} }
} }
@Override
public CompletableFuture<Void> addRoleToMemberFuture(Member member, Long roleId) {
Role role = member.getGuild().getRoleById(roleId);
if(role == null) {
throw new RoleNotFoundInGuildException(roleId, member.getGuild().getIdLong());
}
return member.getGuild().addRoleToMember(member, role).submit();
}
@Override @Override
public void addRoleToMember(Member member, ARole role) { public void addRoleToMember(Member member, ARole role) {
Guild guild = member.getGuild(); addRoleToMemberFuture(member, role.getId());
addRoleToUser(guild, role, member.getIdLong());
} }
@Override @Override
public CompletableFuture<Void> addRoleToMemberFuture(Member member, ARole role) { public CompletableFuture<Void> addRoleToMemberFuture(Member member, ARole role) {
Guild guild = member.getGuild(); return addRoleToMemberFuture(member, role.getId());
return addRoleToUserFuture(guild, role, member.getIdLong());
} }
@Override @Override
public void removeRoleFromMember(Member member, ARole role) { public void removeRoleFromMember(Member member, ARole role) {
Guild guild = member.getGuild(); removeRoleFromMemberAsync(member, role.getId());
removeRoleFromUser(guild, role, member.getIdLong());
} }
@Override @Override
public CompletableFuture<Void> removeRoleFromMemberFuture(Member member, ARole role) { public CompletableFuture<Void> removeRoleFromMemberAsync(Member member, ARole role) {
Guild guild = member.getGuild(); Guild guild = member.getGuild();
return removeRoleFromUserFuture(guild, role, member.getIdLong()); return removeRoleFromUserFuture(guild, role, member.getIdLong());
} }
@Override
public CompletableFuture<Void> removeRoleFromMemberAsync(Member member, Long roleId) {
Role role = member.getGuild().getRoleById(roleId);
if(role == null) {
throw new RoleNotFoundInGuildException(roleId, member.getGuild().getIdLong());
}
return member.getGuild().removeRoleFromMember(member, role).submit();
}
private CompletableFuture<Void> addRoleToUserFuture(Guild guild, ARole role, Long userId) { private CompletableFuture<Void> addRoleToUserFuture(Guild guild, ARole role, Long userId) {
if(role.getDeleted()) { if(role.getDeleted()) {
log.warn("Not possible to add role to user. Role {} was marked as deleted.", role.getId()); log.warn("Not possible to add role to user. Role {} was marked as deleted.", role.getId());
@@ -114,7 +130,7 @@ public class RoleServiceBean implements RoleService {
@Override @Override
public void removeRoleFromUser(AUserInAServer aUserInAServer, ARole role) { public void removeRoleFromUser(AUserInAServer aUserInAServer, ARole role) {
Optional<Guild> guildById = botService.getGuildById(aUserInAServer.getServerReference().getId()); Optional<Guild> guildById = botService.getGuildByIdOptional(aUserInAServer.getServerReference().getId());
if(guildById.isPresent()) { if(guildById.isPresent()) {
removeRoleFromUser(guildById.get(), role, aUserInAServer.getUserReference().getId()); removeRoleFromUser(guildById.get(), role, aUserInAServer.getUserReference().getId());
} else { } else {
@@ -124,7 +140,7 @@ public class RoleServiceBean implements RoleService {
@Override @Override
public CompletableFuture<Void> removeRoleFromUserFuture(AUserInAServer aUserInAServer, ARole role) { public CompletableFuture<Void> removeRoleFromUserFuture(AUserInAServer aUserInAServer, ARole role) {
Optional<Guild> guildById = botService.getGuildById(aUserInAServer.getServerReference().getId()); Optional<Guild> guildById = botService.getGuildByIdOptional(aUserInAServer.getServerReference().getId());
if(guildById.isPresent()) { if(guildById.isPresent()) {
return removeRoleFromUserFuture(guildById.get(), role, aUserInAServer.getUserReference().getId()); return removeRoleFromUserFuture(guildById.get(), role, aUserInAServer.getUserReference().getId());
} else { } else {
@@ -150,7 +166,7 @@ public class RoleServiceBean implements RoleService {
log.warn("Trying to load role {} which is marked as deleted.", role.getId()); log.warn("Trying to load role {} which is marked as deleted.", role.getId());
throw new RoleDeletedException(role); throw new RoleDeletedException(role);
} }
Optional<Guild> guildById = botService.getGuildById(role.getServer().getId()); Optional<Guild> guildById = botService.getGuildByIdOptional(role.getServer().getId());
if(guildById.isPresent()) { if(guildById.isPresent()) {
log.trace("Loading role {} from server {}.", role.getId(), role.getServer().getId()); log.trace("Loading role {} from server {}.", role.getId(), role.getServer().getId());
return guildById.get().getRoleById(role.getId()); return guildById.get().getRoleById(role.getId());
@@ -171,12 +187,17 @@ public class RoleServiceBean implements RoleService {
@Override @Override
public boolean memberHasRole(Member member, Role role) { public boolean memberHasRole(Member member, Role role) {
return member.getRoles().stream().anyMatch(role1 -> role1.getIdLong() == role.getIdLong()); return memberHasRole(member, role.getIdLong());
} }
@Override @Override
public boolean memberHasRole(Member member, ARole role) { public boolean memberHasRole(Member member, ARole role) {
return member.getRoles().stream().anyMatch(role1 -> role1.getIdLong() == role.getId()); return memberHasRole(member, role.getId());
}
@Override
public boolean memberHasRole(Member member, Long roleId) {
return member.getRoles().stream().anyMatch(role1 -> role1.getIdLong() == roleId);
} }
@Override @Override

View File

@@ -10,7 +10,6 @@ import dev.sheldan.abstracto.core.models.template.commands.SetupCompletedNotific
import dev.sheldan.abstracto.core.models.template.commands.SetupInitialMessageModel; import dev.sheldan.abstracto.core.models.template.commands.SetupInitialMessageModel;
import dev.sheldan.abstracto.templating.service.TemplateService; import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -141,8 +140,12 @@ public class SetupServiceBean implements SetupService {
@Transactional @Transactional
public void showExceptionMessage(Throwable throwable, AServerChannelUserId aServerChannelUserId) { public void showExceptionMessage(Throwable throwable, AServerChannelUserId aServerChannelUserId) {
Optional<TextChannel> channelOptional = botService.getTextChannelFromServerOptional(aServerChannelUserId.getGuildId(), aServerChannelUserId.getChannelId()); Optional<TextChannel> channelOptional = botService.getTextChannelFromServerOptional(aServerChannelUserId.getGuildId(), aServerChannelUserId.getChannelId());
Member member = botService.getMemberInServer(aServerChannelUserId.getGuildId(), aServerChannelUserId.getUserId()); botService.getMemberInServerAsync(aServerChannelUserId.getGuildId(), aServerChannelUserId.getUserId()).thenAccept(member ->
channelOptional.ifPresent(textChannel -> exceptionService.reportExceptionToChannel(throwable, textChannel, member)); channelOptional.ifPresent(textChannel -> exceptionService.reportExceptionToChannel(throwable, textChannel, member))
).exceptionally(innserThrowable -> {
log.error("Failed to report exception message for exception {} for user {} in channel {} in server {}.", throwable, aServerChannelUserId.getUserId(), aServerChannelUserId.getChannelId(), aServerChannelUserId.getGuildId(), innserThrowable);
return null;
});
} }
@Transactional @Transactional

View File

@@ -1,23 +0,0 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserInServerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class UserInServerServiceBean implements UserInServerService {
@Autowired
private BotService botService;
@Override
public FullUserInServer getFullUser(AUserInAServer aUserInAServer) {
return FullUserInServer
.builder()
.member(botService.getMemberInServer(aUserInAServer))
.aUserInAServer(aUserInAServer)
.build();
}
}

View File

@@ -1,15 +1,18 @@
package dev.sheldan.abstracto.core.command.handler; package dev.sheldan.abstracto.core.command.handler;
import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.requests.RestAction;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -44,25 +47,27 @@ public class MemberParameterHandlerTest {
} }
@Test @Test
@SuppressWarnings("unchecked")
public void testProperMemberMention() { public void testProperMemberMention() {
oneMemberInIterator(); oneMemberInIterator();
String input = getUserMention(); String input = getUserMention();
Member parsed = (Member) testUnit.handle(input, iterators, Member.class, null); CompletableFuture<Member> parsed = (CompletableFuture) testUnit.handleAsync(input, iterators, Member.class, null);
Assert.assertEquals(parsed, member); Assert.assertEquals(parsed.join(), member);
} }
@Test @Test
@SuppressWarnings("unchecked")
public void testMemberById() { public void testMemberById() {
setupMessage(); setupMessage();
String input = USER_ID.toString(); String input = USER_ID.toString();
Member parsed = (Member) testUnit.handle(input, null, Member.class, message); CompletableFuture<Member> parsed = (CompletableFuture) testUnit.handleAsync(input, null, Member.class, message);
Assert.assertEquals(parsed, member); Assert.assertEquals(parsed.join(), member);
} }
@Test(expected = NumberFormatException.class) @Test(expected = NumberFormatException.class)
public void testInvalidMemberMention() { public void testInvalidMemberMention() {
String input = "test"; String input = "test";
testUnit.handle(input, null, Member.class, null); testUnit.handleAsync(input, null, Member.class, null);
} }
private String getUserMention() { private String getUserMention() {
@@ -76,7 +81,9 @@ public class MemberParameterHandlerTest {
private void setupMessage() { private void setupMessage() {
when(message.getGuild()).thenReturn(guild); when(message.getGuild()).thenReturn(guild);
when(guild.getMemberById(USER_ID)).thenReturn(member); RestAction<Member> restAction = Mockito.mock(RestAction.class);
when(restAction.submit()).thenReturn(CompletableFuture.completedFuture(member));
when(guild.retrieveMemberById(USER_ID)).thenReturn(restAction);
} }
} }

View File

@@ -21,12 +21,19 @@ public class MinIntegerValueValidator implements ParameterValidator {
if(value == null) { if(value == null) {
throw new IllegalArgumentException("Object to validate must not be null"); throw new IllegalArgumentException("Object to validate must not be null");
} }
if(!(value instanceof Long)) { boolean isLong = value instanceof Long;
boolean isInteger = value instanceof Integer;
if(!isLong && !isInteger) {
throw new ValidatorConfigException("Incorrect value passed to max value validator."); throw new ValidatorConfigException("Incorrect value passed to max value validator.");
} }
SingleNumberValidatorParam param = (SingleNumberValidatorParam) getParameters().get(0); SingleNumberValidatorParam param = (SingleNumberValidatorParam) getParameters().get(0);
Long longValue = (Long) value; if(isLong) {
return longValue >= param.getNumber(); Long longValue = (Long) value;
return longValue >= param.getNumber();
} else {
Integer integerValue = (Integer) value;
return integerValue >= param.getNumber();
}
} }
@Override @Override

View File

@@ -3,8 +3,12 @@ package dev.sheldan.abstracto.core.command.handler;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import java.util.concurrent.CompletableFuture;
public interface CommandParameterHandler { public interface CommandParameterHandler {
boolean handles(Class clazz); boolean handles(Class clazz);
Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context); default boolean async() { return false; }
default Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) { return new Object();}
default CompletableFuture handleAsync(String input, CommandParameterIterators iterators, Class clazz, Message context) { return CompletableFuture.completedFuture(null); }
Integer getPriority(); Integer getPriority();
} }

View File

@@ -0,0 +1,17 @@
package dev.sheldan.abstracto.core.models;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
import java.util.concurrent.CompletableFuture;
@Getter
@Setter
@Builder
public class FutureMemberPair {
private CompletableFuture<Member> firstMember;
private CompletableFuture<Member> secondMember;
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.core.models;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
@Getter
@Setter
@Builder
public class MemberPair {
private Member firstMember;
private Member secondMember;
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.core.models;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class ServerChannelMessageUser {
private Long serverId;
private Long channelId;
private Long messageId;
private Long userId;
}

Some files were not shown because too many files have changed in this diff Show More