added assignable role place module including: setting up, configuring, deleting commands and documentation

upgraded JDA version to 4.2.0
refactored multiple interfaces to be more convenient/contain more information (reaction added/removed now gets the actual event)
added generic way to check for conditions. these conditions are provided by modules and are loosely connected via condition context and a condition name
added changeable flag to emotes to indicate that they can be updated via setEmote
refactored emote parsing in command parameters, the command parameters will now contain a fake emote
added feature to embed templates for fields to force a new message regardless of the discord limit
added some more functionality to message and channel service regarding field edit/embed sending
introduced the full emote parameter, to have both the emote (if custom) and a fake aemote at hand
refactored some methods to already throw exceptions within the retrieval methods, instead of optionals which need to be dealt outside
changed getEmotes to getEmotesBag to have duplicates of emotes
fixed setEmote to behave correctly with new parameter types
fixed creation of emotes, which previously created additional instances
fixed templating multiple fields handling
refactored command handling to allow async commands, they are the same interface, but configuration dicates whether or not it is async
added generic exception reporting for async commands
refactored a bunch of service methods to be named optional, and the non optional methods throw exceptions in case nothing is found
added a few more customized exceptions
added clearing freemarker internal template cache to clear cache
added feature to skip, not use, embeds if they look to be empty (no fields, no description, no attachment)
added virtual env to gitignore
fixed initial sync of roles un-marking roles as deleted
added some convenience methods to remove reactions from users directly
fixed post command handling in case it is not a templatable instance
fixed exceptions without cause in generic exception model
This commit is contained in:
Sheldan
2020-07-23 02:22:58 +02:00
parent 5317199bf4
commit fd4d784081
201 changed files with 6547 additions and 527 deletions

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,52 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class ActivateAssignableRolePlace extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
service.activateAssignableRolePlace(commandContext.getUserInitiatedContext().getServer(), name);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("activateAssignableRolePlace")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,75 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.exceptions.AssignableRoleAlreadyDefinedException;
import dev.sheldan.abstracto.assignableroles.exceptions.AssignableRoleNotUsableException;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.FullEmote;
import dev.sheldan.abstracto.core.models.FullRole;
import dev.sheldan.abstracto.core.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class AddRoleToAssignableRolePost extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Autowired
private RoleService roleService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
FullEmote emote = (FullEmote) parameters.get(1);
String description = (String) parameters.get(2);
FullRole role = (FullRole) parameters.get(3);
if(service.hasAssignableRolePlaceEmote(commandContext.getUserInitiatedContext().getServer(), name, emote.getFakeEmote())) {
throw new AssignableRoleAlreadyDefinedException(emote, name);
}
if(!roleService.canBotInteractWithRole(role.getRole())) {
throw new AssignableRoleNotUsableException(role, commandContext.getGuild());
}
return service.addRoleToAssignableRolePlace(commandContext.getUserInitiatedContext().getServer(), name, role.getRole(), emote, description)
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter emote = Parameter.builder().name("emote").type(FullEmote.class).templated(true).build();
Parameter description = Parameter.builder().name("description").type(String.class).templated(true).build();
Parameter role = Parameter.builder().name("role").type(FullRole.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, emote, description, role);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("addRoleToAssignableRolePlace")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.async(true)
.reportsException(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,18 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.core.command.config.ModuleInfo;
import dev.sheldan.abstracto.core.command.config.ModuleInterface;
public class AssignableRoleModule implements ModuleInterface {
public static final String ASSIGNABLE_ROLES = "assignableRoles";
@Override
public ModuleInfo getInfo() {
return ModuleInfo.builder().name(ASSIGNABLE_ROLES).description("Module containing commands for creating and maintaining assignable role posts.").build();
}
@Override
public String getParentModule() {
return "default";
}
}

View File

@@ -0,0 +1,54 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class ChangeAssignablePlaceDescription extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
String newDescription = (String) parameters.get(1);
service.changeAssignablePlaceDescription(commandContext.getUserInitiatedContext().getServer(), name, newDescription);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter newDescription = Parameter.builder().name("newDescription").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, newDescription);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("changeAssignablePlaceDescription")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,62 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.AssignableRolePlaceParameterKey;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import lombok.extern.slf4j.Slf4j;
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 ChangeAssignableRolePlaceConfig extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
AssignableRolePlaceParameterKey configKey = (AssignableRolePlaceParameterKey) parameters.get(1);
Object parameterValue = parameters.get(2);
return service.changeConfiguration(commandContext.getUserInitiatedContext().getServer(), name, configKey, parameterValue)
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter assignableRolePlaceName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter parameterKey = Parameter.builder().name("key").type(AssignableRolePlaceParameterKey.class).templated(true).build();
Parameter parameterValue = Parameter.builder().name("value").type(Object.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(assignableRolePlaceName, parameterKey, parameterValue);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("changeAssignableRolePlaceConfig")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.async(true)
.reportsException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,67 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import net.dv8tion.jda.api.entities.MessageChannel;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class CreateAssignableRolePost extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Autowired
private ChannelManagementService channelManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
MessageChannel channel = (TextChannel) parameters.get(1);
String text = (String) parameters.get(2);
AChannel chosenChannel = channelManagementService.loadChannel(channel.getIdLong());
service.createAssignableRolePlace(commandContext.getUserInitiatedContext().getServer(), name, chosenChannel, text);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter channel = Parameter.builder().name("channel").type(TextChannel.class).templated(true).build();
Parameter text = Parameter.builder().name("text").type(String.class).remainder(true).optional(true).templated(true).build();
List<String> aliases = Arrays.asList("crRPl", "crAssRoPl");
List<Parameter> parameters = Arrays.asList(rolePostName, channel, text);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("createAssignableRolePlace")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.reportsException(true)
.causesReaction(true)
.parameters(parameters)
.aliases(aliases)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,52 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class DeactivateAssignableRolePlace extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
service.deactivateAssignableRolePlace(commandContext.getUserInitiatedContext().getServer(), name);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("deactivateAssignableRolePlace")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,52 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class DeleteAssignableRolePlace extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
service.deleteAssignableRolePlace(commandContext.getUserInitiatedContext().getServer(), name);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("deleteAssignableRolePlace")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,58 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class EditAssignableRolePlaceText extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
String newText = (String) parameters.get(1);
return service.changeText(commandContext.getUserInitiatedContext().getServer(), name, newText).thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter newText = Parameter.builder().name("newText").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, newText);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("editAssignableRolePlaceText")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.async(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,55 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class MoveAssignableRolePlace extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService placeManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
TextChannel newChannel = (TextChannel) parameters.get(1);
placeManagementService.moveAssignableRolePlace(commandContext.getUserInitiatedContext().getServer(), name, newChannel);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter channel = Parameter.builder().name("channel").type(TextChannel.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, channel);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("moveAssignableRolePlace")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,55 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.FullEmote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class RemoveRoleFromAssignableRolePlace extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
FullEmote emote = (FullEmote) parameters.get(1);
service.removeRoleFromAssignableRolePlace(commandContext.getUserInitiatedContext().getServer(), name, emote);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter emote = Parameter.builder().name("emote").type(FullEmote.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, emote);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("removeRoleFromAssignableRolePlace")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,63 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.FullEmote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class SetAssignableRolePosition extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
FullEmote emote = (FullEmote) parameters.get(1);
Integer newPosition = (Integer) parameters.get(2);
if(!service.hasAssignableRolePlaceEmote(commandContext.getUserInitiatedContext().getServer(), name, emote.getFakeEmote())) {
return CommandResult.fromError("Place does not have emote assigned.");
}
if(service.isPositionUsed(commandContext.getUserInitiatedContext().getServer(), name, newPosition)) {
return CommandResult.fromError("Position is already used");
}
service.setEmoteToPosition(commandContext.getUserInitiatedContext().getServer(), name, emote, newPosition);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter emote = Parameter.builder().name("emote").type(FullEmote.class).templated(true).build();
Parameter newPosition = Parameter.builder().name("newPosition").type(Integer.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, emote, newPosition);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("setAssignableRolePosition")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,61 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import dev.sheldan.abstracto.core.service.ChannelService;
import lombok.extern.slf4j.Slf4j;
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 SetupAssignableRolePlace extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Autowired
private ChannelService channelService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
return service.setupAssignableRolePlace(commandContext.getUserInitiatedContext().getServer(), name)
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("setupAssignableRolePlace")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.async(true)
.reportsException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,53 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class ShowAssignableRolePlaceConfig extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
service.showAssignablePlaceConfig(commandContext.getUserInitiatedContext().getServer(), name, commandContext.getChannel());
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("showAssignableRolePlaceConfig")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,47 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
@Component
public class ShowAssignableRolePlaces extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
return service.showAllAssignableRolePlaces(commandContext.getUserInitiatedContext().getServer(), commandContext.getChannel())
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("showAssignableRolePlaces")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.async(true)
.reportsException(true)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,73 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.exceptions.EmoteNotInAssignableRolePlaceException;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.FullEmote;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.EmoteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class SwapAssignableRolePosition extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Autowired
private EmoteService emoteService;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
FullEmote firstEmote = (FullEmote) parameters.get(1);
FullEmote secondEmote = (FullEmote) parameters.get(2);
AServer server = commandContext.getUserInitiatedContext().getServer();
if(emoteService.compareAEmote(firstEmote.getFakeEmote(), secondEmote.getFakeEmote())) {
return CommandResult.fromError("You cannot swap the same emote");
}
if(!service.hasAssignableRolePlaceEmote(server, name, firstEmote.getFakeEmote())) {
throw new EmoteNotInAssignableRolePlaceException(firstEmote, name);
}
if(!service.hasAssignableRolePlaceEmote(server, name, firstEmote.getFakeEmote())) {
throw new EmoteNotInAssignableRolePlaceException(secondEmote, name);
}
service.swapPositions(server, name, firstEmote, secondEmote);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter firstEmote = Parameter.builder().name("firstEmote").type(FullEmote.class).templated(true).build();
Parameter secondEmote = Parameter.builder().name("secondEmote").type(FullEmote.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, firstEmote, secondEmote);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("swapAssignableRolePosition")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,52 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.config.FeatureEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class TestAssignableRolePlace extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
service.testAssignableRolePlace(commandContext.getUserInitiatedContext().getServer(), name, commandContext.getChannel());
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("testAssignableRolePlace")
.module(AssignableRoleModule.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,40 @@
package dev.sheldan.abstracto.assignableroles.listener;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.listener.MessageDeletedListener;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
@Slf4j
public class AssignablePostDeletedListener implements MessageDeletedListener {
@Autowired
private AssignableRolePlacePostManagementService service;
@Override
public void execute(CachedMessage messageBefore, AServerAChannelAUser authorUser, GuildChannelMember authorMember) {
Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(messageBefore.getMessageId());
messageOptional.ifPresent(post -> {
AssignableRolePlace assignablePlace = post.getAssignablePlace();
log.info("Post {} has been deleted in server {} in channel {}, we are removing a post from place {}.", post.getId(), messageBefore.getServerId(), messageBefore.getChannelId(), assignablePlace.getKey());
post.getAssignableRoles().forEach(assignableRole -> assignableRole.setAssignableRolePlacePost(null));
assignablePlace.getMessagePosts().remove(post);
});
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,106 @@
package dev.sheldan.abstracto.assignableroles.listener;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.models.database.AssignedRoleUser;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleServiceBean;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementService;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.listener.ReactedAddedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.EmoteService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.MessageReaction;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class AssignablePostReactionAdded implements ReactedAddedListener {
@Autowired
private AssignableRolePlacePostManagementService service;
@Autowired
private AssignableRoleServiceBean assignableRoleServiceBean;
@Autowired
private EmoteService emoteService;
@Autowired
private AssignableRolePlaceService assignableRolePlaceService;
@Autowired
private AssignedRoleUserManagementService assignedRoleUserManagementService;
@Override
public void executeReactionAdded(CachedMessage message, GuildMessageReactionAddEvent event, AUserInAServer userAdding) {
Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(message.getMessageId());
if(messageOptional.isPresent()) {
MessageReaction reaction = event.getReaction();
AssignableRolePlacePost assignablePlacePost = messageOptional.get();
if(reaction.isSelf()) {
log.info("Ignoring self reaction on assignable role post in server {}.", message.getServerId());
return;
}
MessageReaction.ReactionEmote reactionEmote = event.getReactionEmote();
if(assignablePlacePost.getAssignablePlace().getActive()) {
addAppropriateRoles(event, reaction, assignablePlacePost, reactionEmote, userAdding);
} else {
reaction.removeReaction(event.getUser()).submit();
log.trace("Reaction for assignable place {} in sever {} was added, but place is inactive.", assignablePlacePost.getAssignablePlace().getKey(), userAdding.getServerReference().getId());
}
}
}
private void addAppropriateRoles(GuildMessageReactionAddEvent event, MessageReaction reaction, AssignableRolePlacePost assignablePlacePost, MessageReaction.ReactionEmote reactionEmote, AUserInAServer userAdding) {
boolean validReaction = false;
AssignableRolePlace assignableRolePlace = assignablePlacePost.getAssignablePlace();
for (AssignableRole assignableRole : assignablePlacePost.getAssignableRoles()) {
if (emoteService.isReactionEmoteAEmote(reactionEmote, assignableRole.getEmote())) {
CompletableFuture<Void> future;
if(assignableRolePlace.getUniqueRoles()) {
Optional<AssignedRoleUser> byUserInServer = assignedRoleUserManagementService.findByUserInServerOptional(userAdding);
if(byUserInServer.isPresent()){
future = assignableRolePlaceService.removeExistingReactionsAndRoles(assignableRolePlace, byUserInServer.get());
} else {
future = CompletableFuture.completedFuture(null);
}
} else {
future = CompletableFuture.completedFuture(null);
}
Long assignableRoleId = assignableRole.getId();
future.whenComplete((aVoid, throwable) -> {
if(throwable != null) {
log.warn("Failed to remove previous role assignments for {} in server {} at place {}.",
userAdding.getUserReference().getId(), userAdding.getServerReference().getId(), assignablePlacePost.getAssignablePlace().getKey());
}
assignableRoleServiceBean.assignAssignableRoleToUser(assignableRoleId, event.getMember()).exceptionally(innerThrowable -> {
log.error("Failed to add new role assignment.", innerThrowable);
return null;
});
});
validReaction = true;
break;
}
}
if(!validReaction || assignableRolePlace.getAutoRemove()) {
reaction.removeReaction(event.getUser()).submit();
}
}
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,63 @@
package dev.sheldan.abstracto.assignableroles.listener;
import dev.sheldan.abstracto.assignableroles.config.features.AssignableRoleFeature;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.listener.ReactedRemovedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.RoleService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.MessageReaction;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
@Slf4j
public class AssignablePostReactionRemoved implements ReactedRemovedListener {
@Autowired
private AssignableRolePlacePostManagementService service;
@Autowired
private EmoteService emoteService;
@Autowired
private RoleService roleService;
@Autowired
private AssignableRoleService assignableRoleService;
@Override
public FeatureEnum getFeature() {
return AssignableRoleFeature.ASSIGNABLE_ROLES;
}
@Override
public void executeReactionRemoved(CachedMessage message, GuildMessageReactionRemoveEvent event, AUserInAServer userRemoving) {
Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(message.getMessageId());
if(messageOptional.isPresent()) {
MessageReaction.ReactionEmote reactionEmote = event.getReactionEmote();
AssignableRolePlacePost assignablePlacePost = messageOptional.get();
if(assignablePlacePost.getAssignablePlace().getActive()) {
assignablePlacePost.getAssignableRoles().forEach(assignableRole -> {
if(emoteService.isReactionEmoteAEmote(reactionEmote, assignableRole.getEmote())) {
Long assignableRoleId = assignableRole.getId();
assignableRoleService.removeAssignableRoleFromUser(assignableRole, event.getMember()).exceptionally(throwable -> {
log.error("Failed to remove assignable role {} from user {}.", assignableRoleId, event.getMember(), throwable);
return null;
});
}
});
} else {
log.trace("Reaction for assignable place {} in sever {} was added, but place is inactive.", assignablePlacePost.getAssignablePlace().getKey(), userRemoving.getServerReference().getId());
}
}
}
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.assignableroles.repository;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlacePost;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface AssignableRolePlacePostRepository extends JpaRepository<AssignableRolePlacePost, Long> {
}

View File

@@ -0,0 +1,16 @@
package dev.sheldan.abstracto.assignableroles.repository;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlace;
import dev.sheldan.abstracto.core.models.database.AServer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface AssignableRolePlaceRepository extends JpaRepository<AssignableRolePlace, Long> {
boolean existsByServerAndKey(AServer server, String key);
Optional<AssignableRolePlace> findByServerAndKey(AServer server, String key);
List<AssignableRolePlace> findByServer(AServer server);
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.assignableroles.repository;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRole;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface AssignableRoleRepository extends JpaRepository<AssignableRole, Long> {
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.assignableroles.repository;
import dev.sheldan.abstracto.assignableroles.models.database.AssignedRoleUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface AssignedRoleUserRepository extends JpaRepository<AssignedRoleUser, Long> {
}

View File

@@ -0,0 +1,751 @@
package dev.sheldan.abstracto.assignableroles.service;
import dev.sheldan.abstracto.assignableroles.config.AssignableRolePlaceParameterKey;
import dev.sheldan.abstracto.assignableroles.exceptions.AssignableRolePlaceAlreadyExistsException;
import dev.sheldan.abstracto.assignableroles.exceptions.AssignableRolePlaceChannelDoesNotExist;
import dev.sheldan.abstracto.assignableroles.exceptions.EmoteNotInAssignableRolePlaceException;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.models.database.AssignedRoleUser;
import dev.sheldan.abstracto.assignableroles.models.templates.*;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRoleManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlaceManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService;
import dev.sheldan.abstracto.core.command.exception.CommandParameterKeyValueWrongTypeException;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.EmoteNotUsableException;
import dev.sheldan.abstracto.core.models.FullEmote;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.management.*;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
@Component
@Slf4j
public class AssignableRolePlaceServiceBean implements AssignableRolePlaceService {
public static final String ASSIGNABLE_ROLES_CONFIG_POST_TEMPLATE_KEY = "assignable_roles_config_post";
public static final String ASSIGNABLE_ROLES_POST_TEMPLATE_KEY = "assignable_roles_post";
public static final String ASSIGNABLE_ROLE_PLACES_OVERVIEW_TEMPLATE_KEY = "assignable_role_places_overview";
@Autowired
private AssignableRolePlaceManagementService rolePlaceManagementService;
@Autowired
private AssignableRoleManagementService assignableRoleManagementServiceBean;
@Autowired
private MessageService messageService;
@Autowired
private ChannelService channelService;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private BotService botService;
@Autowired
private EmoteService emoteService;
@Autowired
private AssignableRolePlaceServiceBean self;
@Autowired
private TemplateService templateService;
@Autowired
private EmoteManagementService emoteManagementService;
@Autowired
private AssignableRolePlacePostManagementService postManagementService;
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private RoleManagementService roleManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private AssignableRoleService roleService;
@Override
public void createAssignableRolePlace(AServer server, String name, AChannel channel, String text) {
if(rolePlaceManagementService.doesPlaceExist(server, name)) {
throw new AssignableRolePlaceAlreadyExistsException(name);
}
rolePlaceManagementService.createPlace(server, name, channel, text);
}
@Override
public boolean hasAssignableRolePlaceEmote(AServer server, String placeName, AEmote emote) {
AssignableRolePlace assignableRolePlace = rolePlaceManagementService.findByServerAndKey(server, placeName);
return hasAssignableRolePlaceEmote(assignableRolePlace, emote);
}
@Override
public boolean isPositionUsed(AServer server, String placeName, Integer position) {
AssignableRolePlace assignableRolePlace = rolePlaceManagementService.findByServerAndKey(server, placeName);
return assignableRolePlace.getAssignableRoles().stream().anyMatch(role -> role.getPosition().equals(position));
}
@Override
public CompletableFuture<Void> setEmoteToPosition(AServer server, String placeName, FullEmote emote, Integer position) {
Integer emoteId = emote.getFakeEmote().getId();
AssignableRolePlace assignableRolePlace = rolePlaceManagementService.findByServerAndKey(server, placeName);
Optional<AssignableRole> emoteOptional = assignableRolePlace.getAssignableRoles().stream().filter(role -> role.getEmote().getId().equals(emoteId)).findFirst();
if(emoteOptional.isPresent()) {
AssignableRole toChange = emoteOptional.get();
toChange.setPosition(position);
}
throw new EmoteNotInAssignableRolePlaceException(emote, placeName);
}
@Override
public boolean hasAssignableRolePlaceEmote(AssignableRolePlace place, AEmote emote) {
for (AssignableRole assignableRole : place.getAssignableRoles()) {
if(emoteService.compareAEmote(assignableRole.getEmote(), emote)) {
return true;
}
}
return false;
}
@Override
public CompletableFuture<Void> addRoleToAssignableRolePlace(AServer server, String placeName, ARole role, FullEmote fakeEmote, String description) {
AssignableRolePlace assignableRolePlace = rolePlaceManagementService.findByServerAndKey(server, placeName);
Long placeId = assignableRolePlace.getId();
Long roleId = role.getId();
Long serverId = server.getId();
boolean emoteUsable = true;
if(fakeEmote.getEmote() != null) {
// it only may be unusable if its a custom emote
emoteUsable = emoteService.isEmoteUsableByBot(fakeEmote.getEmote()) && fakeEmote.getEmote().isAvailable();
}
if(emoteUsable) {
AEmote createdEmote = emoteManagementService.createEmote(null, fakeEmote.getFakeEmote(), server.getId(), false);
Integer emoteId = createdEmote.getId();
List<AssignableRolePlacePost> existingMessagePosts = assignableRolePlace.getMessagePosts();
existingMessagePosts.sort(Comparator.comparingLong(AssignableRolePlacePost::getId));
createdEmote.setChangeable(false);
if(!assignableRolePlace.getMessagePosts().isEmpty()){
AssignableRolePlacePost latestPost = existingMessagePosts.get(assignableRolePlace.getMessagePosts().size() - 1);
AssignablePostMessage model = prepareAssignablePostMessageModel(assignableRolePlace);
AssignablePostRole newAssignableRole = AssignablePostRole
.builder()
.description(description)
.emote(fakeEmote)
.forceNewMessage(latestPost.getAssignableRoles().size() >= 20)
.build();
model.getRoles().add(newAssignableRole);
MessageToSend messageToSend = templateService.renderEmbedTemplate(ASSIGNABLE_ROLES_POST_TEMPLATE_KEY, model);
// add it to the last currently existing post
Optional<TextChannel> channelOptional = channelService.getTextChannelInGuild(server.getId(), latestPost.getUsedChannel().getId());
if(channelOptional.isPresent()) {
TextChannel textChannel = channelOptional.get();
if(latestPost.getAssignableRoles().size() < 20) {
return addReactionToExistingAssignableRolePlacePost(fakeEmote, description, assignableRolePlace, placeId, roleId, serverId, emoteId, latestPost, messageToSend, textChannel);
} else {
return addNewMessageToAssignableRolePlace(placeName, fakeEmote, description, roleId, serverId, emoteId, messageToSend, textChannel);
}
} else {
throw new ChannelNotFoundException(latestPost.getUsedChannel().getId());
}
} else {
log.info("Added emote to assignable place {} in server {}, but no message post yet.", placeName, serverId);
self.addAssignableRoleInstanceWithoutPost(placeId, roleId, emoteId, description);
}
} else {
throw new EmoteNotUsableException(fakeEmote.getEmote());
}
return CompletableFuture.completedFuture(null);
}
private CompletableFuture<Void> addReactionToExistingAssignableRolePlacePost(FullEmote fakeEmote, String description, AssignableRolePlace assignableRolePlace, Long placeId, Long roleId, Long serverId, Integer emoteId, AssignableRolePlacePost latestPost, MessageToSend messageToSend, TextChannel textChannel) {
return textChannel.retrieveMessageById(latestPost.getId()).submit()
.thenCompose(message -> messageService.addReactionToMessageWithFuture(fakeEmote.getFakeEmote(), serverId, message))
.thenCompose(aVoid -> {
MessageEmbed embedToUse = messageToSend.getEmbeds().get(assignableRolePlace.getMessagePosts().size() - 1);
return channelService.editEmbedMessageInAChannel(embedToUse, textChannel, latestPost.getId());
})
.thenCompose(message -> {
self.addAssignableRoleInstanceWithPost(message.getIdLong(), placeId, roleId, emoteId, description);
return CompletableFuture.completedFuture(null);
});
}
private CompletableFuture<Void> addNewMessageToAssignableRolePlace(String placeName, FullEmote fakeEmote, String description, Long roleId, Long serverId, Integer emoteId, MessageToSend messageToSend, TextChannel textChannel) {
MessageEmbed embedToUse = messageToSend.getEmbeds().get(messageToSend.getEmbeds().size() - 1);
return channelService.sendEmbedToChannel(embedToUse, textChannel)
.thenCompose(message -> messageService.addReactionToMessageWithFuture(fakeEmote.getFakeEmote(), serverId, message).thenAccept(aVoid ->
self.addNewlyCreatedAssignablePlacePost(placeName, description, roleId, serverId, emoteId, textChannel, message)
));
}
@Transactional
public void addNewlyCreatedAssignablePlacePost(String placeName, String description,Long roleId, Long serverId, Integer emoteId, TextChannel textChannel, Message message) {
AChannel loadedChannel = channelManagementService.loadChannel(textChannel.getIdLong());
AServer loadedServer = serverManagementService.loadOrCreate(serverId);
ARole role = roleManagementService.findRole(roleId);
AEmote emote = emoteManagementService.loadEmote(emoteId);
AssignableRolePlace loadedPlace = rolePlaceManagementService.findByServerAndKey(loadedServer, placeName);
AssignableRolePlacePost newPost = AssignableRolePlacePost
.builder()
.id(message.getIdLong())
.usedChannel(loadedChannel)
.assignablePlace(loadedPlace)
.build();
loadedPlace.getMessagePosts().add(newPost);
assignableRoleManagementServiceBean.addRoleToPlace(loadedPlace, emote, role, description, newPost);
}
@Transactional
public void addAssignableRoleInstanceWithPost(Long messageId, Long placeId, Long roleId, Integer emoteId, String description) {
assignableRoleManagementServiceBean.addRoleToPlace(placeId, emoteId, roleId, description, messageId);
}
@Transactional
public void addAssignableRoleInstanceWithoutPost(Long placeId, Long roleId, Integer emoteId, String description) {
assignableRoleManagementServiceBean.addRoleToPlace(placeId, emoteId, roleId, description);
}
@Override
public CompletableFuture<Void> removeRoleFromAssignableRolePlace(AServer server, String placeName, FullEmote emote) {
AssignableRolePlace assignableRolePlace = rolePlaceManagementService.findByServerAndKey(server, placeName);
for (AssignableRole assignableRole : assignableRolePlace.getAssignableRoles()) {
if(emoteService.compareAEmote(assignableRole.getEmote(), emote.getFakeEmote())) {
return removeRoleFromAssignablePlace(assignableRole, assignableRolePlace).thenAccept(aVoid ->
self.deleteAssignableRoleFromPlace(server.getId(), placeName, assignableRole.getId())
);
}
}
return CompletableFuture.completedFuture(null);
}
@Transactional
public void deleteAssignableRoleFromPlace(Long serverId, String placeName, Long assignableRoleId) {
AServer server = serverManagementService.loadOrCreate(serverId);
AssignableRolePlace assignableRolePlace = rolePlaceManagementService.findByServerAndKey(server, placeName);
Optional<AssignableRole> roleToRemoveOptional = assignableRolePlace.getAssignableRoles().stream().filter(role -> role.getId().equals(assignableRoleId)).findAny();
roleToRemoveOptional.ifPresent(assignableRole -> {
assignableRolePlace.getAssignableRoles().remove(assignableRole);
assignableRole.setAssignablePlace(null);
});
}
private CompletableFuture<Void> removeRoleFromAssignablePlace(AssignableRole role, AssignableRolePlace assignableRolePlace) {
AssignableRolePlacePost post = role.getAssignableRolePlacePost();
if(post != null) {
AServer server = assignableRolePlace.getServer();
TextChannel textChannel = botService.getTextChannelFromServer(server.getId(), post.getUsedChannel().getId());
List<AssignableRole> assignableRoles = assignableRolePlace.getAssignableRoles();
assignableRoles.sort(Comparator.comparing(AssignableRole::getPosition));
Long messageId = post.getId();
CompletableFuture<Message> fieldEditing = channelService.removeFieldFromMessage(textChannel, messageId, assignableRoles.indexOf(role));
CompletableFuture<Void> reactionRemoval = messageService.clearReactionFromMessageWithFuture(role.getEmote(), assignableRolePlace.getServer().getId(), role.getAssignableRolePlacePost().getUsedChannel().getId(), role.getAssignableRolePlacePost().getId());
return CompletableFuture.allOf(fieldEditing, reactionRemoval);
} else {
// this case comes from the situation in which, the emote was deleted ant he initial post setup failed
log.warn("Reaction {} to remove does not have a post attached. The post needs to be setup again, it is most likely not functioning currently anyway.", role.getEmote().getEmoteId());
return CompletableFuture.completedFuture(null);
}
}
@Override
public CompletableFuture<Void> setupAssignableRolePlace(AServer server, String name) {
AssignableRolePlace assignableRolePlace = rolePlaceManagementService.findByServerAndKey(server, name);
List<CompletableFuture<Void>> oldPostDeletionFutures = deleteExistingMessagePostsForPlace(assignableRolePlace);
assignableRolePlace.getMessagePosts().clear();
assignableRolePlace.getAssignableRoles().forEach(assignableRole ->
assignableRole.setAssignableRolePlacePost(null)
);
Long serverId = server.getId();
return CompletableFuture.allOf(oldPostDeletionFutures.toArray(new CompletableFuture[0]))
.thenCompose(aVoid -> self.createAssignableRolePlacePosts(serverId, name));
}
@Override
public CompletableFuture<Void> refreshAssignablePlacePosts(AServer server, String name) {
AssignableRolePlace assignableRolePlace = rolePlaceManagementService.findByServerAndKey(server, name);
return refreshAssignablePlacePosts(assignableRolePlace);
}
@Override
public CompletableFuture<Void> refreshAssignablePlacePosts(AssignableRolePlace place) {
MessageToSend messageToSend = renderAssignablePlacePosts(place);
List<AssignableRolePlacePost> existingMessagePosts = place.getMessagePosts();
existingMessagePosts.sort(Comparator.comparingLong(AssignableRolePlacePost::getId));
AssignableRolePlacePost latestPost = existingMessagePosts.get(place.getMessagePosts().size() - 1);
List<CompletableFuture<Message>> futures = new ArrayList<>();
Optional<TextChannel> channelOptional = channelService.getTextChannelInGuild(place.getServer().getId(), latestPost.getUsedChannel().getId());
if(channelOptional.isPresent()) {
TextChannel textChannel = channelOptional.get();
Iterator<MessageEmbed> iterator = messageToSend.getEmbeds().iterator();
place.getMessagePosts().forEach(post -> {
CompletableFuture<Message> messageCompletableFuture = channelService.editEmbedMessageInAChannel(iterator.next(), textChannel, post.getId());
futures.add(messageCompletableFuture);
});
}
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
}
@Override
public CompletableFuture<Void> refreshTextFromPlace(AssignableRolePlace place) {
List<AssignableRolePlacePost> existingMessagePosts = place.getMessagePosts();
if(!existingMessagePosts.isEmpty()) {
MessageToSend renderedMessage = renderAssignablePlacePosts(place);
existingMessagePosts.sort(Comparator.comparingLong(AssignableRolePlacePost::getId));
AssignableRolePlacePost latestPost = existingMessagePosts.get(0);
Long channelId = latestPost.getUsedChannel().getId();
Optional<TextChannel> channelOptional = channelService.getTextChannelInGuild(place.getServer().getId(), channelId);
if(channelOptional.isPresent()) {
return channelService.editEmbedMessageInAChannel(renderedMessage.getEmbeds().get(0), channelOptional.get(), latestPost.getId()).thenCompose(message -> CompletableFuture.completedFuture(null));
}
throw new ChannelNotFoundException(channelId);
}
return CompletableFuture.completedFuture(null);
}
@Override
public void setAssignablePlaceActiveTo(AServer server, String name, Boolean newValue) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
if(newValue) {
this.activateAssignableRolePlace(place);
} else {
this.deactivateAssignableRolePlace(place);
}
}
private List<CompletableFuture<Void>> deleteExistingMessagePostsForPlace(AssignableRolePlace assignableRolePlace) {
List<CompletableFuture<Void>> oldPostDeletionFutures = new ArrayList<>();
assignableRolePlace.getMessagePosts().forEach(assignableRolePlacePost ->
oldPostDeletionFutures.add(messageService.deleteMessageInChannelInServer(assignableRolePlace.getServer().getId(), assignableRolePlacePost.getUsedChannel().getId(), assignableRolePlacePost.getId()))
);
return oldPostDeletionFutures;
}
@Override
public void deactivateAssignableRolePlace(AServer server, String name) {
setAssignablePlaceActiveTo(server, name, false);
}
@Override
public void deactivateAssignableRolePlace(AssignableRolePlace place) {
place.setActive(false);
log.info("Deactivating assignable role place {} in server {}", place.getId(), place.getServer().getId());
}
@Override
public void activateAssignableRolePlace(AServer server, String name) {
setAssignablePlaceActiveTo(server, name, true);
}
@Override
public void activateAssignableRolePlace(AssignableRolePlace place) {
place.setActive(true);
log.info("Activating assignable role place {} in server {}", place.getId(), place.getServer().getId());
}
@Override
public CompletableFuture<Void> setAssignablePlaceInlineTo(AServer server, String name, Boolean newValue) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
if(newValue) {
return this.inlineAssignableRolePlace(place);
} else {
return this.spreadAssignableRolePlace(place);
}
}
@Override
public CompletableFuture<Void> inlineAssignableRolePlace(AServer server, String name) {
return setAssignablePlaceInlineTo(server, name, true);
}
@Override
public CompletableFuture<Void> inlineAssignableRolePlace(AssignableRolePlace place) {
log.info("Setting assignable role place inline {} in server {} to {}", place.getId(), place.getServer().getId(), true);
place.setInline(true);
return refreshAssignablePlacePosts(place);
}
@Override
public CompletableFuture<Void> spreadAssignableRolePlace(AServer server, String name) {
return setAssignablePlaceInlineTo(server, name, false);
}
@Override
public CompletableFuture<Void> spreadAssignableRolePlace(AssignableRolePlace place) {
log.info("Setting assignable role place inline {} in server {} to {}", place.getId(), place.getServer().getId(), false);
place.setInline(false);
return refreshAssignablePlacePosts(place);
}
@Override
public void setAssignablePlaceUniqueTo(AServer server, String name, Boolean newValue) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
if(newValue) {
this.uniqueAssignableRolePlace(place);
} else {
this.multipleAssignableRolePlace(place);
}
}
@Override
public void uniqueAssignableRolePlace(AServer server, String name) {
setAssignablePlaceInlineTo(server, name, true);
}
@Override
public void uniqueAssignableRolePlace(AssignableRolePlace place) {
log.info("Setting assignable role place uniqueness {} in server {} to {}", place.getId(), place.getServer().getId(), true);
place.setUniqueRoles(true);
}
@Override
public void multipleAssignableRolePlace(AServer server, String name) {
setAssignablePlaceInlineTo(server, name, false);
}
@Override
public void multipleAssignableRolePlace(AssignableRolePlace place) {
log.info("Setting assignable role place uniqueness {} in server {} to {}", place.getId(), place.getServer().getId(), false);
place.setUniqueRoles(false);
}
@Override
public void setAssignablePlaceAutoRemoveTo(AServer server, String name, Boolean newValue) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
if(newValue) {
this.autoRemoveAssignableRolePlace(place);
} else {
this.keepReactionsAssignableRolePlace(place);
}
}
@Override
public void autoRemoveAssignableRolePlace(AServer server, String name) {
setAssignablePlaceAutoRemoveTo(server, name, true);
}
@Override
public void autoRemoveAssignableRolePlace(AssignableRolePlace place) {
log.info("Setting assignable role place auto remove {} in server {} to {}", place.getId(), place.getServer().getId(), true);
place.setAutoRemove(true);
}
@Override
public void keepReactionsAssignableRolePlace(AServer server, String name) {
setAssignablePlaceAutoRemoveTo(server, name, false);
}
@Override
public void keepReactionsAssignableRolePlace(AssignableRolePlace place) {
log.info("Setting assignable role place auto remove {} in server {} to {}", place.getId(), place.getServer().getId(), false);
place.setAutoRemove(false);
}
@Override
public void swapPositions(AServer server, String name, FullEmote firstEmote, FullEmote secondEmote) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
Optional<AssignableRole> firstEmoteOptional = place.getAssignableRoles().stream().filter(role -> emoteService.compareAEmote(role.getEmote(), firstEmote.getFakeEmote())).findFirst();
Optional<AssignableRole> secondEmoteOptional = place.getAssignableRoles().stream().filter(role -> emoteService.compareAEmote(role.getEmote(), secondEmote.getFakeEmote())).findFirst();
if(firstEmoteOptional.isPresent() && secondEmoteOptional.isPresent()) {
AssignableRole firstRole = firstEmoteOptional.get();
AssignableRole secondRole = secondEmoteOptional.get();
int firstPosition = firstRole.getPosition();
firstRole.setPosition(secondRole.getPosition());
secondRole.setPosition(firstPosition);
} else {
if(!firstEmoteOptional.isPresent()) {
throw new EmoteNotInAssignableRolePlaceException(firstEmote, name);
} else {
throw new EmoteNotInAssignableRolePlaceException(secondEmote, name);
}
}
}
@Override
public CompletableFuture<Void> testAssignableRolePlace(AServer server, String name, MessageChannel channel) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
MessageToSend messageToSend = renderAssignablePlacePosts(place);
List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToSendToChannel(messageToSend, channel);
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0]));
}
@Override
public void showAssignablePlaceConfig(AServer server, String name, MessageChannel channel) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
List<AssignablePostConfigRole> roles = new ArrayList<>();
Guild guild = botService.getGuildByIdNullable(server.getId());
List<AssignableRole> assignableRoles = place.getAssignableRoles().stream().sorted(Comparator.comparingInt(AssignableRole::getPosition)).collect(Collectors.toList());
for (AssignableRole role : assignableRoles) {
AEmote emoteForRole = role.getEmote();
Emote jdaEmoteForRole = botService.getEmote(emoteForRole).orElse(null);
Role jdaRole = guild.getRoleById(role.getRole().getId());
AssignablePostConfigRole postRole = AssignablePostConfigRole
.builder()
.description(role.getDescription())
.emote(jdaEmoteForRole)
.position(role.getPosition())
.awardedRole(jdaRole)
.build();
roles.add(postRole);
}
AssignableRolePlaceConfig configModel = AssignableRolePlaceConfig
.builder()
.roles(roles)
.place(place)
.build();
channelService.sendEmbedTemplateInChannel(ASSIGNABLE_ROLES_CONFIG_POST_TEMPLATE_KEY, configModel, channel);
}
@Override
public void moveAssignableRolePlace(AServer server, String name, TextChannel newChannel) {
AChannel channel = channelManagementService.loadChannel(newChannel.getIdLong());
rolePlaceManagementService.moveAssignableRolePlace(server, name, channel);
}
@Override
public void changeAssignablePlaceDescription(AServer server, String name, String newDescription) {
rolePlaceManagementService.changeAssignableRolePlaceDescription(server, name, newDescription);
}
@Override
public CompletableFuture<Void> deleteAssignableRolePlace(AServer server, String name) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
rolePlaceManagementService.deleteAssignablePlace(place);
deleteEmotesFromAssignableRolePlace(place);
List<CompletableFuture<Void>> deleteFutures = deleteExistingMessagePostsForPlace(place);
return CompletableFuture.allOf(deleteFutures.toArray(new CompletableFuture[0]));
}
@Override
public CompletableFuture<Void> changeText(AServer server, String name, String newText) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
place.setText(newText);
return refreshTextFromPlace(place);
}
@Override
public CompletableFuture<Void> removeExistingReactionsAndRoles(AssignableRolePlace place, AssignedRoleUser user) {
Member memberInServer = botService.getMemberInServer(user.getUser());
List<CompletableFuture<Void>> futures = new ArrayList<>();
user.getRoles().forEach(assignableRole -> {
futures.add(roleService.removeAssignableRoleFromUser(assignableRole, memberInServer));
AEmote emoteToUseObject = emoteManagementService.loadEmote(assignableRole.getEmote().getId());
AssignableRolePlacePost assignablePlacePost = assignableRole.getAssignableRolePlacePost();
futures.add(messageService.removeReactionOfUserFromMessageWithFuture(emoteToUseObject, place.getServer().getId(),
assignablePlacePost.getUsedChannel().getId(), assignablePlacePost.getId(), memberInServer));
});
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
}
@Override
public CompletableFuture<Void> changeConfiguration(AServer server, String name, AssignableRolePlaceParameterKey keyToChange, Object newValue) {
Boolean booleanValue = BooleanUtils.toBooleanObject(newValue.toString());
if(booleanValue == null) {
throwBooleanParameterKeyException();
}
switch (keyToChange) {
case INLINE:
return setAssignablePlaceInlineTo(server, name, booleanValue);
case AUTOREMOVE:
setAssignablePlaceAutoRemoveTo(server, name, booleanValue);
return CompletableFuture.completedFuture(null);
case UNIQUE:
setAssignablePlaceUniqueTo(server, name, booleanValue);
return CompletableFuture.completedFuture(null);
case ACTIVE:
setAssignablePlaceActiveTo(server, name, booleanValue);
return CompletableFuture.completedFuture(null);
default:
throw new IllegalArgumentException("Illegal assignable role place parameter key was passed.");
}
}
@Override
public CompletableFuture<Void> showAllAssignableRolePlaces(AServer server, MessageChannel channel) {
List<AssignableRolePlace> assignableRolePlaces = rolePlaceManagementService.findAllByServer(server);
AssignablePlaceOverview overViewModel = AssignablePlaceOverview.builder().places(assignableRolePlaces).build();
List<CompletableFuture<Message>> promises = channelService.sendEmbedTemplateInChannel(ASSIGNABLE_ROLE_PLACES_OVERVIEW_TEMPLATE_KEY, overViewModel, channel);
return CompletableFuture.allOf(promises.toArray(new CompletableFuture[0]));
}
private void throwBooleanParameterKeyException() {
throw new CommandParameterKeyValueWrongTypeException(Arrays.asList("yes", "no", "true", "false", "on", "off"));
}
private void deleteEmotesFromAssignableRolePlace(AssignableRolePlace place) {
place.getAssignableRoles().forEach(role ->
emoteManagementService.deleteEmote(role.getEmote())
);
}
private List<CompletableFuture<Message>> sendAssignablePostMessages(AssignableRolePlace place, MessageChannel channel) {
MessageToSend messageToSend = renderAssignablePlacePosts(place);
return channelService.sendMessageToSendToChannel(messageToSend, channel);
}
private MessageToSend renderAssignablePlacePosts(AssignableRolePlace place) {
AssignablePostMessage model = prepareAssignablePostMessageModel(place);
return templateService.renderEmbedTemplate(ASSIGNABLE_ROLES_POST_TEMPLATE_KEY, model);
}
private AssignablePostMessage prepareAssignablePostMessageModel(AssignableRolePlace place) {
List<AssignablePostRole> roles = new ArrayList<>();
List<AssignableRole> rolesToAdd = place.getAssignableRoles().stream().sorted(Comparator.comparingInt(AssignableRole::getPosition)).collect(Collectors.toList());
int maxPosition = 0;
if(!rolesToAdd.isEmpty()) {
maxPosition = rolesToAdd.get(rolesToAdd.size() - 1).getPosition();
Iterator<AssignableRole> rolesToAddIterator = rolesToAdd.iterator();
AssignableRole current = rolesToAddIterator.next();
AssignablePostRole lastAddedRole = null;
for (int position = 0; position < maxPosition + 1; position++) {
boolean legitEntry = current.getPosition().equals(position);
boolean startOfNewMessage = position > 0 && (position % 21) == 0;
if(legitEntry) {
AEmote emoteForRole = current.getEmote();
Emote jdaEmoteForRole = botService.getEmote(emoteForRole).orElse(null);
FullEmote fullEmote = FullEmote.builder().emote(jdaEmoteForRole).fakeEmote(emoteForRole).build();
AssignablePostRole postRole = AssignablePostRole
.builder()
.description(current.getDescription())
.emote(fullEmote)
.position(position)
.forceNewMessage(startOfNewMessage)
.build();
roles.add(postRole);
lastAddedRole = postRole;
if(rolesToAddIterator.hasNext()) {
current = rolesToAddIterator.next();
}
} else if(startOfNewMessage && lastAddedRole != null) {
lastAddedRole.setForceNewMessage(true);
}
}
}
return AssignablePostMessage
.builder()
.roles(roles)
.place(place)
.maxPosition(maxPosition)
.build();
}
@Transactional
public CompletableFuture<Void> createAssignableRolePlacePosts(Long serverId, String name) {
AServer server = serverManagementService.loadOrCreate(serverId);
AssignableRolePlace assignableRolePlace = rolePlaceManagementService.findByServerAndKey(server, name);
Optional<TextChannel> channelOptional = botService.getTextChannelFromServerOptional(serverId, assignableRolePlace.getChannel().getId());
if(channelOptional.isPresent()) {
MessageChannel channel = channelOptional.get();
List<CompletableFuture<Message>> messageFutures = sendAssignablePostMessages(assignableRolePlace, channel);
return CompletableFuture.allOf(messageFutures.toArray(new CompletableFuture[0]))
.thenCompose(aVoid -> self.addEmotes(messageFutures, name));
} else {
log.warn("Channel to create assignable role post in does not exist.");
throw new AssignableRolePlaceChannelDoesNotExist(assignableRolePlace.getChannel().getId(), name);
}
}
@Transactional
public CompletableFuture<Void> addEmotes(List<CompletableFuture<Message>> assignablePlacePostsMessageFutures, String placeKey) {
try {
Message firstMessage = assignablePlacePostsMessageFutures.get(0).get();
Long serverId = firstMessage.getGuild().getIdLong();
AServer innerServer = serverManagementService.loadOrCreate(serverId);
AssignableRolePlace innerRolePlace = rolePlaceManagementService.findByServerAndKey(innerServer, placeKey);
List<AssignableRole> roleStream = innerRolePlace.getAssignableRoles().stream().sorted(Comparator.comparingInt(AssignableRole::getPosition)).collect(Collectors.toList());
List<CompletableFuture<Void>> reactionFutures = new ArrayList<>();
int usedEmotes = 0;
for (CompletableFuture<Message> messageCompletableFuture : assignablePlacePostsMessageFutures) {
Message sentMessage = messageCompletableFuture.get();
// this uses the actual embed count as a limit, so this relies on fields to be used for description, if this changes, this needs to be changed
MessageEmbed embed = sentMessage.getEmbeds().get(0);
List<AssignableRole> firstRoles = roleStream.subList(usedEmotes, usedEmotes + embed.getFields().size());
usedEmotes += embed.getFields().size();
List<Integer> usedEmoteIds = firstRoles.stream().map(assignableRole -> assignableRole.getEmote().getId()).collect(Collectors.toList());
CompletableFuture<Void> firstMessageFuture = createAssignableRolePlacePost(sentMessage, serverId, usedEmoteIds);
reactionFutures.add(firstMessageFuture);
}
return CompletableFuture.allOf(reactionFutures.toArray(new CompletableFuture[0])).thenCompose(aVoid -> {
self.storeCreatedAssignableRolePlacePosts(placeKey, serverId, assignablePlacePostsMessageFutures);
return CompletableFuture.completedFuture(null);
});
} catch (InterruptedException | ExecutionException e) {
log.error("Failed to process future from sending assignable place posts messages.", e);
throw new AbstractoRunTimeException(e);
}
}
@Transactional
public void storeCreatedAssignableRolePlacePosts(String name, Long serverId, List<CompletableFuture<Message>> futures) {
AServer server = serverManagementService.loadOrCreate(serverId);
AssignableRolePlace updatedPlace = rolePlaceManagementService.findByServerAndKey(server, name);
List<AssignableRole> rolesToAdd = updatedPlace.getAssignableRoles().stream().sorted(Comparator.comparingInt(AssignableRole::getPosition)).collect(Collectors.toList());
int usedEmotes = 0;
for (int i = 0; i < futures.size(); i++) {
CompletableFuture<Message> messageCompletableFuture = futures.get(i);
try {
Message message = messageCompletableFuture.get();
Message sentMessage = messageCompletableFuture.get();
// this uses the actual embed count as a limit, so this relies on fields to be used for description, if this changes, this needs to be changed
MessageEmbed embed = sentMessage.getEmbeds().get(0);
List<AssignableRole> firstRoles = rolesToAdd.subList(usedEmotes, usedEmotes + embed.getFields().size());
usedEmotes += embed.getFields().size();
AssignableRolePlacePost post = AssignableRolePlacePost
.builder()
.id(message.getIdLong())
.usedChannel(updatedPlace.getChannel())
.assignablePlace(updatedPlace)
.build();
firstRoles.forEach(assignableRole ->
assignableRole.setAssignableRolePlacePost(post)
);
updatedPlace.getMessagePosts().add(post);
} catch (Exception e) {
log.error("Failed to get future.", e);
}
}
}
@Transactional
public CompletableFuture<Void> createAssignableRolePlacePost(Message message, Long server, List<Integer> emotesToAdd) {
// TODO might need to guarantee the order
List<CompletableFuture<Void>> futures = new ArrayList<>();
emotesToAdd.forEach(emotesToUse -> {
AEmote emoteToUseObject = emoteManagementService.loadEmote(emotesToUse);
futures.add(messageService.addReactionToMessageWithFuture(emoteToUseObject, server, message));
});
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
}
}

