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);
+ }
}