added experience tracking and automatic role assigning at given levels

includes: set role command to set a role to a given level (it clears out previous levels)
automatic level config generation on startup
min/max/multiplier configuration
job to persist the xp of the previous minute
added delayed scheduler start, so that events have a discord context in order to function
added role listener to automatically add and mark roles when they are created/deleted
fixed user in a server not being created correctly
added role service to give users role given by id
added server reference to role
This commit is contained in:
Sheldan
2020-04-11 17:21:55 +02:00
parent 87c97d5069
commit 6a31dfde8a
72 changed files with 1165 additions and 80 deletions

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>utility</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>experience-tracking-impl</artifactId>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -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";
}
}

View File

@@ -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<Parameter> 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 <level> <roleId>").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;
}
}

View File

@@ -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;
}

View File

@@ -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());
}
}

View File

@@ -0,0 +1,5 @@
package dev.sheldan.abstracto.experience.config;
public class ExperienceFeatures {
public static String EXPERIENCE = "experience";
}

View File

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

View File

@@ -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 {
}

View File

@@ -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<Long, List<AServer>> 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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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, Integer> {
AExperienceLevel findTopByExperienceNeededGreaterThanOrderByExperienceNeededAsc(Long experience);
}

View File

@@ -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, Long> {
AExperienceRole findByRoleServerAndRole(AServer server, ARole role);
List<AExperienceRole> findByLevelAndRoleServer(AExperienceLevel level, AServer server);
List<AExperienceRole> findByRoleServer(AServer server);
}

View File

@@ -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<AUserExperience, Long> {
}

View File

@@ -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;
}
}

View File

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

View File

@@ -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<Long, List<AServer>> 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<AServer> 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<Long, List<AServer>> getRuntimeExperience() {
return runtimeExperience;
}
@Override
public Integer calculateLevel(AUserExperience userInAServer, List<AExperienceLevel> 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<AExperienceRole> 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<AExperienceLevel> 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<AServer> 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<AExperienceLevel> levels = experienceLevelManagementService.getLevelConfig();
List<AExperienceRole> 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<AExperienceRole> 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());
}
}
}

View File

@@ -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<AExperienceLevel> getLevelConfig() {
return experienceLevelRepository.findAll();
}
}

View File

@@ -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<AExperienceRole> existingExperienceRoles = experienceRoleRepository.findByLevelAndRoleServer(level, server);
existingExperienceRoles.forEach(existingRole -> {
experienceRoleRepository.delete(existingRole);
});
}
@Override
public List<AExperienceRole> 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);
}
}

View File

@@ -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<AUserExperience> 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);
}
}

View File

@@ -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

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>utility</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>experience-tracking-int</artifactId>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.templating</groupId>
<artifactId>templating-interface</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -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;
}

View File

@@ -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<AUserExperience> users = new ArrayList<>();
}

View File

@@ -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;
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.abstracto.experience.service;
public interface ExperienceLevelService {
void createExperienceLevel(Integer level, Long experienceNeeded);
Long calculateExperienceForLevel(Integer level);
}

View File

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

View File

@@ -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<Long, List<AServer>> getRuntimeExperience();
Integer calculateLevel(AUserExperience userInAServer, List<AExperienceLevel> levels);
AExperienceRole calculateRole(AUserExperience userInAServer, List<AExperienceRole> roles);
void increaseExpForUser(AUserExperience userInAServer, Long experience, List<AExperienceLevel> levels);
void handleExperienceGain(List<AServer> serverExp);
void handleExperienceRoleForUser(AUserExperience userExperience, List<AExperienceRole> roles);
}

View File

@@ -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<AExperienceLevel> getLevelConfig();
}

View File

@@ -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<AExperienceRole> getExperienceRoleForServer(AServer server);
}

