From 93b02d37edcccbc668ce63297a4a49ee263d3a57 Mon Sep 17 00:00:00 2001 From: Sheldan <5037282+Sheldan@users.noreply.github.com> Date: Wed, 28 Feb 2024 21:26:42 +0100 Subject: [PATCH] [AB-47] adding feature to define custom actions once members reach a certain level --- .../experience-tracking-impl/pom.xml | 5 + .../experience/command/AddLevelAction.java | 193 ++++++++++++++++++ .../experience/command/RemoveLevelAction.java | 171 ++++++++++++++++ .../experience/command/ShowLevelActions.java | 79 +++++++ .../AddMemberToChannelLevelAction.java | 55 +++++ .../listener/AddRoleLevelAction.java | 55 +++++ .../listener/JoiningUserRoleListener.java | 25 ++- .../listener/MemberPendingRoleListener.java | 25 ++- .../RemoveMemberFromChannelLevelAction.java | 55 +++++ .../listener/RemoveRoleLevelAction.java | 56 +++++ .../AddMemberToChannelLevelActionPayload.java | 10 + .../model/AddRoleLevelActionPayload.java | 10 + ...veMemberFromChannelLevelActionPayload.java | 10 + .../model/RemoveRoleLevelActionPayload.java | 10 + .../repository/LevelActionRepository.java | 22 ++ .../service/AUserExperienceServiceBean.java | 50 +++-- .../service/LevelActionServiceBean.java | 170 +++++++++++++++ .../LevelActionManagementServiceBean.java | 75 +++++++ .../resources/experience-config.properties | 6 +- .../migrations/1.5.26/collection.xml | 7 + .../migrations/1.5.26/seedData/command.xml | 25 +++ .../migrations/1.5.26/seedData/data.xml | 6 + .../migrations/1.5.26/tables/level_action.xml | 49 +++++ .../migrations/1.5.26/tables/tables.xml | 8 + .../migrations/experience-changeLog.xml | 1 + .../config/ExperienceFeatureConfig.java | 3 +- .../config/ExperienceFeatureMode.java | 3 +- .../LevelActionNotFoundException.java | 21 ++ .../listener/LevelActionListener.java | 21 ++ .../listener/MemberActionModification.java | 23 +++ .../experience/model/LevelActionPayload.java | 4 + .../model/database/LevelAction.java | 53 +++++ .../model/template/LevelActionDisplay.java | 14 ++ .../model/template/LevelActionsDisplay.java | 12 ++ .../service/LevelActionService.java | 21 ++ .../LevelActionManagementService.java | 20 ++ .../handler/RoleParameterHandlerImpl.java | 30 +-- .../core/service/ChannelServiceBean.java | 21 ++ .../core/service/RoleServiceBean.java | 29 +++ .../core/service/ChannelService.java | 5 + .../abstracto/core/service/RoleService.java | 2 + .../abstracto/core/utils/BeanUtils.java | 0 .../abstracto/core/utils/ParseUtils.java | 69 +++++++ 43 files changed, 1476 insertions(+), 53 deletions(-) create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/AddLevelAction.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/RemoveLevelAction.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/ShowLevelActions.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/AddMemberToChannelLevelAction.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/AddRoleLevelAction.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/RemoveMemberFromChannelLevelAction.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/RemoveRoleLevelAction.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/AddMemberToChannelLevelActionPayload.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/AddRoleLevelActionPayload.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/RemoveMemberFromChannelLevelActionPayload.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/RemoveRoleLevelActionPayload.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/LevelActionRepository.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/LevelActionServiceBean.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/LevelActionManagementServiceBean.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/collection.xml create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/seedData/command.xml create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/seedData/data.xml create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/tables/level_action.xml create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/tables/tables.xml create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/exception/LevelActionNotFoundException.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/listener/LevelActionListener.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/listener/MemberActionModification.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/LevelActionPayload.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/LevelAction.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LevelActionDisplay.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LevelActionsDisplay.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/LevelActionService.java create mode 100644 abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/LevelActionManagementService.java rename abstracto-application/core/{core-impl => core-int}/src/main/java/dev/sheldan/abstracto/core/utils/BeanUtils.java (100%) diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/pom.xml b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/pom.xml index 38e734967..e34becd98 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/pom.xml +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/pom.xml @@ -38,6 +38,11 @@ ${project.version} + + com.google.code.gson + gson + + dev.sheldan.abstracto.core core-int diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/AddLevelAction.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/AddLevelAction.java new file mode 100644 index 000000000..91428fbd8 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/AddLevelAction.java @@ -0,0 +1,193 @@ +package dev.sheldan.abstracto.experience.command; + +import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; +import dev.sheldan.abstracto.core.command.config.CommandConfiguration; +import dev.sheldan.abstracto.core.command.config.HelpInfo; +import dev.sheldan.abstracto.core.command.config.Parameter; +import dev.sheldan.abstracto.core.command.execution.CommandResult; +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.config.FeatureMode; +import dev.sheldan.abstracto.core.interaction.InteractionService; +import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig; +import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandAutoCompleteService; +import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService; +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.models.database.AUserInAServer; +import dev.sheldan.abstracto.core.service.management.ServerManagementService; +import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; +import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition; +import dev.sheldan.abstracto.experience.config.ExperienceFeatureMode; +import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames; +import dev.sheldan.abstracto.experience.exception.LevelActionNotFoundException; +import dev.sheldan.abstracto.experience.listener.LevelActionListener; +import dev.sheldan.abstracto.experience.model.LevelActionPayload; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.service.LevelActionService; +import dev.sheldan.abstracto.experience.service.management.LevelActionManagementService; +import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +@Component +@Slf4j +public class AddLevelAction extends AbstractConditionableCommand { + + private static final String COMMAND_NAME = "addLevelAction"; + private static final String ACTION_PARAMETER_NAME = "action"; + private static final String LEVEL_PARAMETER_NAME = "level"; + private static final String PARAMETER_PARAMETER_NAME = "parameter"; + private static final String MEMBER_PARAMETER_NAME = "member"; + + private static final String RESPONSE_TEMPLATE = "addLevelAction_response"; + + @Autowired + private LevelActionManagementService levelActionManagementService; + + @Autowired + private InteractionService interactionService; + + @Autowired + private SlashCommandParameterService slashCommandParameterService; + + @Autowired + private SlashCommandAutoCompleteService slashCommandAutoCompleteService; + + @Autowired + private LevelActionService levelActionService; + + @Autowired + private UserInServerManagementService userInServerManagementService; + + @Autowired + private ServerManagementService serverManagementService; + + @Autowired + private UserExperienceManagementService userExperienceManagementService; + + @Override + public CompletableFuture executeSlash(SlashCommandInteractionEvent event) { + String actionName = slashCommandParameterService.getCommandOption(ACTION_PARAMETER_NAME, event, String.class); + LevelActionListener listener = levelActionService.getLevelActionListenerForName(actionName) + .orElseThrow(LevelActionNotFoundException::new); + AUserInAServer aUserInAServer; + if(slashCommandParameterService.hasCommandOption(MEMBER_PARAMETER_NAME, event)) { + Member member = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER_NAME, event, Member.class); + aUserInAServer = userInServerManagementService.loadOrCreateUser(member); + } else { + aUserInAServer = null; + } + Integer level = slashCommandParameterService.getCommandOption(LEVEL_PARAMETER_NAME, event, Integer.class); + String parameter = slashCommandParameterService.getCommandOption(PARAMETER_PARAMETER_NAME, event, String.class); + LevelActionPayload payload = listener.createPayload(event.getGuild(), parameter); + AServer server = serverManagementService.loadServer(event.getGuild()); + log.info("Adding level action {} for level {} in server {}.", actionName, level, event.getGuild().getId()); + AUserExperience userExperience = null; + if(aUserInAServer != null) { + Optional aUserExperienceOptional = userExperienceManagementService.findByUserInServerIdOptional(aUserInAServer.getUserInServerId()); + userExperience = aUserExperienceOptional.orElseGet(() -> { + AUserExperience user = userExperienceManagementService.createUserInServer(aUserInAServer); + return userExperienceManagementService.saveUser(user); + }); + } + levelActionManagementService.createLevelAction(level, server, actionName, userExperience, payload); + return interactionService.replyEmbed(RESPONSE_TEMPLATE, event) + .thenApply(interactionHook -> CommandResult.fromSuccess()); + } + + @Override + public List performAutoComplete(CommandAutoCompleteInteractionEvent event) { + if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), ACTION_PARAMETER_NAME)) { + String input = event.getFocusedOption().getValue().toLowerCase(); + List availableLevelActions = levelActionService.getAvailableLevelActions() + .stream() + .map(String::toLowerCase) + .toList(); + if(!input.isEmpty()) { + return availableLevelActions.stream().filter(s -> s.startsWith(input)).toList(); + } else { + return availableLevelActions; + } + } else { + return new ArrayList<>(); + } + } + + @Override + public CommandConfiguration getConfiguration() { + Parameter actionParameter = Parameter + .builder() + .name(ACTION_PARAMETER_NAME) + .templated(true) + .type(String.class) + .supportsAutoComplete(true) + .build(); + + Parameter levelParameter = Parameter + .builder() + .name(LEVEL_PARAMETER_NAME) + .templated(true) + .type(Integer.class) + .build(); + + Parameter parameterParameter = Parameter + .builder() + .name(PARAMETER_PARAMETER_NAME) + .templated(true) + .type(String.class) + .build(); + + Parameter memberParameter = Parameter + .builder() + .name(MEMBER_PARAMETER_NAME) + .templated(true) + .optional(true) + .type(Member.class) + .build(); + + List parameters = Arrays.asList(levelParameter, actionParameter, parameterParameter, memberParameter); + HelpInfo helpInfo = HelpInfo + .builder() + .templated(true) + .build(); + + SlashCommandConfig slashCommandConfig = SlashCommandConfig + .builder() + .enabled(true) + .rootCommandName(ExperienceSlashCommandNames.EXPERIENCE_CONFIG) + .groupName("levelAction") + .commandName("add") + .build(); + + return CommandConfiguration.builder() + .name(COMMAND_NAME) + .module(ExperienceModuleDefinition.EXPERIENCE) + .templated(true) + .slashCommandConfig(slashCommandConfig) + .supportsEmbedException(true) + .slashCommandOnly(true) + .causesReaction(true) + .parameters(parameters) + .help(helpInfo) + .build(); + } + + @Override + public FeatureDefinition getFeature() { + return ExperienceFeatureDefinition.EXPERIENCE; + } + + @Override + public List getFeatureModeLimitations() { + return Arrays.asList(ExperienceFeatureMode.LEVEL_ACTION); + } +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/RemoveLevelAction.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/RemoveLevelAction.java new file mode 100644 index 000000000..f63a5e519 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/RemoveLevelAction.java @@ -0,0 +1,171 @@ +package dev.sheldan.abstracto.experience.command; + +import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; +import dev.sheldan.abstracto.core.command.config.CommandConfiguration; +import dev.sheldan.abstracto.core.command.config.HelpInfo; +import dev.sheldan.abstracto.core.command.config.Parameter; +import dev.sheldan.abstracto.core.command.execution.CommandResult; +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.config.FeatureMode; +import dev.sheldan.abstracto.core.interaction.InteractionService; +import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig; +import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandAutoCompleteService; +import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService; +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.models.database.AUserInAServer; +import dev.sheldan.abstracto.core.service.management.ServerManagementService; +import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; +import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition; +import dev.sheldan.abstracto.experience.config.ExperienceFeatureMode; +import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames; +import dev.sheldan.abstracto.experience.exception.LevelActionNotFoundException; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.service.management.LevelActionManagementService; +import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +@Component +@Slf4j +public class RemoveLevelAction extends AbstractConditionableCommand { + + private static final String COMMAND_NAME = "removeLevelAction"; + private static final String ACTION_PARAMETER_NAME = "action"; + private static final String LEVEL_PARAMETER_NAME = "level"; + private static final String MEMBER_PARAMETER_NAME = "member"; + + private static final String RESPONSE_TEMPLATE = "removeLevelAction_response"; + + @Autowired + private LevelActionManagementService levelActionManagementService; + + @Autowired + private InteractionService interactionService; + + @Autowired + private SlashCommandParameterService slashCommandParameterService; + + @Autowired + private SlashCommandAutoCompleteService slashCommandAutoCompleteService; + + @Autowired + private UserInServerManagementService userInServerManagementService; + + @Autowired + private ServerManagementService serverManagementService; + + @Autowired + private UserExperienceManagementService userExperienceManagementService; + + @Override + public CompletableFuture executeSlash(SlashCommandInteractionEvent event) { + String actionName = slashCommandParameterService.getCommandOption(ACTION_PARAMETER_NAME, event, String.class); + AUserExperience userExperience = null; + if(slashCommandParameterService.hasCommandOption(MEMBER_PARAMETER_NAME, event)) { + Member member = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER_NAME, event, Member.class); + AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member); + userExperience = userExperienceManagementService.findUserInServer(aUserInAServer); + } + Integer level = slashCommandParameterService.getCommandOption(LEVEL_PARAMETER_NAME, event, Integer.class); + AServer server = serverManagementService.loadServer(event.getGuild()); + log.info("Removing level action {} for level {} in server {}.", actionName, level, event.getGuild().getId()); + if(levelActionManagementService.getLevelAction(actionName, level, server, userExperience).isEmpty()) { + throw new LevelActionNotFoundException(); + } + levelActionManagementService.deleteLevelAction(level, server, actionName, userExperience); + return interactionService.replyEmbed(RESPONSE_TEMPLATE, event) + .thenApply(interactionHook -> CommandResult.fromSuccess()); + } + + @Override + public List performAutoComplete(CommandAutoCompleteInteractionEvent event) { + if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), ACTION_PARAMETER_NAME)) { + String input = event.getFocusedOption().getValue().toLowerCase(); + AServer server = serverManagementService.loadServer(event.getGuild()); + Set availableLevelActions = levelActionManagementService.getLevelActionsOfServer(server) + .stream() + .map(levelAction -> levelAction.getAction().toLowerCase()) + .collect(Collectors.toSet()); + if(!input.isEmpty()) { + return availableLevelActions.stream().filter(s -> s.startsWith(input)).toList(); + } else { + return new ArrayList<>(availableLevelActions); + } + } else { + return new ArrayList<>(); + } + } + + @Override + public CommandConfiguration getConfiguration() { + Parameter actionParameter = Parameter + .builder() + .name(ACTION_PARAMETER_NAME) + .templated(true) + .type(String.class) + .supportsAutoComplete(true) + .build(); + + Parameter levelParameter = Parameter + .builder() + .name(LEVEL_PARAMETER_NAME) + .templated(true) + .type(Integer.class) + .build(); + + Parameter memberParameter = Parameter + .builder() + .name(MEMBER_PARAMETER_NAME) + .templated(true) + .optional(true) + .type(Member.class) + .build(); + + List parameters = Arrays.asList(levelParameter, actionParameter, memberParameter); + HelpInfo helpInfo = HelpInfo + .builder() + .templated(true) + .build(); + + SlashCommandConfig slashCommandConfig = SlashCommandConfig + .builder() + .enabled(true) + .rootCommandName(ExperienceSlashCommandNames.EXPERIENCE_CONFIG) + .groupName("levelAction") + .commandName("remove") + .build(); + + return CommandConfiguration.builder() + .name(COMMAND_NAME) + .module(ExperienceModuleDefinition.EXPERIENCE) + .templated(true) + .slashCommandConfig(slashCommandConfig) + .supportsEmbedException(true) + .slashCommandOnly(true) + .causesReaction(true) + .parameters(parameters) + .help(helpInfo) + .build(); + } + + @Override + public FeatureDefinition getFeature() { + return ExperienceFeatureDefinition.EXPERIENCE; + } + + @Override + public List getFeatureModeLimitations() { + return Arrays.asList(ExperienceFeatureMode.LEVEL_ACTION); + } +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/ShowLevelActions.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/ShowLevelActions.java new file mode 100644 index 000000000..e67618d86 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/command/ShowLevelActions.java @@ -0,0 +1,79 @@ +package dev.sheldan.abstracto.experience.command; + +import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; +import dev.sheldan.abstracto.core.command.config.CommandConfiguration; +import dev.sheldan.abstracto.core.command.config.HelpInfo; +import dev.sheldan.abstracto.core.command.execution.CommandResult; +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.config.FeatureMode; +import dev.sheldan.abstracto.core.interaction.InteractionService; +import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig; +import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition; +import dev.sheldan.abstracto.experience.config.ExperienceFeatureMode; +import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames; +import dev.sheldan.abstracto.experience.model.template.LevelActionsDisplay; +import dev.sheldan.abstracto.experience.service.LevelActionService; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@Component +public class ShowLevelActions extends AbstractConditionableCommand { + + private static final String COMMAND_NAME = "showLevelActions"; + private static final String TEMPLATE_KEY = "showLevelActions_response"; + + @Autowired + private InteractionService interactionService; + + @Autowired + private LevelActionService levelActionService; + + @Override + public CompletableFuture executeSlash(SlashCommandInteractionEvent event) { + LevelActionsDisplay levelActionsToDisplay = levelActionService.getLevelActionsToDisplay(event.getGuild()); + return interactionService.replyEmbed(TEMPLATE_KEY, levelActionsToDisplay, event) + .thenApply(interactionHook -> CommandResult.fromSuccess()); + } + + @Override + public CommandConfiguration getConfiguration() { + HelpInfo helpInfo = HelpInfo + .builder() + .templated(true) + .build(); + + SlashCommandConfig slashCommandConfig = SlashCommandConfig + .builder() + .enabled(true) + .rootCommandName(ExperienceSlashCommandNames.EXPERIENCE_CONFIG) + .groupName("levelAction") + .commandName("show") + .build(); + + return CommandConfiguration.builder() + .name(COMMAND_NAME) + .module(ExperienceModuleDefinition.EXPERIENCE) + .templated(true) + .slashCommandConfig(slashCommandConfig) + .supportsEmbedException(true) + .slashCommandOnly(true) + .causesReaction(true) + .help(helpInfo) + .build(); + } + + @Override + public List getFeatureModeLimitations() { + return Arrays.asList(ExperienceFeatureMode.LEVEL_ACTION); + } + + @Override + public FeatureDefinition getFeature() { + return ExperienceFeatureDefinition.EXPERIENCE; + } +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/AddMemberToChannelLevelAction.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/AddMemberToChannelLevelAction.java new file mode 100644 index 000000000..8245fdddb --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/AddMemberToChannelLevelAction.java @@ -0,0 +1,55 @@ +package dev.sheldan.abstracto.experience.listener; + +import com.google.gson.Gson; +import dev.sheldan.abstracto.core.utils.ParseUtils; +import dev.sheldan.abstracto.experience.model.*; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.model.database.LevelAction; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class AddMemberToChannelLevelAction implements LevelActionListener { + + public static final String ADD_MEMBER_TO_CHANNEL_ABOVE_LEVEL = "add_member_to_channel_above_level"; + + @Autowired + private Gson gson; + + @Override + public void apply(AUserExperience userExperience, LevelAction levelAction, MemberActionModification container) { + AddMemberToChannelLevelActionPayload payload = (AddMemberToChannelLevelActionPayload) levelAction.getLoadedPayload(); + log.info("Adding member {} to channel {} in server {}.", userExperience.getUser().getUserReference().getId(), payload.getChannelId(), userExperience.getServer().getId()); + container.getChannelsToAdd().add(payload.getChannelId()); + container.getChannelsToRemove().remove(payload.getChannelId()); + } + + @Override + public void prepareAction(LevelAction levelAction) { + levelAction.setLoadedPayload(gson.fromJson(levelAction.getPayload(), AddMemberToChannelLevelActionPayload.class)); + } + + @Override + public AddMemberToChannelLevelActionPayload createPayload(Guild guild, String input) { + GuildChannel channel = ParseUtils.parseGuildChannelFromText(input, guild); + return AddMemberToChannelLevelActionPayload + .builder() + .channelId(channel.getIdLong()) + .build(); + } + + @Override + public String getName() { + return ADD_MEMBER_TO_CHANNEL_ABOVE_LEVEL; + } + + @Override + public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) { + return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel(); + } + +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/AddRoleLevelAction.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/AddRoleLevelAction.java new file mode 100644 index 000000000..7b35c74d9 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/AddRoleLevelAction.java @@ -0,0 +1,55 @@ +package dev.sheldan.abstracto.experience.listener; + +import com.google.gson.Gson; +import dev.sheldan.abstracto.core.utils.ParseUtils; +import dev.sheldan.abstracto.experience.model.*; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.model.database.LevelAction; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Role; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class AddRoleLevelAction implements LevelActionListener { + + public static final String ADD_ROLE_ABOVE_LEVEL = "add_role_above_level"; + + @Autowired + private Gson gson; + + @Override + public void apply(AUserExperience userExperience, LevelAction levelAction, MemberActionModification container) { + AddRoleLevelActionPayload payload = (AddRoleLevelActionPayload) levelAction.getLoadedPayload(); + log.info("Adding role {} to user {} in server {}.", payload.getRoleId(), userExperience.getUser().getUserReference().getId(), userExperience.getServer().getId()); + container.getRolesToAdd().add(payload.getRoleId()); + container.getRolesToRemove().remove(payload.getRoleId()); + } + + @Override + public void prepareAction(LevelAction levelAction) { + levelAction.setLoadedPayload(gson.fromJson(levelAction.getPayload(), AddRoleLevelActionPayload.class)); + } + + @Override + public LevelActionPayload createPayload(Guild guild, String input) { + Role role = ParseUtils.parseRoleFromText(input, guild); + return AddRoleLevelActionPayload + .builder() + .roleId(role.getIdLong()) + .build(); + } + + @Override + public String getName() { + return ADD_ROLE_ABOVE_LEVEL; + } + + @Override + public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) { + return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel(); + } + +} 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 3fe037854..3d78ad31b 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 @@ -6,10 +6,13 @@ import dev.sheldan.abstracto.core.listener.sync.jda.JoinListener; import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.listener.MemberJoinModel; +import dev.sheldan.abstracto.core.service.FeatureModeService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition; +import dev.sheldan.abstracto.experience.config.ExperienceFeatureMode; import dev.sheldan.abstracto.experience.model.database.AUserExperience; import dev.sheldan.abstracto.experience.service.AUserExperienceService; +import dev.sheldan.abstracto.experience.service.LevelActionService; import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Member; @@ -35,6 +38,12 @@ public class JoiningUserRoleListener implements AsyncJoinListener { @Autowired private UserInServerManagementService userInServerManagementService; + @Autowired + private FeatureModeService featureModeService; + + @Autowired + private LevelActionService levelActionService; + @Override public DefaultListenerResult execute(MemberJoinModel model) { if(model.getMember().isPending()) { @@ -43,12 +52,24 @@ public DefaultListenerResult execute(MemberJoinModel model) { } Optional userInAServerOptional = userInServerManagementService.loadUserOptional(model.getServerId(), model.getJoiningUser().getUserId()); userInAServerOptional.ifPresent(aUserInAServer -> { - Optional userExperienceOptional = userExperienceManagementService.findByUserInServerIdOptional(aUserInAServer.getUserInServerId()); + Long userInServerId = aUserInAServer.getUserInServerId(); + Optional userExperienceOptional = userExperienceManagementService.findByUserInServerIdOptional(userInServerId); if(userExperienceOptional.isPresent()) { log.info("User {} joined {} with previous experience. Setting up experience role again (if necessary).", model.getJoiningUser().getUserId(), model.getServerId()); - userExperienceService.syncForSingleUser(userExperienceOptional.get(), model.getMember(), true).thenAccept(result -> + AUserExperience aUserExperience = userExperienceOptional.get(); + userExperienceService.syncForSingleUser(aUserExperience, model.getMember(), true).thenAccept(result -> log.info("Finished re-assigning experience for re-joining user {} in server {}.", model.getJoiningUser().getUserId(), model.getServerId()) ); + if(featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, aUserInAServer.getServerReference() , ExperienceFeatureMode.LEVEL_ACTION)) { + levelActionService.applyLevelActionsToUser(aUserExperience) + .thenAccept(unused -> { + log.info("Executed level actions for user {}.", userInServerId); + }) + .exceptionally(throwable -> { + log.warn("Failed to execute level actions for user {}.", userInServerId, throwable); + return null; + }); + } } else { log.info("Joined user {} in server {} does not have any previous experience. Not setting up anything.", model.getJoiningUser().getUserId(), model.getServerId()); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/MemberPendingRoleListener.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/MemberPendingRoleListener.java index 295976de3..9051d5d87 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/MemberPendingRoleListener.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/MemberPendingRoleListener.java @@ -5,10 +5,13 @@ import dev.sheldan.abstracto.core.listener.async.jda.AsyncUpdatePendingListener; import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.listener.MemberUpdatePendingModel; +import dev.sheldan.abstracto.core.service.FeatureModeService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition; +import dev.sheldan.abstracto.experience.config.ExperienceFeatureMode; import dev.sheldan.abstracto.experience.model.database.AUserExperience; import dev.sheldan.abstracto.experience.service.AUserExperienceService; +import dev.sheldan.abstracto.experience.service.LevelActionService; import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Member; @@ -34,16 +37,34 @@ public class MemberPendingRoleListener implements AsyncUpdatePendingListener { @Autowired private UserInServerManagementService userInServerManagementService; + @Autowired + private FeatureModeService featureModeService; + + @Autowired + private LevelActionService levelActionService; + @Override public DefaultListenerResult execute(MemberUpdatePendingModel model) { Optional userInAServerOptional = userInServerManagementService.loadUserOptional(model.getServerId(), model.getUser().getUserId()); userInAServerOptional.ifPresent(aUserInAServer -> { - Optional userExperienceOptional = userExperienceManagementService.findByUserInServerIdOptional(aUserInAServer.getUserInServerId()); + Long userInServerId = aUserInAServer.getUserInServerId(); + Optional userExperienceOptional = userExperienceManagementService.findByUserInServerIdOptional(userInServerId); if(userExperienceOptional.isPresent()) { log.info("User {} updated pending status {} with previous experience. Setting up experience role again (if necessary).", model.getUser().getUserId(), model.getServerId()); - userExperienceService.syncForSingleUser(userExperienceOptional.get(), model.getMember(), true).thenAccept(result -> + AUserExperience aUserExperience = userExperienceOptional.get(); + userExperienceService.syncForSingleUser(aUserExperience, model.getMember(), true).thenAccept(result -> log.info("Finished re-assigning experience for update pending user {} in server {}.", model.getUser().getUserId(), model.getServerId()) ); + if(featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, aUserInAServer.getServerReference() , ExperienceFeatureMode.LEVEL_ACTION)) { + levelActionService.applyLevelActionsToUser(aUserExperience) + .thenAccept(unused -> { + log.info("Executed level actions for user {}.", userInServerId); + }) + .exceptionally(throwable -> { + log.warn("Failed to execute level actions for user {}.", userInServerId, throwable); + return null; + }); + } } else { log.info("Member updating pending {} in server {} does not have any previous experience. Not setting up anything.", model.getUser().getUserId(), model.getServerId()); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/RemoveMemberFromChannelLevelAction.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/RemoveMemberFromChannelLevelAction.java new file mode 100644 index 000000000..441bad6c9 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/RemoveMemberFromChannelLevelAction.java @@ -0,0 +1,55 @@ +package dev.sheldan.abstracto.experience.listener; + +import com.google.gson.Gson; +import dev.sheldan.abstracto.core.utils.ParseUtils; +import dev.sheldan.abstracto.experience.model.RemoveMemberFromChannelLevelActionPayload; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.model.database.LevelAction; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class RemoveMemberFromChannelLevelAction implements LevelActionListener { + + public static final String REMOVE_MEMBER_FROM_CHANNEL_ABOVE_LEVEL = "remove_member_from_channel_above_level"; + + @Autowired + private Gson gson; + + @Override + public void apply(AUserExperience userExperience, LevelAction levelAction, MemberActionModification container) { + RemoveMemberFromChannelLevelActionPayload payload = (RemoveMemberFromChannelLevelActionPayload) levelAction.getLoadedPayload(); + log.info("Removing member {} from channel {} in server {}.", userExperience.getUser().getUserReference().getId(), payload.getChannelId(), userExperience.getServer().getId()); + container.getChannelsToRemove().add(payload.getChannelId()); + container.getChannelsToAdd().remove(payload.getChannelId()); + } + + @Override + public void prepareAction(LevelAction levelAction) { + levelAction.setLoadedPayload(gson.fromJson(levelAction.getPayload(), RemoveMemberFromChannelLevelActionPayload.class)); + } + + @Override + public RemoveMemberFromChannelLevelActionPayload createPayload(Guild guild, String input) { + GuildChannel channel = ParseUtils.parseGuildChannelFromText(input, guild); + return RemoveMemberFromChannelLevelActionPayload + .builder() + .channelId(channel.getIdLong()) + .build(); + } + + @Override + public String getName() { + return REMOVE_MEMBER_FROM_CHANNEL_ABOVE_LEVEL; + } + + @Override + public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) { + return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel(); + } + +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/RemoveRoleLevelAction.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/RemoveRoleLevelAction.java new file mode 100644 index 000000000..098547d84 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/RemoveRoleLevelAction.java @@ -0,0 +1,56 @@ +package dev.sheldan.abstracto.experience.listener; + +import com.google.gson.Gson; +import dev.sheldan.abstracto.core.utils.ParseUtils; +import dev.sheldan.abstracto.experience.model.*; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.model.database.LevelAction; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Role; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class RemoveRoleLevelAction implements LevelActionListener { + + public static final String REMOVE_ROLE_ABOVE_LEVEL = "remove_role_above_level"; + + @Autowired + private Gson gson; + + @Override + public void apply(AUserExperience userExperience, LevelAction levelAction, MemberActionModification container) { + RemoveRoleLevelActionPayload payload = (RemoveRoleLevelActionPayload) levelAction.getLoadedPayload(); + log.info("Removing role {} from user {} in server {}.", payload.getRoleId(), userExperience.getUser().getUserReference().getId(), userExperience.getServer().getId()); + container.getRolesToRemove().add(payload.getRoleId()); + container.getRolesToAdd().remove(payload.getRoleId()); + } + + + @Override + public void prepareAction(LevelAction levelAction) { + levelAction.setLoadedPayload(gson.fromJson(levelAction.getPayload(), RemoveRoleLevelActionPayload.class)); + } + + @Override + public LevelActionPayload createPayload(Guild guild, String input) { + Role role = ParseUtils.parseRoleFromText(input, guild); + return RemoveRoleLevelActionPayload + .builder() + .roleId(role.getIdLong()) + .build(); + } + + @Override + public String getName() { + return REMOVE_ROLE_ABOVE_LEVEL; + } + + @Override + public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) { + return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel(); + } + +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/AddMemberToChannelLevelActionPayload.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/AddMemberToChannelLevelActionPayload.java new file mode 100644 index 000000000..9851736b0 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/AddMemberToChannelLevelActionPayload.java @@ -0,0 +1,10 @@ +package dev.sheldan.abstracto.experience.model; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class AddMemberToChannelLevelActionPayload implements LevelActionPayload { + private Long channelId; +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/AddRoleLevelActionPayload.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/AddRoleLevelActionPayload.java new file mode 100644 index 000000000..38aedc85d --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/AddRoleLevelActionPayload.java @@ -0,0 +1,10 @@ +package dev.sheldan.abstracto.experience.model; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class AddRoleLevelActionPayload implements LevelActionPayload { + private Long roleId; +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/RemoveMemberFromChannelLevelActionPayload.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/RemoveMemberFromChannelLevelActionPayload.java new file mode 100644 index 000000000..828a20bc7 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/RemoveMemberFromChannelLevelActionPayload.java @@ -0,0 +1,10 @@ +package dev.sheldan.abstracto.experience.model; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class RemoveMemberFromChannelLevelActionPayload implements LevelActionPayload { + private Long channelId; +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/RemoveRoleLevelActionPayload.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/RemoveRoleLevelActionPayload.java new file mode 100644 index 000000000..426663b0b --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/model/RemoveRoleLevelActionPayload.java @@ -0,0 +1,10 @@ +package dev.sheldan.abstracto.experience.model; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class RemoveRoleLevelActionPayload implements LevelActionPayload { + private Long roleId; +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/LevelActionRepository.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/LevelActionRepository.java new file mode 100644 index 000000000..06a4aab41 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/LevelActionRepository.java @@ -0,0 +1,22 @@ +package dev.sheldan.abstracto.experience.repository; + +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.experience.model.database.AExperienceLevel; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.model.database.LevelAction; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface LevelActionRepository extends JpaRepository { + List findByServerAndAffectedUserIsNullOrServerAndAffectedUser(AServer server, AServer server2, AUserExperience user); + Optional findByServerAndActionAndLevelOrAffectedUserAndLevelAndAction(AServer server, String action, AExperienceLevel level, AUserExperience user, AExperienceLevel level2, String action2); + List findByServer(AServer server); + + void deleteByLevelAndActionAndServer(AExperienceLevel level, String action, AServer server); + void deleteByLevelAndActionAndAffectedUser(AExperienceLevel level, String action, AUserExperience affectedUser); + +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java index bc796695c..478ca2b74 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java @@ -104,6 +104,9 @@ public class AUserExperienceServiceBean implements AUserExperienceService { @Autowired private FeatureModeService featureModeService; + @Autowired + private LevelActionService levelActionService; + @Autowired private AUserExperienceServiceBean self; @@ -306,25 +309,26 @@ public void addExperienceToMember(Member member, Message message) { log.debug("User {} has a experience disable role in server {} - not giving any experience.", member.getIdLong(), serverId); return; } - List levels = experienceLevelManagementService.getLevelConfig(); - levels.sort(Comparator.comparing(AExperienceLevel::getExperienceNeeded)); - - Long minExp = configService.getLongValueOrConfigDefault(ExperienceFeatureConfig.MIN_EXP_KEY, serverId); - Long maxExp = configService.getLongValueOrConfigDefault(ExperienceFeatureConfig.MAX_EXP_KEY, serverId); - Double multiplier = configService.getDoubleValueOrConfigDefault(ExperienceFeatureConfig.EXP_MULTIPLIER_KEY, serverId); - Long experienceRange = maxExp - minExp + 1; - Long gainedExperience = (secureRandom.nextInt(experienceRange.intValue()) + minExp); - gainedExperience = (long) Math.floor(gainedExperience * multiplier); - - List roles = experienceRoleManagementService.getExperienceRolesForServer(server); - roles.sort(Comparator.comparing(role -> role.getLevel().getLevel())); - AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(member); Long userInServerId = userInAServer.getUserInServerId(); - log.debug("Handling {}. The user might gain {}.", userInServerId, gainedExperience); Optional aUserExperienceOptional = userExperienceManagementService.findByUserInServerIdOptional(userInAServer.getUserInServerId()); AUserExperience aUserExperience = aUserExperienceOptional.orElseGet(() -> userExperienceManagementService.createUserInServer(userInAServer)); if(Boolean.FALSE.equals(aUserExperience.getExperienceGainDisabled())) { + List levels = experienceLevelManagementService.getLevelConfig(); + levels.sort(Comparator.comparing(AExperienceLevel::getExperienceNeeded)); + + Long minExp = configService.getLongValueOrConfigDefault(ExperienceFeatureConfig.MIN_EXP_KEY, serverId); + Long maxExp = configService.getLongValueOrConfigDefault(ExperienceFeatureConfig.MAX_EXP_KEY, serverId); + Double multiplier = configService.getDoubleValueOrConfigDefault(ExperienceFeatureConfig.EXP_MULTIPLIER_KEY, serverId); + Long experienceRange = maxExp - minExp + 1; + Long gainedExperience = (secureRandom.nextInt(experienceRange.intValue()) + minExp); + gainedExperience = (long) Math.floor(gainedExperience * multiplier); + + List roles = experienceRoleManagementService.getExperienceRolesForServer(server); + roles.sort(Comparator.comparing(role -> role.getLevel().getLevel())); + + log.debug("Handling {}. The user gains {}.", userInServerId, gainedExperience); + Long oldExperience = aUserExperience.getExperience(); Long newExperienceCount = oldExperience + gainedExperience; aUserExperience.setExperience(newExperienceCount); @@ -367,7 +371,17 @@ public void addExperienceToMember(Member member, Message message) { aUserExperience.setCurrentExperienceRole(calculatedNewRole); } aUserExperience.setMessageCount(aUserExperience.getMessageCount() + 1L); - if(!aUserExperienceOptional.isPresent()) { + if(featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, server, ExperienceFeatureMode.LEVEL_ACTION)) { + levelActionService.applyLevelActionsToUser(aUserExperience) + .thenAccept(unused -> { + log.info("Executed level actions for user {}.", userInServerId); + }) + .exceptionally(throwable -> { + log.warn("Failed to execute level actions for user {}.", userInServerId, throwable); + return null; + }); + } + if(aUserExperienceOptional.isEmpty()) { userExperienceManagementService.saveUser(aUserExperience); } if(!Objects.equals(result.getOldRoleId(), result.getNewRoleId())) { @@ -375,7 +389,7 @@ public void addExperienceToMember(Member member, Message message) { roleService.updateRolesIds(member, Arrays.asList(result.getOldRoleId()), Arrays.asList(result.getNewRoleId())).thenAccept(unused -> { log.debug("Removed role {} from and added role {} to member {} in server {}.", result.getOldRoleId(), result.getNewRoleId(), member.getIdLong(), member.getGuild().getIdLong()); }).exceptionally(throwable -> { - log.warn("Failed to remove role {} from and add role {} to member {} in server {}.", result.getOldRoleId(), member.getIdLong(), member.getGuild().getIdLong(), throwable); + log.warn("Failed to remove role {} from and add role {} to member {} in server {}.", result.getOldRoleId(), result.getNewRoleId(), member.getIdLong(), member.getGuild().getIdLong(), throwable); return null; }); } else { @@ -383,7 +397,7 @@ public void addExperienceToMember(Member member, Message message) { roleService.removeRoleFromMemberAsync(member, result.getOldRoleId()).thenAccept(unused -> { log.debug("Removed role {} from member {} in server {}.", result.getOldRoleId(), member.getIdLong(), member.getGuild().getIdLong()); }).exceptionally(throwable -> { - log.warn("Failed to remove role {} from {} member {} in server {}.", result.getOldRoleId(), member.getIdLong(), member.getGuild().getIdLong(), throwable); + log.warn("Failed to remove role {} from member {} in server {}.", result.getOldRoleId(), member.getIdLong(), member.getGuild().getIdLong(), throwable); return null; }); } @@ -391,7 +405,7 @@ public void addExperienceToMember(Member member, Message message) { roleService.addRoleToMemberAsync(member, result.getNewRoleId()).thenAccept(unused -> { log.debug("Added role {} to member {} in server {}.", result.getNewRoleId(), member.getIdLong(), member.getGuild().getIdLong()); }).exceptionally(throwable -> { - log.warn("Failed to add role {} to {} member {} in server {}.", result.getOldRoleId(), member.getIdLong(), member.getGuild().getIdLong(), throwable); + log.warn("Failed to add role {} to member {} in server {}.", result.getOldRoleId(), member.getIdLong(), member.getGuild().getIdLong(), throwable); return null; }); } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/LevelActionServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/LevelActionServiceBean.java new file mode 100644 index 000000000..be14c4322 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/LevelActionServiceBean.java @@ -0,0 +1,170 @@ +package dev.sheldan.abstracto.experience.service; + +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.models.template.display.MemberDisplay; +import dev.sheldan.abstracto.core.service.ChannelService; +import dev.sheldan.abstracto.core.service.GuildService; +import dev.sheldan.abstracto.core.service.RoleService; +import dev.sheldan.abstracto.core.service.management.ServerManagementService; +import dev.sheldan.abstracto.core.utils.CompletableFutureList; +import dev.sheldan.abstracto.experience.listener.LevelActionListener; +import dev.sheldan.abstracto.experience.listener.MemberActionModification; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.model.database.LevelAction; +import dev.sheldan.abstracto.experience.model.template.LevelActionDisplay; +import dev.sheldan.abstracto.experience.model.template.LevelActionsDisplay; +import dev.sheldan.abstracto.experience.service.management.LevelActionManagementService; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Component +@Slf4j +public class LevelActionServiceBean implements LevelActionService { + + @Autowired + private LevelActionManagementService levelActionManagementService; + + @Autowired(required = false) + private List levelActions = new ArrayList<>(); + + @Autowired + private RoleService roleService; + + @Autowired + private ChannelService channelService; + + @Autowired + private GuildService guildService; + + @Autowired + private ServerManagementService serverManagementService; + + @Override + public CompletableFuture applyLevelActionsToUser(AUserExperience user) { + if(levelActions == null || levelActions.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + List levelActionsOfUserInServer = levelActionManagementService.getLevelActionsOfUserInServer(user); + if(levelActionsOfUserInServer.isEmpty()) { + log.info("No actions available - no actions executed."); + return CompletableFuture.completedFuture(null); + } + + Map> actionConfigMap = new HashMap<>(); + + levelActionsOfUserInServer.forEach(levelAction -> { + if(levelAction.getLevel().getLevel() > user.getLevelOrDefault()) { + return; + } + if(actionConfigMap.containsKey(levelAction.getLevel().getLevel())) { + actionConfigMap.get(levelAction.getLevel().getLevel()).add(levelAction); + } else { + List levelLevelActions = new ArrayList<>(); + levelLevelActions.add(levelAction); + actionConfigMap.put(levelAction.getLevel().getLevel(), levelLevelActions); + } + }); + + Map actionStringListenerMap = levelActions + .stream() + .collect(Collectors.toMap(a -> a.getName().toLowerCase(), Function.identity())); + + List levels = actionConfigMap + .keySet() + .stream() + .sorted() + .toList(); + + log.debug("Performing actions for {} levels.", levels.size()); + + MemberActionModification modification = MemberActionModification + .builder() + .build(); + levels.forEach(level -> { + List actionsOnLevel = actionConfigMap.get(level); + actionsOnLevel.forEach(levelAction -> { + LevelActionListener listener = actionStringListenerMap.get(levelAction.getAction().toLowerCase()); + listener.prepareAction(levelAction); + listener.apply(user, levelAction, modification); + }); + }); + + return evaluateModifications(user, modification); + } + + private CompletableFuture evaluateModifications(AUserExperience user, MemberActionModification modification) { + List> futures = new ArrayList<>(); + Long userId = user.getUser().getUserReference().getId(); + log.info("Updating user {}, rolesToAdd: {}, rolesToRemove: {}", + userId, modification.getRolesToAdd().size(), modification.getRolesToRemove().size()); + if(!modification.getRolesToAdd().isEmpty() || !modification.getRolesToRemove().isEmpty()) { + CompletableFuture roleFuture = roleService.updateRolesIds(user.getUser(), new ArrayList<>(modification.getRolesToAdd()), new ArrayList<>(modification.getRolesToRemove())); + futures.add(roleFuture); + } + log.info("Updating user {}, channelsToAdd: {}, channelsToRemove: {}.", userId, modification.getChannelsToAdd().size(), modification.getChannelsToRemove().size()); + Guild guild = guildService.getGuildById(user.getServer().getId()); + EnumSet permissions = EnumSet.of(Permission.VIEW_CHANNEL, Permission.MESSAGE_SEND); + modification.getChannelsToAdd().forEach(channelId -> { + futures.add(channelService.addMemberViewToChannel(guild, channelId, userId, permissions)); + }); + modification.getChannelsToRemove().forEach(channelId -> { + futures.add(channelService.removeChannelOverrideForMember(guild, channelId, userId)); + }); + if(!futures.isEmpty()) { + return new CompletableFutureList<>(futures).getMainFuture(); + } else { + log.info("Actions resulted in no actions performed."); + return CompletableFuture.completedFuture(null); + } + } + + @Override + public List getAvailableLevelActions() { + return levelActions + .stream() + .map(LevelActionListener::getName) + .map(String::toLowerCase) + .toList(); + } + + @Override + public Optional getLevelActionListenerForName(String name) { + return levelActions + .stream() + .filter(levelActionListener -> levelActionListener.getName().equalsIgnoreCase(name)) + .findFirst(); + } + + @Override + public Optional getLevelAction(AUserExperience userExperience, String action, Integer level) { + return Optional.empty(); + } + + @Override + public LevelActionsDisplay getLevelActionsToDisplay(Guild guild) { + AServer server = serverManagementService.loadServer(guild); + List actions = levelActionManagementService.getLevelActionsOfServer(server) + .stream().map(levelAction -> LevelActionDisplay + .builder() + .actionKey(levelAction.getAction().toLowerCase()) + .level(levelAction.getLevel().getLevel()) + .parameters(levelAction.getPayload()) + .member(levelAction.getAffectedUser() != null ? MemberDisplay.fromAUserInAServer(levelAction.getAffectedUser().getUser()) : null) + .build()) + .toList(); + return LevelActionsDisplay + .builder() + .actions(actions) + .build(); + } + + +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/LevelActionManagementServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/LevelActionManagementServiceBean.java new file mode 100644 index 000000000..7d503d485 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/LevelActionManagementServiceBean.java @@ -0,0 +1,75 @@ +package dev.sheldan.abstracto.experience.service.management; + +import com.google.gson.Gson; +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.experience.model.LevelActionPayload; +import dev.sheldan.abstracto.experience.model.database.AExperienceLevel; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.model.database.LevelAction; +import dev.sheldan.abstracto.experience.repository.LevelActionRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Optional; + +@Component +public class LevelActionManagementServiceBean implements LevelActionManagementService { + + @Autowired + private LevelActionRepository levelActionRepository; + + @Autowired + private ExperienceLevelManagementService experienceLevelManagementService; + + @Autowired + private Gson gson; + + @Override + public LevelAction createLevelAction(Integer level, AServer server, String action, AUserExperience user, String payload) { + AExperienceLevel experienceLevel = experienceLevelManagementService.getLevel(level); + LevelAction levelAction = LevelAction + .builder() + .action(action) + .affectedUser(user) + .payload(payload) + .server(server) + .level(experienceLevel) + .build(); + return levelActionRepository.save(levelAction); + } + + @Override + public void deleteLevelAction(Integer level, AServer server, String action, AUserExperience user) { + AExperienceLevel experienceLevel = experienceLevelManagementService.getLevel(level); + if(user == null) { + levelActionRepository.deleteByLevelAndActionAndServer(experienceLevel, action, server); + } else { + levelActionRepository.deleteByLevelAndActionAndAffectedUser(experienceLevel, action, user); + } + } + + @Override + public LevelAction createLevelAction(Integer level, AServer server, String action, AUserExperience user, LevelActionPayload actionPayload) { + String payload = gson.toJson(actionPayload); + return createLevelAction(level, server, action, user, payload); + } + + @Override + public List getLevelActionsOfUserInServer(AUserExperience aUserInAServer) { + return levelActionRepository.findByServerAndAffectedUserIsNullOrServerAndAffectedUser(aUserInAServer.getServer(), + aUserInAServer.getServer(), aUserInAServer); + } + + @Override + public List getLevelActionsOfServer(AServer server) { + return levelActionRepository.findByServer(server); + } + + @Override + public Optional getLevelAction(String action, Integer level, AServer server, AUserExperience aUserExperience) { + AExperienceLevel experienceLevel = experienceLevelManagementService.getLevel(level); + return levelActionRepository.findByServerAndActionAndLevelOrAffectedUserAndLevelAndAction(server, action.toLowerCase(), + experienceLevel, aUserExperience, experienceLevel, action.toLowerCase()); + } +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/experience-config.properties b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/experience-config.properties index fa4815110..b39139a6c 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/experience-config.properties +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/experience-config.properties @@ -13,4 +13,8 @@ abstracto.systemConfigs.expCooldownSeconds.longValue=60 abstracto.featureModes.levelUpNotification.featureName=experience abstracto.featureModes.levelUpNotification.mode=levelUpNotification -abstracto.featureModes.levelUpNotification.enabled=false \ No newline at end of file +abstracto.featureModes.levelUpNotification.enabled=false + +abstracto.featureModes.levelAction.featureName=experience +abstracto.featureModes.levelAction.mode=levelAction +abstracto.featureModes.levelAction.enabled=false \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/collection.xml b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/collection.xml new file mode 100644 index 000000000..9b55885aa --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/collection.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/seedData/command.xml b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/seedData/command.xml new file mode 100644 index 000000000..1898e7b8e --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/seedData/command.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/seedData/data.xml b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/seedData/data.xml new file mode 100644 index 000000000..e18fc4181 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/seedData/data.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/tables/level_action.xml b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/tables/level_action.xml new file mode 100644 index 000000000..dde6ea45a --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/tables/level_action.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DROP TRIGGER IF EXISTS level_action_update_trigger ON level_action; + CREATE TRIGGER level_action_update_trigger BEFORE UPDATE ON level_action FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure(); + + + DROP TRIGGER IF EXISTS level_action_insert_trigger ON level_action; + CREATE TRIGGER level_action_insert_trigger BEFORE INSERT ON level_action FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure(); + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/tables/tables.xml b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/tables/tables.xml new file mode 100644 index 000000000..027d4394f --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/1.5.26/tables/tables.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/experience-changeLog.xml b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/experience-changeLog.xml index 123cfe425..b2983cca6 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/experience-changeLog.xml +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/migrations/experience-changeLog.xml @@ -6,4 +6,5 @@ + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceFeatureConfig.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceFeatureConfig.java index 88d2ed9c8..74db2d70c 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceFeatureConfig.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceFeatureConfig.java @@ -8,6 +8,7 @@ import java.util.Arrays; import java.util.List; +import static dev.sheldan.abstracto.experience.config.ExperienceFeatureMode.LEVEL_ACTION; import static dev.sheldan.abstracto.experience.config.ExperienceFeatureMode.LEVEL_UP_NOTIFICATION; /** @@ -46,6 +47,6 @@ public List getRequiredSystemConfigKeys() { @Override public List getAvailableModes() { - return Arrays.asList(LEVEL_UP_NOTIFICATION); + return Arrays.asList(LEVEL_UP_NOTIFICATION, LEVEL_ACTION); } } diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceFeatureMode.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceFeatureMode.java index f0a95598c..f17bb7d80 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceFeatureMode.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceFeatureMode.java @@ -5,7 +5,8 @@ @Getter public enum ExperienceFeatureMode implements FeatureMode { - LEVEL_UP_NOTIFICATION("levelUpNotification"); + LEVEL_UP_NOTIFICATION("levelUpNotification"), + LEVEL_ACTION("levelAction"); private final String key; diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/exception/LevelActionNotFoundException.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/exception/LevelActionNotFoundException.java new file mode 100644 index 000000000..8a744e6c7 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/exception/LevelActionNotFoundException.java @@ -0,0 +1,21 @@ +package dev.sheldan.abstracto.experience.exception; + +import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException; +import dev.sheldan.abstracto.core.templating.Templatable; + +public class LevelActionNotFoundException extends AbstractoRunTimeException implements Templatable { + + public LevelActionNotFoundException() { + super("Level action not found."); + } + + @Override + public String getTemplateName() { + return "level_action_not_found_exception"; + } + + @Override + public Object getTemplateModel() { + return new Object(); + } +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/listener/LevelActionListener.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/listener/LevelActionListener.java new file mode 100644 index 000000000..76c6690e6 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/listener/LevelActionListener.java @@ -0,0 +1,21 @@ +package dev.sheldan.abstracto.experience.listener; + +import dev.sheldan.abstracto.experience.model.LevelActionPayload; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.model.database.LevelAction; +import net.dv8tion.jda.api.entities.Guild; +import org.springframework.stereotype.Component; + +@Component +public interface LevelActionListener { + String getName(); + + void apply(AUserExperience userExperience, LevelAction levelAction, MemberActionModification container); + + boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction); + + void prepareAction(LevelAction levelAction); + + + LevelActionPayload createPayload(Guild guild, String input); +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/listener/MemberActionModification.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/listener/MemberActionModification.java new file mode 100644 index 000000000..b55d217b0 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/listener/MemberActionModification.java @@ -0,0 +1,23 @@ +package dev.sheldan.abstracto.experience.listener; + +import lombok.Builder; +import lombok.Getter; + +import java.util.HashSet; +import java.util.Set; + +@Getter +@Builder +public class MemberActionModification { + @Builder.Default + private Set rolesToRemove = new HashSet<>(); + + @Builder.Default + private Set rolesToAdd = new HashSet<>(); + + @Builder.Default + private Set channelsToRemove = new HashSet<>(); + + @Builder.Default + private Set channelsToAdd = new HashSet<>(); +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/LevelActionPayload.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/LevelActionPayload.java new file mode 100644 index 000000000..952f14b97 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/LevelActionPayload.java @@ -0,0 +1,4 @@ +package dev.sheldan.abstracto.experience.model; + +public interface LevelActionPayload { +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/LevelAction.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/LevelAction.java new file mode 100644 index 000000000..bfb19cda7 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/LevelAction.java @@ -0,0 +1,53 @@ +package dev.sheldan.abstracto.experience.model.database; + +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.experience.model.LevelActionPayload; +import jakarta.persistence.*; +import lombok.*; + +import java.time.Instant; + +@Builder +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "level_action") +@Getter +@Setter +@EqualsAndHashCode +public class LevelAction { + + @Id + @Column(name = "id", nullable = false) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "level_id", nullable = false) + private AExperienceLevel level; + + @Column(name = "action", nullable = false) + private String action; + + @Column(name = "payload", nullable = false) + private String payload; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "affected_user_id") + private AUserExperience affectedUser; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "server_id", nullable = false) + private AServer server; + + @Column(name = "created", nullable = false, insertable = false, updatable = false) + private Instant created; + + @Column(name = "updated", insertable = false, updatable = false) + private Instant updated; + + @Builder.Default + @Transient + private LevelActionPayload loadedPayload = null; + +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LevelActionDisplay.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LevelActionDisplay.java new file mode 100644 index 000000000..c8a3a7601 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LevelActionDisplay.java @@ -0,0 +1,14 @@ +package dev.sheldan.abstracto.experience.model.template; + +import dev.sheldan.abstracto.core.models.template.display.MemberDisplay; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class LevelActionDisplay { + private String actionKey; + private Integer level; + private MemberDisplay member; + private String parameters; +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LevelActionsDisplay.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LevelActionsDisplay.java new file mode 100644 index 000000000..695ee54b9 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/template/LevelActionsDisplay.java @@ -0,0 +1,12 @@ +package dev.sheldan.abstracto.experience.model.template; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class LevelActionsDisplay { + private List actions; +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/LevelActionService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/LevelActionService.java new file mode 100644 index 000000000..576c25ec7 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/LevelActionService.java @@ -0,0 +1,21 @@ +package dev.sheldan.abstracto.experience.service; + +import dev.sheldan.abstracto.experience.listener.LevelActionListener; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.model.database.LevelAction; +import dev.sheldan.abstracto.experience.model.template.LevelActionsDisplay; +import net.dv8tion.jda.api.entities.Guild; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +public interface LevelActionService { + CompletableFuture applyLevelActionsToUser(AUserExperience user); + List getAvailableLevelActions(); + Optional getLevelActionListenerForName(String name); + + Optional getLevelAction(AUserExperience userExperience, String action, Integer level); + + LevelActionsDisplay getLevelActionsToDisplay(Guild guild); +} diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/LevelActionManagementService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/LevelActionManagementService.java new file mode 100644 index 000000000..92560b518 --- /dev/null +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/LevelActionManagementService.java @@ -0,0 +1,20 @@ +package dev.sheldan.abstracto.experience.service.management; + +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.experience.model.LevelActionPayload; +import dev.sheldan.abstracto.experience.model.database.AUserExperience; +import dev.sheldan.abstracto.experience.model.database.LevelAction; + +import java.util.List; +import java.util.Optional; + +public interface LevelActionManagementService { + LevelAction createLevelAction(Integer level, AServer server, String action, AUserExperience user, String parameters); + void deleteLevelAction(Integer level, AServer server, String action, AUserExperience user); + LevelAction createLevelAction(Integer level, AServer server, String action, AUserExperience user, LevelActionPayload actionPayload); + + List getLevelActionsOfUserInServer(AUserExperience aUserInAServer); + List getLevelActionsOfServer(AServer server); + + Optional getLevelAction(String action, Integer level, AServer server, AUserExperience aUserExperience); +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/handler/RoleParameterHandlerImpl.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/handler/RoleParameterHandlerImpl.java index 2c897485a..b289ff286 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/handler/RoleParameterHandlerImpl.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/handler/RoleParameterHandlerImpl.java @@ -3,18 +3,14 @@ import dev.sheldan.abstracto.core.command.Command; import dev.sheldan.abstracto.core.command.CommandConstants; import dev.sheldan.abstracto.core.command.config.Parameter; -import dev.sheldan.abstracto.core.command.exception.AbstractoTemplatedException; import dev.sheldan.abstracto.core.command.execution.ParameterPieceType; import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece; import dev.sheldan.abstracto.core.command.handler.provided.RoleParameterHandler; +import dev.sheldan.abstracto.core.utils.ParseUtils; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Role; -import org.apache.commons.lang3.math.NumberUtils; import org.springframework.stereotype.Component; -import java.util.List; -import java.util.regex.Matcher; - @Component public class RoleParameterHandlerImpl implements RoleParameterHandler { @Override @@ -25,29 +21,7 @@ public boolean handles(Class clazz, UnparsedCommandParameterPiece value) { @Override public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Parameter param, Message context, Command command) { String inputString = ((String) input.getValue()).trim(); - Matcher matcher = Message.MentionType.ROLE.getPattern().matcher(inputString); - Role foundRole; - if(matcher.matches() && iterators.getRoleIterator().hasNext()) { - foundRole = iterators.getRoleIterator().next(); - } else { - if(NumberUtils.isParsable(inputString)) { - long roleId = Long.parseLong(inputString); - foundRole = context.getGuild().getRoleById(roleId); - } else { - List roles = context.getGuild().getRolesByName(inputString, true); - if(roles.isEmpty()) { - throw new AbstractoTemplatedException("No role found with name.", "no_role_found_by_name_exception"); - } - if(roles.size() > 1) { - throw new AbstractoTemplatedException("Multiple roles found with name.", "multiple_roles_found_by_name_exception"); - } - foundRole = roles.get(0); - } - } - if(foundRole != null && foundRole.isPublicRole()) { - throw new AbstractoTemplatedException("Public role cannot be used for role parameter.", "everyone_role_not_allowed_exception"); - } - return foundRole; + return ParseUtils.parseRoleFromText(inputString, context.getGuild()); } @Override diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java index bb69b1a9b..22a3a1515 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java @@ -16,6 +16,7 @@ import dev.sheldan.abstracto.core.utils.FileService; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.channel.concrete.Category; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; @@ -727,6 +728,26 @@ public List> sendFileToChannel(String fileContent, St } } + @Override + public CompletableFuture addMemberViewToChannel(Guild guild, Long channelId, Long memberId, Collection permissions) { + return guild.getGuildChannelById(channelId) + .getPermissionContainer() + .getPermissionContainer() + .getManager() + .putMemberPermissionOverride(memberId, permissions, new ArrayList<>()) + .submit(); + } + + @Override + public CompletableFuture removeChannelOverrideForMember(Guild guild, Long channelId, Long memberId) { + return guild + .getGuildChannelById(channelId) + .getPermissionContainer() + .getManager() + .removePermissionOverride(memberId) + .submit(); + } + @PostConstruct public void postConstruct() { 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 fc296746e..fa7addeaf 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 @@ -40,6 +40,9 @@ public class RoleServiceBean implements RoleService { @Autowired private RoleManagementService roleManagementService; + @Autowired + private MemberService memberService; + @Autowired private MetricService metricService; @@ -97,6 +100,22 @@ public CompletableFuture updateRolesIds(Member member, List rolesToR return updateRolesObj(member, rolesObjToRemove, rolesObjToAdd); } + @Override + public CompletableFuture updateRolesIds(AUserInAServer aUserInAServer, List rolesToAdd, List rolesToRemove) { + Guild guild = guildService.getGuildById(aUserInAServer.getServerReference().getId()); + List rolesObjToAdd = rolesToAdd + .stream() + .map(guild::getRoleById) + .toList(); + + List rolesObjToRemove = rolesToRemove + .stream() + .map(guild::getRoleById) + .toList(); + Member member = memberService.getMemberInServer(aUserInAServer); + return guild.modifyMemberRoles(member, rolesObjToAdd, rolesObjToRemove).submit(); + } + @Override public CompletableFuture updateRolesObj(Member member, List rolesToRemove, List rolesToAdd) { return member.getGuild().modifyMemberRoles(member, rolesToAdd, rolesToRemove).submit(); @@ -129,6 +148,16 @@ public CompletableFuture removeRoleFromMemberAsync(Member member, ARole ro return removeRoleFromUserFuture(guild, role, member.getIdLong()); } + @Override + public CompletableFuture removeRoleFromMemberAsync(AUserInAServer user, ARole role) { + Optional guildById = guildService.getGuildByIdOptional(user.getServerReference().getId()); + if(guildById.isPresent()) { + return removeRoleFromUserFuture(guildById.get(), role, user.getUserReference().getId()); + } else { + throw new GuildNotFoundException(user.getServerReference().getId()); + } + } + @Override public CompletableFuture removeRoleFromMemberAsync(Member member, Long roleId) { Role role = member.getGuild().getRoleById(roleId); diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/ChannelService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/ChannelService.java index 23b0b3691..b9e9b45d2 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/ChannelService.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/ChannelService.java @@ -3,6 +3,7 @@ import dev.sheldan.abstracto.core.models.database.AChannel; import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.templating.model.MessageToSend; +import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; @@ -11,6 +12,7 @@ import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.requests.restaction.MessageCreateAction; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -79,4 +81,7 @@ public interface ChannelService { CompletableFuture setSlowModeInChannel(TextChannel textChannel, Integer seconds); List> sendFileToChannel(String fileContent, String fileNameTemplate, String messageTemplate, Object model, MessageChannel channel); List> sendFileToChannel(String fileContent, String fileName, MessageChannel channel); + + CompletableFuture addMemberViewToChannel(Guild guild, Long channelId, Long memberId, Collection permissions); + CompletableFuture removeChannelOverrideForMember(Guild guild, Long channelId, Long memberId); } diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/RoleService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/RoleService.java index 15879f831..4eb1c7be0 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/RoleService.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/RoleService.java @@ -17,12 +17,14 @@ public interface RoleService { CompletableFuture addRoleToUserAsync(AUserInAServer aUserInAServer, ARole role); CompletableFuture addRoleToMemberAsync(Member member, Long roleId); CompletableFuture updateRolesIds(Member member, List rolesToRemove, List rolesToAdd); + CompletableFuture updateRolesIds(AUserInAServer aUserInAServer, List rolesToAdd, List rolesToRemove); CompletableFuture updateRolesObj(Member member, List rolesToRemove, List rolesToAdd); CompletableFuture addRoleToMemberAsync(Member member, Role role); void addRoleToMember(Member member, ARole role); CompletableFuture addRoleToMemberAsync(Member member, ARole role); void removeRoleFromMember(Member member, ARole role); CompletableFuture removeRoleFromMemberAsync(Member member, ARole role); + CompletableFuture removeRoleFromMemberAsync(AUserInAServer user, ARole role); CompletableFuture removeRoleFromMemberAsync(Member member, Long roleId); CompletableFuture addRoleToMemberAsync(Guild guild, Long userId, Role roleById); CompletableFuture removeRoleFromUserAsync(Guild guild, Long userId, Role roleById); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/utils/BeanUtils.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/utils/BeanUtils.java similarity index 100% rename from abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/utils/BeanUtils.java rename to abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/utils/BeanUtils.java diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/utils/ParseUtils.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/utils/ParseUtils.java index 3e4f8db62..da70ee7ea 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/utils/ParseUtils.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/utils/ParseUtils.java @@ -1,9 +1,18 @@ package dev.sheldan.abstracto.core.utils; +import dev.sheldan.abstracto.core.command.exception.AbstractoTemplatedException; import dev.sheldan.abstracto.core.exception.DurationFormatException; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.ISnowflake; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.utils.cache.SnowflakeCacheView; +import org.apache.commons.lang3.math.NumberUtils; import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; @@ -44,4 +53,64 @@ public static Duration parseDuration(String textToParseFrom) { } return start; } + + public static Role parseRoleFromText(String text, Guild guild) { + Role role; + Matcher matcher = Message.MentionType.ROLE.getPattern().matcher(text); + if(matcher.matches()) { + String roleId = matcher.group(1); + role = guild.getRoleById(roleId); + } else { + if(NumberUtils.isParsable(text)) { + role = guild.getRoleById(text); + } else { + List roles = guild.getRolesByName(text, true); + if(roles.isEmpty()) { + throw new AbstractoTemplatedException("No role found with name.", "no_role_found_by_name_exception"); + } + if(roles.size() > 1) { + throw new AbstractoTemplatedException("Multiple roles found with name.", "multiple_roles_found_by_name_exception"); + } + role = roles.get(0); + } + } + if(role != null && role.isPublicRole()) { + throw new AbstractoTemplatedException("Public role cannot be used for role parameter.", "everyone_role_not_allowed_exception"); + } + return role; + } + + public static GuildChannel parseGuildChannelFromText(String text, Guild guild) { + Matcher matcher = Message.MentionType.CHANNEL.getPattern().matcher(text); + GuildChannel textChannel; + if(matcher.matches()) { + String channelId = matcher.group(1); + textChannel = guild.getChannelById(GuildChannel.class, channelId); + } else { + if(NumberUtils.isParsable(text)) { + long channelId = Long.parseLong(text); + return guild.getGuildChannelById(channelId); + } else { + List potentialMatches = new ArrayList<>(); + potentialMatches.addAll(getByName(guild.getTextChannelCache(), text)); + potentialMatches.addAll(getByName(guild.getCategoryCache(), text)); + potentialMatches.addAll(getByName(guild.getVoiceChannelCache(), text)); + potentialMatches.addAll(getByName(guild.getThreadChannelCache(), text)); + potentialMatches.addAll(getByName(guild.getForumChannelCache(), text)); + potentialMatches.addAll(getByName(guild.getStageChannelCache(), text)); + if(potentialMatches.isEmpty()) { + throw new AbstractoTemplatedException("No channel found with name.", "no_channel_found_by_name_exception"); + } + if(potentialMatches.size() > 1) { + throw new AbstractoTemplatedException("Multiple channels found..", "multiple_channels_found_by_name_exception"); + } + return guild.getGuildChannelById(potentialMatches.get(0).getId()); + } + } + return textChannel; + } + + private static List getByName(SnowflakeCacheView cache, String name) { + return cache.getElementsByName(name, true); + } }