diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/ExpScale.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/ExpScale.java index 6b68691c1..5cb07ad87 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/ExpScale.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/ExpScale.java @@ -20,6 +20,7 @@ import java.util.List; public class ExpScale extends AbstractConditionableCommand { public static final String EXP_MULTIPLIER_KEY = "expMultiplier"; + @Autowired private ConfigService configService; diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/LeaderBoardCommand.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/LeaderBoardCommand.java index bbb1e7e00..6bc52d300 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/LeaderBoardCommand.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/LeaderBoardCommand.java @@ -13,7 +13,7 @@ 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.LeaderBoardModel; -import dev.sheldan.abstracto.experience.service.ExperienceTrackerService; +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; @@ -30,7 +30,7 @@ public class LeaderBoardCommand extends AbstractConditionableCommand { public static final String LEADERBOARD_POST_EMBED_TEMPLATE = "leaderboard_post"; @Autowired - private ExperienceTrackerService experienceTrackerService; + private AUserExperienceService userExperienceService; @Autowired private UserExperienceManagementService userExperienceManagementService; @@ -48,12 +48,13 @@ public class LeaderBoardCommand extends AbstractConditionableCommand { @Override public CommandResult execute(CommandContext commandContext) { List parameters = commandContext.getParameters().getParameters(); + // parameter is optional, in case its not present, we default to the 0th page Integer page = parameters.size() > 0 ? (Integer) parameters.get(0) : 0; - LeaderBoard leaderBoard = experienceTrackerService.findLeaderBoardData(commandContext.getUserInitiatedContext().getServer(), page); + LeaderBoard leaderBoard = userExperienceService.findLeaderBoardData(commandContext.getUserInitiatedContext().getServer(), page); LeaderBoardModel leaderBoardModel = (LeaderBoardModel) ContextConverter.fromCommandContext(commandContext, LeaderBoardModel.class); leaderBoardModel.setUserExperiences(converter.fromLeaderBoard(leaderBoard)); - LeaderBoardEntry userRank = experienceTrackerService.getRankOfUserInServer(commandContext.getUserInitiatedContext().getAUserInAServer()); + LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(commandContext.getUserInitiatedContext().getAUserInAServer()); leaderBoardModel.setUserExecuting(converter.fromLeaderBoardEntry(userRank)); MessageToSend messageToSend = templateService.renderEmbedTemplate(LEADERBOARD_POST_EMBED_TEMPLATE, leaderBoardModel); channelService.sendMessageToEndInTextChannel(messageToSend, commandContext.getChannel()); diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/Rank.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/Rank.java index 0c0e36df0..8bd70b0c5 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/Rank.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/Rank.java @@ -13,7 +13,7 @@ import dev.sheldan.abstracto.experience.models.LeaderBoardEntry; import dev.sheldan.abstracto.experience.models.database.AUserExperience; import dev.sheldan.abstracto.experience.models.templates.RankModel; import dev.sheldan.abstracto.experience.service.ExperienceLevelService; -import dev.sheldan.abstracto.experience.service.ExperienceTrackerService; +import dev.sheldan.abstracto.experience.service.AUserExperienceService; import dev.sheldan.abstracto.templating.model.MessageToSend; import dev.sheldan.abstracto.templating.service.TemplateService; import org.springframework.beans.factory.annotation.Autowired; @@ -25,6 +25,7 @@ import java.util.List; @Component public class Rank extends AbstractConditionableCommand { + public static final String RANK_POST_EMBED_TEMPLATE = "rank_post"; @Autowired private LeaderBoardModelConverter converter; @@ -32,7 +33,7 @@ public class Rank extends AbstractConditionableCommand { private TemplateService templateService; @Autowired - private ExperienceTrackerService experienceTrackerService; + private AUserExperienceService userExperienceService; @Autowired private ExperienceLevelService experienceLevelService; @@ -40,11 +41,11 @@ public class Rank extends AbstractConditionableCommand { @Override public CommandResult execute(CommandContext commandContext) { RankModel rankModel = (RankModel) ContextConverter.fromCommandContext(commandContext, RankModel.class); - LeaderBoardEntry userRank = experienceTrackerService.getRankOfUserInServer(commandContext.getUserInitiatedContext().getAUserInAServer()); + LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(commandContext.getUserInitiatedContext().getAUserInAServer()); rankModel.setRankUser(converter.fromLeaderBoardEntry(userRank)); AUserExperience experienceObj = userRank.getExperience(); rankModel.setExperienceToNextLevel(experienceLevelService.calculateExperienceToNextLevel(experienceObj.getCurrentLevel().getLevel(), experienceObj.getExperience())); - MessageToSend messageToSend = templateService.renderEmbedTemplate("rank_post", rankModel); + MessageToSend messageToSend = templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel); channelService.sendMessageToEndInTextChannel(messageToSend, commandContext.getChannel()); return CommandResult.fromSuccess(); diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/SetExpRole.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/SetExpRole.java index 86fc3ffcc..59e89422f 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/SetExpRole.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/SetExpRole.java @@ -41,11 +41,11 @@ public class SetExpRole extends AbstractConditionableCommand { Long roleId = (Long) commandContext.getParameters().getParameters().get(1); ARole role = roleManagementService.findRole(roleId); AServer server = commandContext.getUserInitiatedContext().getServer(); - if(!roleService.isRoleInServer(server, role)) { + if(!roleService.isRoleInServer(role)) { throw new RoleException("Role not found."); } log.info("Setting role {} to be used for level {} on server {}", roleId, level, server.getId()); - experienceRoleService.setRoleToLevel(role, level, server); + experienceRoleService.setRoleToLevel(role, level, server, commandContext.getUserInitiatedContext().getChannel()); return CommandResult.fromSuccess(); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/SyncRoles.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/SyncRoles.java index 58758f937..85cad5aa7 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/SyncRoles.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/SyncRoles.java @@ -8,7 +8,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandContext; import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.experience.config.ExperienceFeatures; -import dev.sheldan.abstracto.experience.service.ExperienceTrackerService; +import dev.sheldan.abstracto.experience.service.AUserExperienceService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -21,13 +21,13 @@ import java.util.List; public class SyncRoles extends AbstractConditionableCommand { @Autowired - private ExperienceTrackerService experienceTrackerService; + private AUserExperienceService userExperienceService; @Override public CommandResult execute(CommandContext commandContext) { AServer server = commandContext.getUserInitiatedContext().getServer(); log.info("Synchronizing roles on server {}", server.getId()); - experienceTrackerService.syncUserRolesWithFeedback(server, commandContext.getUserInitiatedContext().getChannel()); + userExperienceService.syncUserRolesWithFeedback(server, commandContext.getUserInitiatedContext().getChannel()); return CommandResult.fromSuccess(); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/UnSetExpRole.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/UnSetExpRole.java index 2dbd13da8..b61af50b0 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/UnSetExpRole.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/UnSetExpRole.java @@ -29,7 +29,9 @@ public class UnSetExpRole extends AbstractConditionableCommand { public CommandResult execute(CommandContext commandContext) { Long roleId = (Long) commandContext.getParameters().getParameters().get(0); ARole role = roleManagementService.findRole(roleId); - experienceRoleService.unsetRole(role, commandContext.getUserInitiatedContext().getServer()); + // do not check for the existence of the role, because if the role was deleted, users should be able + // to get rid of it in the configuration + experienceRoleService.unsetRole(role, commandContext.getUserInitiatedContext().getServer(), commandContext.getUserInitiatedContext().getChannel()); return CommandResult.fromSuccess(); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfig.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfig.java index 1e7ba3b3f..c5ab4ba29 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfig.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfig.java @@ -10,8 +10,23 @@ import org.springframework.stereotype.Component; @Setter @ConfigurationProperties(prefix = "abstracto.experience") public class ExperienceConfig { + /** + * The default min experience range from the properties file. This is used, when the bot joins a new guild. + */ private Integer minExp; + + /** + * The default max experience range from the properties file. This is used, when the bot joins a new guild. + */ private Integer maxExp; + + /** + * The default multiplier from the properties file. This is used, when the bot joins a new guild. + */ private Integer expMultiplier; + + /** + * The defaul maxLevel from the properties file. This configuration applies globally, as the amount of levels does not depend on the server. + */ private Integer maxLvl; } \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfigListener.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfigListener.java index 2f83c3731..54a0d24c4 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfigListener.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfigListener.java @@ -7,6 +7,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +/** + * Listener responsible to configure the required experience configurations in case the bot joins a new server. + */ @Component @Slf4j public class ExperienceConfigListener implements ServerConfigListener { diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceLevelLoader.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceLevelLoader.java index d7e232efc..8dd2fdea9 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceLevelLoader.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceLevelLoader.java @@ -7,6 +7,10 @@ import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; +/** + * Component responsible to create the amount of {@link dev.sheldan.abstracto.experience.models.database.AExperienceLevel} + * configured in the {@link ExperienceConfig}. This is executed when the application starts up. + */ @Component @Slf4j public class ExperienceLevelLoader { @@ -20,12 +24,7 @@ public class ExperienceLevelLoader { @EventListener public void handleContextRefreshEvent(ContextRefreshedEvent ctxStartEvt) { Integer maxLevel = experienceConfig.getMaxLvl(); - Long experience = 0L; log.info("Setting up experience level configuration."); - experienceLevelService.createExperienceLevel(0, 0L); - for (int i = 1; i < maxLevel; i++) { - experience = experience + experienceLevelService.calculateExperienceForLevel(i - 1); - experienceLevelService.createExperienceLevel(i, experience); - } + experienceLevelService.createLevelsUntil(maxLevel); } } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverter.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverter.java index 0888d70fd..345e9970e 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverter.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/converter/LeaderBoardModelConverter.java @@ -12,12 +12,22 @@ import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; +/** + * Converter used to conver from {@link LeaderBoard} to a list of {@link LeaderBoardEntryModel} + */ @Component public class LeaderBoardModelConverter { @Autowired private BotService botService; + /** + * Converts the complete {@link LeaderBoard} into a list of {@link LeaderBoardEntryModel} which contain additional + * information available for rendering the leaderboard ({@link Member} reference) + * @param leaderBoard The {@link LeaderBoard} object to be converted + * @return The list of {@link LeaderBoardEntryModel} which contain the fully fledged information provided to the + * leaderboard template + */ public List fromLeaderBoard(LeaderBoard leaderBoard) { List models = new ArrayList<>(); leaderBoard.getEntries().forEach(leaderBoardEntry -> { @@ -27,6 +37,13 @@ public class LeaderBoardModelConverter { return models; } + /** + * Converts the given {@link LeaderBoardEntry} to a {@link LeaderBoardEntryModel}, which provides a reference to the + * {@link Member} object of the given {@link AUserInAServer} for convenience in the template + * @param leaderBoardEntry The {@link LeaderBoardEntry} to be converted + * @return The {@link LeaderBoardEntryModel} accompanied with the {@link Member} reference, might be null, if the + * user left the guild + */ public LeaderBoardEntryModel fromLeaderBoardEntry(LeaderBoardEntry leaderBoardEntry) { AUserInAServer entryUser = leaderBoardEntry.getExperience().getUser(); Member entryMember = botService.getMemberInServer(entryUser.getServerReference().getId(), entryUser.getUserReference().getId()); diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/job/ExperiencePersistingJob.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/job/ExperiencePersistingJob.java index b0b6be72f..02841f8c8 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/job/ExperiencePersistingJob.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/job/ExperiencePersistingJob.java @@ -1,7 +1,7 @@ package dev.sheldan.abstracto.experience.job; import dev.sheldan.abstracto.core.models.database.AServer; -import dev.sheldan.abstracto.experience.service.ExperienceTrackerService; +import dev.sheldan.abstracto.experience.service.AUserExperienceService; import lombok.extern.slf4j.Slf4j; import org.quartz.DisallowConcurrentExecution; import org.quartz.JobExecutionContext; @@ -16,6 +16,11 @@ import java.util.HashMap; import java.util.List; +/** + * This {@link QuartzJobBean} is executed regularly and calls the the {@link AUserExperienceService} + * store the tracked experience from runtime. This job also cleans up the already processed entries in the runtime + * experience. + */ @Slf4j @DisallowConcurrentExecution @Component @@ -23,16 +28,16 @@ import java.util.List; public class ExperiencePersistingJob extends QuartzJobBean { @Autowired - private ExperienceTrackerService experienceTrackerService; + private AUserExperienceService userExperienceService; @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { - HashMap> runtimeExperience = experienceTrackerService.getRuntimeExperience(); + HashMap> runtimeExperience = userExperienceService.getRuntimeExperience(); log.info("Running experience persisting job."); Long pastMinute = (Instant.now().getEpochSecond() / 60) - 1; if(runtimeExperience.containsKey(pastMinute)) { log.info("Found experience to persist."); - experienceTrackerService.handleExperienceGain(runtimeExperience.get(pastMinute)); + userExperienceService.handleExperienceGain(runtimeExperience.get(pastMinute)); runtimeExperience.remove(pastMinute); } } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/ExperienceTrackerListener.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/ExperienceTrackerListener.java index e5c9be85b..f512612f3 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/ExperienceTrackerListener.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/ExperienceTrackerListener.java @@ -4,18 +4,22 @@ import dev.sheldan.abstracto.core.listener.MessageReceivedListener; import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.service.management.UserManagementService; import dev.sheldan.abstracto.experience.config.ExperienceFeatures; -import dev.sheldan.abstracto.experience.service.ExperienceTrackerService; +import dev.sheldan.abstracto.experience.service.AUserExperienceService; import net.dv8tion.jda.api.entities.Message; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +/** + * This {@link MessageReceivedListener} is responsible to execute the {@link AUserExperienceService} in order to track + * that a certain user has send a message, and experience should be awarded. + */ @Component public class ExperienceTrackerListener implements MessageReceivedListener { @Autowired - private ExperienceTrackerService experienceTrackerService; + private AUserExperienceService userExperienceService; @Autowired private UserManagementService userManagementService; @@ -24,7 +28,7 @@ public class ExperienceTrackerListener implements MessageReceivedListener { @Transactional(propagation = Propagation.REQUIRES_NEW) public void execute(Message message) { AUserInAServer cause = userManagementService.loadUser(message.getMember()); - experienceTrackerService.addExperience(cause); + userExperienceService.addExperience(cause); } @Override diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/JoiningUserRoleListener.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/JoiningUserRoleListener.java index e16a04846..6f21a3e81 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/JoiningUserRoleListener.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/JoiningUserRoleListener.java @@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.service.management.UserManagementService; import dev.sheldan.abstracto.experience.config.ExperienceFeatures; import dev.sheldan.abstracto.experience.models.database.AUserExperience; -import dev.sheldan.abstracto.experience.service.ExperienceTrackerService; +import dev.sheldan.abstracto.experience.service.AUserExperienceService; import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Guild; @@ -13,6 +13,10 @@ import net.dv8tion.jda.api.entities.Member; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +/** + * If a user joins, this {@link JoinListener} retrieves the previously stored {@link AUserExperience} and gives the + * {@link Member} the necessary {@link net.dv8tion.jda.api.entities.Role} according to the current configuration + */ @Component @Slf4j public class JoiningUserRoleListener implements JoinListener { @@ -24,14 +28,14 @@ public class JoiningUserRoleListener implements JoinListener { private UserManagementService userManagementService; @Autowired - private ExperienceTrackerService experienceTrackerService; + private AUserExperienceService userExperienceService; @Override public void execute(Member member, Guild guild, AUserInAServer aUserInAServer) { AUserExperience userExperience = userExperienceManagementService.findUserInServer(aUserInAServer); if(userExperience != null) { log.info("User {} joined {} with previous experience. Setting up experience role again (if necessary).", member.getUser().getIdLong(), guild.getIdLong()); - experienceTrackerService.syncForSingleUser(userExperience); + userExperienceService.syncForSingleUser(userExperience); } } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceLevelRepository.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceLevelRepository.java index 7e302ae3e..62fd94310 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceLevelRepository.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceLevelRepository.java @@ -4,7 +4,9 @@ import dev.sheldan.abstracto.experience.models.database.AExperienceLevel; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +/** + * Repository to manage the access to the table managed by {@link AExperienceLevel} + */ @Repository public interface ExperienceLevelRepository extends JpaRepository { - AExperienceLevel findTopByExperienceNeededGreaterThanOrderByExperienceNeededAsc(Long experience); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceRoleRepository.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceRoleRepository.java index 6f55462c9..937a116b2 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceRoleRepository.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceRoleRepository.java @@ -9,9 +9,32 @@ import org.springframework.stereotype.Repository; import java.util.List; +/** + * Repository to manage the access to the table managed by {@link AExperienceRole} + */ @Repository public interface ExperienceRoleRepository extends JpaRepository { + /** + * Finds the {@link AExperienceRole} of the given {@link AServer} and {@link ARole} + * @param server The {@link AServer} to retrieve the {@link AExperienceRole} for + * @param role The {@link ARole} to filter for + * @return The {@link AExperienceRole} found or null if the query did not return any results + */ AExperienceRole findByRoleServerAndRole(AServer server, ARole role); + + /** + * Finds a list of {@link AExperienceRole} (if there are multiple ones, because of misconfiguration) of the given + * {@link AExperienceLevel} and {@link AServer} + * @param level The {@link AExperienceLevel} to search for + * @param server The {@link AServer} to search for + * @return The list of {@link AExperienceRole} found by the given parameters + */ List findByLevelAndRoleServer(AExperienceLevel level, AServer server); + + /** + * Finds all {@link AExperienceRole} of the given {@link AServer} + * @param server The {@link AServer} to load the list of {@link AExperienceRole} for + * @return A list of {@link AExperienceRole} configured to be used on the given {@link AServer} + */ List findByRoleServer(AServer server); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/UserExperienceRepository.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/UserExperienceRepository.java index 957777980..187f293b7 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/UserExperienceRepository.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/UserExperienceRepository.java @@ -1,7 +1,7 @@ package dev.sheldan.abstracto.experience.repository; import dev.sheldan.abstracto.core.models.database.AServer; -import dev.sheldan.abstracto.experience.LeaderBoardEntryResult; +import dev.sheldan.abstracto.experience.models.database.LeaderBoardEntryResult; import dev.sheldan.abstracto.experience.models.database.AUserExperience; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -11,11 +11,34 @@ import org.springframework.stereotype.Repository; import java.util.List; +/** + * Repository to manage the access to the table managed by {@link AUserExperience} + */ @Repository public interface UserExperienceRepository extends JpaRepository { + + /** + * Finds all {@link AUserExperience} of the given {@link AServer} + * @param server The {@link AServer} to retriev ethe {@link AUserExperience} for + * @return A complete list of {@link AUserExperience} of the given {@link AServer} + */ List findByUser_ServerReference(AServer server); + + /** + * Retrieves the {@link AUserExperience} ordered by experience, and applies the {@link Pageable} to only filter out certain pages. + * @param server The {@link AServer} to retrieve the {@link AUserExperience} information for + * @param pageable A {@link Pageable} object to indicate the pages which should be retrieved, pagesize is 10 + * @return A list of {@link AUserExperience} of the given {@link AServer} ordered by the experience of the users, paginated by the given + * configuration + */ List findTop10ByUser_ServerReferenceOrderByExperienceDesc(AServer server, Pageable pageable); + /** + * This returns the {@link LeaderBoardEntryResult} object containing the information about the rank of a user in a server. + * @param id The {@link dev.sheldan.abstracto.core.models.database.AUserInAServer} id to search for + * @return the {@link LeaderBoardEntryResult} of this {@link dev.sheldan.abstracto.core.models.database.AUserInAServer} + * containing rank and experience information + */ @Query(value = "WITH user_experience_ranked AS" + "( " + " SELECT id, experience, experience_role_id, level_id, message_count, ROW_NUMBER() OVER ( ORDER BY experience DESC ) " + diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java similarity index 55% rename from abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerServiceBean.java rename to abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java index 25bdf0235..bf9d32683 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerServiceBean.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java @@ -3,10 +3,11 @@ package dev.sheldan.abstracto.experience.service; 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.service.BotService; import dev.sheldan.abstracto.core.service.ConfigService; import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.RoleService; -import dev.sheldan.abstracto.experience.LeaderBoardEntryResult; +import dev.sheldan.abstracto.experience.models.database.LeaderBoardEntryResult; import dev.sheldan.abstracto.experience.models.LeaderBoard; import dev.sheldan.abstracto.experience.models.LeaderBoardEntry; import dev.sheldan.abstracto.experience.models.database.AExperienceLevel; @@ -19,6 +20,7 @@ import dev.sheldan.abstracto.experience.service.management.UserExperienceManagem import dev.sheldan.abstracto.templating.model.MessageToSend; 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.Message; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -27,10 +29,11 @@ import org.springframework.transaction.annotation.Transactional; import java.time.Instant; import java.util.*; import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; @Component @Slf4j -public class ExperienceTrackerServiceBean implements ExperienceTrackerService { +public class AUserExperienceServiceBean implements AUserExperienceService { private HashMap> runtimeExperience = new HashMap<>(); @@ -40,6 +43,9 @@ public class ExperienceTrackerServiceBean implements ExperienceTrackerService { @Autowired private ExperienceLevelService experienceLevelService; + @Autowired + private ExperienceRoleService experienceRoleService; + @Autowired private ExperienceLevelManagementService experienceLevelManagementService; @@ -58,6 +64,13 @@ public class ExperienceTrackerServiceBean implements ExperienceTrackerService { @Autowired private TemplateService templateService; + @Autowired + private BotService botService; + + /** + * Creates the user in the runtime experience, if the user was not in yet. Also creates an entry for the minute, if necessary. + * @param userInAServer The {@link AUserInAServer} to be added to the list of users gaining experience + */ @Override public void addExperience(AUserInAServer userInAServer) { log.trace("Adding experience for user {} in server {}", userInAServer.getUserReference().getId(), userInAServer.getServerReference().getId()); @@ -85,11 +98,17 @@ public class ExperienceTrackerServiceBean implements ExperienceTrackerService { return runtimeExperience; } + /** + * Calculates the level of the given {@link AUserExperience} accoring to the given {@link AExperienceLevel} list + * @param experience The {@link AUserExperience} to calculate the level for + * @param levels The list of {@link AExperienceLevel} representing the level configuration + * @return The appropriate level according to the level config + */ @Override - public Integer calculateLevel(AUserExperience userInAServer, List levels) { + public Integer calculateLevel(AUserExperience experience, List levels) { AExperienceLevel lastLevel = levels.get(0); for (AExperienceLevel level : levels) { - if(level.getExperienceNeeded() >= userInAServer.getExperience()) { + if(level.getExperienceNeeded() >= experience.getExperience()) { return lastLevel.getLevel(); } else { lastLevel = level; @@ -99,34 +118,26 @@ public class ExperienceTrackerServiceBean implements ExperienceTrackerService { } @Override - public AExperienceRole calculateRole(AUserExperience userInAServer, List roles) { - if(roles.size() == 0) { - return null; - } - AExperienceRole lastRole = null; - for (AExperienceRole experienceRole : roles) { - if(userInAServer.getCurrentLevel().getLevel() >= experienceRole.getLevel().getLevel()) { - lastRole = experienceRole; - } else { - return lastRole; - } - } - return lastRole; - } - - @Override - public void increaseExpForUser(AUserExperience userInAServer, Long experience, List levels) { - AUserInAServer user = userInAServer.getUser(); - log.trace("Increasing experience for user {} in server {} by {}.", user.getUserReference().getId(), user.getServerReference().getId(), experience); - userInAServer.setExperience(userInAServer.getExperience() + experience); - Integer correctLevel = calculateLevel(userInAServer, levels); - Integer currentLevel = userInAServer.getCurrentLevel() != null ? userInAServer.getCurrentLevel().getLevel() : 0; + public boolean updateUserlevel(AUserExperience userExperience, List levels) { + AUserInAServer user = userExperience.getUser(); + Integer correctLevel = calculateLevel(userExperience, levels); + Integer currentLevel = userExperience.getCurrentLevel() != null ? userExperience.getCurrentLevel().getLevel() : 0; if(!correctLevel.equals(currentLevel)) { log.info("User {} leveled from {} to {}", user.getUserReference().getId(), currentLevel, correctLevel); - userInAServer.setCurrentLevel(experienceLevelManagementService.getLevel(correctLevel)); + userExperience.setCurrentLevel(experienceLevelManagementService.getLevel(correctLevel)); + return true; } + return false; } + /** + * Calculates the actually gained experience for every user in the given servers and adds them to the users. + * This method only actually increases the message count, and calls other methods for experience gain + * and role change. + * Loads the level and role configuration for each server and sorts them for them to be used. + * Only actually updates the role, if the user also changed level. + * @param servers The list of {@link AServer} containing the user which need to gain experience + */ @Transactional @Override public void handleExperienceGain(List servers) { @@ -137,93 +148,133 @@ public class ExperienceTrackerServiceBean implements ExperienceTrackerService { Integer multiplier = configService.getDoubleValue("expMultiplier", serverExp.getId()).intValue(); PrimitiveIterator.OfInt iterator = new Random().ints(serverExp.getUsers().size(), minExp, maxExp + 1).iterator(); List levels = experienceLevelManagementService.getLevelConfig(); - List roles = experienceRoleManagementService.getExperienceRoleForServer(serverExp); levels.sort(Comparator.comparing(AExperienceLevel::getExperienceNeeded)); + List roles = experienceRoleManagementService.getExperienceRolesForServer(serverExp); roles.sort(Comparator.comparing(role -> role.getLevel().getLevel())); serverExp.getUsers().forEach(userInAServer -> { Integer gainedExperience = iterator.next(); gainedExperience *= multiplier; log.trace("Handling {}. The user gains {}", userInAServer.getUserReference().getId(), gainedExperience); - AUserExperience userExperience = userExperienceManagementService.findUserInServer(userInAServer); - increaseExpForUser(userExperience, gainedExperience.longValue(), levels); - userExperience.setMessageCount(userExperience.getMessageCount() + 1); - handleExperienceRoleForUser(userExperience, roles); + AUserExperience aUserExperience = userExperienceManagementService.incrementExpForUser(userInAServer, gainedExperience.longValue(), 1L); + updateUserlevel(aUserExperience, levels); + updateUserRole(aUserExperience, roles); + userExperienceManagementService.saveUser(aUserExperience); }); }); } + /** + * Calculates the appropriate level of the user and changes the role, if the {@link AExperienceLevel} changes. + * This changes the config in the database, and also gives the {@link net.dv8tion.jda.api.entities.Member} the new + * {@link net.dv8tion.jda.api.entities.Role}. If the user does not warrant an {@link AExperienceRole}, + * this method also removes it. The role is only changed, if the user does not have + * @param userExperience The {@link AUserExperience} object to recalculate the {@link AExperienceRole} for + * @param roles The list of {@link AExperienceRole} used as a role configuration + */ @Override - public void handleExperienceRoleForUser(AUserExperience userExperience, List roles) { + public void updateUserRole(AUserExperience userExperience, List roles) { AUserInAServer user = userExperience.getUser(); log.trace("Updating experience role for user {} in server {}", user.getUserReference().getId(), user.getServerReference().getId()); - AExperienceRole role = calculateRole(userExperience, roles); + AExperienceRole role = experienceRoleService.calculateRole(userExperience, roles); + Member member = botService.getMemberInServer(user.getServerReference(), user.getUserReference()); boolean currentlyHasNoExperienceRole = userExperience.getCurrentExperienceRole() == null; if(role == null) { if(!currentlyHasNoExperienceRole){ roleService.removeRoleFromUser(user, userExperience.getCurrentExperienceRole().getRole()); } + userExperience.setCurrentExperienceRole(null); return; } - if(currentlyHasNoExperienceRole || !role.getRole().getId().equals(userExperience.getCurrentExperienceRole().getRole().getId())) { - log.info("User {} in server {} gets a new role {}", user.getUserReference().getId(), user.getServerReference().getId(), role.getRole().getId()); - if(!currentlyHasNoExperienceRole) { - roleService.removeRoleFromUser(user, userExperience.getCurrentExperienceRole().getRole()); + boolean userHasRoleAlready = roleService.memberHasRole(member, role.getRole()); + if(!userHasRoleAlready) { + if(currentlyHasNoExperienceRole || !role.getRole().getId().equals(userExperience.getCurrentExperienceRole().getRole().getId())) { + log.info("User {} in server {} gets a new role {}", user.getUserReference().getId(), user.getServerReference().getId(), role.getRole().getId()); + if(!currentlyHasNoExperienceRole) { + roleService.removeRoleFromUser(user, userExperience.getCurrentExperienceRole().getRole()); + } + roleService.addRoleToUser(user, role.getRole()); } - userExperience.setCurrentExperienceRole(role); - roleService.addRoleToUser(user, userExperience.getCurrentExperienceRole().getRole()); } + userExperience.setCurrentExperienceRole(role); } + /** + * Synchronizes the {@link net.dv8tion.jda.api.entities.Role} of all {@link net.dv8tion.jda.api.entities.Member} in + * the given {@link AServer}. This might take a long time to complete, because there are a lot of role changes. + * @param server The {@link AServer} to update the users for + */ @Override public void syncUserRoles(AServer server) { List aUserExperiences = userExperienceManagementService.loadAllUsers(server); log.info("Found {} users to synchronize", aUserExperiences.size()); - List roles = experienceRoleManagementService.getExperienceRoleForServer(server); + List roles = experienceRoleManagementService.getExperienceRolesForServer(server); for (int i = 0; i < aUserExperiences.size(); i++) { AUserExperience userExperience = aUserExperiences.get(i); log.trace("Synchronizing {} out of {}", i, aUserExperiences.size()); - handleExperienceRoleForUser(userExperience, roles); + updateUserRole(userExperience, roles); } } + /** + * Synchronizes the roles of all the users and provides feedback to the user executing + * @param server The {@link AServer} to update users for + * @param channel The {@link AChannel} in which the {@link dev.sheldan.abstracto.experience.models.templates.UserSyncStatusModel} + */ @Override public void syncUserRolesWithFeedback(AServer server, AChannel channel) { List aUserExperiences = userExperienceManagementService.loadAllUsers(server); log.info("Found {} users to synchronize", aUserExperiences.size()); - List roles = experienceRoleManagementService.getExperienceRoleForServer(server); - UserSyncStatusModel statusModel = UserSyncStatusModel.builder().currentCount(0).totalUserCount(aUserExperiences.size()).build(); - MessageToSend status = templateService.renderEmbedTemplate("status_message", statusModel); - try { - Message statusMessage = messageService.createStatusMessage(status, channel).get(); - int interval = Math.min(aUserExperiences.size() / 10, 100); - for (int i = 0; i < aUserExperiences.size(); i++) { - if((i % interval) == 1) { - log.trace("Updating feedback message with new index {} out of {}", i, aUserExperiences.size()); - UserSyncStatusModel incrementalStatusModel = UserSyncStatusModel.builder().currentCount(i).totalUserCount(aUserExperiences.size()).build(); - status = templateService.renderEmbedTemplate("status_message", incrementalStatusModel); - messageService.updateStatusMessage(channel, statusMessage.getIdLong(), status); - } - log.trace("Synchronizing {} out of {}", i, aUserExperiences.size()); - AUserExperience userExperience = aUserExperiences.get(i); - handleExperienceRoleForUser(userExperience, roles); - } - UserSyncStatusModel incrementalStatusModel = UserSyncStatusModel.builder().currentCount(aUserExperiences.size()).totalUserCount(aUserExperiences.size()).build(); - status = templateService.renderEmbedTemplate("status_message", incrementalStatusModel); - messageService.updateStatusMessage(channel, statusMessage.getIdLong(), status); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } - + List roles = experienceRoleManagementService.getExperienceRolesForServer(server); + executeActionOnUserExperiencesWithFeedBack(aUserExperiences, channel, (AUserExperience experience) -> { + updateUserRole(experience, roles); + }); } + @Override + public void executeActionOnUserExperiencesWithFeedBack(List experiences, AChannel channel, Consumer toExecute) { + MessageToSend status = getUserSyncStatusUpdateModel(0, experiences.size()); + try { + Message statusMessage = messageService.createStatusMessage(status, channel).get(); + int interval = Math.min(Math.max(experiences.size() / 10, 1), 100); + for (int i = 0; i < experiences.size(); i++) { + if((i % interval) == 1) { + log.trace("Updating feedback message with new index {} out of {}", i, experiences.size()); + status = getUserSyncStatusUpdateModel(i, experiences.size()); + messageService.updateStatusMessage(channel, statusMessage.getIdLong(), status); + } + toExecute.accept(experiences.get(i)); + log.trace("Synchronizing {} out of {}", i, experiences.size()); + } + status = getUserSyncStatusUpdateModel(experiences.size(), experiences.size()); + messageService.updateStatusMessage(channel, statusMessage.getIdLong(), status); + } catch (InterruptedException | ExecutionException e) { + log.info("Failed to synchronize users.", e); + } + } + + private MessageToSend getUserSyncStatusUpdateModel(Integer current, Integer total) { + UserSyncStatusModel statusModel = UserSyncStatusModel.builder().currentCount(current).totalUserCount(total).build(); + return templateService.renderEmbedTemplate("user_sync_status_message", statusModel); + } + + /** + * Retrieves the role configuration and executes the method responsible to sync the experience role of the user + * @param userExperience The {@link AUserExperience} to synchronize the role for + */ @Override public void syncForSingleUser(AUserExperience userExperience) { AUserInAServer user = userExperience.getUser(); log.info("Synchronizing for user {} in server {}", user.getUserReference().getId(), user.getServerReference().getId()); - List roles = experienceRoleManagementService.getExperienceRoleForServer(user.getServerReference()); - handleExperienceRoleForUser(userExperience, roles); + List roles = experienceRoleManagementService.getExperienceRolesForServer(user.getServerReference()); + updateUserRole(userExperience, roles); } + /** + * Retrieves the leaderboard data for the given page of the given server + * @param server The {@link AServer} to retrieve the leaderboard for + * @param page The desired page on the leaderboard. The pagesize is 10 + * @return The {@link LeaderBoard} containing all necessary information concerning the leaderboard + */ @Override public LeaderBoard findLeaderBoardData(AServer server, Integer page) { List experiences = userExperienceManagementService.findLeaderboardUsersPaginated(server, page * 10, (page +1) * 10); @@ -235,6 +286,11 @@ public class ExperienceTrackerServiceBean implements ExperienceTrackerService { return LeaderBoard.builder().entries(entries).build(); } + /** + * Builds an {@link AUserExperience} and loads the appropriate rank of the passed {@link AUserInAServer} + * @param userInAServer The {@link AUserInAServer} to retrieve the {@link LeaderBoardEntry} for + * @return The {@link LeaderBoardEntry} representing one single row in the leaderboard + */ @Override public LeaderBoardEntry getRankOfUserInServer(AUserInAServer userInAServer) { log.info("Retrieving rank for {}", userInAServer.getUserReference().getId()); diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelServiceBean.java index 947801408..1ba6c67aa 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelServiceBean.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelServiceBean.java @@ -13,16 +13,34 @@ public class ExperienceLevelServiceBean implements ExperienceLevelService { @Autowired private ExperienceLevelManagementService experienceLevelManagementService; - @Override - public void createExperienceLevel(Integer level, Long experienceNeeded) { + private void createExperienceLevel(Integer level, Long experienceNeeded) { if(!experienceLevelManagementService.levelExists(level)) { log.trace("Creating new experience level {} with experience needed {}.", level, experienceNeeded); experienceLevelManagementService.createExperienceLevel(level, experienceNeeded); } } + /** + * Creates all {@link AExperienceLevel} until (including 0) up until the passed level + * @param level The max level to create {@link dev.sheldan.abstracto.experience.models.database.AExperienceLevel} for + */ @Override - public Long calculateExperienceForLevel(Integer level) { + public void createLevelsUntil(Integer level) { + createExperienceLevel(0, 0L); + long experience = 0L; + for (int i = 1; i < level; i++) { + experience = experience + calculateExperienceForLevel(i - 1); + createExperienceLevel(i, experience); + } + } + + /** + * Calculates the required experience to reach this level. This calculated experience is relative, in the sense + * the returned experience is the increment from the experience requirement from the level before. + * @param level The level to calculate the experience amount for + * @return The needed experience to reach this level, if the user already has the level below the passed one + */ + private Long calculateExperienceForLevel(Integer level) { return 5L * (level * level) + 50 * level + 100; } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleServiceBean.java index 9a4cccebb..1d7853571 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleServiceBean.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleServiceBean.java @@ -1,9 +1,11 @@ package dev.sheldan.abstracto.experience.service; +import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.ARole; import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.experience.models.database.AExperienceLevel; import dev.sheldan.abstracto.experience.models.database.AExperienceRole; +import dev.sheldan.abstracto.experience.models.database.AUserExperience; import dev.sheldan.abstracto.experience.service.management.ExperienceLevelManagementService; import dev.sheldan.abstracto.experience.service.management.ExperienceRoleManagementService; import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService; @@ -11,7 +13,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; @Component @Slf4j @@ -24,30 +28,79 @@ public class ExperienceRoleServiceBean implements ExperienceRoleService { private ExperienceLevelManagementService experienceLevelService; @Autowired - private ExperienceTrackerService experienceTrackerService; + private AUserExperienceService userExperienceService; @Autowired private UserExperienceManagementService userExperienceManagementService; + /** + * Unsets the current configuration for the passed level, and sets the {@link ARole} to be used for this level + * in the given {@link AServer} + * @param role The {@link ARole} to set the level to + * @param level The level the {@link ARole} should be awareded at + * @param server The {@link AServer} for which this configuration should be done + */ @Override - public void setRoleToLevel(ARole role, Integer level, AServer server) { + public void setRoleToLevel(ARole role, Integer level, AServer server, AChannel feedbackChannel) { AExperienceLevel experienceLevel = experienceLevelService.getLevel(level); - experienceRoleManagementService.unSetLevelInServer(experienceLevel, server); + unsetRole(role, server, feedbackChannel); + experienceRoleManagementService.removeAllRoleAssignmentsForLevelInServer(experienceLevel, server); experienceRoleManagementService.setLevelToRole(experienceLevel, role, server); } + /** + * Deletes the {@link AExperienceRole} and recalculates the experience for all users which currently had the associated + * {@link net.dv8tion.jda.api.entities.Role}. + * @param role The {@link ARole} to remove from the {@link dev.sheldan.abstracto.experience.models.database.AExperienceRole} + * configuration + * @param server The {@link AServer} for which the {@link ARole} should be removed from the configuration + */ @Override - public void unsetRole(ARole role, AServer server) { + public void unsetRole(ARole role, AServer server, AChannel feedbackChannel) { AExperienceRole roleInServer = experienceRoleManagementService.getRoleInServer(role, server); - if(roleInServer.getUsers().size() > 0) { - log.info("Recalculating the roles for {} users, because their current role was removed from experience tracking.", roleInServer.getUsers().size()); - roleInServer.getUsers().forEach(userExperience -> { - List roles = experienceRoleManagementService.getExperienceRoleForServer(server); + if(roleInServer != null) { + if(roleInServer.getUsers().size() > 0) { + log.info("Recalculating the roles for {} users, because their current role was removed from experience tracking.", roleInServer.getUsers().size()); + List roles = experienceRoleManagementService.getExperienceRolesForServer(server); roles.removeIf(role1 -> role1.getId().equals(roleInServer.getId())); - experienceTrackerService.handleExperienceRoleForUser(userExperience, roles); - }); + + userExperienceService.executeActionOnUserExperiencesWithFeedBack(roleInServer.getUsers(), feedbackChannel, (AUserExperience ex) -> { + userExperienceService.updateUserRole(ex, roles); + }); + } + experienceRoleManagementService.unsetRole(roleInServer); } - experienceRoleManagementService.unsetRole(roleInServer); + } + + /** + * Finds the best {@link AExperienceRole} for the level of the passed {@link AUserExperience} + * @param userExperience The {@link AUserExperience} containing the level to calculate the {@link AExperienceRole} + * @param roles The role configuration to be used when calculating the appropriate {@link AExperienceRole} + * @return The best fitting {@link AExperienceRole} according to the level of the {@link AUserExperience} + */ + @Override + public AExperienceRole calculateRole(AUserExperience userExperience, List roles) { + if(roles.size() == 0) { + return null; + } + AExperienceRole lastRole = null; + for (AExperienceRole experienceRole : roles) { + if(userExperience.getCurrentLevel().getLevel() >= experienceRole.getLevel().getLevel()) { + lastRole = experienceRole; + } else { + return lastRole; + } + } + return lastRole; + } + + @Override + public AExperienceLevel getLevelOfNextRole(AExperienceLevel startLevel, AServer server) { + List roles = experienceRoleManagementService.getExperienceRolesForServer(server); + roles = roles.stream().filter(role -> role.getLevel().getLevel() < startLevel.getLevel()).collect(Collectors.toList()); + roles.sort(Comparator.comparing(role -> role.getLevel().getLevel())); + AExperienceRole aExperienceRole = roles.stream().findFirst().orElse(null); + return aExperienceRole != null ? aExperienceRole.getLevel() : AExperienceLevel.builder().level(200).build(); } } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementServiceBean.java index 739b05043..ec09e6554 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementServiceBean.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementServiceBean.java @@ -34,11 +34,6 @@ public class ExperienceLevelManagementServiceBean implements ExperienceLevelMana return experienceLevelRepository.getOne(level); } - @Override - public AExperienceLevel getLevelClosestTo(Long experience) { - return experienceLevelRepository.findTopByExperienceNeededGreaterThanOrderByExperienceNeededAsc(experience); - } - @Override public List getLevelConfig() { return experienceLevelRepository.findAll(); diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementServiceBean.java index b341ca790..f1548c586 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementServiceBean.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementServiceBean.java @@ -18,8 +18,13 @@ public class ExperienceRoleManagementServiceBean implements ExperienceRoleManage @Autowired private ExperienceRoleRepository experienceRoleRepository; + /** + * Removes all assignments of roles for the given level + * @param level The level to remove the roles for + * @param server The server in which this should happen + */ @Override - public void unSetLevelInServer(AExperienceLevel level, AServer server) { + public void removeAllRoleAssignmentsForLevelInServer(AExperienceLevel level, AServer server) { log.trace("Removing all role assignments for level {}.", level.getLevel()); List existingExperienceRoles = experienceRoleRepository.findByLevelAndRoleServer(level, server); existingExperienceRoles.forEach(existingRole -> { @@ -38,7 +43,7 @@ public class ExperienceRoleManagementServiceBean implements ExperienceRoleManage } @Override - public List getExperienceRoleForServer(AServer server) { + public List getExperienceRolesForServer(AServer server) { return experienceRoleRepository.findByRoleServer(server); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementServiceBean.java index ad88b9a70..a319cf609 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementServiceBean.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementServiceBean.java @@ -2,7 +2,7 @@ package dev.sheldan.abstracto.experience.service.management; import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer; -import dev.sheldan.abstracto.experience.LeaderBoardEntryResult; +import dev.sheldan.abstracto.experience.models.database.LeaderBoardEntryResult; import dev.sheldan.abstracto.experience.models.database.AExperienceLevel; import dev.sheldan.abstracto.experience.models.database.AUserExperience; import dev.sheldan.abstracto.experience.repository.UserExperienceRepository; @@ -23,12 +23,6 @@ public class UserExperienceManagementServiceBean implements UserExperienceManage @Autowired private ExperienceLevelManagementService experienceLevelManagementService; - @Override - public void setExperienceTo(AUserExperience aUserInAServer, Long experience) { - aUserInAServer.setExperience(experience); - repository.save(aUserInAServer); - } - @Override public AUserExperience findUserInServer(AUserInAServer aUserInAServer) { Optional byId = repository.findById(aUserInAServer.getUserInServerId()); @@ -38,7 +32,7 @@ public class UserExperienceManagementServiceBean implements UserExperienceManage @Override public AUserExperience createUserInServer(AUserInAServer aUserInAServer) { AExperienceLevel startingLevel = experienceLevelManagementService.getLevel(0); - AUserExperience userExperience = AUserExperience + return AUserExperience .builder() .experience(0L) .messageCount(0L) @@ -46,13 +40,6 @@ public class UserExperienceManagementServiceBean implements UserExperienceManage .id(aUserInAServer.getUserInServerId()) .currentLevel(startingLevel) .build(); - repository.save(userExperience); - return userExperience; - } - - @Override - public void saveUser(AUserExperience userExperience) { - repository.save(userExperience); } @Override @@ -60,6 +47,34 @@ public class UserExperienceManagementServiceBean implements UserExperienceManage return repository.findByUser_ServerReference(server); } + /** + * Creates or updates the {@link AUserExperience} object. Does not change the level or the role. + * @param user The {@link AUserInAServer} to increase the experience for + * @param experience The experience amount to increase by + * @param messageCount The amount of messags to increase the count by + * @return The created/changed {@link AUserExperience} object + */ + @Override + public AUserExperience incrementExpForUser(AUserInAServer user, Long experience, Long messageCount) { + Optional byId = repository.findById(user.getUserInServerId()); + if(byId.isPresent()) { + AUserExperience userExperience = byId.get(); + userExperience.setMessageCount(userExperience.getMessageCount() + messageCount); + userExperience.setExperience(userExperience.getExperience() + experience); + return userExperience; + } else { + AExperienceLevel startingLevel = experienceLevelManagementService.getLevel(0); + return AUserExperience + .builder() + .experience(experience) + .messageCount(messageCount) + .user(user) + .id(user.getUserInServerId()) + .currentLevel(startingLevel) + .build(); + } + } + @Override public List findLeaderboardUsersPaginated(AServer aServer, Integer start, Integer end) { return repository.findTop10ByUser_ServerReferenceOrderByExperienceDesc(aServer, PageRequest.of(start, end)); @@ -69,6 +84,11 @@ public class UserExperienceManagementServiceBean implements UserExperienceManage public LeaderBoardEntryResult getRankOfUserInServer(AUserExperience userExperience) { return repository.getRankOfUserInServer(userExperience.getId()); } + + @Override + public void saveUser(AUserExperience userExperience) { + repository.save(userExperience); + } } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/templates/commands/syncRoles/status_message_embed_en_US.ftl b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/templates/commands/syncRoles/user_sync_status_message_embed_en_US.ftl similarity index 100% rename from abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/templates/commands/syncRoles/status_message_embed_en_US.ftl rename to abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/templates/commands/syncRoles/user_sync_status_message_embed_en_US.ftl diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/LeaderBoardEntryResult.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/LeaderBoardEntryResult.java deleted file mode 100644 index 28ac3278c..000000000 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/LeaderBoardEntryResult.java +++ /dev/null @@ -1,10 +0,0 @@ -package dev.sheldan.abstracto.experience; - -public interface LeaderBoardEntryResult { - Long getId(); - Long getUserInServerId(); - Long getExperience(); - Integer getLevel(); - Long getMessageCount(); - Integer getRank(); -} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/LeaderBoard.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/LeaderBoard.java index d7c52ffc0..201a3873f 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/LeaderBoard.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/LeaderBoard.java @@ -6,9 +6,15 @@ import lombok.Setter; import java.util.List; +/** + * Wrapper object containing a list of {@link LeaderBoardEntry} representing a leaderboard. + */ @Getter @Setter @Builder public class LeaderBoard { + /** + * List of {@link LeaderBoardEntry} representing the leaderboard. + */ private List entries; } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/LeaderBoardEntry.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/LeaderBoardEntry.java index ff47056d9..0e8eecefc 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/LeaderBoardEntry.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/LeaderBoardEntry.java @@ -5,10 +5,19 @@ import lombok.Builder; import lombok.Getter; import lombok.Setter; +/** + * Object containing a {@link AUserExperience} object and the respective rank of the user in the guild. + */ @Getter @Setter @Builder public class LeaderBoardEntry { + /** + * Object representing the current experience status of a user in a guild. + */ private AUserExperience experience; + /** + * The rank this user has in the respective guild. + */ private Integer rank; } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceLevel.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceLevel.java index df94e8d0a..40d068591 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceLevel.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceLevel.java @@ -6,6 +6,9 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; +/** + * Represents an existing level to reach and the total necessary experience needed to reach that level. + */ @Builder @Entity @NoArgsConstructor @@ -14,7 +17,13 @@ import javax.persistence.Table; @Getter @Setter public class AExperienceLevel { + /** + * The unique level from 0 to as defined in the configuration. Will be created on startup. + */ @Id private Integer level; + /** + * The total amount of experience needed for this level. + */ private Long experienceNeeded; } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceRole.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceRole.java index 6182cdfb7..198cf9a00 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceRole.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceRole.java @@ -8,6 +8,10 @@ import javax.persistence.*; import java.util.ArrayList; import java.util.List; +/** + * Represents a role which is given when the user reaches a certain level. These roles are configurable per server and + * roles configured in this table are able to be set to a certain level. + */ @Builder @Entity @NoArgsConstructor @@ -17,28 +21,40 @@ import java.util.List; @Setter public class AExperienceRole { + /** + * The abstracto unique id of this experience role. + */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) - @JoinColumn(name = "level_id") + /** + * Reference to the {@link AExperienceLevel} at which this role is awarded. + */ + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "level_id", nullable = false) private AExperienceLevel level; + /** + * Reference to the {@link AServer} at which this role is used as an experience role. + */ @ManyToOne(fetch = FetchType.LAZY) @Getter @Setter - @JoinColumn(name = "server_id") + @JoinColumn(name = "server_id", nullable = false) private AServer roleServer; - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) - @JoinColumn(name = "role_id") + /** + * Reference to the actual {@link ARole} being awarded. + */ + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "role_id", nullable = false) private ARole role; - @OneToMany( - fetch = FetchType.LAZY, - cascade = CascadeType.ALL, - orphanRemoval = true) + /** + * Current list of {@link dev.sheldan.abstracto.core.models.database.AUserInAServer} which were given this role. + */ + @OneToMany(fetch = FetchType.LAZY) @Builder.Default @JoinColumn(name = "experience_role_id") private List users = new ArrayList<>(); diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AUserExperience.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AUserExperience.java index fe993ff60..c52dfa350 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AUserExperience.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AUserExperience.java @@ -4,7 +4,13 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer; import lombok.*; import javax.persistence.*; +import java.io.Serializable; + +/** + * Mapping table responsible for tracking the experience and message count of a user in a specific server. + * For easier lookup also contains the current level and the currently awarded experience role. + */ @Builder @Entity @NoArgsConstructor @@ -12,24 +18,39 @@ import javax.persistence.*; @Table(name = "user_experience") @Getter @Setter -public class AUserExperience { +public class AUserExperience implements Serializable { + /** + * The {@link AUserInAServer} id which is unique for each user in a server. + */ @Id private Long id; - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @PrimaryKeyJoinColumn private AUserInAServer user; + /** + * The total amount of experience the user has in the guild + */ private Long experience; + /** + * The total amount of messages the user has written in the guild resulting in the experience. + */ private Long messageCount; - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) - @JoinColumn(name = "level_id") + /** + * The {@link AExperienceLevel } which the user currently has. + */ + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) + @JoinColumn(name = "level_id", nullable = false) private AExperienceLevel currentLevel; - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + /** + * The {@link AExperienceRole} the user currently has. Can be null. + */ + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) @JoinColumn(name = "experience_role_id") private AExperienceRole currentExperienceRole; } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/LeaderBoardEntryResult.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/LeaderBoardEntryResult.java new file mode 100644 index 000000000..ec0b372d8 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/LeaderBoardEntryResult.java @@ -0,0 +1,36 @@ +package dev.sheldan.abstracto.experience.models.database; + +/** + * The object returned from the rank retrieval query. + */ +public interface LeaderBoardEntryResult { + + Long getId(); + + /** + * The {@link dev.sheldan.abstracto.core.models.database.AUserInAServer} id of the user + * @return + */ + Long getUserInServerId(); + + /** + * The experience of the {@link dev.sheldan.abstracto.core.models.database.AUserInAServer} + */ + Long getExperience(); + + /** + * The current raw level of the {@link dev.sheldan.abstracto.core.models.database.AUserInAServer} + */ + Integer getLevel(); + + /** + * The amount of messages tracked by the {@link dev.sheldan.abstracto.core.models.database.AUserInAServer} + */ + Long getMessageCount(); + + /** + * The current position of the {@link dev.sheldan.abstracto.core.models.database.AUserInAServer} in the leaderboard + * ordered by experience count + */ + Integer getRank(); +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/LeaderBoardEntryModel.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/LeaderBoardEntryModel.java index 934e81317..e264b9b5f 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/LeaderBoardEntryModel.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/LeaderBoardEntryModel.java @@ -6,6 +6,11 @@ import lombok.Getter; import lombok.Setter; import net.dv8tion.jda.api.entities.Member; +/** + * Model used in the list of members when rendering the leaderboard command. The reason this is necessary, + * is because we need more than just the {@link AUserExperience} object, we also need the position of the user in this + * guild and the {@link Member} for convenience in the templates. + */ @Getter @Setter @Builder diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/LeaderBoardModel.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/LeaderBoardModel.java index 8e070c441..738180b77 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/LeaderBoardModel.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/LeaderBoardModel.java @@ -7,10 +7,21 @@ import lombok.experimental.SuperBuilder; import java.util.List; +/** + * Object containing the complete information passed to the leaderboard template. It contains the leaderboard + * information of the requested page of the total users and the leaderboard information of the user executing the + * command. + */ @Getter @Setter @SuperBuilder public class LeaderBoardModel extends UserInitiatedServerContext { + /** + * List of {@link LeaderBoardEntryModel} containing the information about the users from the requested page. + */ private List userExperiences; + /** + * The {@link LeaderBoardEntryModel} containing the leaderboard information executing the command. + */ private LeaderBoardEntryModel userExecuting; } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/RankModel.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/RankModel.java index de8d5c9c1..1e7325e7e 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/RankModel.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/RankModel.java @@ -5,10 +5,22 @@ import lombok.Getter; import lombok.Setter; import lombok.experimental.SuperBuilder; +/** + * Object containing the provided property to render the rank command template. This includes the + * {@link LeaderBoardEntryModel} object containing the information from the user executing the command, as well as the + * experience needed until next level. + */ @Getter @Setter @SuperBuilder public class RankModel extends UserInitiatedServerContext { + /** + * The {@link LeaderBoardEntryModel} containing the experience information about the user executing the rank + * command. + */ private LeaderBoardEntryModel rankUser; + /** + * The necessary experience until the next level up. + */ private Long experienceToNextLevel; } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/UserSyncStatusModel.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/UserSyncStatusModel.java index 9bf2ed8e8..db77dde2e 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/UserSyncStatusModel.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/templates/UserSyncStatusModel.java @@ -4,10 +4,21 @@ import lombok.Builder; import lombok.Getter; import lombok.Setter; +/** + * Object containing the status update information when the user executes the + * command responsible for synchronizing the users with their experience roles. This is a very barebones + * object as it only contains the current count and the total amount. + */ @Getter @Setter @Builder public class UserSyncStatusModel { + /** + * The amount of users which already have been processed. + */ private Integer currentCount; + /** + * The total amount of users for which the experience roles are being synchronized. + */ private Integer totalUserCount; } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceService.java new file mode 100644 index 000000000..c09cff4af --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceService.java @@ -0,0 +1,128 @@ +package dev.sheldan.abstracto.experience.service; + +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.experience.models.LeaderBoard; +import dev.sheldan.abstracto.experience.models.LeaderBoardEntry; +import dev.sheldan.abstracto.experience.models.database.AExperienceLevel; +import dev.sheldan.abstracto.experience.models.database.AExperienceRole; +import dev.sheldan.abstracto.experience.models.database.AUserExperience; + +import java.util.HashMap; +import java.util.List; +import java.util.function.Consumer; + +/** + * Service providing the required mechanisms to provide experience tracking. + * This includes manipulations on the {@link AUserExperience} table, container for the runtime experience, synchronizing the + * user in the guild and retrieving {@link LeaderBoard} data. + */ +public interface AUserExperienceService { + /** + * Adds the given {@link AUserInAServer} to the list of user who gained experience in the current minute. + * Does not add the user to the list of users, if it is already in there. + * @param userInAServer The {@link AUserInAServer} to be added to the list of users gaining experience + */ + void addExperience(AUserInAServer userInAServer); + + /** + * The current representation of the run time epxerience. Basically a HashMap of minutes to a list of {@link AServer} + * containing a list of {@link AUserInAServer} which should gain experience in the minute used as key in the HashMap + * @return + */ + HashMap> getRuntimeExperience(); + + /** + * Calculates the appropriate level of the given {@link AUserExperience} according to the given {@link AExperienceLevel} + * configuration. + * @param experience The {@link AUserExperience} to calculate the level for + * @param levels The list of {@link AExperienceLevel} representing the level configuration + * @return The appropriate level of {@link AUserExperience} according to the provided {@link AExperienceLevel} configuration + */ + Integer calculateLevel(AUserExperience experience, List levels); + + /** + * Increases the experience of the provided {@link AUserExperience} object and and calculates the new level according + * to the provided list of {@link AExperienceLevel} used as level configuration + * @param userExperience The {@link AUserExperience} to increase the experience for + * @param levels The list of {@link AExperienceLevel} to be used as level configuration + * @return Whether or not the user changed level + */ + boolean updateUserlevel(AUserExperience userExperience, List levels); + + /** + * Iterates through the given list of {@link AServer} and increases the experience of the users contained in the + * {@link AServer} object, also increments the level and changes the role if necessary. + * This uses the respective configurable max/minExp and multiplier for each {@link AServer} and increases the message count + * of each user by 1. + * @param serverExp The list of {@link AServer} containing the users which get experience + */ + void handleExperienceGain(List serverExp); + + /** + * Calculates the currently appropriate {@link AExperienceRole} for the given user and updates the role on the + * {@link net.dv8tion.jda.api.entities.Member} and ond the {@link AUserExperience}. Effectively synchronizes the + * state in the server and the database. + * @param userExperience The {@link AUserExperience} object to recalculate the {@link AExperienceRole} for + * @param roles The list of {@link AExperienceRole} used as a role configuration + */ + void updateUserRole(AUserExperience userExperience, List roles); + + + /** + * Synchronizes the state ({@link AExperienceRole}, {@link net.dv8tion.jda.api.entities.Role}) + * of all the users provided in the {@link AServer} object in the {@link AUserExperience} + * and on the {@link net.dv8tion.jda.api.entities.Member} according + * to how much experience the user has. Runs completely in the background. + * @param server The {@link AServer} to update the users for + */ + void syncUserRoles(AServer server); + + /** + * Synchronizes the state ({@link AExperienceRole}, {@link net.dv8tion.jda.api.entities.Role}) + * of all the users provided in the {@link AServer} object in the {@link AUserExperience} + * and on the {@link net.dv8tion.jda.api.entities.Member} according + * to how much experience the user has. This method provides feedback back to the user in the provided {@link AChannel} + * while the process is going own. + * @param server The {@link AServer} to update users for + * @param channel The {@link AChannel} in which the {@link dev.sheldan.abstracto.experience.models.templates.UserSyncStatusModel} + * should be posted to + */ + void syncUserRolesWithFeedback(AServer server, AChannel channel); + + /** + * Recalculates the role of a single user in a server and synchronize the {@link net.dv8tion.jda.api.entities.Role} + * in the {@link net.dv8tion.jda.api.entities.Guild} + * @param userExperience The {@link AUserExperience} to synchronize the role for + */ + void syncForSingleUser(AUserExperience userExperience); + + /** + * Loads the desired page of the ordered complete leaderboard from the {@link AServer} and returns the information as a {@link LeaderBoard} + * @param server The {@link AServer} to retrieve the leaderboard for + * @param page The desired page on the leaderboard. The pagesize is 10 + * @return The {@link LeaderBoard} containing the {@link LeaderBoardEntry} containing information about the {@link AUserExperience} + * from the desired page + */ + LeaderBoard findLeaderBoardData(AServer server, Integer page); + + /** + * Retrieves the {@link LeaderBoardEntry} from a specific {@link AUserInAServer} containing information about the + * gained experience + * @param userInAServer The {@link AUserInAServer} to retrieve the {@link LeaderBoardEntry} for + * @return The {@link LeaderBoardEntry} containing information about gained experience from the given {@link AUserInAServer} + * and the rank of the user in the server + */ + LeaderBoardEntry getRankOfUserInServer(AUserInAServer userInAServer); + + /** + * Provides a method to execute an action on a list of {@link AUserExperience} and provide feedback in the given {@link AChannel} + * in the form of {@link dev.sheldan.abstracto.experience.models.templates.UserSyncStatusModel} to be rendered with a certain + * template + * @param experiences The list of {@link AUserExperience} to be working on + * @param channel The {@link AChannel} used to provide feedback to the user + * @param toExecute The {@link Consumer} which should be executed on each element of the passed list + */ + void executeActionOnUserExperiencesWithFeedBack(List experiences, AChannel channel, Consumer toExecute); +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelService.java index 7b1ff684f..84fb34308 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelService.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelService.java @@ -1,7 +1,23 @@ package dev.sheldan.abstracto.experience.service; +/** + * Service responsible for operations on {@link dev.sheldan.abstracto.experience.models.database.AExperienceLevel} + * This includes creating and calculations. + */ public interface ExperienceLevelService { - void createExperienceLevel(Integer level, Long experienceNeeded); - Long calculateExperienceForLevel(Integer level); + + /** + * Creates all the levels up until the given level. + * @param level The max level to create {@link dev.sheldan.abstracto.experience.models.database.AExperienceLevel} for + */ + void createLevelsUntil(Integer level); + + /** + * Calculates the required experience until the next level is reached according to the current experience and + * current level. + * @param level The current level to base the calculation of + * @param currentExperience The current experience + * @return The amount of experience required necessary to get to one level higher as currently. + */ Long calculateExperienceToNextLevel(Integer level, Long currentExperience); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleService.java index 54878f804..5b578b1f1 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleService.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleService.java @@ -1,9 +1,42 @@ package dev.sheldan.abstracto.experience.service; +import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.ARole; import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.experience.models.database.AExperienceLevel; +import dev.sheldan.abstracto.experience.models.database.AExperienceRole; +import dev.sheldan.abstracto.experience.models.database.AUserExperience; +import java.util.List; + +/** + * Service providing several methods surrounding {@link dev.sheldan.abstracto.experience.models.database.AExperienceRole}. + */ public interface ExperienceRoleService { - void setRoleToLevel(ARole role, Integer level, AServer server); - void unsetRole(ARole role, AServer server); + /** + * Creates an {@link dev.sheldan.abstracto.experience.models.database.AExperienceRole} according to the given + * parameters + * @param role The {@link ARole} to set the level to + * @param level The level the {@link ARole} should be awareded at + * @param server The {@link AServer} for which this configuration should be done + */ + void setRoleToLevel(ARole role, Integer level, AServer server, AChannel channel); + + /** + * Removes the role from the {@link dev.sheldan.abstracto.experience.models.database.AExperienceRole} configuration + * @param role The {@link ARole} to remove from the {@link dev.sheldan.abstracto.experience.models.database.AExperienceRole} + * configuration + * @param server The {@link AServer} for which the {@link ARole} should be removed from the configuration + */ + void unsetRole(ARole role, AServer server, AChannel feedbackChannel); + + /** + * Calculates the appropriate {@link AExperienceRole} based on the provided list of {@link AExperienceRole} + * @param userExperience The {@link AUserExperience} containing the level to calculate the {@link AExperienceRole} + * @param roles The role configuration to be used when calculating the appropriate {@link AExperienceRole} + * @return The best matching {@link AExperienceRole} accordign to the experience in the provided {@link AUserExperience} + */ + AExperienceRole calculateRole(AUserExperience userExperience, List roles); + + AExperienceLevel getLevelOfNextRole(AExperienceLevel startLevel, AServer server); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerService.java deleted file mode 100644 index 2479a04fd..000000000 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerService.java +++ /dev/null @@ -1,28 +0,0 @@ -package dev.sheldan.abstracto.experience.service; - -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.experience.models.LeaderBoard; -import dev.sheldan.abstracto.experience.models.LeaderBoardEntry; -import dev.sheldan.abstracto.experience.models.database.AExperienceLevel; -import dev.sheldan.abstracto.experience.models.database.AExperienceRole; -import dev.sheldan.abstracto.experience.models.database.AUserExperience; - -import java.util.HashMap; -import java.util.List; - -public interface ExperienceTrackerService { - void addExperience(AUserInAServer userInAServer); - HashMap> getRuntimeExperience(); - Integer calculateLevel(AUserExperience userInAServer, List levels); - AExperienceRole calculateRole(AUserExperience userInAServer, List roles); - void increaseExpForUser(AUserExperience userInAServer, Long experience, List levels); - void handleExperienceGain(List serverExp); - void handleExperienceRoleForUser(AUserExperience userExperience, List roles); - void syncUserRoles(AServer server); - void syncUserRolesWithFeedback(AServer server, AChannel channel); - void syncForSingleUser(AUserExperience userExperience); - LeaderBoard findLeaderBoardData(AServer server, Integer page); - LeaderBoardEntry getRankOfUserInServer(AUserInAServer userInAServer); -} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementService.java index dff9d5279..ccc551629 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementService.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementService.java @@ -4,10 +4,35 @@ import dev.sheldan.abstracto.experience.models.database.AExperienceLevel; import java.util.List; +/** + * Service responsible to create and retrieve {@link AExperienceLevel} objects in the database. + */ public interface ExperienceLevelManagementService { + /** + * Creates the level referenced by the level and the needed experience in the database. + * @param level The unique level this level should reprsent. + * @param neededExperience The total amount of experience required to reach this level. + * @return A newly created {@link AExperienceLevel} instance. + */ AExperienceLevel createExperienceLevel(Integer level, Long neededExperience); + + /** + * Checks if a {@link AExperienceLevel} level indicated by the level exists in the database. Returns true if it does. + * @param level The integer of the level to check for. + * @return A boolean indicating whether or not the level exists in the database. + */ boolean levelExists(Integer level); + + /** + * Retrieves a {@link AExperienceLevel} according to the given level. + * @param level The level of the wanted {@link AExperienceLevel} to look for + * @return If the level exists, returns the {@link AExperienceLevel}, if it does not exists, returns null. + */ AExperienceLevel getLevel(Integer level); - AExperienceLevel getLevelClosestTo(Long experience); + + /** + * Loads the complete level configuration and returns all found {@link AExperienceLevel} objects from the database. + * @return A list of {@link AExperienceLevel} objects representing the currently active configuration. + */ List getLevelConfig(); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementService.java index 447c8ea6a..5ad25ba21 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementService.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementService.java @@ -7,10 +7,47 @@ import dev.sheldan.abstracto.experience.models.database.AExperienceRole; import java.util.List; +/** + * Service responsible to manage the {@link AExperienceRole} configuration of a server. This contains functionality to + * set/unset a level to a certain role, retrieve {@link AExperienceRole} of a certain role and load all for a given + * server. + */ public interface ExperienceRoleManagementService { + /** + * Sets the given {@link AExperienceLevel} to the given {@link ARole} in the {@link AServer}. This will create an + * {@link AExperienceRole} instance and store it. If the role was already set in the server, this sets this role to + * the new level. + * @param level The {@link AExperienceLevel} to set the role for + * @param role The {@link ARole} to set to + * @param server The {@link AServer} in which this should happen. + */ void setLevelToRole(AExperienceLevel level, ARole role, AServer server); - void unSetLevelInServer(AExperienceLevel level, AServer server); + + /** + * Deletes *all* (if there are multiple by some chance) roles which were set to be given at the provided {@link AExperienceLevel} in the {@link AServer} + * @param level The level to remove the roles for + * @param server The server in which this should happen + */ + void removeAllRoleAssignmentsForLevelInServer(AExperienceLevel level, AServer server); + + /** + * Deletes a singular {@link AExperienceRole} directly. + * @param role The {@link AExperienceRole} to delete. + */ void unsetRole(AExperienceRole role); + + /** + * Retrieves the {@link AExperienceRole} which uses the given {@link ARole} in the {@link AServer} + * @param role The {@link ARole} to search for + * @param server The {@link AServer} in which to search in + * @return + */ AExperienceRole getRoleInServer(ARole role, AServer server); - List getExperienceRoleForServer(AServer server); + + /** + * Retrives all {@link AExperienceRole} configured in the given {@link AServer} + * @param server The server to retrieve the list of {@link AExperienceRole} for + * @return A list of {@link AExperienceRole} which are currently configured for the {@link AServer} + */ + List getExperienceRolesForServer(AServer server); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementService.java index c5954a201..9ed5d08e9 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementService.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementService.java @@ -3,19 +3,66 @@ package dev.sheldan.abstracto.experience.service.management; import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer; -import dev.sheldan.abstracto.experience.LeaderBoardEntryResult; -import dev.sheldan.abstracto.experience.models.LeaderBoardEntry; +import dev.sheldan.abstracto.experience.models.database.LeaderBoardEntryResult; import dev.sheldan.abstracto.experience.models.database.AUserExperience; -import org.springframework.data.domain.Pageable; import java.util.List; +/** + * Service used to manage the record in the {@link AUserExperience} table + */ public interface UserExperienceManagementService { - void setExperienceTo(AUserExperience aUserInAServer, Long experience); + /** + * Retrieves the {@link AUserExperience} object for the given {@link AUserInAServer} + * @param aUserInAServer The record in the table referenced by the given {@link AUserInAServer}, if none exists, creates one. + * @return The {@link AUserExperience} object representing the {@link AUserInAServer} + */ AUserExperience findUserInServer(AUserInAServer aUserInAServer); + + /** + * Creates a {@link AUserExperience} object with the default values (0 xp, 0 messages) for the given {@link AUserInAServer} object. + * @param aUserInAServer The {@link AUserInAServer} to create the {@link AUserExperience} object for. + * @return The newly created {@link AUserExperience} object + */ AUserExperience createUserInServer(AUserInAServer aUserInAServer); - void saveUser(AUserExperience userExperience); + + /** + * Loads a list of all {@link AUserExperience} objects for a given {@link AServer}. + * @param server The {@link AServer} to retrieve the list of {@link AUserExperience} for + * @return A list of {@link AUserExperience} objects associated with the given {@link AServer} + */ List loadAllUsers(AServer server); + + /** + * Increments the experience of the {@link AUserInAServer} by the given experience and the message count. + * {@link dev.sheldan.abstracto.experience.models.database.AExperienceLevel} or {@link dev.sheldan.abstracto.experience.models.database.AExperienceRole} + * are not changed. + * @param user The {@link AUserInAServer} to increase the experience for + * @param experience The experience amount to increase by + * @param messageCount The amount of messags to increase the count by + * @return The changed/creates {@link AUserExperience} object containing the values. + */ + AUserExperience incrementExpForUser(AUserInAServer user, Long experience, Long messageCount); + + /** + * Retrieves a list of {@link AUserExperience} ordered by {@link AUserExperience.experience} and only returns the positions between {@code start} and @{code end}. + * @param server The {@link AServer} to retrieve the users for + * @param start The start index in the complete ordered list to return the {@link AUserExperience} elements for + * @param end The end index for which to return a sublist of {@link AUserExperience} elements for + * @return A list desc ordered by {@link AUserExperience.experience} only containing the elements between {@code start} and @{code end} + */ List findLeaderboardUsersPaginated(AServer server, Integer start, Integer end); + + /** + * Returns the {@link LeaderBoardEntryResult} of the given {@link AUserExperience}. + * @param userExperience The {@link AUserExperience} to retrieve the information for + * @return The {@link LeaderBoardEntryResult} containing the experience, message count and rank of the given userExperience. + */ LeaderBoardEntryResult getRankOfUserInServer(AUserExperience userExperience); + + /** + * Persists the {@link AUserExperience} in the database. Required when creating it + * @param userExperience The {@link AUserExperience} to persist + */ + void saveUser(AUserExperience userExperience); } diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/models/database/Warning.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/models/database/Warning.java index ebdae9227..d9e24700e 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/models/database/Warning.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/models/database/Warning.java @@ -23,12 +23,12 @@ public class Warning { @Getter @ManyToOne - @JoinColumn(name = "warnedUserId") + @JoinColumn(name = "warnedUserId", nullable = false) private AUserInAServer warnedUser; @Getter @ManyToOne - @JoinColumn(name = "warningUserId") + @JoinColumn(name = "warningUserId", nullable = false) private AUserInAServer warningUser; @Getter diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/EmbeddedMessage.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/EmbeddedMessage.java index 81e3d3db3..f7bfc1efd 100644 --- a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/EmbeddedMessage.java +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/EmbeddedMessage.java @@ -18,22 +18,22 @@ public class EmbeddedMessage { @Getter @ManyToOne - @JoinColumn(name = "embeddedUser") + @JoinColumn(name = "embeddedUser", nullable = false) private AUserInAServer embeddedUser; @Getter @ManyToOne - @JoinColumn(name = "embeddingUser") + @JoinColumn(name = "embeddingUser", nullable = false) private AUserInAServer embeddingUser; @Getter @ManyToOne - @JoinColumn(name = "originalServer") + @JoinColumn(name = "originalServer", nullable = false) private AServer embeddedServer; @Getter @ManyToOne - @JoinColumn(name = "originalChannel") + @JoinColumn(name = "originalChannel", nullable = false) private AChannel embeddedChannel; @Column @@ -41,12 +41,12 @@ public class EmbeddedMessage { @Getter @ManyToOne - @JoinColumn(name = "embeddingServer") + @JoinColumn(name = "embeddingServer", nullable = false) private AServer embeddingServer; @Getter @ManyToOne - @JoinColumn(name = "embeddingChannel") + @JoinColumn(name = "embeddingChannel", nullable = false) private AChannel embeddingChannel; @Column diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/Reminder.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/Reminder.java index 0a47fe4b2..c3cb09231 100644 --- a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/Reminder.java +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/Reminder.java @@ -24,7 +24,7 @@ public class Reminder { @Getter @ManyToOne - @JoinColumn(name = "remindedUser") + @JoinColumn(name = "remindedUser", nullable = false) private AUserInAServer remindedUser; @Getter @@ -32,12 +32,12 @@ public class Reminder { @Getter @ManyToOne - @JoinColumn(name = "channelId") + @JoinColumn(name = "channelId", nullable = false) private AChannel channel; @Getter @ManyToOne - @JoinColumn(name = "serverId") + @JoinColumn(name = "serverId", nullable = false) private AServer server; @Getter diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/StarboardPost.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/StarboardPost.java index 72347b6ac..662a5eeee 100644 --- a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/StarboardPost.java +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/StarboardPost.java @@ -22,7 +22,7 @@ public class StarboardPost { private Long id; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "poster") + @JoinColumn(name = "poster", nullable = false) private AUser author; @Column @@ -32,11 +32,11 @@ public class StarboardPost { private Long postMessageId; @ManyToOne - @JoinColumn(name = "channelId") + @JoinColumn(name = "channelId", nullable = false) private AChannel starboardChannel; @ManyToOne - @JoinColumn(name = "sourceChannelId") + @JoinColumn(name = "sourceChannelId", nullable = false) private AChannel sourceChanel; @Transient @@ -48,7 +48,9 @@ public class StarboardPost { } @Getter - @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}) + @OneToMany(fetch = FetchType.LAZY, + orphanRemoval = true, + cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinColumn(name="postId") private List reactions; diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/StarboardPostReaction.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/StarboardPostReaction.java index 5d5db0da5..64b4bf781 100644 --- a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/StarboardPostReaction.java +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/StarboardPostReaction.java @@ -19,11 +19,11 @@ public class StarboardPostReaction { private Long id; @ManyToOne - @JoinColumn(name = "reactorId") + @JoinColumn(name = "reactorId", nullable = false) private AUser reactor; @ManyToOne - @JoinColumn(name = "postId") + @JoinColumn(name = "postId", nullable = false) private StarboardPost starboardPost; } diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/Suggestion.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/Suggestion.java index 399e0714e..a7edb240f 100644 --- a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/Suggestion.java +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/database/Suggestion.java @@ -24,7 +24,8 @@ public class Suggestion { @Getter @ManyToOne - @JoinColumn(name = "suggesterId") + @JoinColumn(name = "suggesterId", + nullable = false) private AUserInAServer suggester; @Getter diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ChannelListener.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ChannelListener.java index 4078f2c7e..4e9b2c98f 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ChannelListener.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ChannelListener.java @@ -44,7 +44,6 @@ public class ChannelListener extends ListenerAdapter { AServer serverObject = serverRepository.getOne(event.getGuild().getIdLong()); TextChannel createdChannel = event.getChannel(); AChannelType type = AChannel.getAChannelType(createdChannel.getType()); - AChannel newChannel = channelManagementService.createChannel(createdChannel.getIdLong(), type); - serverManagementService.addChannelToServer(serverObject, newChannel); + channelManagementService.createChannel(createdChannel.getIdLong(), type, serverObject); } } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotServiceBean.java index 0647e8b13..0d4eb36cc 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotServiceBean.java @@ -4,6 +4,8 @@ import dev.sheldan.abstracto.core.exception.ChannelException; import dev.sheldan.abstracto.core.exception.GuildException; import dev.sheldan.abstracto.core.models.GuildChannelMember; import dev.sheldan.abstracto.core.models.database.AEmote; +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.models.database.AUser; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; @@ -69,6 +71,11 @@ public class BotServiceBean implements BotService { } } + @Override + public Member getMemberInServer(AServer server, AUser member) { + return getMemberInServer(server.getId(), member.getId()); + } + @Override public CompletableFuture deleteMessage(Long serverId, Long channelId, Long messageId) { Optional textChannelOptional = getTextChannelFromServer(serverId, channelId); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/RoleServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/RoleServiceBean.java index 25238e4db..f9947c1bb 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/RoleServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/RoleServiceBean.java @@ -3,11 +3,11 @@ package dev.sheldan.abstracto.core.service; import dev.sheldan.abstracto.core.exception.GuildException; import dev.sheldan.abstracto.core.exception.RoleException; import dev.sheldan.abstracto.core.models.database.ARole; -import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.service.management.RoleManagementService; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Role; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -72,12 +72,27 @@ public class RoleServiceBean implements RoleService { } @Override - public boolean isRoleInServer(AServer server, ARole role) { - Optional guildById = botService.getGuildById(server.getId()); + public Role getRoleFromGuild(ARole role) { + Optional guildById = botService.getGuildById(role.getServer().getId()); if(guildById.isPresent()) { - return guildById.get().getRoleById(role.getId()) != null; + return guildById.get().getRoleById(role.getId()); } else { - throw new GuildException(String.format("Failed to load guild %s.", server.getId())); + throw new GuildException(String.format("Failed to load guild %s.", role.getServer().getId())); } } + + @Override + public boolean memberHasRole(Member member, Role role) { + return member.getRoles().stream().anyMatch(role1 -> role1.getIdLong() == role.getIdLong()); + } + + @Override + public boolean memberHasRole(Member member, ARole role) { + return member.getRoles().stream().anyMatch(role1 -> role1.getIdLong() == role.getId()); + } + + @Override + public boolean isRoleInServer(ARole role) { + return getRoleFromGuild(role) != null; + } } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java index 20bb0376a..5a152eec5 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java @@ -104,8 +104,7 @@ public class StartupServiceBean implements Startup { GuildChannel channel1 = available.stream().filter(channel -> channel.getIdLong() == aLong).findFirst().get(); log.trace("Adding new channel: {}", aLong); AChannelType type = AChannel.getAChannelType(channel1.getType()); - AChannel newChannel = channelManagementService.createChannel(channel1.getIdLong(), type); - serverManagementService.addChannelToServer(existingServer, newChannel); + channelManagementService.createChannel(channel1.getIdLong(), type, existingServer); }); Set noLongAvailable = SetUtils.difference(knownChannelsIds, existingChannelsIds); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ChannelManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ChannelManagementServiceBean.java index 137d52a2f..995f682e5 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ChannelManagementServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ChannelManagementServiceBean.java @@ -2,6 +2,7 @@ package dev.sheldan.abstracto.core.service.management; import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.AChannelType; +import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.repository.ChannelRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -20,9 +21,16 @@ public class ChannelManagementServiceBean implements ChannelManagementService { } @Override - public AChannel createChannel(Long id, AChannelType type) { + public AChannel createChannel(Long id, AChannelType type, AServer server) { log.info("Creating channel {} with type {}", id, type); - return repository.save(AChannel.builder().id(id).type(type).deleted(false).build()); + AChannel build = AChannel + .builder() + .id(id) + .type(type) + .server(server) + .deleted(false) + .build(); + return repository.save(build); } @Override diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementServiceBean.java index 794a9c458..87371b138 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementServiceBean.java @@ -22,7 +22,6 @@ public class ConfigManagementServiceBean implements ConfigManagementService { createConfig(serverId, name, value); } else { config.setStringValue(value); - configRepository.save(config); } } @@ -33,7 +32,6 @@ public class ConfigManagementServiceBean implements ConfigManagementService { createConfig(serverId, name, value); } else { config.setDoubleValue(value); - configRepository.save(config); } } @@ -95,7 +93,6 @@ public class ConfigManagementServiceBean implements ConfigManagementService { public void setDoubleValue(Long serverId, String name, Double value) { AConfig config = loadConfig(serverId, name); config.setDoubleValue(value); - configRepository.save(config); } } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java index 68d5c3d15..3a0cd545b 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java @@ -14,7 +14,13 @@ public class RoleManagementServiceBean implements RoleManagementService { @Override public ARole createRole(Long id, AServer server) { - return repository.save(ARole.builder().id(id).server(server).deleted(false).build()); + ARole build = ARole + .builder() + .id(id) + .server(server) + .deleted(false) + .build(); + return repository.save(build); } @Override @@ -25,6 +31,5 @@ public class RoleManagementServiceBean implements RoleManagementService { @Override public void markDeleted(ARole role) { role.setDeleted(true); - repository.save(role); } } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ServerManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ServerManagementServiceBean.java index e1db6f6e0..7b16685a3 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ServerManagementServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ServerManagementServiceBean.java @@ -42,7 +42,6 @@ public class ServerManagementServiceBean implements ServerManagementService { public void addChannelToServer(AServer server, AChannel channel) { server.getChannels().add(channel); channel.setServer(server); - repository.save(server); } @Override diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/condition/FeatureEnabledCondition.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/condition/FeatureEnabledCondition.java index 28382e579..0a137b3bd 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/condition/FeatureEnabledCondition.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/condition/FeatureEnabledCondition.java @@ -19,7 +19,7 @@ public class FeatureEnabledCondition implements CommandCondition { String reason = ""; if(featureName != null) { featureFlagValue = featureFlagManagementService.getFeatureFlagValue(featureName, context.getGuild().getIdLong()); - reason = "Feature has been disabled."; + reason = "Feature has been disabled. Necessary feature is: " + featureName; } return ConditionResult.builder().reason(reason).result(featureFlagValue).build(); } diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/models/database/ACommand.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/models/database/ACommand.java index bfc627894..06f9a1e97 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/models/database/ACommand.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/models/database/ACommand.java @@ -21,7 +21,7 @@ public class ACommand { @ManyToOne(fetch = FetchType.LAZY) @Getter @Setter - @JoinColumn(name = "module_id") + @JoinColumn(name = "module_id", nullable = false) private AModule module; } diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/models/database/AModule.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/models/database/AModule.java index 4d84d98da..e22072f81 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/models/database/AModule.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/command/models/database/AModule.java @@ -26,7 +26,7 @@ public class AModule { @OneToMany( fetch = FetchType.LAZY, - cascade = CascadeType.ALL, + cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true) @Builder.Default @JoinColumn(name = "module_id") diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java index a8691295b..b0d980404 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java @@ -26,7 +26,7 @@ public class AChannel implements SnowFlake { @ManyToOne(fetch = FetchType.LAZY) @Getter @Setter - @JoinColumn(name = "server_id") + @JoinColumn(name = "server_id", nullable = false) private AServer server; @Getter diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroup.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroup.java index 1a877a3aa..c44e24cea 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroup.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroup.java @@ -25,7 +25,7 @@ public class AChannelGroup { @ManyToOne(fetch = FetchType.LAZY) @Getter @Setter - @JoinColumn(name = "group_server") + @JoinColumn(name = "group_server", nullable = false) private AServer server; @ManyToMany diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AEmote.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AEmote.java index 835083cd9..9ed0ed7e7 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AEmote.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AEmote.java @@ -36,6 +36,7 @@ public class AEmote { private Boolean custom; @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "emote_server_id", nullable = false) private AServer serverRef; diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureFlag.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureFlag.java index 9318d450c..734bce20d 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureFlag.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureFlag.java @@ -21,7 +21,7 @@ public class AFeatureFlag implements SnowFlake { @ManyToOne(fetch = FetchType.LAZY) @Getter @Setter - @JoinColumn(name = "server_id") + @JoinColumn(name = "server_id", nullable = false) private AServer server; @Getter diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java index 4935f24b6..6361cba5f 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java @@ -13,13 +13,14 @@ import javax.persistence.*; public class ARole implements SnowFlake { @Id - @Getter @Setter + @Getter + @Setter private Long id; @ManyToOne(fetch = FetchType.LAZY) @Getter @Setter - @JoinColumn(name = "role_server_id") + @JoinColumn(name = "role_server_id", nullable = false) private AServer server; @Getter diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java index ffe4a6022..3b5a259ed 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java @@ -23,7 +23,6 @@ public class AServer implements SnowFlake { @OneToMany( fetch = FetchType.LAZY, - cascade = CascadeType.ALL, orphanRemoval = true) @Builder.Default @JoinColumn(name = "role_server_id") @@ -31,7 +30,7 @@ public class AServer implements SnowFlake { @OneToMany( fetch = FetchType.LAZY, - cascade = CascadeType.ALL, + cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true) @Builder.Default @JoinColumn(name = "server_id") @@ -39,7 +38,7 @@ public class AServer implements SnowFlake { @OneToMany( fetch = FetchType.LAZY, - cascade = CascadeType.ALL, + cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true) @Builder.Default @JoinColumn(name = "group_server") @@ -47,15 +46,17 @@ public class AServer implements SnowFlake { @OneToMany( fetch = FetchType.LAZY, - mappedBy = "serverReference", - cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + cascade = {CascadeType.PERSIST, CascadeType.MERGE}, + orphanRemoval = true) + @JoinColumn(name = "serverReference") @Builder.Default private List users = new ArrayList<>(); @OneToMany( fetch = FetchType.LAZY, - mappedBy = "serverRef", - cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + cascade = {CascadeType.PERSIST, CascadeType.MERGE}, + orphanRemoval = true) + @JoinColumn(name = "emote_server_id") @Builder.Default private List emotes = new ArrayList<>(); diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AUser.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AUser.java index 77a2fd6b4..56c0d2a5f 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AUser.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AUser.java @@ -18,7 +18,6 @@ public class AUser { @OneToMany( fetch = FetchType.LAZY, - mappedBy = "userReference", cascade = {CascadeType.PERSIST, CascadeType.MERGE}) private List servers; } diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java index eaea822d8..678c586aa 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java @@ -17,10 +17,10 @@ public class AUserInAServer { private Long userInServerId; @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) - @JoinColumn(name = "userReference") + @JoinColumn(name = "userReference", nullable = false) private AUser userReference; @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) - @JoinColumn(name = "serverReference") + @JoinColumn(name = "serverReference", nullable = false) private AServer serverReference; } diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/BotService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/BotService.java index 0a8c8dbc3..dd63c0a5c 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/BotService.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/BotService.java @@ -2,11 +2,10 @@ package dev.sheldan.abstracto.core.service; import dev.sheldan.abstracto.core.models.GuildChannelMember; import dev.sheldan.abstracto.core.models.database.AEmote; +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.models.database.AUser; import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.Emote; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.*; import org.springframework.stereotype.Service; import javax.security.auth.login.LoginException; @@ -19,6 +18,7 @@ public interface BotService { JDA getInstance(); GuildChannelMember getServerChannelUser(Long serverId, Long channelId, Long userId); Member getMemberInServer(Long serverId, Long memberId); + Member getMemberInServer(AServer server, AUser member); CompletableFuture deleteMessage(Long serverId, Long channelId, Long messageId); Optional getEmote(Long serverId, AEmote emote); Optional getTextChannelFromServer(Guild serverId, Long textChannelId); diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/RoleService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/RoleService.java index dd173a767..d01976715 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/RoleService.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/RoleService.java @@ -1,8 +1,8 @@ package dev.sheldan.abstracto.core.service; import dev.sheldan.abstracto.core.models.database.ARole; -import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer; +import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Role; public interface RoleService { @@ -10,5 +10,8 @@ public interface RoleService { void removeRoleFromUser(AUserInAServer aUserInAServer, ARole role); void markDeleted(Role role); void markDeleted(Long id); - boolean isRoleInServer(AServer server, ARole role); + Role getRoleFromGuild(ARole role); + boolean memberHasRole(Member member, Role role); + boolean memberHasRole(Member member, ARole role); + boolean isRoleInServer(ARole role); } diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/management/ChannelManagementService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/management/ChannelManagementService.java index e3f45a2e6..8ae1b2c49 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/management/ChannelManagementService.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/management/ChannelManagementService.java @@ -2,10 +2,11 @@ package dev.sheldan.abstracto.core.service.management; import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.AChannelType; +import dev.sheldan.abstracto.core.models.database.AServer; public interface ChannelManagementService { AChannel loadChannel(Long id); - AChannel createChannel(Long id, AChannelType type); + AChannel createChannel(Long id, AChannelType type, AServer server); void markAsDeleted(Long id); void removeChannel(Long id); }