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
new file mode 100644
index 000000000..1070be13b
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/pom.xml
@@ -0,0 +1,22 @@
+
+
+
+ dev.sheldan.abstracto.modules
+ utility
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ experience-tracking-impl
+
+
+
+ dev.sheldan.abstracto.modules
+ experience-tracking-int
+ ${project.version}
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/ExperienceModule.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/ExperienceModule.java
new file mode 100644
index 000000000..7f3afb6db
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/ExperienceModule.java
@@ -0,0 +1,19 @@
+package dev.sheldan.abstracto.experience.commands;
+
+import dev.sheldan.abstracto.core.command.config.ModuleInfo;
+import dev.sheldan.abstracto.core.command.config.ModuleInterface;
+
+public class ExperienceModule implements ModuleInterface {
+
+ public static final String EXPERIENCE = "utility";
+
+ @Override
+ public ModuleInfo getInfo() {
+ return ModuleInfo.builder().name(EXPERIENCE).description("Module containing commands related to experience tracking.").build();
+ }
+
+ @Override
+ public String getParentModule() {
+ return "default";
+ }
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/SetRole.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/SetRole.java
new file mode 100644
index 000000000..9c706eba7
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/commands/SetRole.java
@@ -0,0 +1,58 @@
+package dev.sheldan.abstracto.experience.commands;
+
+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.CommandContext;
+import dev.sheldan.abstracto.core.command.execution.CommandResult;
+import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.service.management.RoleManagementService;
+import dev.sheldan.abstracto.experience.config.ExperienceFeatures;
+import dev.sheldan.abstracto.experience.service.ExperienceRoleService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class SetRole extends AbstractConditionableCommand {
+
+ @Autowired
+ private ExperienceRoleService experienceRoleService;
+
+ @Autowired
+ private RoleManagementService roleManagementService;
+
+
+ @Override
+ public CommandResult execute(CommandContext commandContext) {
+ Integer level = (Integer) commandContext.getParameters().getParameters().get(0);
+ Long roleId = (Long) commandContext.getParameters().getParameters().get(1);
+ ARole role = roleManagementService.findRole(roleId);
+ experienceRoleService.setRoleToLevel(role, level, commandContext.getUserInitiatedContext().getServer());
+ return CommandResult.fromSuccess();
+ }
+
+ @Override
+ public CommandConfiguration getConfiguration() {
+ List parameters = new ArrayList<>();
+ parameters.add(Parameter.builder().name("level").type(Integer.class).build());
+ parameters.add(Parameter.builder().name("roleId").type(Long.class).build());
+ HelpInfo helpInfo = HelpInfo.builder().longHelp("Sets the role to a certain level").usage("setRole ").build();
+ return CommandConfiguration.builder()
+ .name("setRole")
+ .module(ExperienceModule.EXPERIENCE)
+ .description("Sets the role to a certain level")
+ .causesReaction(true)
+ .parameters(parameters)
+ .help(helpInfo)
+ .build();
+ }
+
+ @Override
+ public String getFeature() {
+ return ExperienceFeatures.EXPERIENCE;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfig.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfig.java
new file mode 100644
index 000000000..40f88a336
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfig.java
@@ -0,0 +1,17 @@
+package dev.sheldan.abstracto.experience.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@Getter
+@Setter
+@ConfigurationProperties(prefix = "abstracto.experience")
+public class ExperienceConfig {
+ private Integer minExp;
+ private Integer maxExp;
+ private Integer multiplier;
+ private Integer maxLvl;
+}
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfigListener.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfigListener.java
new file mode 100644
index 000000000..b335c5569
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceConfigListener.java
@@ -0,0 +1,28 @@
+package dev.sheldan.abstracto.experience.config;
+
+import dev.sheldan.abstracto.core.listener.ServerConfigListener;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.core.service.ConfigService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+public class ExperienceConfigListener implements ServerConfigListener {
+
+
+ @Autowired
+ private ExperienceConfig experienceConfig;
+
+ @Autowired
+ private ConfigService service;
+
+ @Override
+ public void updateServerConfig(AServer server) {
+ log.info("Setting up experience for {}", server.getId());
+ service.createDoubleValueIfNotExist("minExp", server.getId(), experienceConfig.getMinExp().doubleValue());
+ service.createDoubleValueIfNotExist("maxExp", server.getId(), experienceConfig.getMaxExp().doubleValue());
+ service.createDoubleValueIfNotExist("multiplier", server.getId(), experienceConfig.getMultiplier().doubleValue());
+ }
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceFeatures.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceFeatures.java
new file mode 100644
index 000000000..975e02fb6
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceFeatures.java
@@ -0,0 +1,5 @@
+package dev.sheldan.abstracto.experience.config;
+
+public class ExperienceFeatures {
+ public static String EXPERIENCE = "experience";
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceLevelLoader.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceLevelLoader.java
new file mode 100644
index 000000000..abe699803
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceLevelLoader.java
@@ -0,0 +1,30 @@
+package dev.sheldan.abstracto.experience.config;
+
+import dev.sheldan.abstracto.experience.service.ExperienceLevelService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+public class ExperienceLevelLoader {
+
+ @Autowired
+ private ExperienceConfig experienceConfig;
+
+ @Autowired
+ private ExperienceLevelService experienceLevelService;
+
+ @EventListener
+ public void handleContextRefreshEvent(ContextRefreshedEvent ctxStartEvt) {
+ Integer maxLevel = experienceConfig.getMaxLvl();
+ Long experience = 0L;
+ experienceLevelService.createExperienceLevel(0, 0L);
+ for (int i = 1; i < maxLevel; i++) {
+ experience = experience + experienceLevelService.calculateExperienceForLevel(i - 1);
+ experienceLevelService.createExperienceLevel(i, experience);
+ }
+ }
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceProperties.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceProperties.java
new file mode 100644
index 000000000..d567fb799
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/config/ExperienceProperties.java
@@ -0,0 +1,9 @@
+package dev.sheldan.abstracto.experience.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+@Configuration
+@PropertySource("classpath:experience.properties")
+public class ExperienceProperties {
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/job/ExperiencePersistingJob.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/job/ExperiencePersistingJob.java
new file mode 100644
index 000000000..1db33e1b4
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/job/ExperiencePersistingJob.java
@@ -0,0 +1,41 @@
+package dev.sheldan.abstracto.experience.job;
+
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.experience.service.ExperienceTrackerService;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.PersistJobDataAfterExecution;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+import org.springframework.stereotype.Component;
+
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.List;
+
+
+@Slf4j
+@DisallowConcurrentExecution
+@Component
+@PersistJobDataAfterExecution
+public class ExperiencePersistingJob extends QuartzJobBean {
+
+ @Autowired
+ private ExperienceTrackerService experienceTrackerService;
+
+ @Override
+ protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
+ HashMap> runtimeExperience = experienceTrackerService.getRuntimeExperience();
+ log.info("Storing experience");
+ Long pastMinute = (Instant.now().getEpochSecond() / 60) - 1;
+ if(runtimeExperience.containsKey(pastMinute)) {
+ experienceTrackerService.handleExperienceGain(runtimeExperience.get(pastMinute));
+ runtimeExperience.remove(pastMinute);
+ }
+ }
+
+
+}
+
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/ExperienceTrackerListener.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/ExperienceTrackerListener.java
new file mode 100644
index 000000000..e5c9be85b
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/listener/ExperienceTrackerListener.java
@@ -0,0 +1,34 @@
+package dev.sheldan.abstracto.experience.listener;
+
+import dev.sheldan.abstracto.core.listener.MessageReceivedListener;
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import dev.sheldan.abstracto.core.service.management.UserManagementService;
+import dev.sheldan.abstracto.experience.config.ExperienceFeatures;
+import dev.sheldan.abstracto.experience.service.ExperienceTrackerService;
+import net.dv8tion.jda.api.entities.Message;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class ExperienceTrackerListener implements MessageReceivedListener {
+
+ @Autowired
+ private ExperienceTrackerService experienceTrackerService;
+
+ @Autowired
+ private UserManagementService userManagementService;
+
+ @Override
+ @Transactional(propagation = Propagation.REQUIRES_NEW)
+ public void execute(Message message) {
+ AUserInAServer cause = userManagementService.loadUser(message.getMember());
+ experienceTrackerService.addExperience(cause);
+ }
+
+ @Override
+ public String getFeature() {
+ return ExperienceFeatures.EXPERIENCE;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceLevelRepository.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceLevelRepository.java
new file mode 100644
index 000000000..7e302ae3e
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceLevelRepository.java
@@ -0,0 +1,10 @@
+package dev.sheldan.abstracto.experience.repository;
+
+import dev.sheldan.abstracto.experience.models.database.AExperienceLevel;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ExperienceLevelRepository extends JpaRepository {
+ AExperienceLevel findTopByExperienceNeededGreaterThanOrderByExperienceNeededAsc(Long experience);
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceRoleRepository.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceRoleRepository.java
new file mode 100644
index 000000000..6f55462c9
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/ExperienceRoleRepository.java
@@ -0,0 +1,17 @@
+package dev.sheldan.abstracto.experience.repository;
+
+import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.experience.models.database.AExperienceLevel;
+import dev.sheldan.abstracto.experience.models.database.AExperienceRole;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface ExperienceRoleRepository extends JpaRepository {
+ AExperienceRole findByRoleServerAndRole(AServer server, ARole role);
+ List findByLevelAndRoleServer(AExperienceLevel level, AServer server);
+ List findByRoleServer(AServer server);
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/UserExperienceRepository.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/UserExperienceRepository.java
new file mode 100644
index 000000000..2f31efae6
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/repository/UserExperienceRepository.java
@@ -0,0 +1,9 @@
+package dev.sheldan.abstracto.experience.repository;
+
+import dev.sheldan.abstracto.experience.models.database.AUserExperience;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface UserExperienceRepository extends JpaRepository {
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelServiceBean.java
new file mode 100644
index 000000000..e2b224c19
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelServiceBean.java
@@ -0,0 +1,25 @@
+package dev.sheldan.abstracto.experience.service;
+
+import dev.sheldan.abstracto.experience.service.management.ExperienceLevelManagementService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ExperienceLevelServiceBean implements ExperienceLevelService {
+
+ @Autowired
+ private ExperienceLevelManagementService experienceLevelManagementService;
+
+ @Override
+ public void createExperienceLevel(Integer level, Long experienceNeeded) {
+ if(!experienceLevelManagementService.levelExists(level)) {
+ experienceLevelManagementService.createExperienceLevel(level, experienceNeeded);
+ }
+ }
+
+ @Override
+ public Long calculateExperienceForLevel(Integer level) {
+ return 5L * (level * level) + 50 * level + 100;
+ }
+
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleServiceBean.java
new file mode 100644
index 000000000..a1a048425
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleServiceBean.java
@@ -0,0 +1,27 @@
+package dev.sheldan.abstracto.experience.service;
+
+import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.experience.models.database.AExperienceLevel;
+import dev.sheldan.abstracto.experience.service.management.ExperienceLevelManagementService;
+import dev.sheldan.abstracto.experience.service.management.ExperienceRoleManagementService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ExperienceRoleServiceBean implements ExperienceRoleService {
+
+ @Autowired
+ private ExperienceRoleManagementService experienceRoleManagementService;
+
+ @Autowired
+ private ExperienceLevelManagementService experienceLevelService;
+
+ @Override
+ public void setRoleToLevel(ARole role, Integer level, AServer server) {
+ AExperienceLevel experienceLevel = experienceLevelService.getLevel(level);
+ experienceRoleManagementService.unSetLevelInServer(experienceLevel, server);
+ experienceRoleManagementService.setLevelToRole(experienceLevel, role, server);
+ }
+
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerServiceBean.java
new file mode 100644
index 000000000..26b4318ac
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerServiceBean.java
@@ -0,0 +1,152 @@
+package dev.sheldan.abstracto.experience.service;
+
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import dev.sheldan.abstracto.core.service.ConfigService;
+import dev.sheldan.abstracto.core.service.RoleService;
+import dev.sheldan.abstracto.experience.models.database.AExperienceLevel;
+import dev.sheldan.abstracto.experience.models.database.AExperienceRole;
+import dev.sheldan.abstracto.experience.models.database.AUserExperience;
+import dev.sheldan.abstracto.experience.service.management.ExperienceLevelManagementService;
+import dev.sheldan.abstracto.experience.service.management.ExperienceRoleManagementService;
+import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Instant;
+import java.util.*;
+
+@Component
+@Slf4j
+public class ExperienceTrackerServiceBean implements ExperienceTrackerService {
+
+ private HashMap> runtimeExperience = new HashMap<>();
+
+ @Autowired
+ private UserExperienceManagementService userExperienceManagementService;
+
+ @Autowired
+ private ExperienceLevelService experienceLevelService;
+
+ @Autowired
+ private ExperienceLevelManagementService experienceLevelManagementService;
+
+ @Autowired
+ private ExperienceRoleManagementService experienceRoleManagementService;
+
+ @Autowired
+ private ConfigService configService;
+
+ @Autowired
+ private RoleService roleService;
+
+ @Override
+ public void addExperience(AUserInAServer userInAServer) {
+ Long second = Instant.now().getEpochSecond() / 60;
+ if(runtimeExperience.containsKey(second)) {
+ List existing = runtimeExperience.get(second);
+ existing.forEach(server -> {
+ if(server.getUsers().stream().noneMatch(userInAServer1 -> userInAServer.getUserInServerId().equals(userInAServer1.getUserInServerId()))) {
+ server.getUsers().add(userInAServer);
+ }
+ });
+
+ } else {
+ AServer server = AServer
+ .builder()
+ .id(userInAServer.getServerReference().getId())
+ .build();
+ server.getUsers().add(userInAServer);
+ runtimeExperience.put(second, Arrays.asList(server));
+ }
+ }
+
+ @Override
+ public HashMap> getRuntimeExperience() {
+ return runtimeExperience;
+ }
+
+ @Override
+ public Integer calculateLevel(AUserExperience userInAServer, List levels) {
+ AExperienceLevel lastLevel = levels.get(0);
+ for (AExperienceLevel level : levels) {
+ if(level.getExperienceNeeded() >= userInAServer.getExperience()) {
+ return lastLevel.getLevel();
+ } else {
+ lastLevel = level;
+ }
+ }
+ return lastLevel.getLevel();
+ }
+
+ @Override
+ public AExperienceRole calculateRole(AUserExperience userInAServer, List roles) {
+ if(roles.size() == 0) {
+ return null;
+ }
+ AExperienceRole lastRole = null;
+ for (AExperienceRole experienceRole : roles) {
+ if(userInAServer.getCurrentLevel().getLevel() >= experienceRole.getLevel().getLevel()) {
+ lastRole = experienceRole;
+ } else {
+ return experienceRole;
+ }
+ }
+ return lastRole;
+ }
+
+ @Override
+ public void increaseExpForUser(AUserExperience userInAServer, Long experience, List levels) {
+ userInAServer.setExperience(userInAServer.getExperience() + experience);
+ Integer correctLevel = calculateLevel(userInAServer, levels);
+ Integer currentLevel = userInAServer.getCurrentLevel() != null ? userInAServer.getCurrentLevel().getLevel() : 0;
+ if(!correctLevel.equals(currentLevel)) {
+ log.info("User {} leveled from {} to {}", userInAServer.getUser().getUserReference().getId(), currentLevel, correctLevel);
+ userInAServer.setCurrentLevel(experienceLevelManagementService.getLevel(correctLevel));
+ }
+ userExperienceManagementService.saveUser(userInAServer);
+ }
+
+ @Transactional
+ @Override
+ public void handleExperienceGain(List servers) {
+ servers.forEach(serverExp -> {
+ log.info("Handling experience for server {}", serverExp.getId());
+ int minExp = configService.getDoubleValue("minExp", serverExp.getId()).intValue();
+ int maxExp = configService.getDoubleValue("maxExp", serverExp.getId()).intValue();
+ Integer multiplier = configService.getDoubleValue("multiplier", serverExp.getId()).intValue();
+ PrimitiveIterator.OfInt iterator = new Random().ints(serverExp.getUsers().size(), minExp, maxExp + 1).iterator();
+ List levels = experienceLevelManagementService.getLevelConfig();
+ List roles = experienceRoleManagementService.getExperienceRoleForServer(serverExp);
+ levels.sort(Comparator.comparing(AExperienceLevel::getExperienceNeeded));
+ serverExp.getUsers().forEach(userInAServer -> {
+ Integer gainedExperience = iterator.next();
+ gainedExperience *= multiplier;
+ log.info("Handling {}. The user gains {}", userInAServer.getUserReference().getId(), gainedExperience);
+ AUserExperience userExperience = userExperienceManagementService.findUserInServer(userInAServer);
+ increaseExpForUser(userExperience, gainedExperience.longValue(), levels);
+ handleExperienceRoleForUser(userExperience, roles);
+ });
+ });
+ }
+
+ @Override
+ public void handleExperienceRoleForUser(AUserExperience userExperience, List roles) {
+ AExperienceRole role = calculateRole(userExperience, roles);
+ if(role == null) {
+ return;
+ }
+ boolean currentlyHasNoExperienceRole = userExperience.getCurrentExperienceRole() == null;
+ if(currentlyHasNoExperienceRole || !role.getRole().getId().equals(userExperience.getCurrentExperienceRole().getId())) {
+ log.info("User {} gets a new role {}", userExperience.getUser().getUserReference().getId(), role.getRole().getId());
+ if(!currentlyHasNoExperienceRole) {
+ roleService.removeRoleFromUser(userExperience.getUser(), userExperience.getCurrentExperienceRole().getRole());
+ }
+ userExperience.setCurrentExperienceRole(role);
+ roleService.addRoleToUser(userExperience.getUser(), userExperience.getCurrentExperienceRole().getRole());
+ }
+ }
+
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementServiceBean.java
new file mode 100644
index 000000000..739b05043
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementServiceBean.java
@@ -0,0 +1,46 @@
+package dev.sheldan.abstracto.experience.service.management;
+
+import dev.sheldan.abstracto.experience.models.database.AExperienceLevel;
+import dev.sheldan.abstracto.experience.repository.ExperienceLevelRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Component
+public class ExperienceLevelManagementServiceBean implements ExperienceLevelManagementService {
+
+ @Autowired
+ private ExperienceLevelRepository experienceLevelRepository;
+
+ @Override
+ public AExperienceLevel createExperienceLevel(Integer level, Long neededExperience) {
+ AExperienceLevel experienceLevel = AExperienceLevel
+ .builder()
+ .experienceNeeded(neededExperience)
+ .level(level)
+ .build();
+ experienceLevelRepository.save(experienceLevel);
+ return experienceLevel;
+ }
+
+ @Override
+ public boolean levelExists(Integer level) {
+ return experienceLevelRepository.existsById(level);
+ }
+
+ @Override
+ public AExperienceLevel getLevel(Integer level) {
+ return experienceLevelRepository.getOne(level);
+ }
+
+ @Override
+ public AExperienceLevel getLevelClosestTo(Long experience) {
+ return experienceLevelRepository.findTopByExperienceNeededGreaterThanOrderByExperienceNeededAsc(experience);
+ }
+
+ @Override
+ public List getLevelConfig() {
+ return experienceLevelRepository.findAll();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementServiceBean.java
new file mode 100644
index 000000000..7600c6165
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementServiceBean.java
@@ -0,0 +1,47 @@
+package dev.sheldan.abstracto.experience.service.management;
+
+import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.experience.models.database.AExperienceLevel;
+import dev.sheldan.abstracto.experience.models.database.AExperienceRole;
+import dev.sheldan.abstracto.experience.repository.ExperienceRoleRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Component
+public class ExperienceRoleManagementServiceBean implements ExperienceRoleManagementService {
+
+ @Autowired
+ private ExperienceRoleRepository experienceRoleRepository;
+
+ @Override
+ public void unSetLevelInServer(AExperienceLevel level, AServer server) {
+ List existingExperienceRoles = experienceRoleRepository.findByLevelAndRoleServer(level, server);
+ existingExperienceRoles.forEach(existingRole -> {
+ experienceRoleRepository.delete(existingRole);
+ });
+ }
+
+ @Override
+ public List getExperienceRoleForServer(AServer server) {
+ return experienceRoleRepository.findByRoleServer(server);
+ }
+
+ @Override
+ public void setLevelToRole(AExperienceLevel level, ARole role, AServer server) {
+ AExperienceRole byRoleServerAndRole = experienceRoleRepository.findByRoleServerAndRole(server, role);
+ if(byRoleServerAndRole != null) {
+ byRoleServerAndRole.setLevel(level);
+ } else {
+ byRoleServerAndRole = AExperienceRole
+ .builder()
+ .level(level)
+ .roleServer(server)
+ .role(role)
+ .build();
+ }
+ experienceRoleRepository.save(byRoleServerAndRole);
+ }
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementServiceBean.java
new file mode 100644
index 000000000..d1ac73778
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementServiceBean.java
@@ -0,0 +1,54 @@
+package dev.sheldan.abstracto.experience.service.management;
+
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import dev.sheldan.abstracto.experience.models.database.AExperienceLevel;
+import dev.sheldan.abstracto.experience.models.database.AUserExperience;
+import dev.sheldan.abstracto.experience.repository.UserExperienceRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+
+@Component
+public class UserExperienceManagementServiceBean implements UserExperienceManagementService {
+
+ @Autowired
+ private UserExperienceRepository repository;
+
+ @Autowired
+ private ExperienceLevelManagementService experienceLevelManagementService;
+
+ @Override
+ public void setExperienceTo(AUserExperience aUserInAServer, Long experience) {
+ aUserInAServer.setExperience(experience);
+ repository.save(aUserInAServer);
+ }
+
+ @Override
+ public AUserExperience findUserInServer(AUserInAServer aUserInAServer) {
+ Optional byId = repository.findById(aUserInAServer.getUserInServerId());
+ return byId.orElseGet(() -> createUserInServer(aUserInAServer));
+ }
+
+ @Override
+ public AUserExperience createUserInServer(AUserInAServer aUserInAServer) {
+ AExperienceLevel startingLevel = experienceLevelManagementService.getLevel(0);
+ AUserExperience userExperience = AUserExperience
+ .builder()
+ .experience(0L)
+ .user(aUserInAServer)
+ .id(aUserInAServer.getUserInServerId())
+ .currentLevel(startingLevel)
+ .build();
+ repository.save(userExperience);
+ return userExperience;
+ }
+
+ @Override
+ public void saveUser(AUserExperience userExperience) {
+ repository.save(userExperience);
+ }
+}
+
+
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/experience.properties b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/experience.properties
new file mode 100644
index 000000000..5d21d1167
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/resources/experience.properties
@@ -0,0 +1,14 @@
+abstracto.scheduling.jobs.experienceJob.name=experienceJob
+abstracto.scheduling.jobs.experienceJob.group=experience
+abstracto.scheduling.jobs.experienceJob.clazz=dev.sheldan.abstracto.experience.job.ExperiencePersistingJob
+abstracto.scheduling.jobs.experienceJob.standAlone=true
+abstracto.scheduling.jobs.experienceJob.active=true
+abstracto.scheduling.jobs.experienceJob.cronExpression=30 * * * * ?
+
+
+abstracto.features.experience=true
+
+abstracto.experience.minExp=10
+abstracto.experience.maxExp=25
+abstracto.experience.multiplier=1
+abstracto.experience.maxLvl=200
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/pom.xml b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/pom.xml
new file mode 100644
index 000000000..97378de80
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/pom.xml
@@ -0,0 +1,28 @@
+
+
+
+ dev.sheldan.abstracto.modules
+ utility
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ experience-tracking-int
+
+
+
+ dev.sheldan.abstracto.templating
+ templating-interface
+ ${project.version}
+
+
+
+ dev.sheldan.abstracto.scheduling
+ scheduling-int
+ ${project.version}
+
+
+
+
\ 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/models/database/AExperienceLevel.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceLevel.java
new file mode 100644
index 000000000..df94e8d0a
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceLevel.java
@@ -0,0 +1,20 @@
+package dev.sheldan.abstracto.experience.models.database;
+
+import lombok.*;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Builder
+@Entity
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(name = "experience_level")
+@Getter
+@Setter
+public class AExperienceLevel {
+ @Id
+ private Integer level;
+ private Long experienceNeeded;
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceRole.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceRole.java
new file mode 100644
index 000000000..6182cdfb7
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AExperienceRole.java
@@ -0,0 +1,45 @@
+package dev.sheldan.abstracto.experience.models.database;
+
+import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import lombok.*;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.List;
+
+@Builder
+@Entity
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(name = "experience_role")
+@Getter
+@Setter
+public class AExperienceRole {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+ @JoinColumn(name = "level_id")
+ private AExperienceLevel level;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @Getter
+ @Setter
+ @JoinColumn(name = "server_id")
+ private AServer roleServer;
+
+ @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+ @JoinColumn(name = "role_id")
+ private ARole role;
+
+ @OneToMany(
+ fetch = FetchType.LAZY,
+ cascade = CascadeType.ALL,
+ orphanRemoval = true)
+ @Builder.Default
+ @JoinColumn(name = "experience_role_id")
+ private List users = new ArrayList<>();
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AUserExperience.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AUserExperience.java
new file mode 100644
index 000000000..a4ddfcf5d
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/models/database/AUserExperience.java
@@ -0,0 +1,34 @@
+package dev.sheldan.abstracto.experience.models.database;
+
+import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import lombok.*;
+
+import javax.persistence.*;
+
+@Builder
+@Entity
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(name = "user_experience")
+@Getter
+@Setter
+public class AUserExperience {
+
+ @Id
+ private Long id;
+
+ @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+ @PrimaryKeyJoinColumn
+ private AUserInAServer user;
+
+ private Long experience;
+
+ @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+ @JoinColumn(name = "level_id")
+ private AExperienceLevel currentLevel;
+
+ @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
+ @JoinColumn(name = "experience_role_id")
+ private AExperienceRole currentExperienceRole;
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelService.java
new file mode 100644
index 000000000..5ef44583a
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceLevelService.java
@@ -0,0 +1,6 @@
+package dev.sheldan.abstracto.experience.service;
+
+public interface ExperienceLevelService {
+ void createExperienceLevel(Integer level, Long experienceNeeded);
+ Long calculateExperienceForLevel(Integer level);
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleService.java
new file mode 100644
index 000000000..4b1277dbf
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceRoleService.java
@@ -0,0 +1,9 @@
+package dev.sheldan.abstracto.experience.service;
+
+import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.experience.models.database.AExperienceRole;
+
+public interface ExperienceRoleService {
+ void setRoleToLevel(ARole role, Integer level, AServer server);
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerService.java
new file mode 100644
index 000000000..148e28f41
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/ExperienceTrackerService.java
@@ -0,0 +1,20 @@
+package dev.sheldan.abstracto.experience.service;
+
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import dev.sheldan.abstracto.experience.models.database.AExperienceLevel;
+import dev.sheldan.abstracto.experience.models.database.AExperienceRole;
+import dev.sheldan.abstracto.experience.models.database.AUserExperience;
+
+import java.util.HashMap;
+import java.util.List;
+
+public interface ExperienceTrackerService {
+ void addExperience(AUserInAServer userInAServer);
+ HashMap> getRuntimeExperience();
+ Integer calculateLevel(AUserExperience userInAServer, List levels);
+ AExperienceRole calculateRole(AUserExperience userInAServer, List roles);
+ void increaseExpForUser(AUserExperience userInAServer, Long experience, List levels);
+ void handleExperienceGain(List serverExp);
+ void handleExperienceRoleForUser(AUserExperience userExperience, List roles);
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementService.java
new file mode 100644
index 000000000..dff9d5279
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceLevelManagementService.java
@@ -0,0 +1,13 @@
+package dev.sheldan.abstracto.experience.service.management;
+
+import dev.sheldan.abstracto.experience.models.database.AExperienceLevel;
+
+import java.util.List;
+
+public interface ExperienceLevelManagementService {
+ AExperienceLevel createExperienceLevel(Integer level, Long neededExperience);
+ boolean levelExists(Integer level);
+ AExperienceLevel getLevel(Integer level);
+ AExperienceLevel getLevelClosestTo(Long experience);
+ List getLevelConfig();
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementService.java
new file mode 100644
index 000000000..56aacb206
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/ExperienceRoleManagementService.java
@@ -0,0 +1,14 @@
+package dev.sheldan.abstracto.experience.service.management;
+
+import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.experience.models.database.AExperienceLevel;
+import dev.sheldan.abstracto.experience.models.database.AExperienceRole;
+
+import java.util.List;
+
+public interface ExperienceRoleManagementService {
+ void setLevelToRole(AExperienceLevel level, ARole role, AServer server);
+ void unSetLevelInServer(AExperienceLevel level, AServer server);
+ List getExperienceRoleForServer(AServer server);
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementService.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementService.java
new file mode 100644
index 000000000..ccd23fc83
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/service/management/UserExperienceManagementService.java
@@ -0,0 +1,12 @@
+package dev.sheldan.abstracto.experience.service.management;
+
+
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import dev.sheldan.abstracto.experience.models.database.AUserExperience;
+
+public interface UserExperienceManagementService {
+ void setExperienceTo(AUserExperience aUserInAServer, Long experience);
+ AUserExperience findUserInServer(AUserInAServer aUserInAServer);
+ AUserExperience createUserInServer(AUserInAServer aUserInAServer);
+ void saveUser(AUserExperience userExperience);
+}
diff --git a/abstracto-application/abstracto-modules/experience-tracking/pom.xml b/abstracto-application/abstracto-modules/experience-tracking/pom.xml
new file mode 100644
index 000000000..e2f41285a
--- /dev/null
+++ b/abstracto-application/abstracto-modules/experience-tracking/pom.xml
@@ -0,0 +1,29 @@
+
+
+
+ dev.sheldan.abstracto
+ abstracto-modules
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ dev.sheldan.abstracto.modules
+ experience-tracking
+ pom
+
+
+ experience-tracking-int
+ experience-tracking-impl
+
+
+
+
+ dev.sheldan.abstracto.core
+ core-interface
+ ${project.version}
+ compile
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/BanServiceBean.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/BanServiceBean.java
index 6cd161ed6..709175972 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/BanServiceBean.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/BanServiceBean.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.models.context.ServerContext;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
@@ -21,7 +21,7 @@ public class BanServiceBean implements BanService {
private static final String BAN_ID_LOG_TEMPLATE = "banid_log";
private static final String BAN_LOG_TARGET = "banLog";
@Autowired
- private Bot bot;
+ private BotService botService;
@Autowired
private TemplateService templateService;
@@ -44,7 +44,7 @@ public class BanServiceBean implements BanService {
}
private void banUser(Long guildId, Long userId, String reason) {
- Optional guildByIdOptional = bot.getGuildById(guildId);
+ Optional guildByIdOptional = botService.getGuildById(guildId);
if(guildByIdOptional.isPresent()) {
log.info("Banning user {} in guild {}.", userId, guildId);
guildByIdOptional.get().ban(userId.toString(), 0, reason).queue();
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/KickServiceBean.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/KickServiceBean.java
index 3da175307..7f8759a02 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/KickServiceBean.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/KickServiceBean.java
@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.exception.GuildException;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.moderation.models.template.commands.KickLogModel;
import dev.sheldan.abstracto.templating.service.TemplateService;
@@ -20,7 +20,7 @@ public class KickServiceBean implements KickService {
private static final String KICK_LOG_TEMPLATE = "kick_log";
private static final String WARN_LOG_TARGET = "warnLog";
@Autowired
- private Bot bot;
+ private BotService botService;
@Autowired
private TemplateService templateService;
@@ -30,7 +30,7 @@ public class KickServiceBean implements KickService {
@Override
public void kickMember(Member member, String reason, KickLogModel kickLogModel) {
- Optional guildById = bot.getGuildById(kickLogModel.getGuild().getIdLong());
+ Optional guildById = botService.getGuildById(kickLogModel.getGuild().getIdLong());
if(guildById.isPresent()) {
guildById.get().kick(member, reason).queue();
this.sendKickLog(kickLogModel);
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/SlowModeServiceBean.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/SlowModeServiceBean.java
index c9082770d..8fc8effa5 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/SlowModeServiceBean.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/SlowModeServiceBean.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.exception.ChannelException;
import dev.sheldan.abstracto.core.models.database.AChannel;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
@@ -16,7 +16,7 @@ import java.util.Optional;
public class SlowModeServiceBean implements SlowModeService {
@Autowired
- private Bot bot;
+ private BotService botService;
@Override
public void setSlowMode(TextChannel channel, Duration duration) {
@@ -29,7 +29,7 @@ public class SlowModeServiceBean implements SlowModeService {
@Override
public void setSlowMode(AChannel channel, Duration duration) {
- Optional textChannelOptional = bot.getTextChannelFromServer(channel.getServer().getId(), channel.getId());
+ Optional textChannelOptional = botService.getTextChannelFromServer(channel.getServer().getId(), channel.getId());
if(textChannelOptional.isPresent()) {
TextChannel textChannel = textChannelOptional.get();
this.setSlowMode(textChannel, duration);
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/WarnServiceBean.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/WarnServiceBean.java
index af55efffc..e88d85871 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/WarnServiceBean.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/service/WarnServiceBean.java
@@ -12,7 +12,7 @@ import dev.sheldan.abstracto.moderation.service.management.WarnManagementService
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
@@ -48,7 +48,7 @@ public class WarnServiceBean implements WarnService {
private TemplateService templateService;
@Autowired
- private Bot bot;
+ private BotService botService;
private static final String WARN_LOG_TEMPLATE = "warn_log";
private static final String WARN_NOTIFICATION_TEMPLATE = "warn_notification";
@@ -60,9 +60,9 @@ public class WarnServiceBean implements WarnService {
AServer serverOfWarning = warnedAUserInAServer.getServerReference();
log.info("User {} is warning {} in server {} because of {}", warningAUser.getId(), warnedAUser.getId(), serverOfWarning.getId(), reason);
Warning warning = warnManagementService.createWarning(warnedAUserInAServer, warningAUserInAServer, reason);
- JDA instance = bot.getInstance();
+ JDA instance = botService.getInstance();
User userBeingWarned = instance.getUserById(warnedAUser.getId());
- Optional guildById = bot.getGuildById(serverOfWarning.getId());
+ Optional guildById = botService.getGuildById(serverOfWarning.getId());
String guildName = "";
if(guildById.isPresent()) {
guildName = guildById.get().getName();
diff --git a/abstracto-application/abstracto-modules/pom.xml b/abstracto-application/abstracto-modules/pom.xml
index 322a59ba4..4f75f0029 100644
--- a/abstracto-application/abstracto-modules/pom.xml
+++ b/abstracto-application/abstracto-modules/pom.xml
@@ -14,6 +14,7 @@
moderation
utility
+ experience-tracking
diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/listener/embed/MessageEmbedRemovalReactionListener.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/listener/embed/MessageEmbedRemovalReactionListener.java
index b6f64815a..be0474c7a 100644
--- a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/listener/embed/MessageEmbedRemovalReactionListener.java
+++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/listener/embed/MessageEmbedRemovalReactionListener.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.listener.ReactedAddedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
@@ -30,7 +30,7 @@ public class MessageEmbedRemovalReactionListener implements ReactedAddedListener
private EmoteManagementService emoteManagementService;
@Autowired
- private Bot bot;
+ private BotService botService;
@Autowired
private MessageEmbedPostManagementService messageEmbedPostManagementService;
@@ -47,7 +47,7 @@ public class MessageEmbedRemovalReactionListener implements ReactedAddedListener
Long guildId = message.getServerId();
AEmote aEmote = emoteService.getEmoteOrFakeEmote(REMOVAL_EMOTE, guildId);
MessageReaction.ReactionEmote reactionEmote = reaction.getReactionEmote();
- Optional emoteInGuild = bot.getEmote(guildId, aEmote);
+ Optional emoteInGuild = botService.getEmote(guildId, aEmote);
if(EmoteUtils.isReactionEmoteAEmote(reactionEmote, aEmote, emoteInGuild.orElse(null))) {
Optional embeddedMessageOptional = messageEmbedPostManagementService.findEmbeddedPostByMessageId(message.getMessageId());
if(embeddedMessageOptional.isPresent()) {
diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/listener/starboard/StarboardListener.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/listener/starboard/StarboardListener.java
index e6b47f071..29a002a25 100644
--- a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/listener/starboard/StarboardListener.java
+++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/listener/starboard/StarboardListener.java
@@ -7,7 +7,7 @@ import dev.sheldan.abstracto.core.models.cache.CachedReaction;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
@@ -40,7 +40,7 @@ public class StarboardListener implements ReactedAddedListener, ReactedRemovedLi
private EmoteManagementService emoteManagementService;
@Autowired
- private Bot bot;
+ private BotService botService;
@Autowired
private MessageCache messageCache;
@@ -72,7 +72,7 @@ public class StarboardListener implements ReactedAddedListener, ReactedRemovedLi
Long guildId = message.getServerId();
AEmote aEmote = emoteService.getEmoteOrFakeEmote(STAR_EMOTE, guildId);
MessageReaction.ReactionEmote reactionEmote = addedReaction.getReactionEmote();
- Optional emoteInGuild = bot.getEmote(guildId, aEmote);
+ Optional emoteInGuild = botService.getEmote(guildId, aEmote);
if(EmoteUtils.isReactionEmoteAEmote(reactionEmote, aEmote, emoteInGuild.orElse(null))) {
Optional reactionOptional = EmoteUtils.getReactionFromMessageByEmote(message, aEmote);
updateStarboardPost(message, reactionOptional.orElse(null), userAdding, true);
@@ -125,7 +125,7 @@ public class StarboardListener implements ReactedAddedListener, ReactedRemovedLi
Long guildId = message.getServerId();
AEmote aEmote = emoteService.getEmoteOrFakeEmote(STAR_EMOTE, guildId);
MessageReaction.ReactionEmote reactionEmote = removedReaction.getReactionEmote();
- Optional emoteInGuild = bot.getEmote(guildId, aEmote);
+ Optional emoteInGuild = botService.getEmote(guildId, aEmote);
if(EmoteUtils.isReactionEmoteAEmote(reactionEmote, aEmote, emoteInGuild.orElse(null))) {
Optional reactionOptional = EmoteUtils.getReactionFromMessageByEmote(message, aEmote);
updateStarboardPost(message, reactionOptional.orElse(null), userRemoving, false);
diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/converter/StarStatsUserConverter.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/converter/StarStatsUserConverter.java
index cb6ae3a39..b69a80049 100644
--- a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/converter/StarStatsUserConverter.java
+++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/converter/StarStatsUserConverter.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.utility.repository.converter;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import dev.sheldan.abstracto.core.models.database.AUser;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.utility.models.template.commands.starboard.StarStatsUser;
import dev.sheldan.abstracto.utility.repository.StarStatsUserResult;
import org.springframework.beans.factory.annotation.Autowired;
@@ -18,7 +18,7 @@ public class StarStatsUserConverter {
private UserManagementService userManagementService;
@Autowired
- private Bot bot;
+ private BotService botService;
public List convertToStarStatsUser(List users, Long serverId) {
List result = new ArrayList<>();
@@ -26,7 +26,7 @@ public class StarStatsUserConverter {
StarStatsUser newUser = StarStatsUser
.builder()
.starCount(starStatsUserResult.getStarCount())
- .member(bot.getMemberInServer(serverId, starStatsUserResult.getUserId()))
+ .member(botService.getMemberInServer(serverId, starStatsUserResult.getUserId()))
.user(AUser.builder().id(starStatsUserResult.getUserId()).build())
.build();
result.add(newUser);
diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/MessageEmbedServiceBean.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/MessageEmbedServiceBean.java
index d3c1c46f3..9960f8642 100644
--- a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/MessageEmbedServiceBean.java
+++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/MessageEmbedServiceBean.java
@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.service.MessageService;
@@ -50,7 +50,7 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
private UserManagementService userManagementService;
@Autowired
- private Bot bot;
+ private BotService botService;
@Autowired
private TemplateService templateService;
@@ -127,8 +127,8 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
AChannel channel = channelManagementService.loadChannel(message.getChannel().getIdLong());
AServer server = serverManagementService.loadOrCreate(message.getGuild().getIdLong());
AUserInAServer user = userManagementService.loadUser(message.getMember());
- Member author = bot.getMemberInServer(embeddedMessage.getServerId(), embeddedMessage.getAuthorId());
- TextChannel sourceChannel = bot.getTextChannelFromServer(embeddedMessage.getServerId(), embeddedMessage.getChannelId()).get();
+ Member author = botService.getMemberInServer(embeddedMessage.getServerId(), embeddedMessage.getAuthorId());
+ TextChannel sourceChannel = botService.getTextChannelFromServer(embeddedMessage.getServerId(), embeddedMessage.getChannelId()).get();
return MessageEmbeddedModel
.builder()
.channel(channel)
diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/RemindServiceBean.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/RemindServiceBean.java
index b70edf640..866a3eed5 100644
--- a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/RemindServiceBean.java
+++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/RemindServiceBean.java
@@ -8,7 +8,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.templating.model.MessageToSend;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import dev.sheldan.abstracto.utility.models.database.Reminder;
@@ -50,7 +50,7 @@ public class RemindServiceBean implements ReminderService {
private SchedulerService schedulerService;
@Autowired
- private Bot bot;
+ private BotService botService;
@Autowired
private ReminderService self;
@@ -92,13 +92,13 @@ public class RemindServiceBean implements ReminderService {
Reminder reminderToRemindFor = reminderManagementService.loadReminder(reminderId);
AServer server = reminderToRemindFor.getServer();
AChannel channel = reminderToRemindFor.getChannel();
- Optional guildToAnswerIn = bot.getGuildById(server.getId());
+ Optional guildToAnswerIn = botService.getGuildById(server.getId());
if(guildToAnswerIn.isPresent()) {
- Optional channelToAnswerIn = bot.getTextChannelFromServer(server.getId(), channel.getId());
+ Optional channelToAnswerIn = botService.getTextChannelFromServer(server.getId(), channel.getId());
// only send the message if the channel still exists, if not, only set the reminder to reminded.
if(channelToAnswerIn.isPresent()) {
AUser userReference = reminderToRemindFor.getRemindedUser().getUserReference();
- Member memberInServer = bot.getMemberInServer(server.getId(), userReference.getId());
+ Member memberInServer = botService.getMemberInServer(server.getId(), userReference.getId());
ExecutedReminderModel build = ExecutedReminderModel
.builder()
.reminder(reminderToRemindFor)
diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/StarboardServiceBean.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/StarboardServiceBean.java
index aab04209a..3b5a8c97c 100644
--- a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/StarboardServiceBean.java
+++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/StarboardServiceBean.java
@@ -7,7 +7,7 @@ import dev.sheldan.abstracto.core.models.AServerAChannelMessage;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.templating.model.MessageToSend;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.PostTargetService;
@@ -40,7 +40,7 @@ public class StarboardServiceBean implements StarboardService {
public static final String STARBOARD_POSTTARGET = "starboard";
public static final String STARBOARD_POST_TEMPLATE = "starboard_post";
@Autowired
- private Bot bot;
+ private BotService botService;
@Autowired
private PostTargetService postTargetService;
@@ -100,9 +100,9 @@ public class StarboardServiceBean implements StarboardService {
}
private StarboardPostModel buildStarboardPostModel(CachedMessage message, Integer starCount) {
- Member member = bot.getMemberInServer(message.getServerId(), message.getAuthorId());
- Optional channel = bot.getTextChannelFromServer(message.getServerId(), message.getChannelId());
- Optional guild = bot.getGuildById(message.getServerId());
+ Member member = botService.getMemberInServer(message.getServerId(), message.getAuthorId());
+ Optional channel = botService.getTextChannelFromServer(message.getServerId(), message.getChannelId());
+ Optional guild = botService.getGuildById(message.getServerId());
AChannel aChannel = AChannel.builder().id(message.getChannelId()).build();
AUser user = AUser.builder().id(message.getAuthorId()).build();
AServer server = AServer.builder().id(message.getServerId()).build();
@@ -139,7 +139,7 @@ public class StarboardServiceBean implements StarboardService {
@Override
public void removeStarboardPost(StarboardPost message) {
AChannel starboardChannel = message.getStarboardChannel();
- bot.deleteMessage(starboardChannel.getServer().getId(), starboardChannel.getId(), message.getStarboardMessageId());
+ botService.deleteMessage(starboardChannel.getServer().getId(), starboardChannel.getId(), message.getStarboardMessageId());
}
@Override
diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/SuggestionServiceBean.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/SuggestionServiceBean.java
index c344f6b0d..d1863aadf 100644
--- a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/SuggestionServiceBean.java
+++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/SuggestionServiceBean.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.templating.model.MessageToSend;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.utils.MessageUtils;
@@ -44,7 +44,7 @@ public class SuggestionServiceBean implements SuggestionService {
private TemplateService templateService;
@Autowired
- private Bot botService;
+ private BotService botService;
@Autowired
private EmoteManagementService emoteManagementService;
diff --git a/abstracto-application/core/core-impl/pom.xml b/abstracto-application/core/core-impl/pom.xml
index 18b7cbea5..082e35a28 100644
--- a/abstracto-application/core/core-impl/pom.xml
+++ b/abstracto-application/core/core-impl/pom.xml
@@ -82,6 +82,12 @@
${project.version}
+
+ dev.sheldan.abstracto.scheduling
+ scheduling-int
+ ${project.version}
+
+
com.github.ben-manes.caffeine
caffeine
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ReactionUpdatedListener.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ReactionUpdatedListener.java
index 92f0f24c5..3695e51c4 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ReactionUpdatedListener.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ReactionUpdatedListener.java
@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
@@ -51,12 +51,12 @@ public class ReactionUpdatedListener extends ListenerAdapter {
private FeatureFlagService featureFlagService;
@Autowired
- private Bot bot;
+ private BotService botService;
@Override
@Transactional
public void onGuildMessageReactionAdd(@Nonnull GuildMessageReactionAddEvent event) {
- if(event.getUserIdLong() == bot.getInstance().getSelfUser().getIdLong()) {
+ if(event.getUserIdLong() == botService.getInstance().getSelfUser().getIdLong()) {
return;
}
CompletableFuture asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());
@@ -115,7 +115,7 @@ public class ReactionUpdatedListener extends ListenerAdapter {
@Override
@Transactional
public void onGuildMessageReactionRemove(@Nonnull GuildMessageReactionRemoveEvent event) {
- if(event.getUserIdLong() == bot.getInstance().getSelfUser().getIdLong()) {
+ if(event.getUserIdLong() == botService.getInstance().getSelfUser().getIdLong()) {
return;
}
CompletableFuture asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ReadyListener.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ReadyListener.java
index f8d6759f4..c356432d0 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ReadyListener.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/ReadyListener.java
@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.service.StartupServiceBean;
+import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import net.dv8tion.jda.api.events.ReadyEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
@@ -18,10 +19,14 @@ public class ReadyListener extends ListenerAdapter {
@Value("${abstracto.startup.synchronize}")
private boolean synchronize;
+ @Autowired
+ private SchedulerService schedulerService;
+
@Override
public void onReady(@Nonnull ReadyEvent event) {
if(synchronize){
startup.synchronize();
}
+ schedulerService.startScheduler();
}
}
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/RoleListener.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/RoleListener.java
new file mode 100644
index 000000000..cf8abc777
--- /dev/null
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/listener/RoleListener.java
@@ -0,0 +1,42 @@
+package dev.sheldan.abstracto.core.listener;
+
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.core.service.RoleService;
+import dev.sheldan.abstracto.core.service.management.RoleManagementService;
+import dev.sheldan.abstracto.core.service.management.ServerManagementService;
+import lombok.extern.slf4j.Slf4j;
+import net.dv8tion.jda.api.events.role.RoleCreateEvent;
+import net.dv8tion.jda.api.events.role.RoleDeleteEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Nonnull;
+
+@Component
+@Slf4j
+public class RoleListener extends ListenerAdapter {
+
+ @Autowired
+ private RoleManagementService service;
+
+ @Autowired
+ private RoleService roleService;
+
+ @Autowired
+ private ServerManagementService serverManagementService;
+
+ @Override
+ @Transactional
+ public void onRoleCreate(@Nonnull RoleCreateEvent event) {
+ AServer server = serverManagementService.loadOrCreate(event.getGuild().getIdLong());
+ service.createRole(event.getRole().getIdLong(), server);
+ }
+
+ @Override
+ @Transactional
+ public void onRoleDelete(@Nonnull RoleDeleteEvent event) {
+ roleService.markDeleted(event.getRole());
+ }
+}
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotServiceBean.java
index 7efecba23..0647e8b13 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotServiceBean.java
@@ -21,7 +21,7 @@ import java.util.concurrent.CompletableFuture;
@Service
@Slf4j
-public class BotServiceBean implements Bot {
+public class BotServiceBean implements BotService {
private JDA instance;
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 864ea5d37..5e4f59fd4 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
@@ -23,7 +23,7 @@ import java.util.concurrent.CompletableFuture;
public class ChannelServiceBean implements ChannelService {
@Autowired
- private Bot botService;
+ private BotService botService;
@Override
public void sendTextInAChannel(String text, AChannel channel) {
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ConfigServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ConfigServiceBean.java
index 7d9bf77ca..b903ea2af 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ConfigServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ConfigServiceBean.java
@@ -24,4 +24,9 @@ public class ConfigServiceBean implements ConfigService{
}
return config.getDoubleValue();
}
+
+ @Override
+ public void createDoubleValueIfNotExist(String name, Long serverId, Double value) {
+ configManagementService.createIfNotExists(serverId, name, value);
+ }
}
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/EmoteServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/EmoteServiceBean.java
index 2528e9c63..bb2828497 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/EmoteServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/EmoteServiceBean.java
@@ -18,7 +18,7 @@ import java.util.Optional;
public class EmoteServiceBean implements EmoteService {
@Autowired
- private Bot botService;
+ private BotService botService;
@Autowired
private EmoteManagementService emoteManagementService;
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageCacheBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageCacheBean.java
index b5265be4b..405d51a44 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageCacheBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageCacheBean.java
@@ -30,7 +30,7 @@ import java.util.concurrent.ExecutionException;
public class MessageCacheBean implements MessageCache {
@Autowired
- private Bot bot;
+ private BotService botService;
@Autowired
private UserManagementService userManagementService;
@@ -81,9 +81,9 @@ public class MessageCacheBean implements MessageCache {
@Async
@Override
public void loadMessage(CompletableFuture future, Long guildId, Long textChannelId, Long messageId) {
- Optional guildOptional = bot.getGuildById(guildId);
+ Optional guildOptional = botService.getGuildById(guildId);
if(guildOptional.isPresent()) {
- Optional textChannelByIdOptional = bot.getTextChannelFromServer(guildOptional.get(), textChannelId);
+ Optional textChannelByIdOptional = botService.getTextChannelFromServer(guildOptional.get(), textChannelId);
if(textChannelByIdOptional.isPresent()) {
TextChannel textChannel = textChannelByIdOptional.get();
textChannel.retrieveMessageById(messageId).queue(message -> {
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java
index ab8177ba6..3482e492d 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java
@@ -19,7 +19,7 @@ import java.util.concurrent.CompletableFuture;
public class MessageServiceBean implements MessageService {
@Autowired
- private Bot bot;
+ private BotService botService;
@Autowired
private EmoteManagementService emoteManagementService;
@@ -29,14 +29,14 @@ public class MessageServiceBean implements MessageService {
@Override
public void addReactionToMessage(String emoteKey, Long serverId, Message message) {
- Optional guildByIdOptional = bot.getGuildById(serverId);
+ Optional guildByIdOptional = botService.getGuildById(serverId);
Optional aEmote = emoteManagementService.loadEmoteByName(emoteKey, serverId);
if(guildByIdOptional.isPresent()) {
Guild guild = guildByIdOptional.get();
if(aEmote.isPresent()) {
AEmote emote = aEmote.get();
if(emote.getCustom()) {
- Emote emoteById = bot.getInstance().getEmoteById(emote.getEmoteId());
+ Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId());
if(emoteById != null) {
message.addReaction(emoteById).queue();
} else {
@@ -57,6 +57,6 @@ public class MessageServiceBean implements MessageService {
@Override
public CompletableFuture deleteMessageInChannelInServer(Long serverId, Long channelId, Long messageId) {
- return bot.deleteMessage(serverId, channelId, messageId);
+ return botService.deleteMessage(serverId, channelId, messageId);
}
}
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PostTargetServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PostTargetServiceBean.java
index 9174ae1e0..984e1f768 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PostTargetServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PostTargetServiceBean.java
@@ -32,7 +32,7 @@ public class PostTargetServiceBean implements PostTargetService {
private PostTargetManagement postTargetManagement;
@Autowired
- private Bot botService;
+ private BotService botService;
@Autowired
private DynamicKeyLoader dynamicKeyLoader;
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
new file mode 100644
index 000000000..ad41881f4
--- /dev/null
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/RoleServiceBean.java
@@ -0,0 +1,72 @@
+package dev.sheldan.abstracto.core.service;
+
+import dev.sheldan.abstracto.core.exception.GuildException;
+import dev.sheldan.abstracto.core.exception.RoleException;
+import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import dev.sheldan.abstracto.core.service.management.RoleManagementService;
+import lombok.extern.slf4j.Slf4j;
+import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.Role;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@Component
+@Slf4j
+public class RoleServiceBean implements RoleService {
+
+ @Autowired
+ private BotService botService;
+
+ @Autowired
+ private RoleManagementService roleManagementService;
+
+ @Override
+ public void addRoleToUser(AUserInAServer aUserInAServer, ARole role) {
+ Optional guildById = botService.getGuildById(aUserInAServer.getServerReference().getId());
+ if(guildById.isPresent()) {
+ Guild guild = guildById.get();
+ Role roleById = guild.getRoleById(role.getId());
+ if(roleById != null) {
+ guild.addRoleToMember(aUserInAServer.getUserReference().getId(), roleById).queue();
+ } else {
+ throw new RoleException(String.format("Failed to load role %s in guild %s", role.getId(), aUserInAServer.getServerReference().getId()));
+ }
+ } else {
+ throw new GuildException(String.format("Failed to load guild %s.", aUserInAServer.getServerReference().getId()));
+ }
+ }
+
+ @Override
+ public void removeRoleFromUser(AUserInAServer aUserInAServer, ARole role) {
+ Optional guildById = botService.getGuildById(aUserInAServer.getServerReference().getId());
+ if(guildById.isPresent()) {
+ Guild guild = guildById.get();
+ Role roleById = guild.getRoleById(role.getId());
+ if(roleById != null) {
+ guild.removeRoleFromMember(aUserInAServer.getUserReference().getId(), roleById).queue();
+ } else {
+ throw new RoleException(String.format("Failed to load role %s in guild %s", role.getId(), aUserInAServer.getServerReference().getId()));
+ }
+ } else {
+ throw new GuildException(String.format("Failed to load guild %s.", aUserInAServer.getServerReference().getId()));
+ }
+ }
+
+ @Override
+ public void markDeleted(Role role) {
+ markDeleted(role.getIdLong());
+ }
+
+ @Override
+ public void markDeleted(Long id) {
+ ARole role = roleManagementService.findRole(id);
+ if(role != null) {
+ roleManagementService.markDeleted(role);
+ } else {
+ throw new RoleException(String.format("Cannot find role %s to mark as deleted.", id));
+ }
+ }
+}
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java
index 7791629b1..c44f0cdb7 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java
@@ -30,7 +30,7 @@ import java.util.stream.Collectors;
public class StartupServiceBean implements Startup {
@Autowired
- private Bot service;
+ private BotService service;
@Autowired
private List extends ListenerAdapter> listeners;
@@ -88,7 +88,7 @@ public class StartupServiceBean implements Startup {
Set availableRoles = SnowflakeUtils.getSnowflakeIds(existingRoles);
Set newRoles = SetUtils.disjunction(availableRoles, knownRolesId);
newRoles.forEach(aLong -> {
- ARole newRole = roleManagementService.createRole(aLong);
+ ARole newRole = roleManagementService.createRole(aLong, existingAServer);
log.debug("Adding new role: {}", aLong);
existingAServer.getRoles().add(newRole);
});
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/EmoteManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/EmoteManagementServiceBean.java
index 79ed598db..fff3d4b10 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/EmoteManagementServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/EmoteManagementServiceBean.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.DynamicKeyLoader;
import dev.sheldan.abstracto.core.exception.EmoteException;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AServer;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.repository.EmoteRepository;
import net.dv8tion.jda.api.entities.Emote;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,7 +26,7 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
private DynamicKeyLoader dynamicKeyLoader;
@Autowired
- private Bot botService;
+ private BotService botService;
@Override
public AEmote loadEmote(Long id) {
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java
index a6e56ca00..68d5c3d15 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java
@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.repository.RoleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -12,7 +13,18 @@ public class RoleManagementServiceBean implements RoleManagementService {
private RoleRepository repository;
@Override
- public ARole createRole(Long id) {
- return repository.save(ARole.builder().id(id).build());
+ public ARole createRole(Long id, AServer server) {
+ return repository.save(ARole.builder().id(id).server(server).deleted(false).build());
+ }
+
+ @Override
+ public ARole findRole(Long id) {
+ return repository.getOne(id);
+ }
+
+ @Override
+ public void markDeleted(ARole role) {
+ role.setDeleted(true);
+ repository.save(role);
}
}
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/UserManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/UserManagementServiceBean.java
index 34f06d2a1..03b49bf8c 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/UserManagementServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/UserManagementServiceBean.java
@@ -52,7 +52,9 @@ public class UserManagementServiceBean implements UserManagementService {
@Override
public AUserInAServer createUserInServer(Long guildId, Long userId) {
- return serverManagementService.addUserToServer(guildId, userId);
+ AUserInAServer aUserInAServer = serverManagementService.addUserToServer(guildId, userId);
+ userInServerRepository.save(aUserInAServer);
+ return aUserInAServer;
}
@Override
diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/exception/RoleException.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/exception/RoleException.java
new file mode 100644
index 000000000..0e424d0fd
--- /dev/null
+++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/exception/RoleException.java
@@ -0,0 +1,7 @@
+package dev.sheldan.abstracto.core.exception;
+
+public class RoleException extends AbstractoRunTimeException {
+ public RoleException(String message) {
+ super(message);
+ }
+}
diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java
index 5c261959f..4935f24b6 100644
--- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java
+++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java
@@ -3,10 +3,7 @@ package dev.sheldan.abstracto.core.models.database;
import dev.sheldan.abstracto.core.models.SnowFlake;
import lombok.*;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import javax.persistence.*;
@Entity
@Table(name="role")
@@ -19,8 +16,14 @@ public class ARole implements SnowFlake {
@Getter @Setter
private Long id;
- @Column(unique = true)
- @Getter @Setter
- private String name;
+ @ManyToOne(fetch = FetchType.LAZY)
+ @Getter
+ @Setter
+ @JoinColumn(name = "role_server_id")
+ private AServer server;
+
+ @Getter
+ @Setter
+ private Boolean deleted;
}
diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java
index 5b71020e0..ffe4a6022 100644
--- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java
+++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java
@@ -21,8 +21,12 @@ public class AServer implements SnowFlake {
private String name;
- @OneToMany(fetch = FetchType.LAZY)
+ @OneToMany(
+ fetch = FetchType.LAZY,
+ cascade = CascadeType.ALL,
+ orphanRemoval = true)
@Builder.Default
+ @JoinColumn(name = "role_server_id")
private List roles = new ArrayList<>();
@OneToMany(
diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/Bot.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/BotService.java
similarity index 97%
rename from abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/Bot.java
rename to abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/BotService.java
index 5d1f8c8e3..0a8c8dbc3 100644
--- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/Bot.java
+++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/BotService.java
@@ -14,7 +14,7 @@ import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Service
-public interface Bot {
+public interface BotService {
void login() throws LoginException;
JDA getInstance();
GuildChannelMember getServerChannelUser(Long serverId, Long channelId, Long userId);
diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/ConfigService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/ConfigService.java
index 409b676ef..46fdc52f1 100644
--- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/ConfigService.java
+++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/ConfigService.java
@@ -3,5 +3,6 @@ package dev.sheldan.abstracto.core.service;
public interface ConfigService {
Double getDoubleValue(String name, Long serverId);
Double getDoubleValue(String name, Long serverId, Double defaultValue);
+ void createDoubleValueIfNotExist(String name, Long serverId, Double value);
}
diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/RoleService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/RoleService.java
new file mode 100644
index 000000000..968fd70c6
--- /dev/null
+++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/RoleService.java
@@ -0,0 +1,12 @@
+package dev.sheldan.abstracto.core.service;
+
+import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import net.dv8tion.jda.api.entities.Role;
+
+public interface RoleService {
+ void addRoleToUser(AUserInAServer aUserInAServer, ARole role);
+ void removeRoleFromUser(AUserInAServer aUserInAServer, ARole role);
+ void markDeleted(Role role);
+ void markDeleted(Long id);
+}
diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementService.java
index 1da8ef98a..4887a0511 100644
--- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementService.java
+++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementService.java
@@ -1,7 +1,10 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.models.database.ARole;
+import dev.sheldan.abstracto.core.models.database.AServer;
public interface RoleManagementService {
- ARole createRole(Long id);
+ ARole createRole(Long id, AServer server);
+ ARole findRole(Long id);
+ void markDeleted(ARole role);
}
diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/utils/ContextUtils.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/utils/ContextUtils.java
index a77c86957..933c078bd 100644
--- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/utils/ContextUtils.java
+++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/utils/ContextUtils.java
@@ -2,13 +2,12 @@ package dev.sheldan.abstracto.core.utils;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
-import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -27,11 +26,11 @@ public class ContextUtils {
private UserManagementService userManagementService;
@Autowired
- private Bot bot;
+ private BotService botService;
public UserInitiatedServerContext fromMessage(CachedMessage message, Class clazz) {
Method m = null;
- GuildChannelMember guildChannelMember = bot.getServerChannelUser(message.getServerId(), message.getChannelId(), message.getAuthorId());
+ GuildChannelMember guildChannelMember = botService.getServerChannelUser(message.getServerId(), message.getChannelId(), message.getAuthorId());
try {
m = clazz.getMethod("builder");
UserInitiatedServerContext.UserInitiatedServerContextBuilder, ?> builder = (UserInitiatedServerContext.UserInitiatedServerContextBuilder) m.invoke(null, null);
diff --git a/abstracto-application/core/core-interface/src/test/java/dev/sheldan/abstracto/core/utils/ContextUtilsTest.java b/abstracto-application/core/core-interface/src/test/java/dev/sheldan/abstracto/core/utils/ContextUtilsTest.java
index 6ecefb390..85f41fe15 100644
--- a/abstracto-application/core/core-interface/src/test/java/dev/sheldan/abstracto/core/utils/ContextUtilsTest.java
+++ b/abstracto-application/core/core-interface/src/test/java/dev/sheldan/abstracto/core/utils/ContextUtilsTest.java
@@ -7,9 +7,8 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.commands.PingModel;
-import dev.sheldan.abstracto.core.service.Bot;
+import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
-import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import org.junit.Before;
import org.junit.Test;
@@ -38,12 +37,12 @@ public class ContextUtilsTest {
private UserManagementService userManagementService;
@Mock
- private Bot bot;
+ private BotService botService;
@Before
public void setup() {
GuildChannelMember build = GuildChannelMember.builder().build();
- when(bot.getServerChannelUser(eq(SERVER_ID), eq(CHANNEL_ID), eq(AUTHOR_ID))).thenReturn(build);
+ when(botService.getServerChannelUser(eq(SERVER_ID), eq(CHANNEL_ID), eq(AUTHOR_ID))).thenReturn(build);
AServer server = AServer.builder().id(SERVER_ID).build();
AUserInAServer aUserInAServer = AUserInAServer.builder().userReference(AUser.builder().id(AUTHOR_ID).build()).serverReference(server).build();
when(userManagementService.loadUser(eq(SERVER_ID), eq(AUTHOR_ID))).thenReturn(aUserInAServer);
diff --git a/abstracto-application/executable/pom.xml b/abstracto-application/executable/pom.xml
index f9c76b7d5..51c483380 100644
--- a/abstracto-application/executable/pom.xml
+++ b/abstracto-application/executable/pom.xml
@@ -63,6 +63,12 @@
${project.version}
+
+ dev.sheldan.abstracto.modules
+ experience-tracking-impl
+ ${project.version}
+
+
dev.sheldan.abstracto.scheduling
scheduling-impl
diff --git a/abstracto-application/scheduling/scheduling-impl/src/main/java/dev/sheldan/abstracto/scheduling/config/SchedulerConfig.java b/abstracto-application/scheduling/scheduling-impl/src/main/java/dev/sheldan/abstracto/scheduling/config/SchedulerConfig.java
index 125b1ebd9..2bf668392 100644
--- a/abstracto-application/scheduling/scheduling-impl/src/main/java/dev/sheldan/abstracto/scheduling/config/SchedulerConfig.java
+++ b/abstracto-application/scheduling/scheduling-impl/src/main/java/dev/sheldan/abstracto/scheduling/config/SchedulerConfig.java
@@ -34,6 +34,7 @@ public class SchedulerConfig {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setOverwriteExistingJobs(true);
factory.setDataSource(dataSource);
+ factory.setAutoStartup(false);
factory.setQuartzProperties(properties);
factory.setJobFactory(jobFactory);
return factory;
diff --git a/abstracto-application/scheduling/scheduling-impl/src/main/java/dev/sheldan/abstracto/scheduling/service/SchedulerServiceBean.java b/abstracto-application/scheduling/scheduling-impl/src/main/java/dev/sheldan/abstracto/scheduling/service/SchedulerServiceBean.java
index 03048f832..1bdc9600c 100644
--- a/abstracto-application/scheduling/scheduling-impl/src/main/java/dev/sheldan/abstracto/scheduling/service/SchedulerServiceBean.java
+++ b/abstracto-application/scheduling/scheduling-impl/src/main/java/dev/sheldan/abstracto/scheduling/service/SchedulerServiceBean.java
@@ -158,4 +158,13 @@ public class SchedulerServiceBean implements SchedulerService {
return false;
}
}
+
+ @Override
+ public void startScheduler() {
+ try {
+ schedulerFactoryBean.getScheduler().start();
+ } catch (SchedulerException e) {
+ log.error("Failed to start scheduler.", e);
+ }
+ }
}
diff --git a/abstracto-application/scheduling/scheduling-int/src/main/java/dev/sheldan/abstracto/scheduling/service/SchedulerService.java b/abstracto-application/scheduling/scheduling-int/src/main/java/dev/sheldan/abstracto/scheduling/service/SchedulerService.java
index 1df1520de..83f8d7f72 100644
--- a/abstracto-application/scheduling/scheduling-int/src/main/java/dev/sheldan/abstracto/scheduling/service/SchedulerService.java
+++ b/abstracto-application/scheduling/scheduling-int/src/main/java/dev/sheldan/abstracto/scheduling/service/SchedulerService.java
@@ -23,4 +23,6 @@ public interface SchedulerService {
boolean executeJob(SchedulerJob job);
boolean executeJobWithParametersOnce(String name, String group, JobDataMap dataMap, Date date);
+
+ void startScheduler();
}