[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) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
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());
List<AssignableRole> assignableRoles = place.getAssignableRoles().stream().sorted(Comparator.comparingInt(AssignableRole::getPosition)).collect(Collectors.toList());
for (AssignableRole role : assignableRoles) {
@@ -609,17 +609,16 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
@Override
public CompletableFuture<Void> removeExistingReactionsAndRoles(AssignableRolePlace place, AssignedRoleUser user) {
Member memberInServer = botService.getMemberInServer(user.getUser());
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());
user.getRoles().forEach(assignableRole -> {
futures.add(roleService.removeAssignableRoleFromUser(assignableRole, memberInServer));
log.trace("Removing role {} from user {} in server {} because of assignable role clearing.", assignableRole.getRole().getId(), memberInServer.getId(), place.getServer().getId());
futures.add(roleService.removeAssignableRoleFromUser(assignableRole, user.getUser()));
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());
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());
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]));
}

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.AssignedRoleUserManagementServiceBean;
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.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
@@ -39,6 +40,9 @@ public class AssignableRoleServiceBean implements AssignableRoleService {
@Autowired
private AssignableRoleServiceBean self;
@Autowired
private BotService botService;
@Override
public CompletableFuture<Void> assignAssignableRoleToUser(Long assignableRoleId, Member toAdd) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
@@ -69,14 +73,29 @@ public class AssignableRoleServiceBean implements AssignableRoleService {
@Override
public CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, Member member) {
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
public CompletableFuture<Void> fullyRemoveAssignableRoleFromUser(AssignableRole assignableRole, Member member) {
Long assignableRoleId = assignableRole.getId();
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
public void removeRoleFromUser(Long assignableRoleId, Member member) {
public void persistRoleRemovalFromUser(Long assignableRoleId, Member member) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(member);
removeRoleFromUser(role, aUserInAServer);

View File

@@ -12,6 +12,8 @@ public interface AssignableRoleService {
void clearAllRolesOfUserInPlace(AssignableRolePlace place, AUserInAServer user);
CompletableFuture<Void> fullyAssignAssignableRoleToUser(Long assignableRoleId, Member toAdd);
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);
void addRoleToUser(Long assignableRoleId, 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.models.LeaderBoard;
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.service.AUserExperienceService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
@@ -28,6 +29,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
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.
@@ -57,15 +59,22 @@ public class LeaderBoardCommand extends AbstractConditionableCommand {
// parameter is optional, in case its not present, we default to the 0th page
Integer page = !parameters.isEmpty() ? (Integer) parameters.get(0) : 1;
LeaderBoard leaderBoard = userExperienceService.findLeaderBoardData(commandContext.getUserInitiatedContext().getServer(), page);
LeaderBoardModel leaderBoardModel = (LeaderBoardModel) ContextConverter.fromCommandContext(commandContext, LeaderBoardModel.class);
leaderBoardModel.setUserExperiences(converter.fromLeaderBoard(leaderBoard));
LeaderBoardModel leaderBoardModel = (LeaderBoardModel) ContextConverter.slimFromCommandContext(commandContext, LeaderBoardModel.class);
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());
LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(commandContext.getUserInitiatedContext().getAUserInAServer());
leaderBoardModel.setUserExecuting(converter.fromLeaderBoardEntry(userRank));
MessageToSend messageToSend = templateService.renderEmbedTemplate(LEADER_BOARD_POST_EMBED_TEMPLATE, leaderBoardModel);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess());
CompletableFuture<LeaderBoardEntryModel> userRankFuture = converter.fromLeaderBoardEntry(userRank);
futures.add(userRankFuture);
return FutureUtils.toSingleFutureGeneric(futures).thenCompose(aVoid -> {
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.ContextConverter;
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.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.experience.config.features.ExperienceFeature;
import dev.sheldan.abstracto.experience.converter.LeaderBoardModelConverter;
import dev.sheldan.abstracto.experience.models.LeaderBoardEntry;
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.service.ExperienceLevelService;
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.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@@ -50,19 +55,35 @@ public class Rank extends AbstractConditionableCommand {
@Autowired
private ChannelService channelService;
@Autowired
private Rank self;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private UserExperienceManagementService userExperienceManagementService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext 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());
rankModel.setRankUser(converter.fromLeaderBoardEntry(userRank));
AUserExperience experienceObj = userRank.getExperience();
CompletableFuture<LeaderBoardEntryModel> future = converter.fromLeaderBoardEntry(userRank);
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());
rankModel.setExperienceToNextLevel(experienceLevelService.calculateExperienceToNextLevel(experienceObj.getCurrentLevel().getLevel(), experienceObj.getExperience()));
MessageToSend messageToSend = templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()));
}
@Override

View File

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

View File

@@ -186,7 +186,7 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
AUserInAServer userInAServer = userInServerManagementService.loadUser(userInAServerId);
gainedExperience = (int) Math.floor(gainedExperience * multiplier);
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);
Optional<AUserExperience> aUserExperienceOptional = userExperienceManagementService.findByUserInServerIdOptional(userInAServerId);
if(aUserExperienceOptional.isPresent()) {
@@ -230,7 +230,7 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
futures.add(resultFuture);
}
} 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())
.experienceRoleId(null)
.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();
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);
Member member = botService.getMemberInServer(user.getServerReference(), user.getUserReference());
boolean currentlyHasNoExperienceRole = userExperience.getCurrentExperienceRole() == null;
// if calculation results in no role, do not add a role
if(role == null) {
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){
return roleService.removeRoleFromUserFuture(user, userExperience.getCurrentExperienceRole().getRole())
.thenApply(returnNullRole);
}
return CompletableFuture.completedFuture(returnNullRole.apply(null));
}
boolean userHasRoleAlready = roleService.memberHasRole(member, role.getRole());
Long experienceRoleId = role.getId();
Function<Void, RoleCalculationResult> fullResult = aVoid -> RoleCalculationResult
.builder()
.experienceRoleId(experienceRoleId)
.userInServerId(userInServerId)
.build();
if(!userHasRoleAlready && (currentlyHasNoExperienceRole || !role.getRole().getId().equals(userExperience.getCurrentExperienceRole().getRole().getId()))) {
log.info("User {} in server {} gets a new role {} because of experience.", user.getUserReference().getId(), user.getServerReference().getId(), role.getRole().getId());
CompletableFuture<Void> removalFuture;
if(!currentlyHasNoExperienceRole && botService.isUserInGuild(userExperience.getUser())) {
removalFuture = roleService.removeRoleFromUserFuture(user, userExperience.getCurrentExperienceRole().getRole());
} else {
removalFuture = CompletableFuture.completedFuture(null);
Long roleId = role.getRole().getId();
// if the new role is already the one configured in the database
Long userId = user.getUserReference().getId();
Long oldUserExperienceRoleId = currentlyHasNoExperienceRole ? 0L : userExperience.getCurrentExperienceRole().getRole().getId();
return botService.getMemberInServerAsync(user).thenCompose(member -> {
boolean userHasRoleAlready = roleService.memberHasRole(member, roleId);
boolean userHasOldRole = false;
boolean rolesChanged = true;
if(!currentlyHasNoExperienceRole) {
userHasOldRole = roleService.memberHasRole(member, oldUserExperienceRoleId);
rolesChanged = !roleId.equals(oldUserExperienceRoleId);
}
CompletableFuture<Void> addRoleFuture = roleService.addRoleToUserFuture(user, role.getRole());
return CompletableFuture.allOf(removalFuture, addRoleFuture).thenApply(fullResult);
}
return CompletableFuture.completedFuture(fullResult.apply(null));
Function<Void, RoleCalculationResult> fullResult = aVoid -> RoleCalculationResult
.builder()
.experienceRoleId(experienceRoleId)
.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) {
AUserInAServer user = userInServerManagementService.loadUser(result.getUserInServerId());
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) {
log.trace("User experience {} gets new experience role with id {}.", userExperience.getId(), 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) {
Optional<AExperienceRole> byRoleServerAndRoleOptional = getRoleInServerOptional(role);
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()) {
log.trace("Role already existed. Updating.");
experienceRole = byRoleServerAndRoleOptional.get();

View File

@@ -66,7 +66,7 @@ public class LeaderBoardCommandTest {
LeaderBoardEntry executingUserRank = LeaderBoardEntry.builder().build();
when(userExperienceService.getRankOfUserInServer(context.getUserInitiatedContext().getAUserInAServer())).thenReturn(executingUserRank);
LeaderBoardEntryModel leaderBoardEntryModel = LeaderBoardEntryModel.builder().build();
when(converter.fromLeaderBoardEntry(executingUserRank)).thenReturn(leaderBoardEntryModel);
when(converter.fromLeaderBoardEntry(executingUserRank)).thenReturn(CompletableFuture.completedFuture(leaderBoardEntryModel));
MessageToSend messageToSend = MessageToSend.builder().build();
when(templateService.renderEmbedTemplate(eq(LeaderBoardCommand.LEADER_BOARD_POST_EMBED_TEMPLATE), any(LeaderBoardModel.class))).thenReturn(messageToSend);
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.CommandResult;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
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.models.LeaderBoardEntry;
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.service.AUserExperienceService;
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.service.TemplateService;
import dev.sheldan.abstracto.test.command.CommandConfigValidator;
@@ -22,8 +25,10 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.experience.commands.Rank.RANK_POST_EMBED_TEMPLATE;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
@@ -47,23 +52,49 @@ public class RankTest {
@Mock
private ChannelService channelService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private Rank self;
@Mock
private AUserInAServer aUserInAServer;
@Mock
private UserExperienceManagementService userExperienceManagementService;
@Test
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;
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();
AExperienceLevel currentLevel = AExperienceLevel.builder().level(currentLevelValue).build();
AUserExperience aUserExperience = AUserExperience.builder().experience(currentExperience).currentLevel(currentLevel).build();
LeaderBoardEntry leaderBoardEntry = LeaderBoardEntry.builder().experience(aUserExperience).build();
when(userExperienceService.getRankOfUserInServer(context.getUserInitiatedContext().getAUserInAServer())).thenReturn(leaderBoardEntry);
LeaderBoardEntryModel leaderBoardEntryModel = LeaderBoardEntryModel.builder().build();
when(converter.fromLeaderBoardEntry(leaderBoardEntry)).thenReturn(leaderBoardEntryModel);
RankModel rankModel = Mockito.mock(RankModel.class);
LeaderBoardEntryModel leaderBoardEntryModel = Mockito.mock(LeaderBoardEntryModel.class);
when(userInServerManagementService.loadUser(context.getAuthor())).thenReturn(aUserInAServer);
when(userExperienceManagementService.findUserInServer(aUserInAServer)).thenReturn(aUserExperience);
when(experienceLevelService.calculateExperienceToNextLevel(currentLevelValue, currentExperience)).thenReturn(140L);
MessageToSend messageToSend = MessageToSend.builder().build();
when(templateService.renderEmbedTemplate(eq(Rank.RANK_POST_EMBED_TEMPLATE), any(RankModel.class))).thenReturn(messageToSend);
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
verify(channelService, Mockito.times(1)).sendMessageToSendToChannel(messageToSend, context.getChannel());
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
when(templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel)).thenReturn(messageToSend);
when(channelService.sendMessageToSendToChannel(messageToSend, context.getChannel())).thenReturn(Arrays.asList(CompletableFuture.completedFuture(null)));
testUnit.renderAndSendRank(context, rankModel, leaderBoardEntryModel).join();
}
@Test

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
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.Setter;
import lombok.experimental.SuperBuilder;
@@ -15,7 +15,7 @@ import java.util.List;
@Getter
@Setter
@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.
*/

View File

@@ -1,6 +1,6 @@
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.Setter;
import lombok.experimental.SuperBuilder;
@@ -13,7 +13,7 @@ import lombok.experimental.SuperBuilder;
@Getter
@Setter
@SuperBuilder
public class RankModel extends UserInitiatedServerContext {
public class RankModel extends SlimUserInitiatedServerContext {
/**
* The {@link LeaderBoardEntryModel} containing the experience information about the user executing the rank
* command.

View File

@@ -31,7 +31,7 @@ public class DeleteWarning extends AbstractConditionableCommand {
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
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));
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.models.database.UserNote;
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.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.Member;
@@ -66,9 +67,12 @@ public class UserNotes extends AbstractConditionableCommand {
} else {
userNotes = userNoteManagementService.loadNotesForServer(commandContext.getUserInitiatedContext().getServer());
}
model.setUserNotes(userNotesConverter.fromNotes(userNotes));
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(USER_NOTES_RESPONSE_TEMPLATE, model, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess());
CompletableFuture<List<NoteEntryModel>> listCompletableFuture = userNotesConverter.fromNotes(userNotes);
return listCompletableFuture.thenCompose(noteEntryModels -> {
model.setUserNotes(noteEntryModels);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(USER_NOTES_RESPONSE_TEMPLATE, model, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess());
});
}
@Override

View File

@@ -22,9 +22,11 @@ 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.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class Warnings extends AbstractConditionableCommand {
@@ -45,8 +47,11 @@ public class Warnings extends AbstractConditionableCommand {
@Autowired
private EventWaiter eventWaiter;
@Autowired
private Warnings self;
@Override
public CommandResult execute(CommandContext commandContext) {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext);
List<Warning> warnsToDisplay;
if(!commandContext.getParameters().getParameters().isEmpty()) {
@@ -55,14 +60,21 @@ public class Warnings extends AbstractConditionableCommand {
} else {
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);
Paginator paginator = paginatorService.createPaginatorFromTemplate(WARNINGS_RESPONSE_TEMPLATE, model, eventWaiter);
paginator.display(commandContext.getChannel());
return CommandResult.fromSuccess();
}
@Override
@@ -74,6 +86,7 @@ public class Warnings extends AbstractConditionableCommand {
.name("warnings")
.module(ModerationModule.MODERATION)
.templated(true)
.async(true)
.causesReaction(true)
.supportsEmbedException(true)
.parameters(parameters)

View File

@@ -1,15 +1,23 @@
package dev.sheldan.abstracto.moderation.converter;
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.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.models.database.UserNote;
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.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class UserNotesConverter {
@@ -19,18 +27,45 @@ public class UserNotesConverter {
@Autowired
private BotService botService;
public List<NoteEntryModel> fromNotes(List<UserNote> userNotes){
List<NoteEntryModel> entryModels = new ArrayList<>();
@Autowired
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 -> {
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
.builder()
.member(botService.getMemberInServer(userNote.getUser()))
.aUserInAServer(userNote.getUser())
.member(member)
.aUserInAServer(note.getUser())
.build();
NoteEntryModel entryModel = NoteEntryModel
.builder()
.note(note)
.fullUser(fullUser)
.note(userNote)
.build();
entryModels.add(entryModel);
});

View File

@@ -1,14 +1,24 @@
package dev.sheldan.abstracto.moderation.converter;
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.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.models.database.Warning;
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.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class WarnEntryConverter {
@@ -16,25 +26,56 @@ public class WarnEntryConverter {
@Autowired
private BotService botService;
public List<WarnEntry> fromWarnings(List<Warning> warnings) {
List<WarnEntry> entries = new ArrayList<>();
@Autowired
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 -> {
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
.builder()
.member(botService.getMemberInServer(warning.getWarnedUser()))
.aUserInAServer(warning.getWarnedUser())
.member(warnedMember)
.aUserInAServer(userInServerManagementService.loadUser(warnedMember))
.build();
Member warningMember = memberPair.getFirstMember().join();
FullUserInServer warningUser = FullUserInServer
.builder()
.member(botService.getMemberInServer(warning.getWarningUser()))
.aUserInAServer(warning.getWarningUser())
.member(warningMember)
.aUserInAServer(userInServerManagementService.loadUser(warningMember))
.build();
WarnEntry entry = WarnEntry
.builder()
.warnedUser(warnedUser)
.warningUser(warningUser)
.warning(warning)
.warning(warnManagementService.findById(warning.getId(), warning.getServerId()))
.build();
entries.add(entry);
});

View File

@@ -20,8 +20,11 @@ public interface UserNoteRepository extends JpaRepository<UserNote, Long> {
List<UserNote> findByUser_ServerReference(AServer server);
@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);
@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) {
Optional<Guild> guildByIdOptional = botService.getGuildById(guildId);
Optional<Guild> guildByIdOptional = botService.getGuildByIdOptional(guildId);
if(guildByIdOptional.isPresent()) {
return banUser(guildByIdOptional.get(), userId, reason);
} else {

View File

@@ -39,6 +39,7 @@ import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -110,22 +111,6 @@ public class MuteServiceBean implements MuteService {
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
public CompletableFuture<Void> muteUserInServer(FullUserInServer userBeingMuted, FullUserInServer userMuting, String reason, Instant unMuteDate, ServerChannelMessage message) {
AServer serverBeingMutedIn = userBeingMuted.getAUserInAServer().getServerReference();
@@ -251,7 +236,7 @@ public class MuteServiceBean implements MuteService {
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.");
MessageToSend message = templateService.renderEmbedTemplate(UN_MUTE_LOG_TEMPLATE, muteLogModel);
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());
MuteRole muteRole = muteRoleManagementService.retrieveMuteRoleForServer(mutingServer);
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;
if(botService.isUserInGuild(guild, mute.getMutedUser())) {
roleRemovalFuture = roleService.removeRoleFromUserFuture(mute.getMutedUser(), muteRole.getRole());
@@ -287,24 +272,40 @@ public class MuteServiceBean implements MuteService {
roleRemovalFuture = CompletableFuture.completedFuture(null);
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
.builder()
.mute(mute)
.mutingUser(botService.getMemberInServer(mute.getMutingUser()))
.unMutedUser(botService.getMemberInServer(mute.getMutedUser()))
.mutingUser(mutingMemberFuture.join())
.unMutedUser(mutedMemberFuture.join())
.guild(guild)
.server(mutingServer)
.build();
CompletableFuture<Void> notificationFuture = sendUnMuteLog(unMuteLog);
return CompletableFuture.allOf(roleRemovalFuture, notificationFuture).thenAccept(aVoid ->
self.endMuteInDatabase(muteId, serverId)
CompletableFuture<Void> notificationFuture = sendUnMuteLogMessage(unMuteLog);
return CompletableFuture.allOf(notificationFuture).thenAccept(aVoid ->
self.endMuteInDatabase(muteId, guild.getIdLong())
);
}
@Transactional
public void endMuteInDatabase(Long muteId, Long serverId) {
Optional<Mute> muteOptional = muteManagementService.findMute(muteId, serverId);
Optional<Mute> muteOptional = muteManagementService.findMuteOptional(muteId, serverId);
muteOptional.ifPresent(mute ->
completelyUnMuteUser(mute.getMutedUser())
);
@@ -315,7 +316,7 @@ public class MuteServiceBean implements MuteService {
@Transactional
public CompletableFuture<Void> endMute(Long muteId, Long 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()) {
return endMute(muteOptional.get());
} else {

View File

@@ -1,5 +1,7 @@
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.service.*;
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 lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -27,9 +28,12 @@ import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
@Slf4j
@Component
@@ -120,7 +124,6 @@ public class WarnServiceBean implements WarnService {
);
}
@NotNull
private List<Long> flattenWarnings(List<Warning> warningsToDecay) {
List<Long> warningIds = new ArrayList<>();
warningsToDecay.forEach(warning ->
@@ -134,7 +137,7 @@ public class WarnServiceBean implements WarnService {
Instant now = Instant.now();
log.info("Decaying {} warnings.", warningIds.size());
warningIds.forEach(warningId -> {
Optional<Warning> warningOptional = warnManagementService.findById(warningId, serverId);
Optional<Warning> warningOptional = warnManagementService.findByIdOptional(warningId, serverId);
warningOptional.ifPresent(warning ->
decayWarning(warning, now)
);
@@ -152,20 +155,47 @@ public class WarnServiceBean implements WarnService {
}
private CompletableFuture<Void> logDecayedWarnings(AServer server, List<Warning> warningsToDecay) {
log.trace("Logging decaying {} warnings in server {}.", warningsToDecay.size(), server.getId());
List<WarnDecayWarning> warnDecayWarnings = new ArrayList<>();
log.trace("Loading members decaying {} warnings in server {}.", warningsToDecay.size(), server.getId());
HashMap<ServerSpecificId, FutureMemberPair> warningMembers = new HashMap<>();
List<CompletableFuture<Member>> allFutures = new ArrayList<>();
Long serverId = server.getId();
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
.builder()
.warnedMember(botService.getMemberInServer(warning.getWarnedUser()))
.warningMember(botService.getMemberInServer(warning.getWarningUser()))
.warnedMember(pair.getFirstMember().join())
.warningMember(pair.getSecondMember().join())
.warning(warning)
.build();
warnDecayWarnings.add(warnDecayWarning);
});
WarnDecayLogModel warnDecayLogModel = WarnDecayLogModel
.builder()
.guild(botService.getGuildByIdNullable(server.getId()))
.guild(botService.getGuildById(server.getId()))
.server(server)
.warnings(warnDecayWarnings)
.build();

View File

@@ -1,5 +1,6 @@
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.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
@@ -48,10 +49,15 @@ public class MuteManagementServiceBean implements MuteManagementService {
}
@Override
public Optional<Mute> findMute(Long muteId, Long serverId) {
public Optional<Mute> findMuteOptional(Long muteId, Long 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
public Mute saveMute(Mute mute) {
muteRepository.save(mute);

View File

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

View File

@@ -1,5 +1,6 @@
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.database.AServer;
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) {
log.info("Creating warning with id {} for user {} in server {} cast by user {}.",
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()
.reason(reason)
.warnedUser(warnedAUser)
@@ -64,10 +65,15 @@ public class WarnManagementServiceBean implements WarnManagementService {
}
@Override
public Optional<Warning> findById(Long id, Long serverId) {
public Optional<Warning> findByIdOptional(Long id, Long 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
public void deleteWarning(Warning warning) {
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.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.test.command.CommandTestUtilities;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
@@ -16,6 +17,7 @@ import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
@Slf4j
public class DecayWarningsTest {
@InjectMocks

View File

@@ -39,7 +39,7 @@ public class DeleteWarningTest {
AUserInAServer warningUser = MockUtils.getUserObject(6L, server);
Warning existingWarning = Warning.builder().warnedUser(warnedUser).warningUser(warningUser).build();
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);
verify(warnManagementService, times(1)).deleteWarning(existingWarning);
CommandTestUtilities.checkSuccessfulCompletion(result);

View File

@@ -64,15 +64,16 @@ public class UserNotesTest {
when(userNoteManagementService.loadNotesForUser(userNoteUser)).thenReturn(userNotes);
NoteEntryModel firstConvertedNote = 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);
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
verify(channelService, times(1)).sendEmbedTemplateInChannel(eq(UserNotes.USER_NOTES_RESPONSE_TEMPLATE), captor.capture(), eq(parameters.getChannel()));
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++) {
NoteEntryModel usedEntry = usedModel.getUserNotes().get(i);
NoteEntryModel expectedEntry = convertedNotes.get(i);
NoteEntryModel expectedEntry = notes.get(i);
Assert.assertEquals(expectedEntry, usedEntry);
}
Assert.assertEquals(userNoteUser, usedModel.getSpecifiedUser().getAUserInAServer());
@@ -89,15 +90,16 @@ public class UserNotesTest {
when(userNoteManagementService.loadNotesForServer(parameters.getUserInitiatedContext().getServer())).thenReturn(userNotes);
NoteEntryModel firstConvertedNote = 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);
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()));
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++) {
NoteEntryModel usedEntry = usedModel.getUserNotes().get(i);
NoteEntryModel expectedEntry = convertedNotes.get(i);
NoteEntryModel expectedEntry = notes.get(i);
Assert.assertEquals(expectedEntry, usedEntry);
}
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.WarningsModel;
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.CommandTestUtilities;
import net.dv8tion.jda.api.entities.Member;
@@ -25,6 +24,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@@ -52,23 +52,37 @@ public class WarningsTest {
@Captor
private ArgumentCaptor<WarningsModel> captor;
@Mock
private Warnings self;
@Test
public void testNoParametersForWarningsCommand(){
CommandContext noParams = CommandTestUtilities.getNoParameters();
Warning firstWarning = Warning.builder().build();
WarnEntry firstModelWarning = WarnEntry.builder().build();
Warning secondWarning = Warning.builder().build();
WarnEntry secondModelWarning = WarnEntry.builder().build();
Warning firstWarning = Mockito.mock(Warning.class);
WarnEntry firstModelWarning = Mockito.mock(WarnEntry.class);
Warning secondWarning = Mockito.mock(Warning.class);
WarnEntry secondModelWarning = Mockito.mock(WarnEntry.class);
List<Warning> warningsToDisplay = Arrays.asList(firstWarning, secondWarning);
List<WarnEntry> modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning);
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);
when(paginatorService.createPaginatorFromTemplate(eq(Warnings.WARNINGS_RESPONSE_TEMPLATE), captor.capture(), eq(eventWaiter))).thenReturn(paginator);
CommandResult result = testUnit.execute(noParams);
CommandTestUtilities.checkSuccessfulCompletion(result);
List<WarnEntry> modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning);
testUnit.renderWarnings(noParams, modelWarnings);
WarningsModel warningsModel = captor.getValue();
Assert.assertEquals(warningsToDisplay.size(), warningsModel.getWarnings().size());
Assert.assertEquals(firstModelWarning, warningsModel.getWarnings().get(0));
Assert.assertEquals(secondModelWarning, warningsModel.getWarnings().get(1));
}
@@ -77,29 +91,26 @@ public class WarningsTest {
public void testExecuteWarningsForMember(){
Member member = Mockito.mock(Member.class);
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(member));
AUserInAServer warnedUser = MockUtils.getUserObject(5L, parameters.getUserInitiatedContext().getServer());
when(userInServerManagementService.loadUser(member)).thenReturn(warnedUser);
Warning firstWarning = Warning.builder().build();
WarnEntry firstModelWarning = WarnEntry.builder().build();
Warning secondWarning = Warning.builder().build();
WarnEntry secondModelWarning = WarnEntry.builder().build();
AUserInAServer warnedUser = Mockito.mock(AUserInAServer.class);
Warning firstWarning = Mockito.mock(Warning.class);
WarnEntry firstModelWarning = Mockito.mock(WarnEntry.class);
Warning secondWarning = Mockito.mock(Warning.class);
WarnEntry secondModelWarning = Mockito.mock(WarnEntry.class);
List<Warning> warningsToDisplay = Arrays.asList(firstWarning, secondWarning);
when(warnManagementService.getAllWarnsForUser(warnedUser)).thenReturn(warningsToDisplay);
List<WarnEntry> modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning);
when(warnEntryConverter.fromWarnings(warningsToDisplay)).thenReturn(modelWarnings);
Paginator paginator = Mockito.mock(Paginator.class);
when(paginatorService.createPaginatorFromTemplate(eq(Warnings.WARNINGS_RESPONSE_TEMPLATE), captor.capture(), eq(eventWaiter))).thenReturn(paginator);
CommandResult result = testUnit.execute(parameters);
CommandTestUtilities.checkSuccessfulCompletion(result);
WarningsModel warningsModel = captor.getValue();
Assert.assertEquals(warningsToDisplay.size(), warningsModel.getWarnings().size());
Assert.assertEquals(firstModelWarning, warningsModel.getWarnings().get(0));
Assert.assertEquals(secondModelWarning, warningsModel.getWarnings().get(1));
when(userInServerManagementService.loadUser(member)).thenReturn(warnedUser);
when(warnManagementService.getAllWarnsForUser(warnedUser)).thenReturn(warningsToDisplay);
when(warnEntryConverter.fromWarnings(warningsToDisplay)).thenReturn(CompletableFuture.completedFuture(modelWarnings));
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
verify(self, times(1)).renderWarnings(parameters, modelWarnings);
}
@Test(expected = IncorrectParameterTypeException.class)
public void testIncorrectParameterType() {
CommandTestUtilities.executeWrongParametersTest(testUnit);
CommandTestUtilities.executeWrongParametersTestAsync(testUnit);
}
@Test

View File

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

View File

@@ -1,11 +1,14 @@
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.service.BotService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.moderation.models.database.Warning;
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 org.junit.Assert;
import org.junit.Test;
@@ -17,11 +20,14 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
@Slf4j
public class WarnEntryConverterTest {
@InjectMocks
@@ -30,34 +36,74 @@ public class WarnEntryConverterTest {
@Mock
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
public void testWithEmptyList() {
List<WarnEntry> entryModels = testUnit.fromWarnings(Collections.emptyList());
Assert.assertEquals(0, entryModels.size());
CompletableFuture<List<WarnEntry>> entryModels = testUnit.fromWarnings(Collections.emptyList());
Assert.assertEquals(0, entryModels.join().size());
}
@Test
public void testWithSomeUserNotes() {
AServer server = MockUtils.getServer();
AUserInAServer warnedUserInAServer = MockUtils.getUserObject(4L, server);
AUserInAServer warningUserInAServer = MockUtils.getUserObject(6L, server);
AUserInAServer warnedUser = Mockito.mock(AUserInAServer.class);
AUserInAServer warningUser = Mockito.mock(AUserInAServer.class);
Member warnedMember = Mockito.mock(Member.class);
Member warningMember = Mockito.mock(Member.class);
when(botService.getMemberInServer(warnedUserInAServer)).thenReturn(warnedMember);
when(botService.getMemberInServer(warningUserInAServer)).thenReturn(warningMember);
Warning firstNote = Warning.builder().warnedUser(warnedUserInAServer).warningUser(warningUserInAServer).build();
Warning secondNote = Warning.builder().warnedUser(warnedUserInAServer).warningUser(warningUserInAServer).build();
List<WarnEntry> entryModels = testUnit.fromWarnings(Arrays.asList(firstNote, secondNote));
Assert.assertEquals(2, entryModels.size());
WarnEntry firstEntry = entryModels.get(0);
when(botService.getMemberInServerAsync(warnedUser)).thenReturn(CompletableFuture.completedFuture(warnedMember));
when(botService.getMemberInServerAsync(warningUser)).thenReturn(CompletableFuture.completedFuture(warningMember));
Warning firstNote = Warning.builder().warnId(new ServerSpecificId(3L, 4L)).warnedUser(warnedUser).warningUser(warningUser).build();
Warning secondNote = Warning.builder().warnId(new ServerSpecificId(3L, 5L)).warnedUser(warnedUser).warningUser(warningUser).build();
testUnit.fromWarnings(Arrays.asList(firstNote, secondNote));
verify(self, times(1)).loadFullWarnEntries(any());
}
@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(warnedMember, firstEntry.getWarnedUser().getMember());
Assert.assertEquals(warningUserInAServer, firstEntry.getWarningUser().getAUserInAServer());
Assert.assertEquals(warnedUserInAServer, firstEntry.getWarnedUser().getAUserInAServer());
WarnEntry secondEntry = entryModels.get(1);
Assert.assertEquals(warnedUser, firstEntry.getWarnedUser().getAUserInAServer());
Assert.assertEquals(warningUser, firstEntry.getWarningUser().getAUserInAServer());
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(warnedMember, secondEntry.getWarnedUser().getMember());
Assert.assertEquals(warningUserInAServer, secondEntry.getWarningUser().getAUserInAServer());
Assert.assertEquals(warnedUserInAServer, secondEntry.getWarnedUser().getAUserInAServer());
Assert.assertEquals(warnedUser, 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);
MessageToSend mockedMessage = Mockito.mock(MessageToSend.class);
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);
verify(mockedGuild, times(1)).ban(userId.toString(), 0, REASON);
verify(postTargetService, times(1)).sendEmbedInPostTarget(mockedMessage, ModerationPostTarget.BAN_LOG, serverId);
@@ -81,7 +81,7 @@ public class BanServiceBeanTest {
Long userId = 8L;
Long serverId = 5L;
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);
}

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.service.TemplateService;
import dev.sheldan.abstracto.test.MockUtils;
import dev.sheldan.abstracto.test.command.CommandTestUtilities;
import net.dv8tion.jda.api.entities.*;
import org.junit.Before;
import org.junit.Test;
@@ -43,6 +42,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
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.*;
@RunWith(MockitoJUnitRunner.class)
@@ -234,24 +234,6 @@ public class MuteServiceBeanTest {
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
public void testMuteMemberWithLog() {
when(userInServerManagementService.loadUser(memberBeingMuted)).thenReturn(userBeingMuted);
@@ -294,15 +276,30 @@ public class MuteServiceBeanTest {
public void testEndMute() {
setupUnMuteMocks(true);
when(mute.getMutedUser()).thenReturn(userBeingMuted);
when(mute.getMutingUser()).thenReturn(userMuting);
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(botService.getMemberInServerAsync(userBeingMuted)).thenReturn(CompletableFuture.completedFuture(memberBeingMuted));
when(botService.getMemberInServerAsync(userMuting)).thenReturn(CompletableFuture.completedFuture(memberMuting));
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)
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);
}
@@ -373,10 +370,12 @@ public class MuteServiceBeanTest {
private void executeUnMuteWithLogTest(boolean stillInGuild) {
when(mute.getMutedUser()).thenReturn(userBeingMuted);
when(mute.getMutingUser()).thenReturn(userMuting);
when(mute.getServer()).thenReturn(server);
setupUnMuteMocks(stillInGuild);
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);
}
@@ -385,10 +384,8 @@ public class MuteServiceBeanTest {
when(mute.getMuteId()).thenReturn(new ServerSpecificId(SERVER_ID, MUTE_ID));
when(muteManagementService.getAMuteOf(userBeingMuted)).thenReturn(mute);
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.getMemberInServer(userBeingMuted)).thenReturn(memberBeingMuted);
when(templateService.renderEmbedTemplate(eq(MuteServiceBean.UN_MUTE_LOG_TEMPLATE), any(UnMuteLog.class))).thenReturn(messageToSend);
}
private void verifyDirectMute() {

View File

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

View File

@@ -69,7 +69,7 @@ public class MuteManagementServiceBeanTest {
Long serverId = 7L;
Mute mute = Mute.builder().muteId(new ServerSpecificId(serverId, id)).build();
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());
foundMuteOptional.ifPresent(foundMute -> Assert.assertEquals(id, foundMute.getMuteId().getId()));
}
@@ -79,7 +79,7 @@ public class MuteManagementServiceBeanTest {
Long id = 5L;
Long serverId = 7L;
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());
}

View File

@@ -99,7 +99,7 @@ public class WarnManagementServiceBeanTest {
Long serverId = 8L;
Warning existingWarning = getWarning();
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());
warningOptional.ifPresent(foundWarning -> Assert.assertEquals(existingWarning, foundWarning));
}
@@ -109,7 +109,7 @@ public class WarnManagementServiceBeanTest {
Long warnId = 6L;
Long serverId = 8L;
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());
}

View File

@@ -1,6 +1,6 @@
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.Setter;
import lombok.experimental.SuperBuilder;
@@ -13,7 +13,7 @@ import java.util.List;
@Getter
@Setter
@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
*/

View File

@@ -12,7 +12,6 @@ import java.util.concurrent.CompletableFuture;
public interface MuteService {
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> applyMuteRole(AUserInAServer aUserInAServer);
CompletableFuture<Void> muteMemberWithLog(MuteContext context);

View File

@@ -31,7 +31,8 @@ public interface MuteManagementService {
* @param muteId The id of the mute to search for
* @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.

View File

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

View File

@@ -15,6 +15,7 @@ public interface WarnManagementService {
List<Warning> getAllWarnsForUser(AUserInAServer aUserInAServer);
List<Warning> getAllWarningsOfServer(AServer server);
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);
}

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.CommandResult;
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.config.ModMailFeatures;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
@@ -36,14 +37,19 @@ public class AnonReply extends AbstractConditionableCommand {
@Autowired
private ModMailThreadManagementService modMailThreadManagementService;
@Autowired
private BotService botService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
// text is optional, for example if only an attachment is sent
String text = parameters.size() == 1 ? (String) parameters.get(0) : "";
ModMailThread thread = modMailThreadManagementService.getByChannel(commandContext.getUserInitiatedContext().getChannel());
return modMailThreadService.relayMessageToDm(thread, text, commandContext.getMessage(), true, commandContext.getChannel(), commandContext.getUndoActions())
.thenApply(aVoid -> CommandResult.fromSuccess());
Long threadId = thread.getId();
return botService.getMemberInServerAsync(thread.getUser()).thenCompose(member ->
modMailThreadService.relayMessageToDm(threadId, text, commandContext.getMessage(), true, commandContext.getChannel(), commandContext.getUndoActions(), member)
).thenApply(aVoid -> CommandResult.fromSuccess());
}
@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.ContextConverter;
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.service.ChannelService;
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());
return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromSuccess());
} else {
FullUserInServer fullUser = FullUserInServer
.builder()
.aUserInAServer(user)
.member(targetUser)
.build();
return modMailThreadService.createModMailThreadForUser(fullUser, null, commandContext.getChannel(), false, commandContext.getUndoActions())
return modMailThreadService.createModMailThreadForUser(targetUser, null, commandContext.getChannel(), false, commandContext.getUndoActions())
.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.CommandResult;
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.config.ModMailFeatures;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
@@ -35,13 +36,18 @@ public class Reply extends AbstractConditionableCommand {
@Autowired
private ModMailThreadManagementService modMailThreadManagementService;
@Autowired
private BotService botService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String text = parameters.size() == 1 ? (String) parameters.get(0) : "";
ModMailThread thread = modMailThreadManagementService.getByChannel(commandContext.getUserInitiatedContext().getChannel());
return modMailThreadService.relayMessageToDm(thread, text, commandContext.getMessage(), false, commandContext.getChannel(), commandContext.getUndoActions())
.thenApply(aVoid -> CommandResult.fromSuccess());
Long threadId = thread.getId();
return botService.getMemberInServerAsync(thread.getUser()).thenCompose(member ->
modMailThreadService.relayMessageToDm(threadId, text, commandContext.getMessage(), false, commandContext.getChannel(), commandContext.getUndoActions(), member)
).thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override

View File

@@ -41,13 +41,19 @@ public class ModMailMessageListener implements PrivateMessageReceivedListener {
@Override
@Transactional
public void execute(Message message) {
if(message.getAuthor().isBot()) {
return;
}
AUser user = userManagementService.loadUser(message.getAuthor().getIdLong());
if(modMailThreadManagementService.hasOpenModMailThread(user)) {
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
// 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);
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 {
log.info("User {} does not have an open modmail thread. Crating prompt.", user.getId());
modMailThreadService.createModMailPrompt(user, message);

View File

@@ -1,10 +1,13 @@
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.modmail.models.database.ModMailMessage;
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 net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
@@ -24,56 +27,75 @@ public class ModMailMessageServiceBean implements ModMailMessageService {
private BotService botService;
@Override
public List<CompletableFuture<Message>> loadModMailMessages(List<ModMailMessage> modMailMessages) {
public LoadedModmailThreadMessageList loadModMailMessages(List<ModMailMessage> modMailMessages) {
if(modMailMessages.isEmpty()) {
return new ArrayList<>();
return LoadedModmailThreadMessageList.builder().build();
}
// all message must be from the same thread
ModMailThread thread = modMailMessages.get(0).getThreadReference();
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 -> {
ServerChannelMessage.ServerChannelMessageBuilder serverChannelMessageBuilder = ServerChannelMessage
ServerChannelMessageUser.ServerChannelMessageUserBuilder serverChannelMessageBuilder = ServerChannelMessageUser
.builder()
.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
.messageId(modMailMessage.getMessageId())
.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())) {
log.trace("Message {} was from DM.", modMailMessage.getMessageId());
serverChannelMessageBuilder
.channelId(modMailMessage.getThreadReference().getChannel().getId())
.serverId(modMailMessage.getThreadReference().getServer().getId());
.channelId(modMailMessage.getThreadReference().getChannel().getId());
}
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
// 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
// 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());
if(textChannelFromServer.isPresent()) {
TextChannel modMailThread = textChannelFromServer.get();
botService.getInstance().openPrivateChannelById(thread.getUser().getUserReference().getId()).queue(privateChannel -> {
Iterator<CompletableFuture<Message>> iterator = messageFutures.iterator();
Long userId = thread.getUser().getUserReference().getId();
botService.getInstance().openPrivateChannelById(userId).queue(privateChannel -> {
Iterator<LoadedModmailThreadMessage> iterator = messageFutures.iterator();
messageIds.forEach(serverChannelMessage -> {
log.trace("Loading message {}.", serverChannelMessage.getMessageId());
// TODO fix out of order promises
// depending what the source of the message is, we need to fetch the message from the correct channel
CompletableFuture<Message> messageFuture;
CompletableFuture<Member> memberFuture = botService.getMemberInServerAsync(serverChannelMessage.getServerId(), serverChannelMessage.getUserId());
if(serverChannelMessage.getChannelId() == null){
privateChannel.retrieveMessageById(serverChannelMessage.getMessageId()).queue(message -> iterator.next().complete(message), throwable -> {
log.info("Failed to load message in private channel with user {}", thread.getUser().getUserReference().getId());
iterator.next().complete(null);
});
messageFuture = privateChannel.retrieveMessageById(serverChannelMessage.getMessageId()).submit();
} else {
modMailThread.retrieveMessageById(serverChannelMessage.getMessageId()).queue(message -> iterator.next().complete(message), throwable -> {
log.info("Failed to load message {} in thread {}", serverChannelMessage.getMessageId(), modMailThread.getIdLong());
iterator.next().complete(null);
});
messageFuture = modMailThread.retrieveMessageById(serverChannelMessage.getMessageId()).submit();
}
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.service.*;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerService;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.modmail.config.*;
import dev.sheldan.abstracto.modmail.exception.ModMailCategoryIdException;
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.dto.ServerChoice;
import dev.sheldan.abstracto.modmail.models.template.*;
@@ -41,6 +42,7 @@ import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
@Transactional
public class ModMailThreadServiceBean implements ModMailThreadService {
/**
@@ -89,9 +91,6 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Autowired
private MessageService messageService;
@Autowired
private UserInServerService userInServerService;
@Autowired
private FeatureFlagService featureFlagService;
@@ -116,6 +115,9 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Autowired
private ModMailFeatureValidator modMailFeatureValidator;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ModMailThreadServiceBean self;
@@ -129,80 +131,81 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Override
public CompletableFuture<Void> createModMailThreadForUser(FullUserInServer aUserInAServer, Message initialMessage, MessageChannel feedBackChannel, boolean userInitiated, List<UndoActionInstance> undoActions) {
Long serverId = aUserInAServer.getAUserInAServer().getServerReference().getId();
public CompletableFuture<Void> createModMailThreadForUser(Member member, Message initialMessage, MessageChannel feedBackChannel, boolean userInitiated, List<UndoActionInstance> undoActions) {
Long serverId = member.getGuild().getIdLong();
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);
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 -> {
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
* 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 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 undoActions The list of actions to undo, in case an exception occurs
*/
@Transactional
public CompletableFuture<Void> performModMailThreadSetup(FullUserInServer aUserInAServer, 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);
Long userInServerId = aUserInAServer.getAUserInAServer().getUserInServerId();
CompletableFuture<Void> headerFuture = sendModMailHeader(channel, aUserInAServer);
CompletableFuture<Void> userReplyMessage;
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(), member.getId(), channel.getGuild().getId(), userInitiated);
CompletableFuture<Void> headerFuture = sendModMailHeader(channel, member);
CompletableFuture<Message> userReplyMessage;
if(initialMessage != null){
log.trace("Sending initial message {} of user {} to modmail thread {}.", initialMessage.getId(), aUserInAServer.getMember().getId(), channel.getId());
userReplyMessage = self.sendUserReply(channel, null, initialMessage, aUserInAServer.getAUserInAServer());
log.trace("Sending initial message {} of user {} to modmail thread {}.", initialMessage.getId(), member.getId(), channel.getId());
userReplyMessage = self.sendUserReply(channel, 0L, initialMessage, member, false);
} else {
log.trace("No initial message to send.");
userReplyMessage = CompletableFuture.completedFuture(null);
}
CompletableFuture notificationFuture;
if (userInitiated) {
notificationFuture = self.sendModMailNotification(aUserInAServer);
notificationFuture = self.sendModMailNotification(member);
} else {
notificationFuture = CompletableFuture.completedFuture(null);
}
return CompletableFuture.allOf(headerFuture, notificationFuture, userReplyMessage).thenAccept(aVoid -> {
undoActions.clear();
self.setupModMailThreadInDB(initialMessage, channel, userInServerId);
self.setupModMailThreadInDB(initialMessage, channel, member, userReplyMessage.join());
});
}
@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());
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(userInServerId);
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(member);
ModMailThread thread = createThreadObject(channel, aUserInAServer);
if(initialMessage != null) {
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}
* @param aUserInAServer The {@link FullUserInServer} which opened the thread
* @param member The {@link FullUserInServer} which opened the thread
*/
@Transactional
public CompletableFuture<Void> sendModMailNotification(FullUserInServer aUserInAServer) {
log.info("Sending modmail notification for new modmail thread about user {} in server {}.", aUserInAServer.getMember().getId(), aUserInAServer.getMember().getGuild().getId());
List<ModMailRole> rolesToPing = modMailRoleManagementService.getRolesForServer(aUserInAServer.getAUserInAServer().getServerReference());
log.trace("Pinging {} roles to notify about modmail thread about user {} in server {}.", rolesToPing.size(), aUserInAServer.getMember().getId(), aUserInAServer.getMember().getGuild().getId());
public CompletableFuture<Void> sendModMailNotification(Member member) {
Long serverId = member.getGuild().getIdLong();
log.info("Sending modmail notification for new modmail thread about user {} in server {}.", member.getId(), serverId);
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
.builder()
.threadUser(aUserInAServer)
.member(member)
.roles(rolesToPing)
.build();
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]));
}
@@ -237,7 +240,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
if(!knownServers.isEmpty()) {
log.info("There are {} shared servers between user and abstracto.", knownServers.size());
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++) {
AUserInAServer aUserInAServer = knownServers.get(i);
// 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();
FullGuild guild = FullGuild
.builder()
.guild(botService.getGuildByIdNullable(serverReference.getId()))
.guild(botService.getGuildById(serverReference.getId()))
.server(serverReference)
.build();
// TODO support more than this limited amount of servers
String reactionEmote = NUMBER_EMOJI.get(i);
ServerChoice serverChoice = ServerChoice.builder().guild(guild).reactionEmote(reactionEmote).build();
choices.put(reactionEmote, aUserInAServer);
choices.put(reactionEmote, aUserInAServer.getServerReference().getId());
availableGuilds.add(serverChoice);
}
}
@@ -269,28 +272,28 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.setEventWaiter(eventWaiter)
.setDescription(text)
.setAction(reactionEmote -> {
AUserInAServer chosenServer = choices.get(reactionEmote.getEmoji());
log.trace("Executing action for creationg a modmail thread in server {} for user {}.", chosenServer.getServerReference().getId(), chosenServer.getUserReference().getId());
Member memberInServer = botService.getMemberInServer(chosenServer);
FullUserInServer fullUser = FullUserInServer.builder().member(memberInServer).aUserInAServer(chosenServer).build();
self.createModMailThreadForUser(fullUser, initialMessage, initialMessage.getChannel(), true, new ArrayList<>()).exceptionally(throwable -> {
log.error("Failed to setup thread correctly", throwable);
return null;
});
Long chosenServerId = choices.get(reactionEmote.getEmoji());
log.trace("Executing action for creationg a modmail thread in server {} for user {}.", chosenServerId, initialMessage.getAuthor().getIdLong());
botService.getMemberInServerAsync(chosenServerId, initialMessage.getAuthor().getIdLong()).thenCompose(member ->
self.createModMailThreadForUser(member, initialMessage, initialMessage.getChannel(), true, new ArrayList<>()).exceptionally(throwable -> {
log.error("Failed to setup thread correctly", throwable);
return null;
})
);
})
.build();
log.trace("Displaying server choice message for user {} in channel {}.", user.getId(), initialMessage.getChannel().getId());
menu.display(initialMessage.getChannel());
} else if(availableGuilds.size() == 1) {
// if exactly one server is available, open the thread directly
AUserInAServer chosenServer = 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());
Member memberInServer = botService.getMemberInServer(chosenServer);
FullUserInServer fullUser = FullUserInServer.builder().member(memberInServer).aUserInAServer(chosenServer).build();
self.createModMailThreadForUser(fullUser, initialMessage, initialMessage.getChannel(), true, new ArrayList<>()).exceptionally(throwable -> {
log.error("Failed to setup thread correctly", throwable);
return null;
});
Long chosenServerId = choices.get(availableGuilds.get(0).getReactionEmote());
log.info("Only one server available to modmail. Directly opening modmail thread for user {} in server {}.", initialMessage.getAuthor().getId(), chosenServerId);
botService.getMemberInServerAsync(chosenServerId, initialMessage.getAuthor().getIdLong()).thenCompose(member ->
self.createModMailThreadForUser(member, initialMessage, initialMessage.getChannel(), true, new ArrayList<>()).exceptionally(throwable -> {
log.error("Failed to setup thread correctly", throwable);
return null;
})
);
} else {
log.info("No server available to open a modmail thread in.");
// 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
* the user which the thread is about
* @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());
ModMailThread latestThread = modMailThreadManagementService.getLatestModMailThread(aUserInAServer.getAUserInAServer());
List<ModMailThread> oldThreads = modMailThreadManagementService.getModMailThreadForUser(aUserInAServer.getAUserInAServer());
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(member);
ModMailThread latestThread = modMailThreadManagementService.getLatestModMailThread(aUserInAServer);
List<ModMailThread> oldThreads = modMailThreadManagementService.getModMailThreadForUser(aUserInAServer);
ModMailThreaderHeader header = ModMailThreaderHeader
.builder()
.threadUser(aUserInAServer)
.member(member)
.latestModMailThread(latestThread)
.pastModMailThreadCount((long)oldThreads.size())
.build();
@@ -323,19 +327,25 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
}
@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());
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(modMailThread.getServer().getId(), modMailThread.getChannel().getId());
if(textChannelFromServer.isPresent()) {
TextChannel textChannel = textChannelFromServer.get();
return self.sendUserReply(textChannel, modMailThread, message, modMailThread.getUser());
} else {
log.warn("Closing modmail thread {}, because it seems the channel {} in server {} got deleted.", modMailThread.getId(), modMailThread.getChannel().getId(), modMailThread.getServer().getId());
// in this case there was no text channel on the server associated with the mod mail thread
// close the existing one, so the user can start a new one
self.closeModMailThreadInDb(modMailThread.getId());
return message.getChannel().sendMessage(templateService.renderTemplate("modmail_failed_to_forward_message", new Object())).submit().thenApply(message1 -> null);
}
return botService.getMemberInServerAsync(modMailThread.getServer().getId(), message.getAuthor().getIdLong()).thenCompose(member -> {
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(serverId, channelId);
if(textChannelFromServer.isPresent()) {
TextChannel textChannel = textChannelFromServer.get();
return self.sendUserReply(textChannel, modmailThreadId, message, member, true);
} else {
log.warn("Closing modmail thread {}, because it seems the channel {} in server {} got deleted.", modmailThreadId, channelId, serverId);
// in this case there was no text channel on the server associated with the mod mail thread
// close the existing one, so the user can start a new one
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
* side.
* @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 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)
*/
public CompletableFuture<Void> sendUserReply(TextChannel textChannel, ModMailThread modMailThread, Message message, AUserInAServer userInServer) {
boolean modMailThreadExists = modMailThread != null;
FullUserInServer fullUser = FullUserInServer
.builder()
.aUserInAServer(userInServer)
.member(botService.getMemberInServer(userInServer))
.build();
List<FullUserInServer> subscribers = new ArrayList<>();
public CompletableFuture<Message> sendUserReply(TextChannel textChannel, Long modMailThreadId, Message message, Member member, boolean modMailThreadExists) {
List<CompletableFuture<Member>> subscriberMemberFutures = new ArrayList<>();
if(modMailThreadExists) {
ModMailThread modMailThread = modMailThreadManagementService.getById(modMailThreadId);
List<ModMailThreadSubscriber> subscriberList = modMailSubscriberManagementService.getSubscribersForThread(modMailThread);
subscriberList.forEach(modMailThreadSubscriber -> {
FullUserInServer subscriber = FullUserInServer
.builder()
.aUserInAServer(modMailThreadSubscriber.getSubscriber())
.member(botService.getMemberInServer(modMailThreadSubscriber.getSubscriber()))
.build();
subscribers.add(subscriber);
});
log.trace("Pinging {} subscribers for modmail thread {}.", subscriberList.size(), modMailThread.getId());
subscriberList.forEach(modMailThreadSubscriber ->
subscriberMemberFutures.add(botService.getMemberInServerAsync(modMailThreadSubscriber.getSubscriber()))
);
if(subscriberList.isEmpty()) {
subscriberMemberFutures.add(CompletableFuture.completedFuture(null));
}
log.trace("Pinging {} subscribers for modmail thread {}.", subscriberList.size(), modMailThreadId);
} else {
subscriberMemberFutures.add(CompletableFuture.completedFuture(null));
}
ModMailUserReplyModel modMailUserReplyModel = ModMailUserReplyModel
.builder()
.modMailThread(modMailThread)
.postedMessage(message)
.threadUser(fullUser)
.subscribers(subscribers)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_user_message", modMailUserReplyModel);
List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToSendToChannel(messageToSend, textChannel);
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0]))
.thenCompose(aVoid -> {
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) {
self.postProcessSendMessages(textChannel, completableFutures.get(0).join());
}
});
return FutureUtils.toSingleFutureGeneric(subscriberMemberFutures).thenCompose(firstVoid -> {
List<FullUserInServer> subscribers = new ArrayList<>();
ModMailUserReplyModel modMailUserReplyModel = ModMailUserReplyModel
.builder()
.postedMessage(message)
.member(member)
.subscribers(subscribers)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_user_message", modMailUserReplyModel);
List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToSendToChannel(messageToSend, textChannel);
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0]))
.thenCompose(aVoid -> {
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);
})
.thenApply(aVoid -> {
if(modMailThreadExists) {
self.postProcessSendMessages(textChannel, completableFutures.get(0).join());
}
return completableFutures.get(0).join();
});
});
}
@@ -397,7 +407,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
*/
@Transactional
public void postProcessSendMessages(TextChannel textChannel, Message message) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getById(textChannel.getIdLong());
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(textChannel.getIdLong());
if(modMailThreadOpt.isPresent()) {
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);
@@ -410,39 +420,35 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
}
@Override
public CompletableFuture<Void> relayMessageToDm(ModMailThread modMailThread, String text, Message message, boolean anonymous, MessageChannel feedBack, List<UndoActionInstance> undoActions) {
Long modMailThreadId = modMailThread.getId();
log.info("Relaying message {} to user {} in modmail thread {} on server {}.", message.getId(), modMailThread.getUser().getUserReference().getId(), modMailThread.getId(), modMailThread.getServer().getId());
User userById = botService.getInstance().getUserById(modMailThread.getUser().getUserReference().getId());
if(userById != null) {
AUserInAServer moderator = userInServerManagementService.loadUser(message.getMember());
Member userInGuild = botService.getMemberInServer(modMailThread.getUser());
FullUserInServer fullThreadUser = FullUserInServer
.builder()
.aUserInAServer(modMailThread.getUser())
.member(userInGuild)
.build();
ModMailModeratorReplyModel.ModMailModeratorReplyModelBuilder modMailModeratorReplyModelBuilder = ModMailModeratorReplyModel
.builder()
.text(text)
.modMailThread(modMailThread)
.postedMessage(message)
.anonymous(anonymous)
.threadUser(fullThreadUser);
if(anonymous) {
log.trace("Message is sent anonymous.");
modMailModeratorReplyModelBuilder.moderator(botService.getBotInGuild(modMailThread.getServer()));
} else {
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)
);
public CompletableFuture<Void> relayMessageToDm(Long modmailThreadId, String text, Message message, boolean anonymous, MessageChannel feedBack, List<UndoActionInstance> undoActions, Member targetMember) {
log.info("Relaying message {} to user {} in modmail thread {} on server {}.", message.getId(), targetMember.getId(), modmailThreadId, targetMember.getGuild().getId());
AUserInAServer moderator = userInServerManagementService.loadUser(message.getMember());
ModMailThread modMailThread = modMailThreadManagementService.getById(modmailThreadId);
FullUserInServer fullThreadUser = FullUserInServer
.builder()
.aUserInAServer(modMailThread.getUser())
.member(targetMember)
.build();
ModMailModeratorReplyModel.ModMailModeratorReplyModelBuilder modMailModeratorReplyModelBuilder = ModMailModeratorReplyModel
.builder()
.text(text)
.modMailThread(modMailThread)
.postedMessage(message)
.anonymous(anonymous)
.threadUser(fullThreadUser);
if(anonymous) {
log.trace("Message is sent anonymous.");
modMailModeratorReplyModelBuilder.moderator(botService.getBotInGuild(modMailThread.getServer()));
} else {
// should be loaded, because we are currently processing a command caused by the message
Member moderatorMember = botService.getMemberInServer(moderator);
modMailModeratorReplyModelBuilder.moderator(moderatorMember);
}
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
@@ -457,17 +463,21 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
Long modMailThreadId = modMailThread.getId();
log.info("Starting closing procedure for thread {}", modMailThread.getId());
List<ModMailMessage> modMailMessages = modMailThread.getMessages();
Long userId = modMailThread.getUser().getUserReference().getId();
Long serverId = modMailThread.getServer().getId();
if(logThread) {
List<CompletableFuture<Message>> messages = modMailMessageService.loadModMailMessages(modMailMessages);
CompletableFuture<Void> messagesFuture = FutureUtils.toSingleFutureGeneric(messages);
LoadedModmailThreadMessageList messages = modMailMessageService.loadModMailMessages(modMailMessages);
CompletableFuture<Void> messagesFuture = FutureUtils.toSingleFuture(messages.getAllFutures());
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);
} else {
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 undoActions The list of {@link UndoActionInstance} to execute in case of exceptions
* @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
public CompletableFuture<Void> logMessagesToModMailLog(String note, Boolean notifyUser, Long modMailThreadId, List<UndoActionInstance> undoActions, List<CompletableFuture<Message>> messages) {
log.trace("Logging {} modmail messages for modmail thread {}.", messages.size(), modMailThreadId);
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.getMessageList().size(), modMailThreadId);
try {
CompletableFutureList<Message> list = self.logModMailThread(modMailThreadId, messages, note, undoActions);
return list.getMainFuture().thenCompose(avoid -> {
@@ -490,7 +502,9 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
Message message = messageCompletableFuture.join();
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) {
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
*/
@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);
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getById(modMailThreadId);
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
if(modMailThreadOpt.isPresent()) {
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();
User user = botService.getMemberInServer(modMailThread.getUser()).getUser();
HashMap<String, String> closingMessage = new HashMap<>();
String defaultValue = templateService.renderSimpleTemplate("modmail_closing_user_message_description");
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)
);
} 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);
}
} else {
@@ -539,7 +552,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
*/
@Transactional
public CompletableFuture<Void> deleteChannelAndClose(Long modMailThreadId, List<UndoActionInstance> undoActions) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getById(modMailThreadId);
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get();
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.
*/
@Transactional
public CompletableFutureList<Message> logModMailThread(Long modMailThreadId, List<CompletableFuture<Message>> messages, String note, List<UndoActionInstance> undoActions) {
log.info("Logging mod mail thread {} with {} messages.", modMailThreadId, messages.size());
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getById(modMailThreadId);
public CompletableFutureList<Message> logModMailThread(Long modMailThreadId, LoadedModmailThreadMessageList messages, String note, List<UndoActionInstance> undoActions) {
log.info("Logging mod mail thread {} with {} messages.", modMailThreadId, messages.getMessageList().size());
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get();
List<ModMailLoggedMessageModel> loggedMessages = new ArrayList<>();
messages.forEach(future -> {
messages.getMessageList().forEach(futures -> {
try {
CompletableFuture<Message> future = futures.getMessageFuture();
if(!future.isCompletedExceptionally()) {
Message loadedMessage = future.join();
if(loadedMessage != null) {
@@ -591,12 +605,13 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.stream()
.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."));
Member author = futures.getMemberFuture().join();
ModMailLoggedMessageModel modMailLoggedMessageModel =
ModMailLoggedMessageModel
.builder()
.message(loadedMessage)
.modMailMessage(modmailMessage)
.author(userInServerService.getFullUser(modmailMessage.getAuthor()))
.author(author) // doesnt work for the messages from the DM channel
.build();
loggedMessages.add(modMailLoggedMessageModel);
}
@@ -634,7 +649,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
*/
@Transactional
public void closeModMailThreadInDb(Long modMailThreadId) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getById(modMailThreadId);
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get();
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) {
List<CompletableFuture<Message>> messageFutures = new ArrayList<>();
// TODO order messages
loadedMessages.forEach(message -> {
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);
@@ -673,7 +689,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
*/
@Transactional
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()) {
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);

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.AUserInAServer;
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.ModMailThreadState;
import dev.sheldan.abstracto.modmail.repository.ModMailThreadRepository;
@@ -32,10 +33,15 @@ public class ModMailThreadManagementServiceBean implements ModMailThreadManageme
}
@Override
public Optional<ModMailThread> getById(Long modMailThreadId) {
public Optional<ModMailThread> getByIdOptional(Long modMailThreadId) {
return modMailThreadRepository.findById(modMailThreadId);
}
@Override
public ModMailThread getById(Long modMailThreadId) {
return getByIdOptional(modMailThreadId).orElseThrow(() -> new ModMailThreadNotFoundException(modMailThreadId));
}
@Override
public ModMailThread getByChannel(AChannel channel) {
return modMailThreadRepository.findByChannel(channel);

View File

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

View File

@@ -42,7 +42,7 @@ public class ModMailFeatureValidatorBean implements ModMailFeatureValidator {
*/
@Override
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()) {
Guild guild = guildById.get();
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.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
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.
*/
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.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import java.util.List;
@@ -25,7 +26,7 @@ public class ModMailNotificationModel extends ServerContext {
/**
* 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.
*/

View File

@@ -1,10 +1,10 @@
package dev.sheldan.abstracto.modmail.models.template;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
import lombok.Builder;
import lombok.Getter;
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
@@ -15,9 +15,9 @@ import lombok.Setter;
@Builder
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
* for the user

View File

@@ -1,10 +1,10 @@
package dev.sheldan.abstracto.modmail.models.template;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
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.List;
@@ -17,17 +17,13 @@ import java.util.List;
@Builder
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
*/
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
* when the user sends a new message

View File

@@ -1,10 +1,10 @@
package dev.sheldan.abstracto.modmail.service;
import dev.sheldan.abstracto.modmail.models.database.ModMailMessage;
import dev.sheldan.abstracto.modmail.models.dto.LoadedModmailThreadMessageList;
import net.dv8tion.jda.api.entities.Message;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* 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
* @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;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.UndoActionInstance;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
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.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}
* 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}
* @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 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
*/
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.
@@ -52,18 +52,20 @@ public interface ModMailThreadService {
* @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
*/
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
* 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 message The pure {@link Message} containing the command which caused the reply
* @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 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,

View File

@@ -29,7 +29,14 @@ public interface ModMailThreadManagementService {
* @param modMailThreadId The ID of the mod mail to search for
* @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

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.utils.FutureUtils;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -33,9 +32,10 @@ public class StarStats extends AbstractConditionableCommand {
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
StarStatsModel result = starboardService.retrieveStarStats(commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(STARSTATS_RESPONSE_TEMPLATE, result, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess());
return starboardService.retrieveStarStats(commandContext.getGuild().getIdLong())
.thenCompose(starStatsModel ->
FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(STARSTATS_RESPONSE_TEMPLATE, starStatsModel, commandContext.getChannel()))
).thenApply(o -> CommandResult.fromSuccess());
}
@Override

View File

@@ -47,7 +47,7 @@ public class MessageEmbedListener implements MessageReceivedListener {
continue;
}
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())
.thenAccept(cachedMessageConsumer)
.exceptionally(throwable -> {
@@ -62,10 +62,14 @@ public class MessageEmbedListener implements MessageReceivedListener {
}
@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 {}.",
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

View File

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

View File

@@ -1,7 +1,9 @@
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.management.UserInServerManagementService;
import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsUser;
import dev.sheldan.abstracto.utility.repository.StarStatsUserResult;
import org.springframework.beans.factory.annotation.Autowired;
@@ -9,6 +11,7 @@ import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class StarStatsUserConverter {
@@ -16,17 +19,26 @@ public class StarStatsUserConverter {
@Autowired
private BotService botService;
public List<StarStatsUser> convertToStarStatsUser(List<StarStatsUserResult> users, Long serverId) {
List<StarStatsUser> result = new ArrayList<>();
users.forEach(starStatsUserResult -> {
StarStatsUser newUser = StarStatsUser
.builder()
.starCount(starStatsUserResult.getStarCount())
.member(botService.getMemberInServer(serverId, starStatsUserResult.getUserId()))
.user(AUser.builder().id(starStatsUserResult.getUserId()).build())
.build();
result.add(newUser);
});
@Autowired
private UserInServerManagementService userInServerManagementService;
public List<CompletableFuture<StarStatsUser>> convertToStarStatsUser(List<StarStatsUserResult> users, Long serverId) {
List<CompletableFuture<StarStatsUser>> result = new ArrayList<>();
users.forEach(starStatsUserResult ->
result.add(createStarStatsUser(serverId, starStatsUserResult))
);
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.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.CompletionStage;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -60,7 +60,7 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
private ChannelService channelService;
@Autowired
private MessageEmbedService self;
private MessageEmbedServiceBean self;
@Autowired
private MessageCache messageCache;
@@ -102,7 +102,7 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
.thenAccept(cachedMessage -> self.embedLink(cachedMessage, target, userEmbeddingUserInServerId, embeddingMessage)
).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;
})
);
@@ -110,42 +110,50 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
@Override
@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);
if(causeOpt.isPresent()) {
AUserInAServer cause = causeOpt.get();
MessageEmbeddedModel messageEmbeddedModel = buildTemplateParameter(embeddingMessage, cachedMessage);
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;
});
return buildTemplateParameter(embeddingMessage, cachedMessage).thenCompose(messageEmbeddedModel ->
self.sendEmbeddingMessage(cachedMessage, target, userEmbeddingUserInServerId, messageEmbeddedModel)
);
} else {
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());
AUserInAServer user = userInServerManagementService.loadUser(message.getMember());
Member author = botService.getMemberInServer(embeddedMessage.getServerId(), embeddedMessage.getAuthorId());
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServerOptional(embeddedMessage.getServerId(), embeddedMessage.getChannelId());
TextChannel sourceChannel = textChannelFromServer.orElse(null);
AChannel channel = channelManagementService.loadChannel(message.getChannel().getIdLong());
@@ -155,7 +163,7 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
.server(server)
.member(message.getMember())
.aUserInAServer(user)
.author(author)
.author(member)
.sourceChannel(sourceChannel)
.embeddingUser(message.getMember())
.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.database.AChannel;
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.utils.FutureUtils;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
@@ -36,6 +36,7 @@ import java.util.concurrent.*;
@Slf4j
public class RemindServiceBean implements ReminderService {
public static final String REMINDER_TEMPLATE_TEXT = "remind_reminder";
@Autowired
private ReminderManagementService reminderManagementService;
@@ -52,7 +53,7 @@ public class RemindServiceBean implements ReminderService {
private BotService botService;
@Autowired
private ReminderService self;
private RemindServiceBean self;
@Autowired
private ChannelService channelService;
@@ -61,6 +62,7 @@ public class RemindServiceBean implements ReminderService {
@Qualifier("reminderScheduler")
private ScheduledExecutorService instantReminderScheduler;
@Override
public Reminder createReminderInForUser(AUserInAServer user, String remindText, Duration remindIn, Message message) {
AChannel channel = channelManagementService.loadChannel(message.getChannel().getIdLong());
@@ -99,7 +101,7 @@ public class RemindServiceBean implements ReminderService {
@Override
@Transactional
public void executeReminder(Long reminderId) {
Reminder reminderToRemindFor = reminderManagementService.loadReminder(reminderId).orElseThrow(() -> new ReminderNotFoundException(reminderId));
Reminder reminderToRemindFor = reminderManagementService.loadReminder(reminderId);
if(reminderToRemindFor.isReminded()) {
return;
}
@@ -107,22 +109,15 @@ public class RemindServiceBean implements ReminderService {
AChannel channel = reminderToRemindFor.getChannel();
log.info("Executing reminder {} in channel {} in server {} for user {}.",
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()) {
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.
if(channelToAnswerIn.isPresent()) {
AUser userReference = reminderToRemindFor.getRemindedUser().getUserReference();
Member memberInServer = botService.getMemberInServer(server.getId(), userReference.getId());
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());
botService.getMemberInServerAsync(server.getId(), reminderToRemindFor.getRemindedUser().getUserReference().getId()).thenAccept(member ->
self.sendReminderText(reminderId, channelToAnswerIn.get(), member)
);
} else {
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);
}
@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
public void unRemind(Long reminderId, AUserInAServer aUserInAServer) {
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.database.*;
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.core.service.BotService;
import dev.sheldan.abstracto.core.service.ConfigService;
@@ -32,6 +33,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;
@@ -80,22 +82,24 @@ public class StarboardServiceBean implements StarboardService {
private StarboardServiceBean self;
@Override
public void createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser) {
StarboardPostModel starboardPostModel = buildStarboardPostModel(message, userExceptAuthor.size());
List<Long> userExceptAuthorIds = new ArrayList<>();
userExceptAuthor.forEach(aUserInAServer -> userExceptAuthorIds.add(aUserInAServer.getUserInServerId()));
public CompletableFuture<Void> createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser) {
Long starredUserId = starredUser.getUserInServerId();
List<Long> userExceptAuthorIds = userExceptAuthor.stream().map(AUserInAServer::getUserInServerId).collect(Collectors.toList());
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);
PostTarget starboard = postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), message.getServerId());
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, StarboardPostTarget.STARBOARD, message.getServerId());
Long starboardChannelId = starboard.getChannelReference().getId();
Long starredUserId = starredUser.getUserInServerId();
CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenAccept(aVoid ->
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;
});
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenAccept(aVoid ->
self.persistPost(message, userExceptAuthorIds, completableFutures, starboardChannelId, starredUserId)
);
}
@Transactional
@@ -120,41 +124,41 @@ public class StarboardServiceBean implements StarboardService {
});
}
private StarboardPostModel buildStarboardPostModel(CachedMessage message, Integer starCount) {
Member member = botService.getMemberInServer(message.getServerId(), message.getAuthorId());
Optional<TextChannel> channel = botService.getTextChannelFromServerOptional(message.getServerId(), message.getChannelId());
Optional<Guild> guild = botService.getGuildById(message.getServerId());
AChannel aChannel = AChannel.builder().id(message.getChannelId()).build();
AUser user = AUser.builder().id(message.getAuthorId()).build();
AServer server = AServer.builder().id(message.getServerId()).build();
String starLevelEmote = getAppropriateEmote(message.getServerId(), starCount);
return StarboardPostModel
.builder()
.message(message)
.author(member)
.channel(channel.orElse(null))
.aChannel(aChannel)
.starCount(starCount)
.guild(guild.orElse(null))
.user(user)
.server(server)
.starLevelEmote(starLevelEmote)
.build();
private CompletableFuture<StarboardPostModel> buildStarboardPostModel(CachedMessage message, Integer starCount) {
return botService.getMemberInServerAsync(message.getServerId(), message.getAuthorId()).thenApply(member -> {
Optional<TextChannel> channel = botService.getTextChannelFromServerOptional(message.getServerId(), message.getChannelId());
Optional<Guild> guild = botService.getGuildByIdOptional(message.getServerId());
AChannel aChannel = AChannel.builder().id(message.getChannelId()).build();
AUser user = AUser.builder().id(message.getAuthorId()).build();
AServer server = AServer.builder().id(message.getServerId()).build();
String starLevelEmote = getAppropriateEmote(message.getServerId(), starCount);
return StarboardPostModel
.builder()
.message(message)
.author(member)
.channel(channel.orElse(null))
.aChannel(aChannel)
.starCount(starCount)
.guild(guild.orElse(null))
.user(user)
.server(server)
.starLevelEmote(starLevelEmote)
.build();
});
}
@Override
public 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());
StarboardPostModel starboardPostModel = buildStarboardPostModel(message, userExceptAuthor.size());
MessageToSend messageToSend = templateService.renderEmbedTemplate(STARBOARD_POST_TEMPLATE, starboardPostModel);
List<CompletableFuture<Message>> futures = postTargetService.editOrCreatedInPostTarget(post.getStarboardMessageId(), messageToSend, StarboardPostTarget.STARBOARD, message.getServerId());
Long starboardPostId = post.getId();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(aVoid -> {
Optional<StarboardPost> innerPost = starboardPostManagementService.findByStarboardPostId(starboardPostId);
innerPost.ifPresent(starboardPost -> starboardPostManagementService.setStarboardPostMessageId(starboardPost, futures.get(0).join().getIdLong()));
}).exceptionally(throwable -> {
log.error("Failed to update starboard post {}.", post.getId(), throwable);
return null;
public CompletableFuture<Void> updateStarboardPost(StarboardPost post, CachedMessage message, List<AUserInAServer> userExceptAuthor) {
int starCount = userExceptAuthor.size();
log.info("Updating starboard post {} in server {} with reactors {}.", post.getId(), post.getSourceChanel().getServer().getId(), starCount);
return buildStarboardPostModel(message, starCount).thenCompose(starboardPostModel -> {
MessageToSend messageToSend = templateService.renderEmbedTemplate(STARBOARD_POST_TEMPLATE, starboardPostModel);
List<CompletableFuture<Message>> futures = postTargetService.editOrCreatedInPostTarget(post.getStarboardMessageId(), messageToSend, StarboardPostTarget.STARBOARD, message.getServerId());
Long starboardPostId = post.getId();
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(aVoid -> {
Optional<StarboardPost> innerPost = starboardPostManagementService.findByStarboardPostId(starboardPostId);
innerPost.ifPresent(starboardPost -> starboardPostManagementService.setStarboardPostMessageId(starboardPost, futures.get(0).join().getIdLong()));
});
});
}
@@ -166,28 +170,35 @@ public class StarboardServiceBean implements StarboardService {
}
@Override
public StarStatsModel retrieveStarStats(Long serverId) {
public CompletableFuture<StarStatsModel> retrieveStarStats(Long serverId) {
int count = 3;
List<StarboardPost> starboardPosts = starboardPostManagementService.retrieveTopPosts(serverId, count);
List<StarStatsUser> topStarGivers = starboardPostReactorManagementService.retrieveTopStarGiver(serverId, count);
List<StarStatsPost> starStatsPosts = starboardPosts.stream().map(this::fromStarboardPost).collect(Collectors.toList());
List<StarStatsUser> topStarReceiver = starboardPostReactorManagementService.retrieveTopStarReceiver(serverId, count);
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<CompletableFuture<StarStatsUser>> topStarGiverFutures = starboardPostReactorManagementService.retrieveTopStarGiver(serverId, count);
List<CompletableFuture<StarStatsUser>> topStarReceiverFutures = starboardPostReactorManagementService.retrieveTopStarReceiver(serverId, count);
List<CompletableFuture> allFutures = new ArrayList<>();
allFutures.addAll(topStarGiverFutures);
allFutures.addAll(topStarReceiverFutures);
return FutureUtils.toSingleFuture(allFutures).thenApply(aVoid -> {
List<StarboardPost> starboardPosts = starboardPostManagementService.retrieveTopPosts(serverId, count);
List<StarStatsPost> starStatsPosts = starboardPosts.stream().map(this::fromStarboardPost).collect(Collectors.toList());
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) {

View File

@@ -1,10 +1,9 @@
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.service.CounterService;
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.core.service.BotService;
import dev.sheldan.abstracto.core.service.MessageService;
@@ -73,7 +72,7 @@ public class SuggestionServiceBean implements SuggestionService {
long guildId = member.getGuild().getIdLong();
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);
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenCompose(aVoid -> {
return FutureUtils.toSingleFutureGeneric(completableFutures).thenCompose(aVoid -> {
Message message = completableFutures.get(0).join();
log.trace("Posted message, adding reaction for suggestion {} to message {}.", newSuggestionId, message.getId());
CompletableFuture<Void> firstReaction = messageService.addReactionToMessageWithFuture(SUGGESTION_YES_EMOTE, guildId, message);
@@ -110,26 +109,22 @@ public class SuggestionServiceBean implements SuggestionService {
suggestionLog.setOriginalMessageId(originalMessageId);
suggestionLog.setOriginalMessageUrl(MessageUtils.buildMessageUrl(serverId, channelId, originalMessageId));
AUserInAServer suggester = suggestion.getSuggester();
Optional<Guild> guildByIdOptional = botService.getGuildById(serverId);
if(guildByIdOptional.isPresent()) {
Guild guildById = guildByIdOptional.get();
Member memberById = guildById.getMemberById(suggester.getUserReference().getId());
suggestionLog.setSuggester(memberById);
suggestionLog.setState(suggestion.getState());
suggestionLog.setSuggestionId(suggestion.getId());
TextChannel textChannelById = guildById.getTextChannelById(channelId);
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);
TextChannel textChannelById = botService.getTextChannelFromServer(serverId, channelId);
CompletableFuture<Member> memberById = botService.getMemberInServerAsync(serverId, suggester.getUserReference().getId());
suggestionLog.setState(suggestion.getState());
suggestionLog.setSuggestionId(suggestion.getId());
CompletableFuture<Void> finalFuture = new CompletableFuture<>();
memberById.whenComplete((member, throwable) -> {
if(throwable == null) {
suggestionLog.setSuggester(member);
}
} else {
log.warn("Not possible to update suggestion {}, because guild {} was not found.", suggestion.getId(), serverId);
throw new GuildNotFoundException(serverId);
}
textChannelById.retrieveMessageById(originalMessageId).submit().thenCompose(message ->
self.updateSuggestionMessageText(text, suggestionLog, message)
).thenAccept(aVoid -> finalFuture.complete(null));
});
return finalFuture;
}
@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.database.AUserInAServer;
import dev.sheldan.abstracto.utility.exception.ReminderNotFoundException;
import dev.sheldan.abstracto.utility.models.database.Reminder;
import dev.sheldan.abstracto.utility.repository.ReminderRepository;
import lombok.extern.slf4j.Slf4j;
@@ -39,10 +40,15 @@ public class ReminderManagementServiceBean implements ReminderManagementService
}
@Override
public Optional<Reminder> loadReminder(Long reminderId) {
public Optional<Reminder> loadReminderOptional(Long reminderId) {
return reminderRepository.findById(reminderId);
}
@Override
public Reminder loadReminder(Long reminderId) {
return loadReminderOptional(reminderId).orElseThrow(() -> new ReminderNotFoundException(reminderId));
}
@Override
public void setReminded(Reminder reminder) {
reminder.setReminded(true);

View File

@@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
@@ -52,13 +53,13 @@ public class StarboardPostReactorManagementServiceBean implements StarboardPostR
}
@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);
return converter.convertToStarStatsUser(starGivers, serverId);
}
@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);
return converter.convertToStarStatsUser(starReceivers, serverId);
}

View File

@@ -34,7 +34,7 @@ public class StarStatsTest {
CommandContext noParameters = CommandTestUtilities.getNoParameters();
when(noParameters.getGuild().getIdLong()).thenReturn(noParameters.getUserInitiatedContext().getChannel().getId());
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);
verify(channelService, times(1)).sendEmbedTemplateInChannel(StarStats.STARSTATS_RESPONSE_TEMPLATE, starStatsModel, noParameters.getChannel());
CommandTestUtilities.checkSuccessfulCompletionAsync(result);

View File

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

View File

@@ -1,6 +1,9 @@
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.management.UserInServerManagementService;
import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsUser;
import dev.sheldan.abstracto.utility.repository.StarStatsUserResult;
import net.dv8tion.jda.api.entities.Member;
@@ -14,6 +17,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@@ -26,6 +30,8 @@ public class StarStatsUserConverterTest {
@Mock
private BotService botService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Test
public void testConversionOfMultipleItems() {
@@ -37,24 +43,34 @@ public class StarStatsUserConverterTest {
List<StarStatsUserResult> results = new ArrayList<>();
StarStatsUserResult firstResult = Mockito.mock(StarStatsUserResult.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.getStarCount()).thenReturn(firstStarCount);
results.add(firstResult);
StarStatsUserResult secondResult = Mockito.mock(StarStatsUserResult.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.getStarCount()).thenReturn(secondStarCount);
results.add(secondResult);
List<StarStatsUser> starStatsUsers = testUnit.convertToStarStatsUser(results, serverId);
StarStatsUser firstConverted = starStatsUsers.get(0);
List<CompletableFuture<StarStatsUser>> starStatsUsers = testUnit.convertToStarStatsUser(results, serverId);
StarStatsUser firstConverted = starStatsUsers.get(0).join();
Assert.assertEquals(firstStarCount, firstConverted.getStarCount());
Assert.assertEquals(firstMember, firstConverted.getMember());
Assert.assertEquals(firstUserId, firstConverted.getUser().getId());
StarStatsUser secondConverted = starStatsUsers.get(1);
StarStatsUser secondConverted = starStatsUsers.get(1).join();
Assert.assertEquals(secondStarCount, secondConverted.getStarCount());
Assert.assertEquals(secondMember, secondConverted.getMember());
Assert.assertEquals(secondUserId, secondConverted.getUser().getId());
@@ -66,7 +82,7 @@ public class StarStatsUserConverterTest {
Long serverId = 5L;
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());
Assert.assertEquals(0, starStatsUsers.size());

View File

@@ -1,8 +1,6 @@
package dev.sheldan.abstracto.utility.service;
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.template.listener.MessageEmbeddedModel;
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.ServerManagementService;
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.test.MockUtils;
import dev.sheldan.abstracto.utility.models.MessageEmbedLink;
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.Message;
import net.dv8tion.jda.api.entities.TextChannel;
@@ -28,7 +23,6 @@ import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@@ -60,7 +54,7 @@ public class MessageEmbedServiceBeanTest {
private ChannelService channelService;
@Mock
private MessageEmbedService self;
private MessageEmbedServiceBean self;
@Mock
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 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
public void testNoLinkInString(){
String message = "test";
@@ -182,38 +186,16 @@ public class MessageEmbedServiceBeanTest {
}
@Test
public void testEmbeddingLink() {
Long channelId = 6L;
Long serverId = 4L;
AServer server = MockUtils.getServer(serverId);
AChannel aChannel = MockUtils.getTextChannel(server, channelId);
Long userEmbeddingUserInServerId = 5L;
AUserInAServer embeddingUser = MockUtils.getUserObject(userEmbeddingUserInServerId, server);
Long authorId = 7L;
AUserInAServer authorUser = MockUtils.getUserObject(authorId, server);
Long firstMessageId = 6L;
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);
public void testLoadingEmbeddingModel() {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getAuthorId()).thenReturn(USER_ID);
when(userInServerManagementService.loadUserConditional(EMBEDDING_USER_IN_SERVER_ID)).thenReturn(Optional.of(embeddingUser));
when(botService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(embeddingMember));
MessageEmbeddedModel model = Mockito.mock(MessageEmbeddedModel.class);
when(self.loadMessageEmbedModel(embeddingMessage, cachedMessage, embeddingMember)).thenReturn(model);
when(self.sendEmbeddingMessage(cachedMessage, textChannel, EMBEDDING_USER_IN_SERVER_ID, model)).thenReturn(CompletableFuture.completedFuture(null));
testUnit.embedLink(cachedMessage, textChannel, EMBEDDING_USER_IN_SERVER_ID, embeddingMessage).join();
}
@Test

View File

@@ -14,7 +14,6 @@ import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.test.MockUtils;
import dev.sheldan.abstracto.utility.exception.ReminderNotFoundException;
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 net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
@@ -32,10 +31,13 @@ import org.quartz.JobDataMap;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import static dev.sheldan.abstracto.utility.service.RemindServiceBean.REMINDER_TEMPLATE_TEXT;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
@@ -74,6 +76,8 @@ public class RemindServiceBeanTest {
@Mock
private ScheduledExecutorService instantReminderScheduler;
private static final Long REMINDER_ID = 5L;
@Before
public void setup() {
when(message.getIdLong()).thenReturn(5L);
@@ -89,8 +93,7 @@ public class RemindServiceBeanTest {
String remindText = "text";
Duration duration = Duration.ofSeconds(62);
when(channelManagementService.loadChannel(channel.getIdLong())).thenReturn(aChannel);
Long reminderId = 5L;
Reminder createdReminder = Reminder.builder().targetDate(Instant.now().plus(duration)).text(remindText).id(reminderId).build();
Reminder createdReminder = Reminder.builder().targetDate(Instant.now().plus(duration)).text(remindText).id(REMINDER_ID).build();
Long messageId = 5L;
when(reminderManagementService.createReminder(any(AServerAChannelAUser.class), eq(remindText), any(Instant.class), eq(messageId))).thenReturn(createdReminder);
String triggerKey = "trigger";
@@ -111,8 +114,7 @@ public class RemindServiceBeanTest {
String remindText = "text";
Duration duration = Duration.ofSeconds(50);
when(channelManagementService.loadChannel(channel.getIdLong())).thenReturn(aChannel);
Long reminderId = 5L;
Reminder createdReminder = Reminder.builder().targetDate(Instant.now().plus(duration)).text(remindText).id(reminderId).build();
Reminder createdReminder = Reminder.builder().targetDate(Instant.now().plus(duration)).text(remindText).id(REMINDER_ID).build();
Long messageId = 5L;
when(reminderManagementService.createReminder(any(AServerAChannelAUser.class), eq(remindText), any(Instant.class), eq(messageId))).thenReturn(createdReminder);
Reminder returnedReminder = testUnit.createReminderInForUser(user, remindText, duration, message);
@@ -126,36 +128,31 @@ public class RemindServiceBeanTest {
AServer server = MockUtils.getServer();
AChannel aChannel = MockUtils.getTextChannel(server, 4L);
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(reminderId).build();
when(reminderManagementService.loadReminder(reminderId)).thenReturn(Optional.of(remindedReminder));
Reminder remindedReminder = Reminder.builder().reminded(false).remindedUser(remindedUser).reminderDate(Instant.now()).targetDate(Instant.now()).server(server).channel(aChannel).id(REMINDER_ID).build();
when(reminderManagementService.loadReminder(REMINDER_ID)).thenReturn(remindedReminder);
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));
Member mockedMember = Mockito.mock(Member.class);
when(botService.getMemberInServer(server.getId(), remindedUser.getUserReference().getId())).thenReturn(mockedMember);
MessageToSend messageToSend = MessageToSend.builder().build();
when(templateService.renderEmbedTemplate(eq("remind_reminder"), any(ExecutedReminderModel.class))).thenReturn(messageToSend);
testUnit.executeReminder(reminderId);
when(botService.getMemberInServerAsync(server.getId(), remindedUser.getUserReference().getId())).thenReturn(CompletableFuture.completedFuture(mockedMember));
testUnit.executeReminder(REMINDER_ID);
verify(reminderManagementService, times(1)).setReminded(remindedReminder);
verify(channelService, times(1)).sendMessageToSendToChannel(messageToSend, channel);
}
@Test
public void testExecuteReminderFromNotFoundChannel() {
AServer server = MockUtils.getServer();
AChannel aChannel = MockUtils.getTextChannel(server, 4L);
Long reminderId = 5L;
AUserInAServer remindedUser = Mockito.mock(AUserInAServer.class);
AUser user = Mockito.mock(AUser.class);
when(user.getId()).thenReturn(6L);
when(remindedUser.getUserReference()).thenReturn(user);
Reminder remindedReminder = Reminder.builder().reminded(false).server(server).remindedUser(remindedUser).channel(aChannel).id(reminderId).build();
when(reminderManagementService.loadReminder(reminderId)).thenReturn(Optional.of(remindedReminder));
Reminder remindedReminder = Reminder.builder().reminded(false).server(server).remindedUser(remindedUser).channel(aChannel).id(REMINDER_ID).build();
when(reminderManagementService.loadReminder(REMINDER_ID)).thenReturn(remindedReminder);
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());
testUnit.executeReminder(reminderId);
testUnit.executeReminder(REMINDER_ID);
verify(reminderManagementService, times(1)).setReminded(remindedReminder);
}
@@ -170,8 +167,8 @@ public class RemindServiceBeanTest {
when(user.getId()).thenReturn(6L);
when(remindedUser.getUserReference()).thenReturn(user);
Reminder remindedReminder = Reminder.builder().reminded(false).server(server).channel(aChannel).remindedUser(remindedUser).id(reminderId).build();
when(reminderManagementService.loadReminder(reminderId)).thenReturn(Optional.of(remindedReminder));
when(botService.getGuildById(server.getId())).thenReturn(Optional.empty());
when(reminderManagementService.loadReminder(reminderId)).thenReturn(remindedReminder);
when(botService.getGuildByIdOptional(server.getId())).thenReturn(Optional.empty());
testUnit.executeReminder(reminderId);
verify(reminderManagementService, times(1)).setReminded(remindedReminder);
}
@@ -180,17 +177,9 @@ public class RemindServiceBeanTest {
public void testExecuteRemindedReminder() {
Long reminderId = 5L;
Reminder remindedReminder = Reminder.builder().reminded(true).build();
when(reminderManagementService.loadReminder(reminderId)).thenReturn(Optional.of(remindedReminder));
when(reminderManagementService.loadReminder(reminderId)).thenReturn(remindedReminder);
testUnit.executeReminder(reminderId);
verify(botService, times(0)).getGuildById(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());
verify(botService, times(0)).getGuildByIdOptional(anyLong());
}
@Test
@@ -225,4 +214,21 @@ public class RemindServiceBeanTest {
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.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.utility.service.StarboardServiceBean.STARBOARD_POST_TEMPLATE;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
@@ -93,6 +94,19 @@ public class StarboardServiceBeanTest {
@Mock
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
private ArgumentCaptor<AUserInAServer> userInAServerArgumentCaptor;
@@ -108,34 +122,40 @@ public class StarboardServiceBeanTest {
AUserInAServer userReacting = MockUtils.getUserObject(4L, server);
AUserInAServer starredUser = MockUtils.getUserObject(5L, server);
Long channelId = 10L;
Long starredUserId = starredUser.getUserReference().getId();
CachedMessage message = CachedMessage
.builder()
.authorId(starredUser.getUserReference().getId())
.authorId(starredUserId)
.serverId(server.getId())
.channelId(channelId)
.build();
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.getGuildById(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);
when(botService.getGuildByIdOptional(server.getId())).thenReturn(Optional.of(guild));
ADefaultConfig config = ADefaultConfig.builder().longValue(3L).build();
when(defaultConfigManagementService.getDefaultConfig(StarboardServiceBean.STAR_LEVELS_CONFIG_KEY)).thenReturn(config);
when(configService.getLongValue("starLvl3", server.getId())).thenReturn(3L);
when(configService.getLongValue("starLvl2", server.getId())).thenReturn(2L);
when(emoteService.getUsableEmoteOrDefault(server.getId(), "star2")).thenReturn("b");
testUnit.createStarboardPost(message, userExceptAuthor, userReacting, starredUser);
verify(self, times(1)).persistPost(eq(message), anyList(), eq(futures), eq(channelId), eq(starredUser.getUserInServerId()));
List<StarboardPostModel> starboardPostModels = starboardPostModelArgumentCaptor.getAllValues();
Assert.assertEquals(1, starboardPostModels.size());
StarboardPostModel usedModel = starboardPostModels.get(0);
Assert.assertEquals("b", usedModel.getStarLevelEmote());
when(self.sendStarboardPostAndStore(eq(message), eq(starredUserId), anyList(), any())).thenReturn(CompletableFuture.completedFuture(null));
testUnit.createStarboardPost(message, userExceptAuthor, userReacting, starredUser).join();
}
@Test
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
@@ -189,12 +209,13 @@ public class StarboardServiceBeanTest {
Long starboardPostId = 47L;
StarboardPost post = StarboardPost.builder().postMessageId(postMessageId).starboardMessageId(oldPostId).sourceChanel(sourceChannel).id(starboardPostId).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(sendPost.getIdLong()).thenReturn(newPostId);
ADefaultConfig config = ADefaultConfig.builder().longValue(4L).build();
when(defaultConfigManagementService.getDefaultConfig(StarboardServiceBean.STAR_LEVELS_CONFIG_KEY)).thenReturn(config);
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<>();
testUnit.updateStarboardPost(post, message, userExceptAuthor);
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();
List<StarboardPost> topPosts = Arrays.asList(post1, post2);
when(starboardPostManagementService.retrieveTopPosts(server.getId(), limit)).thenReturn(topPosts);
StarStatsUser statsUser = StarStatsUser.builder().build();
StarStatsUser statsUser2 = StarStatsUser.builder().build();
List<StarStatsUser> topGiver = Arrays.asList(statsUser, statsUser2);
CompletableFuture<StarStatsUser> statsUser = CompletableFuture.completedFuture(Mockito.mock(StarStatsUser.class));
CompletableFuture<StarStatsUser> statsUser2 = CompletableFuture.completedFuture(Mockito.mock(StarStatsUser.class));
List<CompletableFuture<StarStatsUser>> topGiver = Arrays.asList(statsUser, statsUser2);
when(starboardPostReactorManagementService.retrieveTopStarGiver(server.getId(), limit)).thenReturn(topGiver);
when(starboardPostReactorManagementService.retrieveTopStarReceiver(server.getId(), limit)).thenReturn(topGiver);
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(), "starboardBadge2")).thenReturn("2");
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();
Assert.assertEquals(limit.intValue(), badgeEmotes.size());
Assert.assertEquals("1", badgeEmotes.get(0));

View File

@@ -1,9 +1,9 @@
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.AChannel;
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.service.BotService;
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.templating.model.MessageToSend;
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.exception.SuggestionNotFoundException;
import dev.sheldan.abstracto.utility.exception.SuggestionUpdateException;
@@ -74,49 +73,64 @@ public class SuggestionServiceBeanTest {
@Mock
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
public void testCreateSuggestionMessage() {
String suggestionText = "text";
AServer server = MockUtils.getServer();
SuggestionLog log = Mockito.mock(SuggestionLog.class);
when(log.getServer()).thenReturn(server);
Long suggestionId = 5L;
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();
when(templateService.renderEmbedTemplate(eq(SuggestionServiceBean.SUGGESTION_LOG_TEMPLATE), any(SuggestionLog.class))).thenReturn(messageToSend);
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));
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);
verify( messageService, times(1)).addReactionToMessageWithFuture(SuggestionServiceBean.SUGGESTION_YES_EMOTE, server.getId(), suggestionMessage);
verify( messageService, times(1)).addReactionToMessageWithFuture(SuggestionServiceBean.SUGGESTION_NO_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_ID, suggestionMessage);
}
@Test
public void testCreateSuggestion() {
Member member = Mockito.mock(Member.class);
when(member.getGuild()).thenReturn(guild);
when(suggesterMember.getGuild()).thenReturn(guild);
when(guild.getId()).thenReturn("5");
String text = "text";
Message message = Mockito.mock(Message.class);
Long suggestionId = 3L;
testUnit.persistSuggestionInDatabase(member, text, message, suggestionId);
verify(suggestionManagementService, times(1)).createSuggestion(member, text, message, suggestionId);
testUnit.persistSuggestionInDatabase(suggesterMember, text, message, SUGGESTION_ID);
verify(suggestionManagementService, times(1)).createSuggestion(suggesterMember, text, message, SUGGESTION_ID);
}
@Test
public void testAcceptExistingSuggestion() {
Member suggesterMember = Mockito.mock(Member.class);
executeAcceptWithMember(suggesterMember);
}
@Test(expected = SuggestionNotFoundException.class)
public void testAcceptNotExistingSuggestion() {
Long suggestionId = 5L;
when(suggestionManagementService.getSuggestion(suggestionId)).thenReturn(Optional.empty());
testUnit.acceptSuggestion(suggestionId, CLOSING_TEXT, SuggestionLog.builder().build());
when(suggestionManagementService.getSuggestion(SUGGESTION_ID)).thenReturn(Optional.empty());
testUnit.acceptSuggestion(SUGGESTION_ID, CLOSING_TEXT, SuggestionLog.builder().build());
}
@Test
@@ -126,38 +140,24 @@ public class SuggestionServiceBeanTest {
@Test(expected = ChannelNotFoundException.class)
public void testAcceptSuggestionInNoTextChannel() {
Long suggestionId = 5L;
setupForNoTextChannel(suggestionId);
testUnit.acceptSuggestion(suggestionId, CLOSING_TEXT, SuggestionLog.builder().build());
setupForNoTextChannel();
testUnit.acceptSuggestion(SUGGESTION_ID, CLOSING_TEXT, SuggestionLog.builder().build());
}
private void setupForNoTextChannel(Long suggestionId) {
AServer server = MockUtils.getServer();
Long channelId = 5L;
private void setupForNoTextChannel() {
Long messageId = 7L;
AChannel channel = MockUtils.getTextChannel(server, channelId);
AUserInAServer suggester = MockUtils.getUserObject(4L, server);
Suggestion suggestionToAccept = Suggestion
.builder()
.channel(channel)
.server(server)
.id(suggestionId)
.id(SUGGESTION_ID)
.suggester(suggester)
.messageId(messageId)
.build();
when(suggestionManagementService.getSuggestion(suggestionId)).thenReturn(Optional.of(suggestionToAccept));
when(botService.getGuildById(server.getId())).thenReturn(Optional.of(guild));
Member suggesterMember = Mockito.mock(Member.class);
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());
when(server.getId()).thenReturn(SERVER_ID);
when(channel.getId()).thenReturn(CHANNEL_ID);
when(botService.getTextChannelFromServer(SERVER_ID, CHANNEL_ID)).thenThrow(new ChannelNotFoundException(CHANNEL_ID));
when(suggestionManagementService.getSuggestion(SUGGESTION_ID)).thenReturn(Optional.of(suggestionToAccept));
}
@Test(expected = SuggestionUpdateException.class)
@@ -169,7 +169,6 @@ public class SuggestionServiceBeanTest {
@Test
public void testUpdateSuggestionMessageWithEmbed() {
AServer server = MockUtils.getServer();
SuggestionLog log = SuggestionLog.builder().server(server).build();
MessageEmbed embed = Mockito.mock(MessageEmbed.class);
when(embed.getDescription()).thenReturn("description");
@@ -185,15 +184,13 @@ public class SuggestionServiceBeanTest {
@Test
public void testRejectExistingSuggestion() {
Member suggesterMember = Mockito.mock(Member.class);
executeRejectWithMember(suggesterMember);
}
@Test(expected = SuggestionNotFoundException.class)
public void testRejectNotExistingSuggestion() {
Long suggestionId = 5L;
when(suggestionManagementService.getSuggestion(suggestionId)).thenReturn(Optional.empty());
testUnit.rejectSuggestion(suggestionId, CLOSING_TEXT, SuggestionLog.builder().build());
when(suggestionManagementService.getSuggestion(SUGGESTION_ID)).thenReturn(Optional.empty());
testUnit.rejectSuggestion(SUGGESTION_ID, CLOSING_TEXT, SuggestionLog.builder().build());
}
@Test
@@ -203,82 +200,51 @@ public class SuggestionServiceBeanTest {
@Test(expected = ChannelNotFoundException.class)
public void testRejectSuggestionInNoTextChannel() {
Long suggestionId = setupForNoTextChannel();
testUnit.rejectSuggestion(suggestionId, CLOSING_TEXT, SuggestionLog.builder().build());
setupForNoTextChannel();
testUnit.rejectSuggestion(SUGGESTION_ID, CLOSING_TEXT, SuggestionLog.builder().build());
}
@Test(expected = GuildNotFoundException.class)
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;
private void executeAcceptWithMember(Member actualMember) {
Long messageId = 7L;
SuggestionLog logParameter = SuggestionLog.builder().build();
Suggestion suggestionToAccept = setupClosing(suggesterMember, suggestionId, channelId, messageId);
Suggestion suggestionToAccept = setupClosing(messageId);
RestAction<Message> retrievalAction = Mockito.mock(RestAction.class);
when(textChannel.retrieveMessageById(messageId)).thenReturn(retrievalAction);
Message suggestionMessage = Mockito.mock(Message.class);
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);
}
private void executeRejectWithMember(Member suggesterMember) {
Long suggestionId = 5L;
Long channelId = 5L;
private void executeRejectWithMember(Member actualMember) {
Long messageId = 7L;
SuggestionLog logParameter = SuggestionLog.builder().build();
Suggestion suggestionToAccept = setupClosing(suggesterMember, suggestionId, channelId, messageId);
Suggestion suggestionToAccept = setupClosing(messageId);
RestAction<Message> retrievalAction = Mockito.mock(RestAction.class);
when(textChannel.retrieveMessageById(messageId)).thenReturn(retrievalAction);
Message suggestionMessage = Mockito.mock(Message.class);
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);
}
private Suggestion setupClosing(Member suggesterMember, Long suggestionId, Long channelId, Long messageId) {
AServer server = MockUtils.getServer();
AChannel channel = MockUtils.getTextChannel(server, channelId);
AUserInAServer suggester = MockUtils.getUserObject(4L, server);
private Suggestion setupClosing(Long messageId) {
Suggestion suggestionToAccept = Suggestion
.builder()
.channel(channel)
.server(server)
.id(suggestionId)
.id(SUGGESTION_ID)
.suggester(suggester)
.messageId(messageId)
.build();
when(suggestionManagementService.getSuggestion(suggestionId)).thenReturn(Optional.of(suggestionToAccept));
when(botService.getGuildById(server.getId())).thenReturn(Optional.of(guild));
when(guild.getTextChannelById(channelId)).thenReturn(textChannel);
when(guild.getMemberById(suggester.getUserReference().getId())).thenReturn(suggesterMember);
when(server.getId()).thenReturn(SERVER_ID);
when(channel.getId()).thenReturn(CHANNEL_ID);
when(suggester.getUserReference()).thenReturn(suggesterUser);
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;
}
}

View File

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

View File

@@ -18,6 +18,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@@ -108,11 +109,11 @@ public class StarboardPostReactorManagementServiceBeanTest {
StarStatsUser user1 = Mockito.mock(StarStatsUser.class);
StarStatsUser user2 = Mockito.mock(StarStatsUser.class);
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(user1, starStatsUsers.get(0));
Assert.assertEquals(user1, starStatsUsers.get(0).join());
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 user2 = Mockito.mock(StarStatsUser.class);
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(user1, starStatsUsers.get(0));
Assert.assertEquals(user1, starStatsUsers.get(0).join());
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);
List<StarStatsUserResult> results = Arrays.asList(result1, result2);
when(repository.findTopStarGiverInServer(serverId, amountToRetrieve)).thenReturn(results);
List<StarStatsUser> statsUser = new ArrayList<>();
statsUser.add(user1);
List<CompletableFuture<StarStatsUser>> statsUser = new ArrayList<>();
statsUser.add(CompletableFuture.completedFuture(user1));
if (amountToRetrieve > 1) {
statsUser.add(user2);
statsUser.add(CompletableFuture.completedFuture(user2));
}
when(converter.convertToStarStatsUser(results, serverId)).thenReturn(statsUser);
}
@@ -147,10 +148,10 @@ public class StarboardPostReactorManagementServiceBeanTest {
StarStatsUserResult result2 = Mockito.mock(StarStatsUserResult.class);
List<StarStatsUserResult> results = Arrays.asList(result1, result2);
when(repository.retrieveTopStarReceiverInServer(serverId, amountToRetrieve)).thenReturn(results);
List<StarStatsUser> statsUser = new ArrayList<>();
statsUser.add(user1);
List<CompletableFuture<StarStatsUser>> statsUser = new ArrayList<>();
statsUser.add(CompletableFuture.completedFuture(user1));
if (amountToRetrieve > 1) {
statsUser.add(user2);
statsUser.add(CompletableFuture.completedFuture(user2));
}
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 java.util.List;
import java.util.concurrent.CompletableFuture;
public interface MessageEmbedService {
List<MessageEmbedLink> getLinksInMessage(String message);
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 java.util.List;
import java.util.concurrent.CompletableFuture;
public interface StarboardService {
void createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser);
void updateStarboardPost(StarboardPost post, CachedMessage message, List<AUserInAServer> userExceptAuthor);
CompletableFuture<Void> createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser);
CompletableFuture<Void> updateStarboardPost(StarboardPost post, CachedMessage message, List<AUserInAServer> userExceptAuthor);
void deleteStarboardMessagePost(StarboardPost message);
StarStatsModel retrieveStarStats(Long serverId);
CompletableFuture<StarStatsModel> retrieveStarStats(Long serverId);
StarStatsPost fromStarboardPost(StarboardPost starboardPost);
}

View File

@@ -10,7 +10,8 @@ import java.util.Optional;
public interface ReminderManagementService {
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);
Reminder saveReminder(Reminder reminder);
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 java.util.List;
import java.util.concurrent.CompletableFuture;
public interface StarboardPostReactorManagementService {
void addReactor(StarboardPost post, AUserInAServer user);
void removeReactor(StarboardPost post, AUserInAServer user);
void removeReactors(StarboardPost post);
Integer getStarCount(Long serverId);
List<StarStatsUser> retrieveTopStarGiver(Long serverId, Integer count);
List<StarStatsUser> retrieveTopStarReceiver(Long serverId, Integer count);
List<CompletableFuture<StarStatsUser>> retrieveTopStarGiver(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.management.*;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
@@ -30,6 +31,7 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static java.util.Comparator.comparing;
@@ -106,27 +108,24 @@ public class CommandReceivedHandler extends ListenerAdapter {
tryToExecuteFoundCommand(event, commandContextBuilder, foundCommand, unParsedParameter);
} catch (Exception e) {
log.error("Exception when executing command.", e);
CommandResult commandResult = CommandResult.fromError(e.getMessage(), e);
CommandContext commandContext = commandContextBuilder.build();
self.executePostCommandListener(null, commandContext, commandResult);
reportException(commandContextBuilder, null, e, "Exception when executing command.");
}
}
private void tryToExecuteFoundCommand(@Nonnull MessageReceivedEvent event, CommandContext.CommandContextBuilder commandContextBuilder, Command foundCommand, UnParsedCommandParameter unParsedParameter) {
try {
Parameters parsedParameters = getParsedParameters(unParsedParameter, foundCommand, event.getMessage());
CompletableFuture<Parameters> parsingFuture = getParsedParameters(unParsedParameter, foundCommand, event.getMessage());
parsingFuture.thenAccept(parsedParameters -> {
validateCommandParameters(parsedParameters, foundCommand);
CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build();
ConditionResult conditionResult = commandService.isCommandExecutable(foundCommand, commandContext);
CommandResult commandResult = null;
if(conditionResult.isResult()) {
if(foundCommand.getConfiguration().isAsync()) {
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());
if(foundCommand.getConfiguration().isAsync()) {
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.executeAsync(commandContext).thenAccept(result ->
executePostCommandListener(foundCommand, commandContext, result)
foundCommand.executeAsync(commandContext).thenAccept(result ->
executePostCommandListener(foundCommand, commandContext, result)
).exceptionally(throwable -> {
log.error("Asynchronous command {} failed.", foundCommand.getConfiguration().getName(), throwable);
UserInitiatedServerContext rebuildUserContext = buildTemplateParameter(event);
@@ -155,12 +154,21 @@ public class CommandReceivedHandler extends ListenerAdapter {
if(commandResult != null) {
self.executePostCommandListener(foundCommand, commandContext, commandResult);
}
} catch (Exception e) {
log.error("Exception when executing command.", e);
CommandResult commandResult = CommandResult.fromError(e.getMessage(), e);
CommandContext commandContext = commandContextBuilder.build();
self.executePostCommandListener(foundCommand, commandContext, commandResult);
}
}).exceptionally(throwable -> {
reportException(commandContextBuilder, foundCommand, throwable, "Exception when executing command.");
return null;
});
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) {
@@ -210,10 +218,10 @@ public class CommandReceivedHandler extends ListenerAdapter {
.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<>();
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());
Iterator<TextChannel> channelIterator = message.getMentionedChannels().iterator();
@@ -224,6 +232,7 @@ public class CommandReceivedHandler extends ListenerAdapter {
CommandParameterIterators iterators = new CommandParameterIterators(channelIterator, emoteIterator, memberIterator, roleIterator);
boolean reminderActive = false;
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++) {
if(i < command.getConfiguration().getParameters().size() && !param.isRemainder()) {
param = command.getConfiguration().getParameters().get(i);
@@ -236,7 +245,13 @@ public class CommandReceivedHandler extends ListenerAdapter {
try {
if(handler.handles(param.getType())) {
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;
}
} 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 org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
@Component
@@ -15,14 +16,19 @@ public class MemberParameterHandler implements CommandParameterHandler {
}
@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);
if(matcher.matches()) {
return iterators.getMemberIterator().next();
return CompletableFuture.completedFuture(iterators.getMemberIterator().next());
} else {
// TODO add handling for names
long emoteId = Long.parseLong(input);
return context.getGuild().getMemberById(emoteId);
long userId = Long.parseLong(input);
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()))
.aUserInAServer(userInServerManagementService.loadUser(cachedMessage.getServerId(), cachedMessage.getAuthorId()))
.build();
GuildChannelMember authorMember = GuildChannelMember
.builder()
.guild(botService.getGuildByIdNullable(cachedMessage.getServerId()))
.textChannel(botService.getTextChannelFromServerOptional(cachedMessage.getServerId(), cachedMessage.getChannelId()).orElseThrow(() -> new ChannelNotFoundException(cachedMessage.getChannelId())))
.member(botService.getMemberInServer(cachedMessage.getServerId(), cachedMessage.getAuthorId()))
.build();
listener.forEach(messageDeletedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageDeletedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, cachedMessage.getServerId())) {
return;
}
try {
self.executeIndividualMessageDeletedListener(cachedMessage, authorUser, authorMember, messageDeletedListener);
} catch (AbstractoRunTimeException e) {
log.error("Listener {} failed with exception:", messageDeletedListener.getClass().getName(), e);
}
botService.getMemberInServerAsync(cachedMessage.getServerId(), cachedMessage.getAuthorId()).thenAccept(member -> {
GuildChannelMember authorMember = GuildChannelMember
.builder()
.guild(botService.getGuildById(cachedMessage.getServerId()))
.textChannel(botService.getTextChannelFromServerOptional(cachedMessage.getServerId(), cachedMessage.getChannelId()).orElseThrow(() -> new ChannelNotFoundException(cachedMessage.getChannelId())))
.member(botService.getMemberInServer(cachedMessage.getServerId(), cachedMessage.getAuthorId()))
.build();
listener.forEach(messageDeletedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageDeletedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, cachedMessage.getServerId())) {
return;
}
try {
self.executeIndividualMessageDeletedListener(cachedMessage, authorUser, authorMember, messageDeletedListener);
} catch (AbstractoRunTimeException 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)

View File

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

View File

@@ -252,7 +252,7 @@ public class ChannelServiceBean implements ChannelService {
@Override
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()) {
Guild guild = guildById.get();
Category categoryById = guild.getCategoryById(categoryId);

View File

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

View File

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

View File

@@ -32,7 +32,7 @@ public class RoleServiceBean implements RoleService {
@Override
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()) {
addRoleToUser(guildById.get(), role, aUserInAServer.getUserReference().getId());
} else {
@@ -42,7 +42,7 @@ public class RoleServiceBean implements RoleService {
@Override
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()) {
return addRoleToUserFuture(guildById.get(), role, aUserInAServer.getUserReference().getId());
} 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
public void addRoleToMember(Member member, ARole role) {
Guild guild = member.getGuild();
addRoleToUser(guild, role, member.getIdLong());
addRoleToMemberFuture(member, role.getId());
}
@Override
public CompletableFuture<Void> addRoleToMemberFuture(Member member, ARole role) {
Guild guild = member.getGuild();
return addRoleToUserFuture(guild, role, member.getIdLong());
return addRoleToMemberFuture(member, role.getId());
}
@Override
public void removeRoleFromMember(Member member, ARole role) {
Guild guild = member.getGuild();
removeRoleFromUser(guild, role, member.getIdLong());
removeRoleFromMemberAsync(member, role.getId());
}
@Override
public CompletableFuture<Void> removeRoleFromMemberFuture(Member member, ARole role) {
public CompletableFuture<Void> removeRoleFromMemberAsync(Member member, ARole role) {
Guild guild = member.getGuild();
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) {
if(role.getDeleted()) {
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
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()) {
removeRoleFromUser(guildById.get(), role, aUserInAServer.getUserReference().getId());
} else {
@@ -124,7 +140,7 @@ public class RoleServiceBean implements RoleService {
@Override
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()) {
return removeRoleFromUserFuture(guildById.get(), role, aUserInAServer.getUserReference().getId());
} else {
@@ -150,7 +166,7 @@ public class RoleServiceBean implements RoleService {
log.warn("Trying to load role {} which is marked as deleted.", role.getId());
throw new RoleDeletedException(role);
}
Optional<Guild> guildById = botService.getGuildById(role.getServer().getId());
Optional<Guild> guildById = botService.getGuildByIdOptional(role.getServer().getId());
if(guildById.isPresent()) {
log.trace("Loading role {} from server {}.", role.getId(), role.getServer().getId());
return guildById.get().getRoleById(role.getId());
@@ -171,12 +187,17 @@ public class RoleServiceBean implements RoleService {
@Override
public boolean memberHasRole(Member member, Role role) {
return member.getRoles().stream().anyMatch(role1 -> role1.getIdLong() == role.getIdLong());
return memberHasRole(member, role.getIdLong());
}
@Override
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

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.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -141,8 +140,12 @@ public class SetupServiceBean implements SetupService {
@Transactional
public void showExceptionMessage(Throwable throwable, AServerChannelUserId aServerChannelUserId) {
Optional<TextChannel> channelOptional = botService.getTextChannelFromServerOptional(aServerChannelUserId.getGuildId(), aServerChannelUserId.getChannelId());
Member member = botService.getMemberInServer(aServerChannelUserId.getGuildId(), aServerChannelUserId.getUserId());
channelOptional.ifPresent(textChannel -> exceptionService.reportExceptionToChannel(throwable, textChannel, member));
botService.getMemberInServerAsync(aServerChannelUserId.getGuildId(), aServerChannelUserId.getUserId()).thenAccept(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

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;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.requests.RestAction;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.when;
@@ -44,25 +47,27 @@ public class MemberParameterHandlerTest {
}
@Test
@SuppressWarnings("unchecked")
public void testProperMemberMention() {
oneMemberInIterator();
String input = getUserMention();
Member parsed = (Member) testUnit.handle(input, iterators, Member.class, null);
Assert.assertEquals(parsed, member);
CompletableFuture<Member> parsed = (CompletableFuture) testUnit.handleAsync(input, iterators, Member.class, null);
Assert.assertEquals(parsed.join(), member);
}
@Test
@SuppressWarnings("unchecked")
public void testMemberById() {
setupMessage();
String input = USER_ID.toString();
Member parsed = (Member) testUnit.handle(input, null, Member.class, message);
Assert.assertEquals(parsed, member);
CompletableFuture<Member> parsed = (CompletableFuture) testUnit.handleAsync(input, null, Member.class, message);
Assert.assertEquals(parsed.join(), member);
}
@Test(expected = NumberFormatException.class)
public void testInvalidMemberMention() {
String input = "test";
testUnit.handle(input, null, Member.class, null);
testUnit.handleAsync(input, null, Member.class, null);
}
private String getUserMention() {
@@ -76,7 +81,9 @@ public class MemberParameterHandlerTest {
private void setupMessage() {
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) {
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.");
}
SingleNumberValidatorParam param = (SingleNumberValidatorParam) getParameters().get(0);
Long longValue = (Long) value;
return longValue >= param.getNumber();
if(isLong) {
Long longValue = (Long) value;
return longValue >= param.getNumber();
} else {
Integer integerValue = (Integer) value;
return integerValue >= param.getNumber();
}
}
@Override

View File

@@ -3,8 +3,12 @@ package dev.sheldan.abstracto.core.command.handler;
import net.dv8tion.jda.api.entities.Message;
import java.util.concurrent.CompletableFuture;
public interface CommandParameterHandler {
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();
}

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