[AB-xxx] adding sticky roles module

This commit is contained in:
Sheldan
2024-02-12 22:52:29 +01:00
parent 94a18559d3
commit 59575e0b49
45 changed files with 1317 additions and 19 deletions

View File

@@ -23,6 +23,7 @@
<module>remind</module>
<module>suggestion</module>
<module>repost-detection</module>
<module>sticky-roles</module>
<module>webservices</module>
<module>logging</module>
<module>invite-filter</module>

View File

@@ -9,11 +9,6 @@
<artifactId>remind-impl</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>

View File

@@ -14,11 +14,6 @@
<module>repost-detection-impl</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>

View File

@@ -9,9 +9,4 @@
<artifactId>repost-detection-int</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>

View File

@@ -0,0 +1,26 @@
<?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>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.25-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sticky-roles</artifactId>
<packaging>pom</packaging>
<modules>
<module>sticky-roles-int</module>
<module>sticky-roles-impl</module>
</modules>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,43 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>sticky-roles</artifactId>
<version>1.5.25-SNAPSHOT</version>
</parent>
<artifactId>sticky-roles-impl</artifactId>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/liquibase.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>sticky-roles-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,18 @@
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>liquibase</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<outputDirectory>.</outputDirectory>
<directory>${project.basedir}/src/main/resources/migrations</directory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,103 @@
package dev.sheldan.abstracto.stickyroles.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.stickyroles.config.StickyRolesFeatureDefinition;
import dev.sheldan.abstracto.stickyroles.config.StickyRolesSlashCommandNames;
import dev.sheldan.abstracto.stickyroles.service.StickyRoleService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ConfigureStickyRole extends AbstractConditionableCommand {
private static final String COMMAND_NAME = "configureStickyRole";
private static final String STICKY_PARAMETER_NAME = "sticky";
private static final String ROLE_PARAMETER_NAME = "role";
private static final String RESPONSE_TEMPLATE = "configureStickyRole_response";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private StickyRoleService stickyRoleService;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Boolean newState = slashCommandParameterService.getCommandOption(STICKY_PARAMETER_NAME, event, Boolean.class);
Role role = slashCommandParameterService.getCommandOption(ROLE_PARAMETER_NAME, event, Role.class);
stickyRoleService.setRoleStickiness(role, newState);
return interactionService.replyEmbed(RESPONSE_TEMPLATE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter roleParameter = Parameter
.builder()
.name(ROLE_PARAMETER_NAME)
.type(Role.class)
.optional(false)
.templated(true)
.build();
Parameter stateParameter = Parameter
.builder()
.name(STICKY_PARAMETER_NAME)
.type(Boolean.class)
.optional(false)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(roleParameter, stateParameter);
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(StickyRolesSlashCommandNames.STICKY_ROLES)
.commandName("configure")
.build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name(COMMAND_NAME)
.module(UtilityModuleDefinition.UTILITY)
.parameters(parameters)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.slashCommandOnly(true)
.supportsEmbedException(true)
.help(helpInfo)
.causesReaction(true)
.build();
}
@Override
public FeatureDefinition getFeature() {
return StickyRolesFeatureDefinition.STICKY_ROLES;
}
}

View File