View File

@@ -0,0 +1,69 @@
package dev.sheldan.abstracto.assignableroles.service;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRoleManagementServiceBean;
import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementServiceBean;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.CompletableFuture;
@Component
public class AssignableRoleServiceBean implements AssignableRoleService {
@Autowired
private RoleService roleService;
@Autowired
private AssignedRoleUserManagementService assignedRoleUserManagementService;
@Autowired
private AssignableRoleManagementServiceBean assignableRoleManagementServiceBean;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private AssignedRoleUserManagementServiceBean assignedRoleUserManagementServiceBean;
@Autowired
private AssignableRoleServiceBean self;
@Override
public CompletableFuture<Void> assignAssignableRoleToUser(Long assignableRoleId, Member toAdd) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
return roleService.addRoleToMemberFuture(toAdd, role.getRole()).thenApply(aVoid -> {
self.persistRoleAssignment(assignableRoleId, toAdd);
return null;
});
}
@Override
public CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, Member member) {
Long assignableRoleId = assignableRole.getId();
return roleService.removeRoleFromMemberFuture(member, assignableRole.getRole()).thenApply(aVoid -> {
self.persistRoleRemoval(assignableRoleId, member);
return null;
});
}
@Transactional
public void persistRoleAssignment(Long assignableRoleId, Member member) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(member);
assignedRoleUserManagementServiceBean.addAssignedRoleToUser(role, aUserInAServer);
}
@Transactional
public void persistRoleRemoval(Long assignableRoleId, Member member) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(member);
assignedRoleUserManagementServiceBean.removeAssignedRoleFromUser(role, aUserInAServer);
}
}