View File

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

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>dev.sheldan.abstracto</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking</artifactId>
<packaging>pom</packaging>
<modules>
<module>experience-tracking-int</module>
<module>experience-tracking-impl</module>
</modules>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-interface</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -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<Guild> guildByIdOptional = bot.getGuildById(guildId);
Optional<Guild> guildByIdOptional = botService.getGuildById(guildId);
if(guildByIdOptional.isPresent()) {
log.info("Banning user {} in guild {}.", userId, guildId);
guildByIdOptional.get().ban(userId.toString(), 0, reason).queue();

View File

@@ -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<Guild> guildById = bot.getGuildById(kickLogModel.getGuild().getIdLong());
Optional<Guild> guildById = botService.getGuildById(kickLogModel.getGuild().getIdLong());
if(guildById.isPresent()) {
guildById.get().kick(member, reason).queue();
this.sendKickLog(kickLogModel);

View File

@@ -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<TextChannel> textChannelOptional = bot.getTextChannelFromServer(channel.getServer().getId(), channel.getId());
Optional<TextChannel> textChannelOptional = botService.getTextChannelFromServer(channel.getServer().getId(), channel.getId());
if(textChannelOptional.isPresent()) {
TextChannel textChannel = textChannelOptional.get();
this.setSlowMode(textChannel, duration);

View File

@@ -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<Guild> guildById = bot.getGuildById(serverOfWarning.getId());
Optional<Guild> guildById = botService.getGuildById(serverOfWarning.getId());
String guildName = "<defaultName>";
if(guildById.isPresent()) {
guildName = guildById.get().getName();

View File

@@ -14,6 +14,7 @@
<modules>
<module>moderation</module>
<module>utility</module>
<module>experience-tracking</module>
</modules>

View File

@@ -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<Emote> emoteInGuild = bot.getEmote(guildId, aEmote);
Optional<Emote> emoteInGuild = botService.getEmote(guildId, aEmote);
if(EmoteUtils.isReactionEmoteAEmote(reactionEmote, aEmote, emoteInGuild.orElse(null))) {
Optional<EmbeddedMessage> embeddedMessageOptional = messageEmbedPostManagementService.findEmbeddedPostByMessageId(message.getMessageId());
if(embeddedMessageOptional.isPresent()) {

View File

@@ -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<Emote> emoteInGuild = bot.getEmote(guildId, aEmote);
Optional<Emote> emoteInGuild = botService.getEmote(guildId, aEmote);
if(EmoteUtils.isReactionEmoteAEmote(reactionEmote, aEmote, emoteInGuild.orElse(null))) {
Optional<CachedReaction> 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<Emote> emoteInGuild = bot.getEmote(guildId, aEmote);
Optional<Emote> emoteInGuild = botService.getEmote(guildId, aEmote);
if(EmoteUtils.isReactionEmoteAEmote(reactionEmote, aEmote, emoteInGuild.orElse(null))) {
Optional<CachedReaction> reactionOptional = EmoteUtils.getReactionFromMessageByEmote(message, aEmote);
updateStarboardPost(message, reactionOptional.orElse(null), userRemoving, false);

View File

@@ -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<StarStatsUser> convertToStarStatsUser(List<StarStatsUserResult> users, Long serverId) {
List<StarStatsUser> 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);

View File

@@ -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)

View File

@@ -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<Guild> guildToAnswerIn = bot.getGuildById(server.getId());
Optional<Guild> guildToAnswerIn = botService.getGuildById(server.getId());
if(guildToAnswerIn.isPresent()) {
Optional<TextChannel> channelToAnswerIn = bot.getTextChannelFromServer(server.getId(), channel.getId());
Optional<TextChannel> 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)

View File

@@ -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<TextChannel> channel = bot.getTextChannelFromServer(message.getServerId(), message.getChannelId());
Optional<Guild> guild = bot.getGuildById(message.getServerId());
Member member = botService.getMemberInServer(message.getServerId(), message.getAuthorId());
Optional<TextChannel> channel = botService.getTextChannelFromServer(message.getServerId(), message.getChannelId());
Optional<Guild> guild = botService.getGuildById(message.getServerId());
AChannel aChannel = AChannel.builder().id(message.getChannelId()).build();
AUser user = AUser.builder().id(message.getAuthorId()).build();
AServer server = AServer.builder().id(message.getServerId()).build();
@@ -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

View File

@@ -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;

View File

@@ -82,6 +82,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>

View File

@@ -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<CachedMessage> 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<CachedMessage> asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -21,7 +21,7 @@ import java.util.concurrent.CompletableFuture;
@Service
@Slf4j
public class BotServiceBean implements Bot {
public class BotServiceBean implements BotService {
private JDA instance;

View File

@@ -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) {

View File

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

View File

@@ -18,7 +18,7 @@ import java.util.Optional;
public class EmoteServiceBean implements EmoteService {
@Autowired
private Bot botService;
private BotService botService;
@Autowired
private EmoteManagementService emoteManagementService;

View File

@@ -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<CachedMessage> future, Long guildId, Long textChannelId, Long messageId) {
Optional<Guild> guildOptional = bot.getGuildById(guildId);
Optional<Guild> guildOptional = botService.getGuildById(guildId);
if(guildOptional.isPresent()) {
Optional<TextChannel> textChannelByIdOptional = bot.getTextChannelFromServer(guildOptional.get(), textChannelId);
Optional<TextChannel> textChannelByIdOptional = botService.getTextChannelFromServer(guildOptional.get(), textChannelId);
if(textChannelByIdOptional.isPresent()) {
TextChannel textChannel = textChannelByIdOptional.get();
textChannel.retrieveMessageById(messageId).queue(message -> {

View File

@@ -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<Guild> guildByIdOptional = bot.getGuildById(serverId);
Optional<Guild> guildByIdOptional = botService.getGuildById(serverId);
Optional<AEmote> 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<Void> deleteMessageInChannelInServer(Long serverId, Long channelId, Long messageId) {
return bot.deleteMessage(serverId, channelId, messageId);
return botService.deleteMessage(serverId, channelId, messageId);
}
}

View File

@@ -32,7 +32,7 @@ public class PostTargetServiceBean implements PostTargetService {
private PostTargetManagement postTargetManagement;
@Autowired
private Bot botService;
private BotService botService;
@Autowired
private DynamicKeyLoader dynamicKeyLoader;

View File

@@ -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<Guild> 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<Guild> 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));
}
}
}

View File

@@ -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<Long> availableRoles = SnowflakeUtils.getSnowflakeIds(existingRoles);
Set<Long> 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);
});

View File

@@ -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) {

View File

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

View File

@@ -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

View File

@@ -0,0 +1,7 @@
package dev.sheldan.abstracto.core.exception;
public class RoleException extends AbstractoRunTimeException {
public RoleException(String message) {
super(message);
}
}

View File

@@ -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;
}

View File

@@ -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<ARole> roles = new ArrayList<>();
@OneToMany(

View File

@@ -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);

View File

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

View File

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

View File

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

View File

@@ -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 <T extends UserInitiatedServerContext> UserInitiatedServerContext fromMessage(CachedMessage message, Class<T> 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);

View File

@@ -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);

View File

@@ -63,6 +63,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-impl</artifactId>

View File

@@ -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;

View File

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

View File

@@ -23,4 +23,6 @@ public interface SchedulerService {
boolean executeJob(SchedulerJob job);
boolean executeJobWithParametersOnce(String name, String group, JobDataMap dataMap, Date date);
void startScheduler();
}