@@ -0,0 +1,94 @@
package dev.sheldan.abstracto.stickyroles.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.models.template.display.RoleDisplay;
import dev.sheldan.abstracto.stickyroles.config.StickyRolesFeatureDefinition;
import dev.sheldan.abstracto.stickyroles.config.StickyRolesSlashCommandNames;
import dev.sheldan.abstracto.stickyroles.model.database.StickyRole;
import dev.sheldan.abstracto.stickyroles.model.template.StickyRoleDisplayModel;
import dev.sheldan.abstracto.stickyroles.model.template.StickyRolesDisplayModel;
import dev.sheldan.abstracto.stickyroles.service.StickyRoleService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ShowStickyRoles extends AbstractConditionableCommand {
private static final String COMMAND_NAME = "showStickyRoles";
private static final String RESPONSE_TEMPLATE = "showStickyRoles_response";
@Autowired
private StickyRoleService stickyRoleService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
List<StickyRole> stickyRoles = stickyRoleService.getStickyRolesForServer(event.getGuild());
log.info("Showing sticky role config for {} roles in server {}.", stickyRoles.size(), event.getGuild().getIdLong());
StickyRolesDisplayModel model = getModel(stickyRoles);
return interactionService.replyEmbed(RESPONSE_TEMPLATE, model, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
private StickyRolesDisplayModel getModel(List<StickyRole> stickyRoles) {
List<StickyRoleDisplayModel> displayRoles = stickyRoles
.stream()
.map(stickyRole -> StickyRoleDisplayModel
.builder()
.roleDisplay(RoleDisplay.fromARole(stickyRole.getRole()))
.sticky(stickyRole.getSticky())
.build())
.toList();
return StickyRolesDisplayModel
.builder()
.roles(displayRoles)
.build();
}
@Override
public CommandConfiguration getConfiguration() {
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(StickyRolesSlashCommandNames.STICKY_ROLES)
.commandName("show")
.build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name(COMMAND_NAME)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.slashCommandOnly(true)
.supportsEmbedException(true)
.help(helpInfo)
.causesReaction(true)
.build();
}
@Override
public FeatureDefinition getFeature() {
return StickyRolesFeatureDefinition.STICKY_ROLES;
}
}

View File

@@ -0,0 +1,103 @@
package dev.sheldan.abstracto.stickyroles.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.stickyroles.config.StickyRoleFeatureMode;
import dev.sheldan.abstracto.stickyroles.config.StickyRolesFeatureDefinition;
import dev.sheldan.abstracto.stickyroles.config.StickyRolesSlashCommandNames;
import dev.sheldan.abstracto.stickyroles.service.StickyRoleService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ToggleStickiness extends AbstractConditionableCommand {
private static final String COMMAND_NAME = "toggleStickiness";
private static final String RESPONSE_TEMPLATE = "toggleStickiness_response";
private static final String STICKY_PARAMETER_NAME = "sticky";
@Autowired
private StickyRoleService stickyRoleService;
@Autowired
private InteractionService interactionService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Member targetMember = event.getMember();
Boolean newState = slashCommandParameterService.getCommandOption(STICKY_PARAMETER_NAME, event, Boolean.class);
stickyRoleService.setStickiness(targetMember, newState);
return interactionService.replyEmbed(RESPONSE_TEMPLATE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter stateParameter = Parameter
.builder()
.name(STICKY_PARAMETER_NAME)
.type(Boolean.class)
.optional(false)
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(StickyRolesSlashCommandNames.STICKY_ROLES_PUBLIC)
.commandName("toggle")
.build();
List<Parameter> parameters = Arrays.asList(stateParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name(COMMAND_NAME)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.parameters(parameters)
.slashCommandOnly(true)
.supportsEmbedException(true)
.help(helpInfo)
.causesReaction(true)
.build();
}
@Override
public FeatureDefinition getFeature() {
return StickyRolesFeatureDefinition.STICKY_ROLES;
}
@Override
public List<FeatureMode> getFeatureModeLimitations() {
return Arrays.asList(StickyRoleFeatureMode.ALLOW_SELF_MANAGEMENT);
}
}

View File

@@ -0,0 +1,129 @@
package dev.sheldan.abstracto.stickyroles.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.stickyroles.config.StickyRolesFeatureDefinition;
import dev.sheldan.abstracto.stickyroles.config.StickyRolesSlashCommandNames;
import dev.sheldan.abstracto.stickyroles.service.StickyRoleService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ToggleStickinessManagement extends AbstractConditionableCommand {
private static final String COMMAND_NAME = "toggleStickinessManagement";
private static final String RESPONSE_TEMPLATE = "toggleStickinessManagement_response";
private static final String MEMBER_PARAMETER_NAME = "member";
private static final String STICKY_PARAMETER_NAME = "sticky";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private StickyRoleService stickyRoleService;
@Autowired
private UserService userService;
@Autowired
private ToggleStickinessManagement self;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Boolean newState = slashCommandParameterService.getCommandOption(STICKY_PARAMETER_NAME, event, Boolean.class);
if(slashCommandParameterService.hasCommandOptionWithFullType(MEMBER_PARAMETER_NAME, event, OptionType.USER)) {
Member targetMember = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER_NAME, event, User.class, Member.class);
stickyRoleService.setStickiness(targetMember, newState);
return interactionService.replyEmbed(RESPONSE_TEMPLATE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
} else {
String userIdStr = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER_NAME, event, User.class, String.class);
Long userId = Long.parseLong(userIdStr);
return userService.retrieveUserForId(userId).thenCompose(user -> {
self.callService(event, user, newState);
return interactionService.replyEmbed(RESPONSE_TEMPLATE, event);
}).thenApply(interactionHook -> CommandResult.fromSuccess());
}
}
@Transactional
public void callService(SlashCommandInteractionEvent event, User user, Boolean newState) {
stickyRoleService.setStickiness(user, event.getGuild(), newState);
}
@Override
public CommandConfiguration getConfiguration() {
Parameter memberParameter = Parameter
.builder()
.name(MEMBER_PARAMETER_NAME)
.type(User.class)
.optional(false)
.templated(true)
.build();
Parameter stateParameter = Parameter
.builder()
.name(STICKY_PARAMETER_NAME)
.type(Boolean.class)
.optional(false)
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(StickyRolesSlashCommandNames.STICKY_ROLES)
.commandName("manage")
.build();
List<Parameter> parameters = Arrays.asList(memberParameter, stateParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name(COMMAND_NAME)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.async(true)
.slashCommandOnly(true)
.supportsEmbedException(true)
.help(helpInfo)
.causesReaction(true)
.build();
}
@Override
public FeatureDefinition getFeature() {
return StickyRolesFeatureDefinition.STICKY_ROLES;
}
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.stickyroles.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:stickyRoles-config.properties")
public class StickyRolesConfig {
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.abstracto.stickyroles.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncUpdatePendingListener;
import dev.sheldan.abstracto.core.models.listener.MemberUpdatePendingModel;
import dev.sheldan.abstracto.stickyroles.config.StickyRolesFeatureDefinition;
import dev.sheldan.abstracto.stickyroles.service.StickyRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class StickyRolesJoinListener implements AsyncUpdatePendingListener {
@Autowired
private StickyRoleService stickyRoleService;
@Override
public DefaultListenerResult execute(MemberUpdatePendingModel model) {
stickyRoleService.handleJoin(model.getMember());
return DefaultListenerResult.PROCESSED;
}
@Override
public FeatureDefinition getFeature() {
return StickyRolesFeatureDefinition.STICKY_ROLES;
}
}

View File

@@ -0,0 +1,36 @@
package dev.sheldan.abstracto.stickyroles.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncLeaveListener;
import dev.sheldan.abstracto.core.models.listener.MemberLeaveModel;
import dev.sheldan.abstracto.stickyroles.config.StickyRolesFeatureDefinition;
import dev.sheldan.abstracto.stickyroles.service.StickyRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class StickyRolesLeaveListener implements AsyncLeaveListener {
@Autowired
private StickyRoleService stickyRoleService;
@Override
public DefaultListenerResult execute(MemberLeaveModel model) {
if(model.getMember() != null) {
stickyRoleService.handleLeave(model.getMember());
return DefaultListenerResult.PROCESSED;
} else {
log.warn("Member object was not found for storing sticky roles for user {} in server {}.", model.getLeavingUser().getUserId(), model.getServerId());
}
return DefaultListenerResult.IGNORED;
}
@Override
public FeatureDefinition getFeature() {
return StickyRolesFeatureDefinition.STICKY_ROLES;
}
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.stickyroles.repository;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.stickyroles.model.database.StickyRole;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface StickyRoleRepository extends JpaRepository<StickyRole, Long> {
List<StickyRole> findStickyRoleByServer(AServer server);
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.stickyroles.repository;
import dev.sheldan.abstracto.stickyroles.model.database.StickyRoleUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface StickyRoleUserRepository extends JpaRepository<StickyRoleUser, Long> {
}

View File

@@ -0,0 +1,132 @@
package dev.sheldan.abstracto.stickyroles.service;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.stickyroles.model.database.StickyRole;
import dev.sheldan.abstracto.stickyroles.model.database.StickyRoleUser;
import dev.sheldan.abstracto.stickyroles.service.management.StickyRoleManagementService;
import dev.sheldan.abstracto.stickyroles.service.management.StickyRoleUserManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Component
@Slf4j
public class StickyRoleServiceBean implements StickyRoleService {
@Autowired
private StickyRoleManagementService stickyRoleManagementService;
@Autowired
private StickyRoleUserManagementService stickyRoleUserManagementService;
@Autowired
private RoleService roleService;
@Autowired
private ServerManagementService serverManagementService;
@Override
public void setRoleStickiness(Role role, Boolean stickiness) {
StickyRole stickyRole = stickyRoleManagementService.getOrCreateStickyRole(role);
log.info("Setting stickiness of role {} in server {} to {}", role.getIdLong(), role.getGuild().getIdLong(), stickiness);
stickyRole.setSticky(stickiness);
}
@Override
public void setStickiness(Member member, Boolean stickiness) {
StickyRoleUser user = stickyRoleUserManagementService.getOrCreateStickyRoleUser(member);
user.setSticky(stickiness);
log.info("Setting stickiness of member {} in server {} to {}", member.getIdLong(), member.getGuild().getIdLong(), stickiness);
if(!stickiness) {
clearStickyRolesForUser(user);
}
}
@Override
public void setStickiness(User user, Guild guild, Boolean stickiness) {
StickyRoleUser stickyUser = stickyRoleUserManagementService.getOrCreateStickyRoleUser(guild.getIdLong(), user.getIdLong());
stickyUser.setSticky(stickiness);
log.info("Setting stickiness of member {} in server {} to {}", user.getIdLong(), guild.getIdLong(), stickiness);
if(!stickiness) {
clearStickyRolesForUser(stickyUser);
}
}
@Override
public void handleLeave(Member member) {
log.info("Handling user leave of member {} from server {} regarding sticky roles.", member.getIdLong(), member.getGuild().getIdLong());
StickyRoleUser user = stickyRoleUserManagementService.getOrCreateStickyRoleUser(member);
clearStickyRolesForUser(user);
if(user.getSticky()) {
List<Role> memberRoles = member.getRoles();
log.info("Member was marked as sticky - storing {} roles.", memberRoles.size());
Set<Long> memberRoleIds = memberRoles
.stream()
.map(ISnowflake::getIdLong)
.collect(Collectors.toSet());
List<StickyRole> existingStickyRolesOfUser = stickyRoleManagementService.getRoles(new ArrayList<>(memberRoleIds));
Set<Long> existingStickyRoleIdsOfUser = existingStickyRolesOfUser
.stream()
.map(StickyRole::getId)
.collect(Collectors.toSet());
memberRoleIds.removeAll(existingStickyRoleIdsOfUser);
List<StickyRole> newStickyRoles = memberRoleIds
.stream()
.map(rid -> stickyRoleManagementService.createStickyRole(rid))
.toList();
log.debug("Creating {} new roles.", newStickyRoles.size());
List<StickyRole> stickyRolesOfUser = new ArrayList<>(existingStickyRolesOfUser);
stickyRolesOfUser.addAll(newStickyRoles);
stickyRolesOfUser.forEach(stickyRole -> {
stickyRole.getUsers().add(user);
});
user.setRoles(stickyRolesOfUser);
}
}
private static void clearStickyRolesForUser(StickyRoleUser user) {
log.debug("Clearing sticky roles for user {}", user.getId());
user.getRoles().forEach(stickyRole -> {
stickyRole.getUsers().remove(user);
});
user.getRoles().clear();
}
@Override
public void handleJoin(Member member) {
log.info("Handling server join for member {} in server {} regarding sticky roles.", member.getIdLong(), member.getGuild().getIdLong());
StickyRoleUser user = stickyRoleUserManagementService.getOrCreateStickyRoleUser(member);
if(user.getSticky()) {
List<Long> rolesToAdd = user
.getRoles()
.stream()
.filter(StickyRole::getSticky)
.filter(r -> Boolean.FALSE.equals(r.getRole().getDeleted()))
.map(StickyRole::getId)
.toList();
log.info("Adding {} roles to user {} in server {}", rolesToAdd.size(), member.getIdLong(), member.getGuild().getIdLong());
roleService.updateRolesIds(member, new ArrayList<>(), rolesToAdd).thenAccept(unused -> {
log.info("Successfully added {} roles to user {} in server {}", rolesToAdd.size(), member.getIdLong(), member.getGuild().getIdLong());
}).exceptionally(throwable -> {
log.warn("Failed to add {} roles to user {} in server {}", rolesToAdd.size(), member.getIdLong(), member.getGuild().getIdLong(), throwable);
return null;
});
} else {
log.info("Not re-applying roles for member {} in server {} as they opted out.", member.getIdLong(), member.getGuild().getIdLong());
}
}
@Override
public List<StickyRole> getStickyRolesForServer(Guild guild) {
AServer server = serverManagementService.loadServer(guild);
return stickyRoleManagementService.getStickyRolesForServer(server);
}
}

View File

@@ -0,0 +1,60 @@
package dev.sheldan.abstracto.stickyroles.service.management;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.stickyroles.model.database.StickyRole;
import dev.sheldan.abstracto.stickyroles.repository.StickyRoleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
public class StickyRoleManagementServieBean implements StickyRoleManagementService {
@Autowired
private StickyRoleRepository stickyRoleRepository;
@Autowired
private RoleManagementService roleManagementService;
@Override
public StickyRole getOrCreateStickyRole(Long roleId) {
Optional<StickyRole> existingRole = stickyRoleRepository.findById(roleId);
return existingRole.orElseGet(() -> createStickyRole(roleId));
}
@Override
public StickyRole createStickyRole(Long roleId) {
ARole aRole = roleManagementService.findRole(roleId);
StickyRole role = StickyRole
.builder()
.id(roleId)
.sticky(StickyRoleManagementService.DEFAULT_STICKINESS)
.role(aRole)
.server(aRole.getServer())
.build();
return stickyRoleRepository.save(role);
}
@Override
public List<StickyRole> createStickyRoles(List<Long> roleIds) {
return roleIds
.stream()
.map(this::createStickyRole)
.toList();
}
@Override
public List<StickyRole> getRoles(List<Long> roleIds) {
return stickyRoleRepository.findAllById(roleIds);
}
@Override
public List<StickyRole> getStickyRolesForServer(AServer server) {
return stickyRoleRepository.findStickyRoleByServer(server);
}
}

View File

@@ -0,0 +1,53 @@
package dev.sheldan.abstracto.stickyroles.service.management;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.stickyroles.model.database.StickyRoleUser;
import dev.sheldan.abstracto.stickyroles.repository.StickyRoleUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class StickyRoleUserManagementServiceBean implements StickyRoleUserManagementService {
@Autowired
private StickyRoleUserRepository repository;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Override
public StickyRoleUser getOrCreateStickyRoleUser(Long serverId, Long userId) {
ServerUser serverUser = ServerUser
.builder()
.userId(userId)
.serverId(serverId)
.build();
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(serverUser);
return repository.findById(userInAServer.getUserInServerId()).orElseGet(() -> createStickyroleUser(userInAServer));
}
@Override
public StickyRoleUser createStickyroleUser(Long serverId, Long userId) {
ServerUser serverUser = ServerUser
.builder()
.userId(userId)
.serverId(serverId)
.build();
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(serverUser);
return createStickyroleUser(userInAServer);
}
@Override
public StickyRoleUser createStickyroleUser(AUserInAServer userInAServer) {
StickyRoleUser stickyRoleUser = StickyRoleUser
.builder()
.user(userInAServer)
.server(userInAServer.getServerReference())
.id(userInAServer.getUserInServerId())
.sticky(true)
.build();
return repository.save(stickyRoleUser);
}
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd" >
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,33 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd" >
<property name="utilityModule" value="(SELECT id FROM module WHERE name = 'utility')"/>
<property name="stickyRolesFeature" value="(SELECT id FROM feature WHERE key = 'stickyRoles')"/>
<changeSet author="Sheldan" id="stickyRole-commands">
<insert tableName="command">
<column name="name" value="configureStickyRole"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${stickyRolesFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="toggleStickiness"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${stickyRolesFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="showStickyRoles"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${stickyRolesFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="toggleStickinessManagement"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${stickyRolesFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,8 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd" >
<include file="feature.xml" relativeToChangelogFile="true"/>
<include file="command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,11 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd" >
<changeSet author="Sheldan" id="stickyRoles_feature-insertion">
<insert tableName="feature">
<column name="key" value="stickyRoles"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,37 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd" >
<changeSet author="Sheldan" id="sticky_role-table">
<createTable tableName="sticky_role">
<column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_sticky_role"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="sticky" type="BOOLEAN" defaultValue="true">
<constraints nullable="false"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
<addForeignKeyConstraint baseColumnNames="id" baseTableName="sticky_role" constraintName="fk_sticky_role_role"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="role" validate="true"/>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="sticky_role" constraintName="fk_sticky_role_server"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="server" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS sticky_role_update_trigger ON sticky_role;
CREATE TRIGGER sticky_role_update_trigger BEFORE UPDATE ON sticky_role FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS sticky_role_insert_trigger ON sticky_role;
CREATE TRIGGER sticky_role_insert_trigger BEFORE INSERT ON sticky_role FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,67 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd" >
<changeSet author="Sheldan" id="sticky_role_user-table">
<createTable tableName="sticky_role_user">
<column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_sticky_role_user"/>
</column>
<column name="sticky" type="BOOLEAN" defaultValue="true">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
<addForeignKeyConstraint baseColumnNames="id" baseTableName="sticky_role_user" constraintName="fk_sticky_role_user_user"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="sticky_role_user"
constraintName="fk_sticky_role_user_server" deferrable="false" initiallyDeferred="false"
onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server"
validate="true"/>
<sql>
DROP TRIGGER IF EXISTS sticky_role_user_update_trigger ON sticky_role_user;
CREATE TRIGGER sticky_role_user_update_trigger BEFORE UPDATE ON sticky_role_user FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS sticky_role_user_insert_trigger ON sticky_role_user;
CREATE TRIGGER sticky_role_user_insert_trigger BEFORE INSERT ON sticky_role_user FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
<changeSet author="Sheldan" id="sticky_role_user_mapping-table">
<createTable tableName="sticky_role_user_mapping">
<column name="role_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="user_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="role_id" baseTableName="sticky_role_user_mapping" constraintName="fk_sticky_role_user_mapping_role"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="sticky_role" validate="true"/>
<addForeignKeyConstraint baseColumnNames="user_id" baseTableName="sticky_role_user_mapping" constraintName="fk_sticky_role_user_mapping_user"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="sticky_role_user" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS sticky_role_user_mapping_update_trigger ON sticky_role_user_mapping;
CREATE TRIGGER sticky_role_user_mapping_update_trigger BEFORE UPDATE ON sticky_role_user_mapping FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS sticky_role_user_mapping_insert_trigger ON sticky_role_user_mapping;
CREATE TRIGGER sticky_role_user_mapping_insert_trigger BEFORE INSERT ON sticky_role_user_mapping FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,8 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd" >
<include file="sticky_role.xml" relativeToChangelogFile="true"/>
<include file="sticky_role_user.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,6 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd" >
<include file="1.5.25/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,6 @@
abstracto.featureFlags.stickyRoles.featureName=stickyRoles
abstracto.featureFlags.stickyRoles.enabled=false
abstracto.featureModes.allowSelfManagement.featureName=stickyRoles
abstracto.featureModes.allowSelfManagement.mode=allowSelfManagement
abstracto.featureModes.allowSelfManagement.enabled=false

View File

@@ -0,0 +1,15 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>sticky-roles</artifactId>
<version>1.5.25-SNAPSHOT</version>
</parent>
<artifactId>sticky-roles-int</artifactId>
</project>

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.stickyroles.config;
import dev.sheldan.abstracto.core.config.FeatureMode;
import lombok.Getter;
@Getter
public enum StickyRoleFeatureMode implements FeatureMode {
ALLOW_SELF_MANAGEMENT("allowSelfManagement");
private final String key;
StickyRoleFeatureMode(String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,23 @@
package dev.sheldan.abstracto.stickyroles.config;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.FeatureMode;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class StickyRolesFeatureConfig implements FeatureConfig {
@Override
public FeatureDefinition getFeature() {
return StickyRolesFeatureDefinition.STICKY_ROLES;
}
@Override
public List<FeatureMode> getAvailableModes() {
return Arrays.asList(StickyRoleFeatureMode.ALLOW_SELF_MANAGEMENT);
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.stickyroles.config;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import lombok.Getter;
@Getter
public enum StickyRolesFeatureDefinition implements FeatureDefinition {
STICKY_ROLES("stickyRoles");
private String key;
StickyRolesFeatureDefinition(String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.abstracto.stickyroles.config;
public class StickyRolesSlashCommandNames {
public static final String STICKY_ROLES = "stickyroles";
public static final String STICKY_ROLES_PUBLIC = "stickyrolespublic";
}

View File

@@ -0,0 +1,52 @@
package dev.sheldan.abstracto.stickyroles.model.database;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import jakarta.persistence.*;
import lombok.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name="sticky_role")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
public class StickyRole {
@Id
@Column(name = "id", nullable = false)
private Long id;
/**
* Reference to the actual {@link ARole} being maintained
*/
@OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@PrimaryKeyJoinColumn
private ARole role;
@Column(name = "sticky", nullable = false)
private Boolean sticky;
@ManyToMany
@JoinTable(
name = "sticky_role_user_mapping",
joinColumns = @JoinColumn(name = "role_id"),
inverseJoinColumns = @JoinColumn(name = "user_id"))
@Builder.Default
private List<StickyRoleUser> users = new ArrayList<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "server_id", nullable = false)
private AServer server;
@Column(name = "created", nullable = false, insertable = false, updatable = false)
private Instant created;
@Column(name = "updated", insertable = false, updatable = false)
private Instant updated;
}

View File

@@ -0,0 +1,51 @@
package dev.sheldan.abstracto.stickyroles.model.database;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import jakarta.persistence.*;
import lombok.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "sticky_role_user")
@Getter
@Setter
@EqualsAndHashCode
public class StickyRoleUser {
/**
* The ID of the {@link AUserInAServer user} which is represented by this object
*/
@Id
@Column(name = "id", nullable = false)
private Long id;
/**
* The {@link AUserInAServer user} which is represented by this object
*/
@OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@PrimaryKeyJoinColumn
private AUserInAServer user;
@Column(name = "sticky")
private Boolean sticky;
@ManyToMany(mappedBy = "users")
@Builder.Default
private List<StickyRole> roles = new ArrayList<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "server_id", nullable = false)
private AServer server;
@Column(name = "created", nullable = false, insertable = false, updatable = false)
private Instant created;
@Column(name = "updated", insertable = false, updatable = false)
private Instant updated;
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.stickyroles.model.template;
import dev.sheldan.abstracto.core.models.template.display.RoleDisplay;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class StickyRoleDisplayModel {
private RoleDisplay roleDisplay;
private Boolean sticky;
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.stickyroles.model.template;
import lombok.Builder;
import lombok.Getter;
import java.util.List;
@Getter
@Builder
public class StickyRolesDisplayModel {
private List<StickyRoleDisplayModel> roles;
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.abstracto.stickyroles.service;
import dev.sheldan.abstracto.stickyroles.model.database.StickyRole;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.User;
import java.util.List;
public interface StickyRoleService {
default void ignoreRoleFromStickyRoles(Role role) {
setRoleStickiness(role, false);
}
default void addRoleToStickyRoles(Role role) {
setRoleStickiness(role, true);
}
void setRoleStickiness(Role role, Boolean stickiness);
void setStickiness(Member member, Boolean newState);
void setStickiness(User user, Guild guild, Boolean newState);
void handleLeave(Member member);
void handleJoin(Member member);
List<StickyRole> getStickyRolesForServer(Guild guild);
}

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.stickyroles.service.management;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.stickyroles.model.database.StickyRole;
import net.dv8tion.jda.api.entities.Role;
import java.util.List;
public interface StickyRoleManagementService {
default StickyRole getOrCreateStickyRole(Role role) {
return getOrCreateStickyRole(role.getIdLong());
}
StickyRole getOrCreateStickyRole(Long roleId);
StickyRole createStickyRole(Long roleId);
List<StickyRole> createStickyRoles(List<Long> roleIds);
List<StickyRole> getRoles(List<Long> roleIds);
List<StickyRole> getStickyRolesForServer(AServer server);
Boolean DEFAULT_STICKINESS = true;
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.stickyroles.service.management;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.stickyroles.model.database.StickyRoleUser;
import net.dv8tion.jda.api.entities.Member;
public interface StickyRoleUserManagementService {
default StickyRoleUser getOrCreateStickyRoleUser(Member member) {
return getOrCreateStickyRoleUser(member.getGuild().getIdLong(), member.getIdLong());
}
StickyRoleUser getOrCreateStickyRoleUser(Long serverId, Long userId);
StickyRoleUser createStickyroleUser(Long serverId, Long userId);
StickyRoleUser createStickyroleUser(AUserInAServer userInAServer);
}

View File

@@ -30,6 +30,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>sticky-roles-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation-int</artifactId>

View File

@@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Service
public class CommandManager implements CommandRegistry {
@@ -54,7 +53,7 @@ public class CommandManager implements CommandRegistry {
@Override
public Optional<Command> findCommandByParameters(String name, UnParsedCommandParameter unParsedCommandParameter, Long serverId) {
Optional<Command> commandOptional = commands.stream().filter(getCommandByNameAndParameterPredicate(name, unParsedCommandParameter, serverId)).findFirst();
if(!commandOptional.isPresent()) {
if(commandOptional.isEmpty()) {
commandOptional = getCommandViaAliasAndParameter(name, unParsedCommandParameter, serverId);
}
return commandOptional;
@@ -139,7 +138,7 @@ public class CommandManager implements CommandRegistry {
.getDependentFeatures()
.stream()
.map(s -> featureConfigService.getFeatureEnum(s))
.collect(Collectors.toList());
.toList();
boolean required = false;
for (FeatureDefinition featureDefinition : featureDefinitions) {
if(featureFlagService.getFeatureFlagValue(featureDefinition, serverId)) {

View File

@@ -46,6 +46,7 @@ public class AsyncLeaveListenerBean extends ListenerAdapter {
.build();
return MemberLeaveModel
.builder()
.member(event.getMember())
.leavingUser(serverUser)
.user(event.getUser())
.build();

View File

@@ -10,7 +10,7 @@ public class FeatureNotFoundException extends AbstractoRunTimeException implemen
private final FeatureNotFoundExceptionModel model;
public FeatureNotFoundException(String feature, List<String> availableFeatures) {
super("Feature not found.");
super(String.format("Feature %s not found.", feature));
this.model = FeatureNotFoundExceptionModel
.builder()
.featureName(feature)

View File

@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.core.models.ServerUser;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
@Getter
@@ -13,6 +14,7 @@ import net.dv8tion.jda.api.entities.User;
public class MemberLeaveModel implements FeatureAwareListenerModel {
private ServerUser leavingUser;
private User user;
private Member member;
@Override
public Long getServerId() {
return leavingUser.getServerId();