View File

@@ -0,0 +1,77 @@
package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.exceptions.AssignableRolePlaceNotFoundException;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.repository.AssignableRoleRepository;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AssignableRoleManagementServiceBean implements AssignableRoleManagementService {
@Autowired
private AssignableRolePlaceManagementService rolePlaceManagementService;
@Autowired
private EmoteManagementService emoteManagementService;
@Autowired
private RoleManagementService roleManagementService;
@Autowired
private AssignableRolePlacePostManagementService postManagementService;
@Autowired
private AssignableRoleRepository repository;
@Override
public AssignableRole addRoleToPlace(AssignableRolePlace place, AEmote emote, ARole role, String description, AssignableRolePlacePost post) {
Integer maxPosition = place.getAssignableRoles().stream().map(AssignableRole::getPosition).max(Integer::compareTo).orElse(0);
if(!place.getAssignableRoles().isEmpty()) {
maxPosition += 1;
}
AssignableRole roleToAdd = AssignableRole
.builder()
.assignablePlace(place)
.emote(emote)
.role(role)
.requiredLevel(0)
.position(maxPosition)
.description(description)
.assignableRolePlacePost(post)
.build();
place.getAssignableRoles().add(roleToAdd);
return roleToAdd;
}
@Override
public AssignableRole addRoleToPlace(Long placeId, Integer emoteId, Long roleId, String description, Long messageId) {
AssignableRolePlace place = rolePlaceManagementService.findByPlaceId(placeId).orElseThrow(() -> new AssignableRolePlaceNotFoundException(placeId));
AEmote emote = emoteManagementService.loadEmote(emoteId);
ARole role = roleManagementService.findRole(roleId);
AssignableRolePlacePost post = postManagementService.findByMessageId(messageId);
AssignableRole assignableRole = addRoleToPlace(place, emote, role, description, post);
post.getAssignableRoles().add(assignableRole);
return assignableRole;
}
@Override
public AssignableRole addRoleToPlace(Long placeId, Integer emoteId, Long roleId, String description) {
AssignableRolePlace place = rolePlaceManagementService.findByPlaceId(placeId).orElseThrow(() -> new AssignableRolePlaceNotFoundException(placeId));
AEmote emote = emoteManagementService.loadEmote(emoteId);
ARole role = roleManagementService.findRole(roleId);
return addRoleToPlace(place, emote, role, description, null);
}
@Override
public AssignableRole getByAssignableRoleId(Long assignableRoleId) {
return repository.findById(assignableRoleId).orElseThrow(() -> new AbstractoRunTimeException("Assignable role not found"));
}
}

View File

@@ -0,0 +1,72 @@
package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.exceptions.AssignableRolePlaceNotFoundException;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.repository.AssignableRolePlaceRepository;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
public class AssignableRolePlaceManagementServiceBean implements AssignableRolePlaceManagementService {
@Autowired
private AssignableRolePlaceRepository repository;
@Override
public AssignableRolePlace createPlace(AServer server, String name, AChannel channel, String text) {
AssignableRolePlace place = AssignableRolePlace
.builder()
.channel(channel)
.server(server)
.text(text)
.key(name)
.build();
repository.save(place);
return place;
}
@Override
public boolean doesPlaceExist(AServer server, String name) {
return repository.existsByServerAndKey(server, name);
}
@Override
public AssignableRolePlace findByServerAndKey(AServer server, String name) {
// todo use other exception or adapt exception
return repository.findByServerAndKey(server, name).orElseThrow(() -> new AssignableRolePlaceNotFoundException(0L));
}
@Override
public Optional<AssignableRolePlace> findByPlaceId(Long id) {
return repository.findById(id);
}
@Override
public void moveAssignableRolePlace(AServer server, String name, AChannel newChannel) {
AssignableRolePlace assignablePlaceToChange = findByServerAndKey(server, name);
assignablePlaceToChange.setChannel(newChannel);
}
@Override
public void changeAssignableRolePlaceDescription(AServer server, String name, String newDescription) {
AssignableRolePlace assignablePlaceToChange = findByServerAndKey(server, name);
assignablePlaceToChange.setText(newDescription);
}
@Override
public void deleteAssignablePlace(AssignableRolePlace toDelete) {
repository.delete(toDelete);
}
@Override
public List<AssignableRolePlace> findAllByServer(AServer server) {
return repository.findByServer(server);
}
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.exceptions.AssignablePlacePostNotFoundException;
import dev.sheldan.abstracto.assignableroles.repository.AssignableRolePlacePostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
public class AssignableRolePlacePostManagementServiceBean implements AssignableRolePlacePostManagementService {
@Autowired
private AssignableRolePlacePostRepository repository;
@Override
public Optional<AssignableRolePlacePost> findByMessageIdOptional(Long messageId) {
return repository.findById(messageId);
}
@Override
public AssignableRolePlacePost findByMessageId(Long messageId) {
return findByMessageIdOptional(messageId).orElseThrow(() -> new AssignablePlacePostNotFoundException(messageId));
}
}

View File

@@ -0,0 +1,54 @@
package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.exceptions.AssignedUserNotFoundException;
import dev.sheldan.abstracto.assignableroles.models.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.models.database.AssignedRoleUser;
import dev.sheldan.abstracto.assignableroles.repository.AssignedRoleUserRepository;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
public class AssignedRoleUserManagementServiceBean implements AssignedRoleUserManagementService {
@Autowired
private AssignedRoleUserRepository repository;
@Override
public void addAssignedRoleToUser(AssignableRole assignableRole, AUserInAServer aUserInAServer) {
Optional<AssignedRoleUser> optional = findByUserInServerOptional(aUserInAServer);
AssignedRoleUser user = optional.orElseGet(() -> createAssignedRoleUser(aUserInAServer));
assignableRole.getAssignedUsers().add(user);
user.getRoles().add(assignableRole);
}
@Override
public void removeAssignedRoleFromUser(AssignableRole assignableRole, AUserInAServer aUserInAServer) {
AssignedRoleUser user = findByUserInServer(aUserInAServer);
assignableRole.getAssignedUsers().remove(user);
user.getRoles().remove(assignableRole);
}
@Override
public AssignedRoleUser createAssignedRoleUser(AUserInAServer aUserInAServer) {
AssignedRoleUser newUser = AssignedRoleUser.builder().user(aUserInAServer).id(aUserInAServer.getUserInServerId()).build();
return repository.save(newUser);
}
@Override
public boolean doesAssignedRoleUserExist(AUserInAServer aUserInAServer) {
return repository.existsById(aUserInAServer.getUserInServerId());
}
@Override
public Optional<AssignedRoleUser> findByUserInServerOptional(AUserInAServer aUserInAServer) {
return repository.findById(aUserInAServer.getUserInServerId());
}
@Override
public AssignedRoleUser findByUserInServer(AUserInAServer aUserInAServer) {
return findByUserInServerOptional(aUserInAServer).orElseThrow(() -> new AssignedUserNotFoundException(aUserInAServer));
}
}

View File

@@ -0,0 +1,111 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<property name="experienceModule" value="(SELECT id FROM module WHERE name = 'assignableRoles')"/>
<property name="experienceFeature" value="(SELECT id FROM feature WHERE key = 'assignableRole')"/>
<property name="today" value="(SELECT NOW())"/>
<changeSet author="Sheldan" id="assignable_roles-commands">
<insert tableName="command">
<column name="name" value="addRoleToAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="setupAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="activateAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="deactivateAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="removeRoleFromAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="changeAssignableRolePlaceConfig"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="setAssignableRolePosition"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="swapAssignableRolePosition"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="testAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="deleteAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="createAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="changeAssignablePlaceDescription"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="showAssignableRolePlaceConfig"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="editAssignableRolePlaceText"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="moveAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="showAssignableRolePlaces"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

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

View File

@@ -0,0 +1,18 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<property name="assignableFeature" value="(SELECT id FROM feature WHERE key = 'assignableRole')"/>
<property name="today" value="(SELECT NOW())"/>
<changeSet author="Sheldan" id="assignableRole_default_feature_flag-insertion">
<insert tableName="default_feature_flag">
<column name="enabled" value="false"/>
<column name="feature_id" valueComputed="${assignableFeature}" />
<column name="created" valueComputed="${today}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,16 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<property name="today" value="(SELECT NOW())"/>
<changeSet author="Sheldan" id="assignable_role-feature-insertion">
<insert tableName="feature">
<column name="key" value="assignableRole"/>
<column name="created" valueComputed="${today}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,16 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<property name="today" value="(SELECT NOW())"/>
<changeSet author="Sheldan" id="assignable_role-module-insertion">
<insert tableName="module">
<column name="name" value="assignableRoles"/>
<column name="created" valueComputed="${today}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,50 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="assignable_role-table">
<createTable tableName="assignable_role">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="assignable_role_pkey"/>
</column>
<column name="emote_id" type="INTEGER">
<constraints nullable="false"/>
</column>
<column name="role_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="assignable_place_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="assignable_role_place_post_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="description" type="VARCHAR(255)"/>
<column name="required_level" type="INTEGER"/>
<column name="position" type="INTEGER"/>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
</changeSet>
<changeSet author="Sheldan" id="assignable_role-fk_assignable_role_emote">
<addForeignKeyConstraint baseColumnNames="emote_id" baseTableName="assignable_role" constraintName="fk_assignable_role_emote" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="emote" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="assignable_role-fk_assignable_role_role">
<addForeignKeyConstraint baseColumnNames="role_id" baseTableName="assignable_role" constraintName="fk_assignable_role_role" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="role" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="assignable_role-fk_assignable_role_place">
<addForeignKeyConstraint baseColumnNames="assignable_place_id" baseTableName="assignable_role" constraintName="fk_assignable_role_place" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="assignable_role_place" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="assignable_role-fk_assignable_role_post">
<addForeignKeyConstraint baseColumnNames="assignable_role_place_post_id" baseTableName="assignable_role" constraintName="fk_assignable_role_post" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="assignable_role_place_post" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,39 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="assignable_role_place-table">
<createTable tableName="assignable_role_place">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="assignable_role_place_pkey"/>
</column>
<column name="channel_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="key" type="VARCHAR(255)"/>
<column name="text" type="VARCHAR(255)"/>
<column name="active" type="BOOLEAN"/>
<column name="inline" type="BOOLEAN"/>
<column name="unique_roles" type="BOOLEAN"/>
<column name="auto_remove" type="BOOLEAN"/>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
</changeSet>
<changeSet author="Sheldan" id="assignable_role_place-fk_assignable_role_place_channel">
<addForeignKeyConstraint baseColumnNames="channel_id" baseTableName="assignable_role_place" constraintName="fk_assignable_role_place_channel" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="channel" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="assignable_role_place-fk_assignable_role_place_server">
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="assignable_role_place" constraintName="fk_assignable_role_place_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
</changeSet>
</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:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="assignable_role_place_post-table">
<createTable tableName="assignable_role_place_post">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="assignable_role_place_post_pkey"/>
</column>
<column name="assignable_place_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="channel_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
</changeSet>
<changeSet author="Sheldan" id="assignable_role_place_post-fk_assignable_role_place_post_place_id">
<addForeignKeyConstraint baseColumnNames="assignable_place_id" baseTableName="assignable_role_place_post" constraintName="fk_assignable_role_place_post_place_id" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="assignable_role_place" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="assignable_role_place_post-fk_assignable_role_place_post_channel">
<addForeignKeyConstraint baseColumnNames="channel_id" baseTableName="assignable_role_place_post" constraintName="fk_assignable_role_place_post_channel" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="channel" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,38 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="assigned_role_user-table">
<createTable tableName="assigned_role_user">
<column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="assigned_role_user_pkey"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
</changeSet>
<changeSet author="Sheldan" id="assigned_role_in_user-table">
<createTable tableName="assigned_role_in_user">
<column name="assigned_role_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="user_id" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<changeSet author="Sheldan" id="assigned_role_in_user-fk_assigned_role_in_user_assignable_role">
<addForeignKeyConstraint baseColumnNames="assigned_role_id" baseTableName="assigned_role_in_user" constraintName="fk_assigned_role_in_user_assignable_role" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="assignable_role" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="assigned_role_in_user-fk_assigned_role_in_user_assigned_user">
<addForeignKeyConstraint baseColumnNames="user_id" baseTableName="assigned_role_in_user" constraintName="fk_assigned_role_in_user_assigned_user" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="assigned_role_user" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="assigned_role_user-fk_assigned_role_user_user">
<addForeignKeyConstraint baseColumnNames="id" baseTableName="assigned_role_user" constraintName="fk_assigned_role_user_user" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="auser" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,13 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<include file="assignable_role_place.xml" relativeToChangelogFile="true"/>
<include file="assignable_role_place_post.xml" relativeToChangelogFile="true"/>
<include file="assignable_role.xml" relativeToChangelogFile="true"/>
<include file="assigned_role_user.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:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../dbchangelog-3.8.xsd" >
<include file="assignableRoles-tables/tables.xml" relativeToChangelogFile="true"/>
<include file="assignableRoles-seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

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