[AB-365] introducing slash commands for a selection of commands

adding method for pinning a message
moving suggestion to correct deployment
This commit is contained in:
Sheldan
2022-05-17 00:39:06 +02:00
parent 1913bc930d
commit 1d6de3f1e8
286 changed files with 8021 additions and 3065 deletions

View File

@@ -13,7 +13,6 @@ import dev.sheldan.abstracto.core.command.model.CommandConfirmationModel;
import dev.sheldan.abstracto.core.command.model.CommandConfirmationPayload;
import dev.sheldan.abstracto.core.command.service.CommandManager;
import dev.sheldan.abstracto.core.command.service.CommandService;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.command.service.PostCommandExecution;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
@@ -37,7 +36,6 @@ import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.UnexpectedRollbackException;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@@ -63,34 +61,13 @@ public class CommandReceivedHandler extends ListenerAdapter {
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
@Lazy
private CommandReceivedHandler self;
@Autowired
private RoleManagementService roleManagementService;
@Autowired
private CommandService commandService;
@Autowired
private EmoteService emoteService;
@Autowired
private ExceptionService exceptionService;
@Autowired
private EmoteManagementService emoteManagementService;
@Autowired
private RoleService roleService;
@Autowired
private List<CommandParameterHandler> parameterHandlers;
@@ -393,7 +370,11 @@ public class CommandReceivedHandler extends ListenerAdapter {
public CompletableFuture<Parameters> getParsedParameters(UnParsedCommandParameter unParsedCommandParameter, Command command, Message message) {
List<ParseResult> parsedParameters = new ArrayList<>();
List<Parameter> parameters = command.getConfiguration().getParameters();
List<Parameter> parameters = command
.getConfiguration()
.getParameters()
.stream()
.filter(parameter -> !parameter.getSlashCommandOnly()).collect(Collectors.toList());
if (parameters == null || parameters.isEmpty()) {
return CompletableFuture.completedFuture(Parameters.builder().parameters(new ArrayList<>()).build());
}
@@ -507,13 +488,16 @@ public class CommandReceivedHandler extends ListenerAdapter {
private List<Object> extractParametersFromParsed(List<ParseResult> results) {
List<Object> usableParameters = new ArrayList<>();
results.forEach(parseResult -> {
boolean lastWasRemainder = false;
for (ParseResult parseResult : results) {
if (parseResult.getParameter().isRemainder() && !parseResult.getParameter().isListParam() && parseResult.getResult() instanceof String) {
if (usableParameters.isEmpty() || !(usableParameters.get(usableParameters.size() - 1) instanceof String)) {
usableParameters.add(parseResult.getResult());
} else {
} else if(lastWasRemainder){
int lastIndex = usableParameters.size() - 1;
usableParameters.set(lastIndex, usableParameters.get(lastIndex).toString() + " " + parseResult.getResult().toString());
} else {
usableParameters.add(parseResult.getResult());
}
} else if (parseResult.getParameter().isListParam()) {
if (usableParameters.isEmpty()) {
@@ -527,7 +511,8 @@ public class CommandReceivedHandler extends ListenerAdapter {
} else {
usableParameters.add(parseResult.getResult());
}
});
lastWasRemainder = parseResult.getParameter().isRemainder();
}
return usableParameters;
}

View File

@@ -0,0 +1,124 @@
package dev.sheldan.abstracto.core.command;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.service.CommandService;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.command.service.PostCommandExecution;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
@Slf4j
public class SlashCommandListenerBean extends ListenerAdapter {
@Autowired(required = false)
@Getter
private List<Command> commands = new ArrayList<>();
@Autowired
@Qualifier("slashCommandExecutor")
private TaskExecutor slashCommandExecutor;
@Autowired
private SlashCommandListenerBean self;
@Autowired
private CommandService commandService;
@Autowired
private List<PostCommandExecution> executions;
public List<Command> getSlashCommands() {
if(commands == null || commands.isEmpty()) {
return new ArrayList<>();
}
return commands.stream()
.filter(command -> command.getConfiguration()
.getSlashCommandConfig().isEnabled())
.collect(Collectors.toList());
}
@Override
public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) {
if(commands == null || commands.isEmpty()) return;
CompletableFuture.runAsync(() -> self.executeListenerLogic(event), slashCommandExecutor).exceptionally(throwable -> {
log.error("Failed to execute listener logic in async button event.", throwable);
return null;
});
}
@Transactional
public void executeListenerLogic(SlashCommandInteractionEvent event) {
Optional<Command> potentialCommand = findCommand(event);
potentialCommand.ifPresent(command -> {
try {
commandService.isCommandExecutable(command, event).thenAccept(conditionResult -> {
CompletableFuture<CommandResult> commandOutput;
if(conditionResult.isResult()) {
commandOutput = command.executeSlash(event).thenApply(commandResult -> {
log.info("Command {} in server {} was executed.", command.getConfiguration().getName(), event.getGuild().getIdLong());
return commandResult;
});
} else {
commandOutput = CompletableFuture.completedFuture(CommandResult.fromCondition(conditionResult));
}
commandOutput.thenAccept(commandResult -> {
self.executePostCommandListener(command, event, commandResult);
}).exceptionally(throwable -> {
log.error("Error while handling post execution of command {}", command.getConfiguration().getName(), throwable);
CommandResult commandResult = CommandResult.fromError(throwable.getMessage(), throwable);
self.executePostCommandListener(command, event, commandResult);
return null;
});
}).exceptionally(throwable -> {
log.error("Error while executing command {}", command.getConfiguration().getName(), throwable);
CommandResult commandResult = CommandResult.fromError(throwable.getMessage(), throwable);
self.executePostCommandListener(command, event, commandResult);
return null;
});
} catch (Exception exception) {
log.error("Error while checking if command {} is executable.", command.getConfiguration().getName(), exception);
CommandResult commandResult = CommandResult.fromError(exception.getMessage(), exception);
self.executePostCommandListener(command, event, commandResult);
}
});
}
@Transactional
public void executePostCommandListener(Command foundCommand, SlashCommandInteractionEvent event, CommandResult result) {
for (PostCommandExecution postCommandExecution : executions) {
postCommandExecution.executeSlash(event, result, foundCommand);
}
}
private Optional<Command> findCommand(SlashCommandInteractionEvent event) {
return commands
.stream()
.filter(command -> command.getConfiguration().getSlashCommandConfig().isEnabled())
.filter(command -> command.getConfiguration().getSlashCommandConfig().matchesInteraction(event.getInteraction()))
.findAny();
}
@PostConstruct
public void filterPostProcessors() {
executions = executions
.stream()
.filter(PostCommandExecution::supportsSlash)
.collect(Collectors.toList());
}
}

View File

@@ -8,7 +8,6 @@ import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiec
import dev.sheldan.abstracto.core.command.handler.provided.ChannelGroupTypeParameterHandler;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import dev.sheldan.abstracto.core.service.management.ChannelGroupTypeManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -19,9 +18,6 @@ public class ChannelGroupTypeParameterHandlerImpl implements ChannelGroupTypePar
@Autowired
private ChannelGroupTypeManagementService channelGroupTypeManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Override
public boolean handles(Class clazz, UnparsedCommandParameterPiece value) {
return clazz.equals(ChannelGroupType.class) && value.getType().equals(ParameterPieceType.STRING);

View File

@@ -0,0 +1,31 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.ParameterPieceType;
import dev.sheldan.abstracto.core.command.execution.UnparsedCommandParameterPiece;
import dev.sheldan.abstracto.core.command.handler.provided.InstantParameterHandler;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.stereotype.Component;
import java.time.Instant;
@Component
public class InstantParameterHandlerImpl implements InstantParameterHandler {
@Override
public Object handle(UnparsedCommandParameterPiece input, CommandParameterIterators iterators, Parameter param, Message context, Command command) {
return Instant.ofEpochSecond(Long.parseLong((String) input.getValue()));
}
@Override
public boolean handles(Class clazz, UnparsedCommandParameterPiece value) {
return clazz.equals(Instant.class) && value.getType().equals(ParameterPieceType.STRING);
}
@Override
public Integer getPriority() {
return CommandConstants.CORE_HANDLER_PRIORITY;
}
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.command.listener;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.MessageContextCommandListener;
import dev.sheldan.abstracto.core.listener.async.entity.FeatureActivationListener;
import dev.sheldan.abstracto.core.listener.sync.jda.MessageContextCommandListenerBean;
@@ -13,6 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
@Slf4j
@@ -36,6 +38,9 @@ public class MessageContextCommandFeatureActivationListener implements FeatureAc
if(listeners == null || listeners.isEmpty()) {
return DefaultListenerResult.IGNORED;
}
listeners = listeners
.stream().filter(command -> command.getFeature().getKey().equals(model.getFeatureName()))
.collect(Collectors.toList());
Guild guild = guildService.getGuildById(model.getServerId());
listeners.forEach(messageContextCommandListener -> {
if(featureModeService.necessaryFeatureModesMet(messageContextCommandListener, model.getServerId())) {

View File

@@ -1,10 +1,9 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.command.listener;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.MessageContextCommandListener;
import dev.sheldan.abstracto.core.listener.async.entity.FeatureActivationListener;
import dev.sheldan.abstracto.core.listener.async.entity.FeatureDeactivationListener;
import dev.sheldan.abstracto.core.listener.sync.jda.MessageContextCommandListenerBean;
import dev.sheldan.abstracto.core.models.listener.FeatureActivationListenerModel;
import dev.sheldan.abstracto.core.models.listener.FeatureDeactivationListenerModel;
import dev.sheldan.abstracto.core.service.ContextCommandService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
@@ -15,6 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
@Slf4j
@@ -38,6 +38,9 @@ public class MessageContextCommandFeatureDeactivationListener implements Feature
if(listeners == null || listeners.isEmpty()) {
return DefaultListenerResult.IGNORED;
}
listeners = listeners
.stream().filter(command -> command.getFeature().getKey().equals(model.getFeatureName()))
.collect(Collectors.toList());
Guild guild = guildService.getGuildById(model.getServerId());
listeners.forEach(messageContextCommandListener -> {
if(featureModeService.necessaryFeatureModesMet(messageContextCommandListener, model.getServerId())) {

View File

@@ -0,0 +1,65 @@
package dev.sheldan.abstracto.core.command.listener;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.SlashCommandListenerBean;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.slash.SlashCommandService;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.entity.FeatureActivationListener;
import dev.sheldan.abstracto.core.models.listener.FeatureActivationListenerModel;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Component
@Slf4j
public class SlashCommandFeatureActivationListener implements FeatureActivationListener {
@Autowired
private SlashCommandListenerBean slashCommandListenerBean;
@Autowired
private BotService botService;
@Autowired
private FeatureModeService featureModeService;
@Autowired
private SlashCommandService slashCommandService;
@Override
public DefaultListenerResult execute(FeatureActivationListenerModel model) {
List<Command> incomingSlashCommands = slashCommandListenerBean.getSlashCommands()
.stream()
.filter(command -> command.getFeature().getKey().equals(model.getFeatureName()))
.collect(Collectors.toList());
if(incomingSlashCommands.isEmpty()) {
return DefaultListenerResult.IGNORED;
}
JDA jda = botService.getInstance();
Guild guild = jda.getGuildById(model.getServerId());
log.info("Updating slash commands for guild {}.", guild.getIdLong());
List<Pair<List<CommandConfiguration>, SlashCommandData>> commandsToUpDate = new ArrayList<>();
incomingSlashCommands.forEach(command -> {
if(!featureModeService.necessaryFeatureModesMet(command, guild.getIdLong())) {
return;
}
log.info("Updating slash command {} in guild {}.", command.getConfiguration().getName(), guild.getId());
slashCommandService.convertCommandConfigToCommandData(command.getConfiguration(), commandsToUpDate);
});
slashCommandService.addGuildSlashCommands(guild, commandsToUpDate)
.thenAccept(commands1 -> log.info("Updating {} slash commands in guild {}.", commandsToUpDate.size(), guild.getIdLong()));
return DefaultListenerResult.PROCESSED;
}
}

View File

@@ -0,0 +1,91 @@
package dev.sheldan.abstracto.core.command.listener;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.SlashCommandListenerBean;
import dev.sheldan.abstracto.core.command.model.database.ACommand;
import dev.sheldan.abstracto.core.command.model.database.ACommandInAServer;
import dev.sheldan.abstracto.core.command.service.management.CommandInServerManagementService;
import dev.sheldan.abstracto.core.command.service.management.CommandManagementService;
import dev.sheldan.abstracto.core.command.slash.SlashCommandService;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.entity.FeatureDeactivationListener;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.listener.FeatureDeactivationListenerModel;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.ISnowflake;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
@Component
@Slf4j
public class SlashCommandFeatureDeactivationListener implements FeatureDeactivationListener {
@Autowired
private SlashCommandListenerBean slashCommandListenerBean;
@Autowired
private BotService botService;
@Autowired
private SlashCommandService slashCommandService;
@Autowired
private CommandInServerManagementService commandInServerManagementService;
@Autowired
private CommandManagementService commandManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Override
public DefaultListenerResult execute(FeatureDeactivationListenerModel model) {
List<Command> commandsToDelete = slashCommandListenerBean.getSlashCommands()
.stream()
.filter(command -> command.getFeature().getKey().equals(model.getFeatureName()))
.collect(Collectors.toList());
if(commandsToDelete.isEmpty()) {
return DefaultListenerResult.IGNORED;
}
JDA jda = botService.getInstance();
Guild guild = jda.getGuildById(model.getServerId());
log.info("Updating slash commands for guild {}.", guild.getIdLong());
guild.retrieveCommands().queue(commands -> {
List<Long> existingCommands = commands
.stream()
.filter(command -> command.getType().equals(net.dv8tion.jda.api.interactions.commands.Command.Type.SLASH))
.map(ISnowflake::getIdLong)
.collect(Collectors.toList());
log.info("Loaded {} slash commands for guild {}.", commands.size(), guild.getIdLong());
Set<Long> commandIdsToDelete = new HashSet<>();
List<Long> commandInServerIdsToUnset = new ArrayList<>();
AServer server = serverManagementService.loadServer(guild.getIdLong());
commandsToDelete.forEach(aCommandToDelete -> {
ACommand aCommand = commandManagementService.findCommandByName(aCommandToDelete.getConfiguration().getName());
ACommandInAServer commandInServer = commandInServerManagementService.getCommandForServer(aCommand, server);
if(commandInServer.getSlashCommandId() != null && existingCommands.contains(commandInServer.getSlashCommandId())) {
commandIdsToDelete.add(commandInServer.getSlashCommandId());
commandInServerIdsToUnset.add(commandInServer.getSlashCommandId());
}
});
slashCommandService.deleteGuildSlashCommands(guild, new ArrayList<>(commandIdsToDelete), commandInServerIdsToUnset).whenComplete((unused, throwable) -> {
if(throwable != null) {
log.error("Failed to delete {} commands from guild {} for feature {}.", commandIdsToDelete.size(), guild.getIdLong(), model.getFeatureName(), throwable);
} else {
log.info("Deleted {} commands for guild {} for feature {}.", commandIdsToDelete.size(), guild.getIdLong(), model.getFeatureName());
}
});
},
throwable -> log.error("Failed to load commands for guild {}.", guild.getIdLong(), throwable));
return DefaultListenerResult.PROCESSED;
}
}

View File

@@ -34,6 +34,7 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -168,6 +169,16 @@ public class CommandServiceBean implements CommandService {
}
}
@Override
public CompletableFuture<ConditionResult> isCommandExecutable(Command command, SlashCommandInteractionEvent slashCommandInteractionEvent) {
if(command instanceof ConditionalCommand) {
ConditionalCommand castedCommand = (ConditionalCommand) command;
return checkConditions(slashCommandInteractionEvent, command, castedCommand.getConditions());
} else {
return ConditionResult.fromAsyncSuccess();
}
}
@Override
public UnParsedCommandParameter getUnParsedCommandParameter(String messageContent, Message message) {
return new UnParsedCommandParameter(messageContent, message);
@@ -196,6 +207,41 @@ public class CommandServiceBean implements CommandService {
.build();
}
private CompletableFuture<ConditionResult> checkConditions(SlashCommandInteractionEvent slashCommandInteractionEvent, Command command, List<CommandCondition> conditions) {
if(conditions != null && !conditions.isEmpty()) {
List<CompletableFuture<ConditionResult>> futures = new ArrayList<>();
for (CommandCondition condition : conditions) {
if(condition.isAsync()) {
futures.add(condition.shouldExecuteAsync(slashCommandInteractionEvent, command));
} else {
futures.add(CompletableFuture.completedFuture(condition.shouldExecute(slashCommandInteractionEvent, command)));
}
}
CompletableFuture<ConditionResult> resultFuture = new CompletableFuture<>();
CompletableFutureList<ConditionResult> futureList = new CompletableFutureList<>(futures);
futureList.getMainFuture().whenComplete((unused, throwable) -> {
List<ConditionResult> results = futureList.getObjects();
boolean foundResult = false;
for (ConditionResult conditionResult : results) {
if (!conditionResult.isResult()) {
foundResult = true;
resultFuture.complete(conditionResult);
break;
}
}
if(!foundResult) {
resultFuture.complete(ConditionResult.fromSuccess());
}
}).exceptionally(throwable -> {
resultFuture.completeExceptionally(throwable);
return null;
});
return resultFuture;
} else {
return ConditionResult.fromAsyncSuccess();
}
}
private CompletableFuture<ConditionResult> checkConditions(CommandContext commandContext, Command command, List<CommandCondition> conditions) {
if(conditions != null && !conditions.isEmpty()) {
List<CompletableFuture<ConditionResult>> futures = new ArrayList<>();

View File

@@ -7,9 +7,12 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ResultState;
import dev.sheldan.abstracto.core.command.model.condition.GenericConditionModel;
import dev.sheldan.abstracto.core.command.service.PostCommandExecution;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.ReactionService;
import net.dv8tion.jda.api.entities.GuildChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -23,9 +26,16 @@ public class ConditionPostExecution implements PostCommandExecution {
@Autowired
private ChannelService channelService;
@Autowired
private InteractionService interactionService;
@Override
public void execute(CommandContext commandContext, CommandResult commandResult, Command command) {
if(commandResult.getResult().equals(ResultState.CONDITION) && commandResult.getConditionResult() != null && !commandResult.getConditionResult().isResult() && commandResult.getConditionResult().getConditionDetail() != null) {
if(commandResult.getResult().equals(ResultState.CONDITION)
&& commandResult.getConditionResult() != null &&
!commandResult.getConditionResult().isResult()
&& commandResult.getConditionResult().getConditionDetail() != null
&& commandResult.getConditionResult().isReportResult()) {
reactionService.addReactionToMessage(CoreFeatureConfig.WARN_REACTION_KEY, commandContext.getGuild().getIdLong(), commandContext.getMessage());
GenericConditionModel conditionModel = GenericConditionModel
.builder()
@@ -40,4 +50,30 @@ public class ConditionPostExecution implements PostCommandExecution {
channelService.sendEmbedTemplateInTextChannelList(GENERIC_COMMAND_EXCEPTION_MODEL_KEY, conditionModel, commandContext.getChannel());
}
}
@Override
public void executeSlash(SlashCommandInteractionEvent interaction, CommandResult commandResult, Command command) {
if(commandResult.getResult().equals(ResultState.CONDITION)
&& commandResult.getConditionResult() != null &&
!commandResult.getConditionResult().isResult()
&& commandResult.getConditionResult().getConditionDetail() != null
&& commandResult.getConditionResult().isReportResult()) {
GenericConditionModel conditionModel = GenericConditionModel
.builder()
.conditionDetail(commandResult.getConditionResult().getConditionDetail())
.guildChannelMember(GuildChannelMember
.builder()
.guild(interaction.getGuild())
.textChannel((GuildChannel) interaction.getChannel())
.member(interaction.getMember())
.build())
.build();
interactionService.replyEmbed(GENERIC_COMMAND_EXCEPTION_MODEL_KEY, conditionModel, interaction.getInteraction());
}
}
@Override
public boolean supportsSlash() {
return true;
}
}

View File

@@ -6,6 +6,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ResultState;
import dev.sheldan.abstracto.core.command.service.CommandCoolDownService;
import dev.sheldan.abstracto.core.command.service.PostCommandExecution;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -24,4 +25,17 @@ public class CoolDownPostExecution implements PostCommandExecution {
commandCoolDownService.updateCoolDowns(command, commandContext);
}
}
@Override
public boolean supportsSlash() {
return true;
}
@Override
public void executeSlash(SlashCommandInteractionEvent interaction, CommandResult commandResult, Command command) {
ResultState result = commandResult.getResult();
if(result.equals(ResultState.SUCCESSFUL) || result.equals(ResultState.IGNORED)) {
commandCoolDownService.updateCoolDowns(command, interaction);
}
}
}

View File

@@ -10,6 +10,7 @@ import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.command.service.PostCommandExecution;
import dev.sheldan.abstracto.core.service.ConfigService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -41,4 +42,27 @@ public class ExceptionPostExecution implements PostCommandExecution {
}
}
}
@Override
public void executeSlash(SlashCommandInteractionEvent interaction, CommandResult commandResult, Command command) {
ResultState result = commandResult.getResult();
if(result.equals(ResultState.ERROR)) {
Throwable throwable = commandResult.getThrowable();
if(throwable != null) {
if(throwable instanceof CommandNotFoundException){
String configValue = configService.getStringValueOrConfigDefault(CoreFeatureConfig.NO_COMMAND_REPORTING_CONFIG_KEY, interaction.getGuild().getIdLong());
if(!BooleanUtils.toBoolean(configValue)) {
return;
}
}
log.info("Exception handling for exception {}.", throwable.getClass().getSimpleName());
exceptionService.reportSlashException(throwable, interaction, command);
}
}
}
@Override
public boolean supportsSlash() {
return true;
}
}

View File

@@ -19,6 +19,7 @@ import dev.sheldan.abstracto.core.service.ChannelGroupService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.CoolDownChannelGroupManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -75,6 +76,12 @@ public class CommandCoolDownServiceBean implements CommandCoolDownService {
@Override
public CoolDownCheckResult allowedToExecuteCommand(Command command, CommandContext context) {
Long serverId = context.getGuild().getIdLong();
Long channelId = context.getChannel().getIdLong();
Long memberId = context.getAuthor().getIdLong();
return allowedToExecuteCommand(command, serverId, channelId, memberId);
}
private CoolDownCheckResult allowedToExecuteCommand(Command command, Long serverId, Long channelId, Long memberId) {
Instant now = Instant.now();
String commandName = command.getConfiguration().getName();
Duration serverCooldown = null;
@@ -90,7 +97,7 @@ public class CommandCoolDownServiceBean implements CommandCoolDownService {
if(storage.getChannelGroupCoolDowns().containsKey(serverId)) {
Map<Long, CommandReUseMap> serverMap = storage.getChannelGroupCoolDowns().get(serverId);
if(!serverMap.keySet().isEmpty()) {
Long channelId = context.getChannel().getIdLong();
AChannel channel = channelManagementService.loadChannel(channelId);
List<AChannelGroup> channelGroups =
channelGroupService.getChannelGroupsOfChannelWithType(channel, COOL_DOWN_CHANNEL_GROUP_TYPE);
@@ -108,7 +115,6 @@ public class CommandCoolDownServiceBean implements CommandCoolDownService {
if(storage.getMemberCoolDowns().containsKey(serverId)) {
Map<Long, CommandReUseMap> serverMap = storage.getMemberCoolDowns().get(serverId);
if(!serverMap.keySet().isEmpty()) {
Long memberId = context.getAuthor().getIdLong();
if(serverMap.containsKey(memberId)) {
CommandReUseMap commandReUseMap = serverMap.get(memberId);
Duration durationToExecuteIn = getDurationToExecuteIn(now, commandName, commandReUseMap);
@@ -121,6 +127,14 @@ public class CommandCoolDownServiceBean implements CommandCoolDownService {
return createCooldownCheckResult(serverId, commandName, serverCooldown, channelCooldown, memberCooldown);
}
@Override
public CoolDownCheckResult allowedToExecuteCommand(Command command, SlashCommandInteractionEvent slashCommandInteractionEvent) {
Long serverId = slashCommandInteractionEvent.getGuild().getIdLong();
Long channelId = slashCommandInteractionEvent.getChannel().getIdLong();
Long memberId = slashCommandInteractionEvent.getMember().getIdLong();
return allowedToExecuteCommand(command, serverId, channelId, memberId);
}
public CoolDownCheckResult createCooldownCheckResult(Long serverId, String commandName, Duration serverCooldown, Duration channelCooldown, Duration memberCooldown) {
if(serverCooldown != null || channelCooldown != null || memberCooldown != null) {
Long serverSeconds = serverCooldown != null ? serverCooldown.getSeconds() : 0L;
@@ -393,6 +407,24 @@ public class CommandCoolDownServiceBean implements CommandCoolDownService {
}
}
@Override
public void updateCoolDowns(Command command, SlashCommandInteractionEvent event) {
takeLock();
try {
AServerChannelUserId contextIds = AServerChannelUserId
.builder()
.channelId(event.getChannel().getIdLong())
.userId(event.getMember().getIdLong())
.guildId(event.getGuild().getIdLong())
.build();
addServerCoolDown(command, contextIds.getGuildId(), false);
addChannelCoolDown(command, contextIds.toServerChannelId(), false);
addMemberCoolDown(command, contextIds, false);
} finally {
releaseLock();
}
}
@Override
public void setCoolDownConfigForChannelGroup(AChannelGroup aChannelGroup, Duration groupCoolDown, Duration memberCoolDown) {
CoolDownChannelGroup cdChannelGroup = coolDownChannelGroupManagementService.findByChannelGroupId(aChannelGroup.getId());

View File

@@ -21,7 +21,10 @@ import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageChannel;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -30,6 +33,7 @@ import org.springframework.stereotype.Component;
public class ExceptionServiceBean implements ExceptionService {
public static final String MODEL_WRAPPER_TEMPLATE_KEY = "model_wrapper";
public static final String GENERIC_INTERACTION_EXCEPTION = "generic_interaction_exception";
@Autowired
private ChannelService channelService;
@@ -68,30 +72,43 @@ public class ExceptionServiceBean implements ExceptionService {
@Override
public void reportExceptionToInteraction(Throwable exception, ButtonClickedListenerModel interactionContext, ButtonClickedListener executedListener) {
ButtonInteractionEvent event = interactionContext.getEvent();
if(executedListener != null) {
log.info("Reporting generic exception {} of listener {} towards channel {} in server {}.",
exception.getClass().getSimpleName(), executedListener.getClass().getSimpleName(), interactionContext.getEvent().getChannel().getIdLong(),
interactionContext.getEvent().getGuild().getIdLong());
exception.getClass().getSimpleName(), executedListener.getClass().getSimpleName(), event.getChannel().getIdLong(),
event.getGuild().getIdLong());
} else {
log.info("Reporting generic exception {} towards channel {} in server {}.",
exception.getClass().getSimpleName(), interactionContext.getEvent().getChannel().getIdLong(),
interactionContext.getEvent().getGuild().getIdLong());
exception.getClass().getSimpleName(), event.getChannel().getIdLong(),
event.getGuild().getIdLong());
}
try {
reportGenericInteractionException(exception, interactionContext);
reportGenericInteractionException(exception, event.getInteraction());
} catch (Exception e) {
log.error("Failed to notify about exception.", e);
}
}
@Override
public void reportSlashException(Throwable exception, SlashCommandInteractionEvent event, Command command) {
log.info("Reporting exception of {} command {} in channel {} in guild {} from user {}.",
exception.getClass().getSimpleName(), command.getConfiguration().getName(),
event.getChannel().getIdLong(), event.getGuild().getIdLong(), event.getMember().getIdLong(), exception);
reportGenericInteractionException(exception, event.getInteraction());
}
private void reportGenericException(Throwable throwable, CommandContext context) {
GenericExceptionModel exceptionModel = buildCommandModel(throwable, context);
channelService.sendEmbedTemplateInTextChannelList("generic_command_exception", exceptionModel, context.getChannel());
}
private void reportGenericInteractionException(Throwable throwable, ButtonClickedListenerModel interactionContext) {
GenericInteractionExceptionModel exceptionModel = buildInteractionExceptionModel(throwable, interactionContext);
interactionService.sendMessageToInteraction("generic_interaction_exception", exceptionModel, interactionContext.getEvent().getInteraction().getHook());
private void reportGenericInteractionException(Throwable throwable, IReplyCallback replyCallback) {
GenericInteractionExceptionModel exceptionModel = buildInteractionExceptionModel(throwable, replyCallback);
if(replyCallback.isAcknowledged()) {
interactionService.sendMessageToInteraction(GENERIC_INTERACTION_EXCEPTION, exceptionModel, replyCallback.getHook());
} else {
interactionService.replyEmbed(GENERIC_INTERACTION_EXCEPTION, exceptionModel, replyCallback);
}
}
@Override
@@ -127,16 +144,21 @@ public class ExceptionServiceBean implements ExceptionService {
}
}
private GenericInteractionExceptionModel buildInteractionExceptionModel(Throwable throwable, ButtonClickedListenerModel context) {
private GenericInteractionExceptionModel buildInteractionExceptionModel(Throwable throwable, IReplyCallback context) {
return GenericInteractionExceptionModel
.builder()
.member(context.getEvent().getMember())
.user(context.getEvent().getUser())
.member(context.getMember())
.user(context.getUser())
.throwable(throwable)
.build();
}
private GenericExceptionModel buildCommandModel(Throwable throwable, CommandContext context) {
FullUserInServer fullUser = FullUserInServer.builder().member(context.getAuthor()).aUserInAServer(userInServerManagementService.loadUserOptional(context.getGuild().getIdLong(), context.getAuthor().getIdLong()).orElse(null)).build();
FullUserInServer fullUser = FullUserInServer
.builder()
.member(context.getAuthor())
.aUserInAServer(userInServerManagementService.loadUserOptional(context.getGuild().getIdLong(), context.getAuthor().getIdLong())
.orElse(null))
.build();
return GenericExceptionModel
.builder()
.user(fullUser)

View File

@@ -9,6 +9,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Slf4j
public class CommandInServerManagementServiceBean implements CommandInServerManagementService {
@@ -19,6 +21,11 @@ public class CommandInServerManagementServiceBean implements CommandInServerMana
@Override
public ACommandInAServer createCommandInServer(ACommand command, AServer server) {
return createCommandInServer(command, server, null);
}
@Override
public ACommandInAServer createCommandInServer(ACommand command, AServer server, Long commandId) {
ACommandInAServer commandInAServer = ACommandInAServer
.builder()
.commandReference(command)
@@ -44,4 +51,14 @@ public class CommandInServerManagementServiceBean implements CommandInServerMana
public ACommandInAServer getCommandForServer(ACommand command, Long serverId) {
return repository.findByServerReference_IdAndCommandReference(serverId, command);
}
@Override
public ACommandInAServer getCommandForServer(Long commandInServerId) {
return repository.getOne(commandInServerId);
}
@Override
public List<ACommandInAServer> getCommandsForServer(List<Long> commandInServerId) {
return repository.findAllById(commandInServerId);
}
}

View File

@@ -0,0 +1,205 @@
package dev.sheldan.abstracto.core.command.slash;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.model.database.ACommand;
import dev.sheldan.abstracto.core.command.model.database.ACommandInAServer;
import dev.sheldan.abstracto.core.command.service.management.CommandInServerManagementService;
import dev.sheldan.abstracto.core.command.service.management.CommandManagementService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.interactions.commands.Command;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
public class SlashCommandServiceBean implements SlashCommandService {
@Autowired
private TemplateService templateService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private CommandInServerManagementService commandInServerManagementService;
@Autowired
private CommandManagementService commandManagementService;
@Autowired
private SlashCommandServiceBean self;
@Override
public void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List<Pair<List<CommandConfiguration>, SlashCommandData>> existingCommands) {
boolean isTemplated = commandConfiguration.isTemplated();
SlashCommandConfig slashConfig = commandConfiguration.getSlashCommandConfig();
String description;
String internalCommandName = commandConfiguration.getName();
if(!isTemplated) {
description = commandConfiguration.getDescription();
} else {
description = templateService.renderSimpleTemplate(internalCommandName + "_description");
}
String rootName = slashConfig.getSlashCompatibleRootName();
String groupName = slashConfig.getSlashCompatibleGroupName();
String commandName = slashConfig.getSlashCompatibleCommandName();
Optional<SlashCommandData> existingRootCommand = existingCommands
.stream()
.filter(commandData -> commandData.getSecond().getName().equals(rootName))
.map(Pair::getSecond)
.findAny();
SlashCommandData rootCommand = existingRootCommand.orElseGet(() -> Commands.slash(rootName, description));
if(commandName != null) {
SubcommandData slashCommand = new SubcommandData(commandName, description);
if(groupName == null) {
rootCommand.addSubcommands(slashCommand);
} else {
Optional<SubcommandGroupData> commandGroup = rootCommand
.getSubcommandGroups()
.stream()
.filter(subcommandGroupData -> subcommandGroupData.getName().equals(groupName))
.findAny();
SubcommandGroupData groupData = commandGroup.orElseGet(() -> new SubcommandGroupData(groupName, description));
groupData.addSubcommands(slashCommand);
rootCommand.addSubcommandGroups(groupData);
}
List<OptionData> requiredParameters = getParameters(commandConfiguration, isTemplated, internalCommandName);
slashCommand.addOptions(requiredParameters);
} else {
List<OptionData> requiredParameters = getParameters(commandConfiguration, isTemplated, internalCommandName);
rootCommand.addOptions(requiredParameters);
}
if(!existingRootCommand.isPresent()) {
Optional<Pair<List<CommandConfiguration>, SlashCommandData>> existingCommand = existingCommands
.stream()
.filter(listSlashCommandDataPair -> listSlashCommandDataPair.getSecond().equals(rootCommand))
.findAny();
if(existingCommand.isPresent()) {
existingCommand.get().getFirst().add(commandConfiguration);
} else {
existingCommands.add(Pair.of(new ArrayList<>(Arrays.asList(commandConfiguration)), rootCommand));
}
}
}
private List<OptionData> getParameters(CommandConfiguration commandConfiguration, boolean isTemplated, String internalCommandName) {
List<OptionData> requiredParameters = new ArrayList<>();
List<OptionData> optionalParameters = new ArrayList<>();
commandConfiguration.getParameters().forEach(parameter -> {
List<OptionType> types = slashCommandParameterService.getTypesFromParameter(parameter.getType());
if(types.size() > 1) {
if(parameter.isListParam()) {
for (int i = 0; i < parameter.getListSize(); i++) {
for (OptionType type : types) {
String parameterName = slashCommandParameterService.getFullQualifiedParameterName(parameter.getSlashCompatibleName(), type) + "_" + i;
String parameterDescription = isTemplated ? templateService.renderSimpleTemplate(internalCommandName + "_parameter_" + parameter.getName()) : parameter.getDescription();
optionalParameters.add(new OptionData(type, parameterName, parameterDescription, false));
}
}
} else {
types.forEach(type -> {
String parameterName = slashCommandParameterService.getFullQualifiedParameterName(parameter.getSlashCompatibleName(), type);
String parameterDescription = isTemplated ? templateService.renderSimpleTemplate(internalCommandName + "_parameter_" + parameter.getName()) : parameter.getDescription();
optionalParameters.add(new OptionData(type, parameterName, parameterDescription, false));
});
}
} else {
OptionType type = types.get(0);
String parameterDescription = isTemplated ? templateService.renderSimpleTemplate(internalCommandName + "_parameter_" + parameter.getName()) : parameter.getDescription();
if(parameter.isListParam()) {
for (int i = 0; i < parameter.getListSize(); i++) {
optionalParameters.add(new OptionData(type, parameter.getSlashCompatibleName() + "_" + i, parameterDescription, false));
}
} else {
if(!parameter.isOptional()) {
requiredParameters.add(new OptionData(type, parameter.getSlashCompatibleName(), parameterDescription, true));
} else {
optionalParameters.add(new OptionData(type, parameter.getSlashCompatibleName(), parameterDescription, false));
}
}
}
});
requiredParameters.addAll(optionalParameters);
return requiredParameters;
}
@Override
public CompletableFuture<List<Command>> updateGuildSlashCommand(Guild guild, List<Pair<List<CommandConfiguration>, SlashCommandData>> commandData) {
List<CommandData> commands = commandData
.stream()
.map(Pair::getSecond)
.collect(Collectors.toList());
return guild.updateCommands().addCommands(commands).submit().thenApply(createdCommands -> {
self.storeCreatedCommands(guild, commandData, createdCommands);
return createdCommands;
});
}
@Override
public CompletableFuture<Void> deleteGuildSlashCommands(Guild guild, List<Long> slashCommandId, List<Long> commandInServerIds) {
List<CompletableFuture<Void>> commandFutures = slashCommandId
.stream()
.map(commandI -> guild.deleteCommandById(commandI).submit())
.collect(Collectors.toList());
return new CompletableFutureList<>(commandFutures).getMainFuture().thenAccept(unused -> {
self.unsetCommandInServerSlashId(commandInServerIds);
});
}
@Transactional
public void unsetCommandInServerSlashId(List<Long> commandInServerIds) {
List<ACommandInAServer> commandsForServer = commandInServerManagementService.getCommandsForServer(commandInServerIds);
commandsForServer.forEach(aCommandInAServer -> aCommandInAServer.setSlashCommandId(null));
}
@Override
public CompletableFuture<Void> addGuildSlashCommands(Guild guild, List<Pair<List<CommandConfiguration>, SlashCommandData>> commandData) {
List<CommandData> commands = commandData
.stream()
.map(Pair::getSecond)
.collect(Collectors.toList());
List<CompletableFuture<Command>> upsertFutures = commands
.stream()
.map(upsertCommand -> guild.upsertCommand(upsertCommand).submit())
.collect(Collectors.toList());
CompletableFutureList<Command> allFutures = new CompletableFutureList<>(upsertFutures);
return allFutures.getMainFuture()
.thenAccept(unused -> self.storeCreatedCommands(guild, commandData, allFutures.getObjects()));
}
@Transactional
public void storeCreatedCommands(Guild guild, List<Pair<List<CommandConfiguration>, SlashCommandData>> commandData, List<Command> createdCommands) {
commandData.forEach(commandConfigurationSlashCommandDataPair -> {
SlashCommandData slashCommandData = commandConfigurationSlashCommandDataPair.getSecond();
commandConfigurationSlashCommandDataPair.getFirst().forEach(commandConfiguration -> {
ACommand aCommand = commandManagementService.findCommandByName(commandConfiguration.getName());
ACommandInAServer commandInServer = commandInServerManagementService.getCommandForServer(aCommand, guild.getIdLong());
Command createdCommand = createdCommands.stream().filter(command -> doesCommandMatch(slashCommandData, command)).findFirst().orElse(null);
if(createdCommand != null) {
commandInServer.setSlashCommandId(createdCommand.getIdLong());
}
});
});
}
private boolean doesCommandMatch(SlashCommandData commandConfig, Command command) {
return commandConfig.getName().equals(command.getName());
}
}

View File

@@ -0,0 +1,190 @@
package dev.sheldan.abstracto.core.command.slash.parameter;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.service.EmoteService;
import net.dv8tion.jda.api.entities.Emoji;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Locale;
@Component
public class SlashCommandParameterServiceBean implements SlashCommandParameterService {
@Autowired
private EmoteService emoteService;
@Autowired
private List<SlashCommandParameterProvider> parameterProviders;
@Override
public <T, Z> Z getCommandOption(String name, SlashCommandInteractionEvent event, Class<T> parameterType, Class<Z> slashParameterType) {
name = name.toLowerCase(Locale.ROOT);
List<OptionType> potentialOptionTypes = getTypesFromParameter(parameterType);
OptionType actualOptionType = potentialOptionTypes.size() == 1 ? potentialOptionTypes.get(0) : null;
if (potentialOptionTypes.size() > 1) {
for (OptionType optionType: potentialOptionTypes) {
if(event.getOption(getFullQualifiedParameterName(name, optionType)) != null) {
actualOptionType = optionType;
break;
}
}
}
if(actualOptionType == null) {
throw new IllegalArgumentException(String.format("Could not determine option type for parameter %s", name));
}
if(potentialOptionTypes.size() > 1) {
name = getFullQualifiedParameterName(name, actualOptionType);
}
if(actualOptionType == OptionType.BOOLEAN) {
return slashParameterType.cast(event.getOption(name).getAsBoolean());
} else if (actualOptionType == OptionType.ATTACHMENT) {
return slashParameterType.cast(event.getOption(name).getAsAttachment());
} else if (actualOptionType == OptionType.NUMBER) {
return slashParameterType.cast(event.getOption(name).getAsDouble());
} else if(actualOptionType == OptionType.STRING) {
return slashParameterType.cast(event.getOption(name).getAsString());
} else if(actualOptionType == OptionType.INTEGER) {
return slashParameterType.cast(event.getOption(name).getAsInt());
} else if(actualOptionType == OptionType.ROLE) {
return slashParameterType.cast(event.getOption(name).getAsRole());
} else if(actualOptionType == OptionType.MENTIONABLE) {
return slashParameterType.cast(event.getOption(name).getAsMentionable());
} else if(actualOptionType == OptionType.CHANNEL) {
return slashParameterType.cast(event.getOption(name).getAsGuildChannel());
} else if(actualOptionType == OptionType.USER) {
if(parameterType.equals(User.class) && slashParameterType.equals(User.class)) {
return slashParameterType.cast(event.getOption(name).getAsUser());
} else {
return slashParameterType.cast(event.getOption(name).getAsMember());
}
} else {
throw new AbstractoRunTimeException("Unknown parameter type");
}
}
@Override
public <T, Z> boolean hasCommandOption(String name, SlashCommandInteractionEvent event, Class<T> parameterType, Class<Z> slashParameterType) {
name = name.toLowerCase(Locale.ROOT);
List<OptionType> potentialOptionTypes = getTypesFromParameter(parameterType);
OptionType actualOptionType = potentialOptionTypes.size() == 1 ? potentialOptionTypes.get(0) : null;
if (potentialOptionTypes.size() > 1) {
for (OptionType optionType: potentialOptionTypes) {
if(event.getOption(getFullQualifiedParameterName(name, optionType)) != null) {
actualOptionType = optionType;
break;
}
}
}
if(actualOptionType == null) {
return false;
}
if(potentialOptionTypes.size() > 1) {
name = getFullQualifiedParameterName(name, actualOptionType);
}
if(actualOptionType == OptionType.BOOLEAN) {
return slashParameterType.isInstance(event.getOption(name).getAsBoolean());
} else if (actualOptionType == OptionType.ATTACHMENT) {
return slashParameterType.isInstance(event.getOption(name).getAsAttachment());
} else if (actualOptionType == OptionType.NUMBER) {
return slashParameterType.isInstance(event.getOption(name).getAsDouble());
} else if(actualOptionType == OptionType.STRING) {
return slashParameterType.isInstance(event.getOption(name).getAsString());
} else if(actualOptionType == OptionType.INTEGER) {
return slashParameterType.isInstance(event.getOption(name).getAsInt());
} else if(actualOptionType == OptionType.ROLE) {
return slashParameterType.isInstance(event.getOption(name).getAsRole());
} else if(actualOptionType == OptionType.MENTIONABLE) {
return slashParameterType.isInstance(event.getOption(name).getAsMentionable());
} else if(actualOptionType == OptionType.CHANNEL) {
return slashParameterType.isInstance(event.getOption(name).getAsGuildChannel());
} else if(actualOptionType == OptionType.USER) {
if (parameterType.equals(User.class) && slashParameterType.equals(User.class)) {
return slashParameterType.isInstance(event.getOption(name).getAsUser());
} else {
return slashParameterType.isInstance(event.getOption(name).getAsMember());
}
} else {
return false;
}
}
@Override
public <T> T getCommandOption(String name, SlashCommandInteractionEvent event, Class<T> parameterType) {
return getCommandOption(name, event, parameterType, parameterType);
}
@Override
public Object getCommandOption(String name, SlashCommandInteractionEvent event) {
return event.getOption(name);
}
@Override
public Boolean hasCommandOption(String name, SlashCommandInteractionEvent event) {
return event.getOption(name) != null;
}
@Override
public Boolean hasCommandOptionWithFullType(String name, SlashCommandInteractionEvent event, OptionType optionType) {
return hasCommandOption(getFullQualifiedParameterName(name, optionType), event);
}
@Override
public AEmote loadAEmoteFromString(String input, SlashCommandInteractionEvent event) {
Emoji emoji = loadEmoteFromString(input, event);
return emoteService.getFakeEmoteFromEmoji(emoji);
}
@Override
public Emoji loadEmoteFromString(String input, SlashCommandInteractionEvent event) {
if(StringUtils.isNumeric(input)) {
long emoteId = Long.parseLong(input);
return Emoji.fromEmote(event.getGuild().getEmoteById(emoteId));
}
return Emoji.fromMarkdown(input);
}
@Override
public List<OptionType> getTypesFromParameter(Class clazz) {
return parameterProviders
.stream()
.filter(slashCommandParameterProvider -> slashCommandParameterProvider.getOptionMapping().getType().equals(clazz))
.findAny()
.map(slashCommandParameterProvider -> slashCommandParameterProvider.getOptionMapping().getOptionTypes())
.orElseThrow(() -> new IllegalArgumentException(String.format("Unknown type for slash command parameter desired %s", clazz.getName())));
}
@Override
public String getFullQualifiedParameterName(String name, OptionType type) {
switch (type) {
case STRING:
return name + "_string";
case INTEGER:
return name + "_integer";
case BOOLEAN:
return name + "_boolean";
case USER:
return name + "_user";
case CHANNEL:
return name + "_channel";
case ROLE:
return name + "_role";
case MENTIONABLE:
return name + "_mentionable";
case NUMBER:
return name + "_number";
case ATTACHMENT:
return name + "_attachment";
default: throw new IllegalArgumentException(String.format("Not supported parameter type %s", type));
}
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import dev.sheldan.abstracto.core.models.database.AChannel;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class AChannelSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(AChannel.class)
.optionTypes(Arrays.asList(OptionType.CHANNEL, OptionType.STRING))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import dev.sheldan.abstracto.core.models.database.AEmote;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class AEmoteSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(AEmote.class)
.optionTypes(Arrays.asList(OptionType.STRING))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import dev.sheldan.abstracto.core.models.database.ARole;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class ARoleSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(ARole.class)
.optionTypes(Arrays.asList(OptionType.ROLE, OptionType.STRING))
.build();
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class BooleanSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(Boolean.class)
.optionTypes(Arrays.asList(OptionType.BOOLEAN))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class ChannelGroupTypeSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(ChannelGroupType.class)
.optionTypes(Arrays.asList(OptionType.STRING))
.build();
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class DoubleSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(Double.class)
.optionTypes(Arrays.asList(OptionType.NUMBER))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.Arrays;
@Component
public class DurationSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(Duration.class)
.optionTypes(Arrays.asList(OptionType.STRING))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class EmoteSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(Emote.class)
.optionTypes(Arrays.asList(OptionType.STRING))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.Arrays;
@Component
public class FileSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(File.class)
.optionTypes(Arrays.asList(OptionType.ATTACHMENT))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.entities.GuildChannel;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class GuildChannelSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(GuildChannel.class)
.optionTypes(Arrays.asList(OptionType.CHANNEL))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class GuildMessageChannelSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(GuildMessageChannel.class)
.optionTypes(Arrays.asList(OptionType.CHANNEL))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.Arrays;
@Component
public class InstantSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(Instant.class)
.optionTypes(Arrays.asList(OptionType.INTEGER))
.build();
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class IntegerSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(Integer.class)
.optionTypes(Arrays.asList(OptionType.INTEGER))
.build();
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class LongSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(Long.class)
.optionTypes(Arrays.asList(OptionType.INTEGER))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class MemberSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(Member.class)
.optionTypes(Arrays.asList(OptionType.USER))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class RoleSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(Role.class)
.optionTypes(Arrays.asList(OptionType.ROLE))
.build();
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class StringSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(String.class)
.optionTypes(Arrays.asList(OptionType.STRING))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class TextChannelSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(TextChannel.class)
.optionTypes(Arrays.asList(OptionType.CHANNEL))
.build();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.command.slash.parameter.provider.provided;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandOptionTypeMapping;
import dev.sheldan.abstracto.core.command.slash.parameter.provider.SlashCommandParameterProvider;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class UserSlashCommandParameterProvider implements SlashCommandParameterProvider {
@Override
public SlashCommandOptionTypeMapping getOptionMapping() {
return SlashCommandOptionTypeMapping
.builder()
.type(User.class)
.optionTypes(Arrays.asList(OptionType.USER, OptionType.STRING))
.build();
}
}

View File

@@ -1,29 +1,47 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.entities.GuildChannel;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class AddToChannelGroup extends AbstractConditionableCommand {
public static final String ADD_TO_CHANNEL_GROUP_COMMAND = "addToChannelGroup";
public static final String CHANNEL_PARAMETER = "channel";
public static final String NAME_PARAMETER = "name";
private static final String ADD_TO_CHANNEL_GROUP_RESPONSE_TEMPLATE = "addToChannelGroup_response";
@Autowired
private ChannelGroupService channelGroupService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
String name = (String) commandContext.getParameters().getParameters().get(0);
@@ -35,18 +53,53 @@ public class AddToChannelGroup extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String channelGroupName = slashCommandParameterService.getCommandOption(NAME_PARAMETER, event, String.class);
GuildChannel channel = slashCommandParameterService.getCommandOption(CHANNEL_PARAMETER, event, TextChannel.class, GuildChannel.class);
if(!channel.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
channelGroupService.addChannelToChannelGroup(channelGroupName, channel);
return interactionService.replyEmbed(ADD_TO_CHANNEL_GROUP_RESPONSE_TEMPLATE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter channelGroupName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter channelToAdd = Parameter.builder().name("channel").type(TextChannel.class).templated(true).build();
Parameter channelGroupName = Parameter
.builder()
.name(NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter channelToAdd = Parameter
.builder()
.name(CHANNEL_PARAMETER)
.type(TextChannel.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(channelGroupName, channelToAdd);
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
List<String> aliases = Arrays.asList("addTChGrp", "chGrpCh+");
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CHANNELS)
.commandName(ADD_TO_CHANNEL_GROUP_COMMAND)
.build();
return CommandConfiguration.builder()
.name("addToChannelGroup")
.name(ADD_TO_CHANNEL_GROUP_COMMAND)
.module(ChannelsModuleDefinition.CHANNELS)
.aliases(aliases)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.templated(true)

View File

@@ -1,31 +1,48 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.management.ChannelGroupTypeManagementService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class CreateChannelGroup extends AbstractConditionableCommand {
private static final String CREATE_CHANNEL_GROUP_COMMAND = "createChannelGroup";
private static final String NAME_PARAMETER = "name";
private static final String GROUP_TYPE_PARAMETER = "groupType";
private static final String CREATE_CHANNEL_GROUP_RESPONSE_TEMPLATE = "createChannelGroup_response";
@Autowired
private ChannelGroupService channelGroupService;
@Autowired
private ChannelGroupTypeManagementService channelGroupTypeManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
@@ -36,18 +53,51 @@ public class CreateChannelGroup extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String channelGroupName = slashCommandParameterService.getCommandOption(NAME_PARAMETER, event, String.class);
String channelGroupType = slashCommandParameterService.getCommandOption(GROUP_TYPE_PARAMETER, event, ChannelGroupType.class, String.class);
ChannelGroupType actualGroupType = channelGroupTypeManagementService.findChannelGroupTypeByKey((channelGroupType).trim());
channelGroupService.createChannelGroup(channelGroupName, event.getGuild().getIdLong(), actualGroupType);
return interactionService.replyEmbed(CREATE_CHANNEL_GROUP_RESPONSE_TEMPLATE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter channelGroupName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter channelGroupType = Parameter.builder().name("groupType").type(ChannelGroupType.class).templated(true).build();
Parameter channelGroupName = Parameter
.builder()
.name(NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter channelGroupType = Parameter
.builder()
.name(GROUP_TYPE_PARAMETER)
.type(ChannelGroupType.class)
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CHANNELS)
.commandName(CREATE_CHANNEL_GROUP_COMMAND)
.build();
List<Parameter> parameters = Arrays.asList(channelGroupName, channelGroupType);
List<String> aliases = Arrays.asList("+ChGroup");
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("createChannelGroup")
.name(CREATE_CHANNEL_GROUP_COMMAND)
.module(ChannelsModuleDefinition.CHANNELS)
.parameters(parameters)
.aliases(aliases)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.templated(true)
.help(helpInfo)

View File

@@ -1,26 +1,42 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class DeleteChannelGroup extends AbstractConditionableCommand {
private static final String DELETE_CHANNEL_GROUP_COMMAND = "deleteChannelGroup";
private static final String NAME_PARAMETER = "name";
private static final String DELETE_CHANNEL_GROUP_RESPONSE = "deleteChannelGroup_response";
@Autowired
private ChannelGroupService channelGroupService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
String groupName = (String) commandContext.getParameters().getParameters().get(0);
@@ -28,17 +44,42 @@ public class DeleteChannelGroup extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String channelGroupName = slashCommandParameterService.getCommandOption(NAME_PARAMETER, event, String.class);
channelGroupService.deleteChannelGroup(channelGroupName, event.getGuild().getIdLong());
return interactionService.replyEmbed(DELETE_CHANNEL_GROUP_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter channelGroupName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter channelGroupName = Parameter
.builder()
.name(NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(channelGroupName);
List<String> aliases = Arrays.asList("-ChGroup");
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CHANNELS)
.commandName(DELETE_CHANNEL_GROUP_COMMAND)
.build();
return CommandConfiguration.builder()
.name("deleteChannelGroup")
.name(DELETE_CHANNEL_GROUP_COMMAND)
.module(ChannelsModuleDefinition.CHANNELS)
.parameters(parameters)
.aliases(aliases)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.templated(true)

View File

@@ -1,23 +1,39 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class DisableChannelGroup extends AbstractConditionableCommand {
private static final String CHANNEL_GROUP_NAME_PARAMETER = "channelGroupName";
private static final String DISABLE_CHANNEL_GROUP_COMMAND = "disableChannelGroup";
private static final String DISABLE_CHANNEL_GROUP_RESPONSE = "disableChannelGroup_response";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private ChannelGroupService channelGroupService;
@@ -28,15 +44,40 @@ public class DisableChannelGroup extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String channelGroupName = slashCommandParameterService.getCommandOption(CHANNEL_GROUP_NAME_PARAMETER, event, String.class);
channelGroupService.disableChannelGroup(channelGroupName, event.getGuild().getIdLong());
return interactionService.replyEmbed(DISABLE_CHANNEL_GROUP_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter channelGroupName = Parameter.builder().name("channelGroupName").type(String.class).templated(true).build();
Parameter channelGroupName = Parameter
.builder()
.name(CHANNEL_GROUP_NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(channelGroupName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CHANNELS)
.commandName(DISABLE_CHANNEL_GROUP_COMMAND)
.build();
return CommandConfiguration.builder()
.name("disableChannelGroup")
.name(DISABLE_CHANNEL_GROUP_COMMAND)
.module(ChannelsModuleDefinition.CHANNELS)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.templated(true)

View File

@@ -1,27 +1,43 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.PostTargetNotValidException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class DisablePostTarget extends AbstractConditionableCommand {
private static final String DISABLE_POSTTARGET_COMMAND = "disablePosttarget";
private static final String NAME_PARAMETER = "name";
private static final String DISABLE_POST_TARGET_RESPONSE = "disablePosttarget_response";
@Autowired
private PostTargetService postTargetService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
String targetName = (String) commandContext.getParameters().getParameters().get(0);
@@ -32,11 +48,22 @@ public class DisablePostTarget extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String postTargetName = slashCommandParameterService.getCommandOption(NAME_PARAMETER, event, String.class);
if(!postTargetService.validPostTarget(postTargetName)) {
throw new PostTargetNotValidException(postTargetName, postTargetService.getAvailablePostTargets());
}
postTargetService.disablePostTarget(postTargetName, event.getGuild().getIdLong());
return interactionService.replyEmbed(DISABLE_POST_TARGET_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter postTargetName = Parameter
.builder()
.name("name")
.name(NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
@@ -45,10 +72,19 @@ public class DisablePostTarget extends AbstractConditionableCommand {
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.POST_TARGET)
.commandName("disable")
.build();
return CommandConfiguration.builder()
.name("disablePosttarget")
.name(DISABLE_POSTTARGET_COMMAND)
.module(ChannelsModuleDefinition.CHANNELS)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.templated(true)

View File

@@ -1,26 +1,42 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class EnableChannelGroup extends AbstractConditionableCommand {
private static final String CHANNEL_GROUP_NAME_PARAMETER = "channelGroupName";
private static final String ENABLE_CHANNEL_GROUP_COMMAND = "enableChannelGroup";
private static final String ENABLE_CHANNEL_GROUP_RESPONSE = "enableChannelGroup_response";
@Autowired
private ChannelGroupService channelGroupService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
String channelGroupName = (String) commandContext.getParameters().getParameters().get(0);
@@ -28,15 +44,40 @@ public class EnableChannelGroup extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String channelGroupName = slashCommandParameterService.getCommandOption(CHANNEL_GROUP_NAME_PARAMETER, event, String.class);
channelGroupService.enableChannelGroup(channelGroupName, event.getGuild().getIdLong());
return interactionService.replyEmbed(ENABLE_CHANNEL_GROUP_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter channelGroupName = Parameter.builder().name("channelGroupName").type(String.class).templated(true).build();
Parameter channelGroupName = Parameter
.builder()
.name(CHANNEL_GROUP_NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(channelGroupName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CHANNELS)
.commandName(ENABLE_CHANNEL_GROUP_COMMAND)
.build();
return CommandConfiguration.builder()
.name("enableChannelGroup")
.name(ENABLE_CHANNEL_GROUP_COMMAND)
.module(ChannelsModuleDefinition.CHANNELS)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.templated(true)

View File

@@ -1,27 +1,43 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.PostTargetNotValidException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class EnablePostTarget extends AbstractConditionableCommand {
private static final String ENABLE_POSTTARGET_COMMAND = "enablePosttarget";
private static final String NAME_PARAMETER = "name";
private static final String ENABLE_POSTTARGET_RESPONSE = "enablePosttarget_response";
@Autowired
private PostTargetService postTargetService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
String targetName = (String) commandContext.getParameters().getParameters().get(0);
@@ -32,11 +48,22 @@ public class EnablePostTarget extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String postTargetName = slashCommandParameterService.getCommandOption(NAME_PARAMETER, event, String.class);
if(!postTargetService.validPostTarget(postTargetName)) {
throw new PostTargetNotValidException(postTargetName, postTargetService.getAvailablePostTargets());
}
postTargetService.enablePostTarget(postTargetName, event.getGuild().getIdLong());
return interactionService.replyEmbed(ENABLE_POSTTARGET_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter postTargetName = Parameter
.builder()
.name("name")
.name(NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
@@ -45,10 +72,19 @@ public class EnablePostTarget extends AbstractConditionableCommand {
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.POST_TARGET)
.commandName("enable")
.build();
return CommandConfiguration.builder()
.name("enablePosttarget")
.name(ENABLE_POSTTARGET_COMMAND)
.module(ChannelsModuleDefinition.CHANNELS)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.templated(true)

View File

@@ -1,13 +1,15 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.template.commands.ListChannelGroupsModel;
@@ -17,15 +19,19 @@ import dev.sheldan.abstracto.core.service.management.ChannelGroupManagementServi
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class ListChannelGroups extends AbstractConditionableCommand {
public static final String LIST_CHANNEL_GROUPS_COMMAND = "listChannelGroups";
@Autowired
private TemplateService templateService;
@@ -41,24 +47,52 @@ public class ListChannelGroups extends AbstractConditionableCommand {
@Autowired
private ChannelGroupService channelGroupService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
AServer server = serverManagementService.loadServer(commandContext.getGuild());
List<AChannelGroup> channelGroups = channelGroupManagementService.findAllInServer(server);
ListChannelGroupsModel template = (ListChannelGroupsModel) ContextConverter.fromCommandContext(commandContext, ListChannelGroupsModel.class);
template.setGroups(channelGroupService.convertAChannelGroupToChannelGroupChannel(channelGroups));
MessageToSend response = templateService.renderEmbedTemplate("listChannelGroups_response", template, commandContext.getGuild().getIdLong());
MessageToSend response = getMessageToSend(commandContext.getGuild());
channelService.sendMessageToSendToChannel(response, commandContext.getChannel());
return CommandResult.fromIgnored();
}
private MessageToSend getMessageToSend(Guild guild) {
AServer server = serverManagementService.loadServer(guild);
List<AChannelGroup> channelGroups = channelGroupManagementService.findAllInServer(server);
ListChannelGroupsModel template = ListChannelGroupsModel
.builder()
.groups(channelGroupService.convertAChannelGroupToChannelGroupChannel(channelGroups))
.build();
return templateService.renderEmbedTemplate("listChannelGroups_response", template, guild.getIdLong());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
MessageToSend response = getMessageToSend(event.getGuild());
return interactionService.replyMessageToSend(response, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<String> aliases = Arrays.asList("lsChGrp");
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CHANNELS)
.commandName(LIST_CHANNEL_GROUPS_COMMAND)
.build();
return CommandConfiguration.builder()
.name("listChannelGroups")
.name(LIST_CHANNEL_GROUPS_COMMAND)
.module(ChannelsModuleDefinition.CHANNELS)
.slashCommandConfig(slashCommandConfig)
.aliases(aliases)
.templated(true)
.help(helpInfo)

View File

@@ -1,27 +1,34 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.exception.SlashCommandParameterMissingException;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.exception.PostTargetNotValidException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.PostTarget;
import dev.sheldan.abstracto.core.models.template.commands.PostTargetDisplayModel;
import dev.sheldan.abstracto.core.models.template.commands.PostTargetModelEntry;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -35,7 +42,11 @@ import java.util.concurrent.CompletableFuture;
@Slf4j
public class PostTargetCommand extends AbstractConditionableCommand {
public static final String POST_TARGET_SHOW_TARGETS = "posttarget_show_targets";
private static final String POST_TARGET_SHOW_TARGETS = "posttarget_show_targets";
private static final String POSTTARGET_COMMAND = "posttarget";
private static final String NAME_PARAMETER = "name";
private static final String CHANNEL_PARAMETER = "channel";
private static final String POSTTARGET_RESPONSE_TEMPLATE = "posttarget_response";
@Autowired
private PostTargetManagement postTargetManagement;
@@ -49,66 +60,133 @@ public class PostTargetCommand extends AbstractConditionableCommand {
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private InteractionService interactionService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
Guild guild = commandContext.getGuild();
if(commandContext.getParameters().getParameters().isEmpty()) {
log.debug("Displaying existing post targets for guild {}.", commandContext.getGuild().getId());
PostTargetDisplayModel posttargetDisplayModel = (PostTargetDisplayModel) ContextConverter.fromCommandContext(commandContext, PostTargetDisplayModel.class);
AServer server = serverManagementService.loadServer(commandContext.getGuild());
List<PostTarget> postTargets = postTargetService.getPostTargets(server);
posttargetDisplayModel.setPostTargets(new ArrayList<>());
List<PostTargetModelEntry> postTargetEntries = posttargetDisplayModel.getPostTargets();
postTargets.forEach(target -> {
Optional<GuildMessageChannel> channelFromAChannel = channelService.getGuildMessageChannelFromAChannelOptional(target.getChannelReference());
PostTargetModelEntry targetEntry = PostTargetModelEntry
.builder()
.channel(channelFromAChannel.orElse(null))
.disabled(target.getDisabled())
.postTarget(target).build();
postTargetEntries.add(targetEntry);
});
List<String> postTargetConfigs = postTargetService.getPostTargetsOfEnabledFeatures(server);
postTargetConfigs.forEach(postTargetName -> {
if(postTargetEntries.stream().noneMatch(postTargetModelEntry -> postTargetModelEntry.getPostTarget().getName().equalsIgnoreCase(postTargetName))) {
PostTarget fakeEntry = PostTarget
.builder()
.name(postTargetName)
.build();
PostTargetModelEntry postTargetEntry = PostTargetModelEntry
.builder()
.postTarget(fakeEntry)
.disabled(false)
.build();
postTargetEntries.add(postTargetEntry);
}
});
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(POST_TARGET_SHOW_TARGETS, posttargetDisplayModel, commandContext.getChannel()))
log.debug("Displaying existing post targets for guild {}.", guild.getId());
MessageToSend messageToSend = getMessageToSendForPosttargetDisplay(guild);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
String targetName = (String) commandContext.getParameters().getParameters().get(0);
GuildChannel channel = (GuildChannel) commandContext.getParameters().getParameters().get(1);
validateAndCreatePosttarget(guild, targetName, channel);
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
}
private void validateAndCreatePosttarget(Guild guild, String targetName, GuildChannel channel) {
if(!postTargetService.validPostTarget(targetName)) {
throw new PostTargetNotValidException(targetName, postTargetService.getAvailablePostTargets());
}
GuildChannel channel = (GuildChannel) commandContext.getParameters().getParameters().get(1);
if(!channel.getGuild().equals(commandContext.getGuild())) {
if(!channel.getGuild().equals(guild)) {
throw new EntityGuildMismatchException();
}
Guild guild = channel.getGuild();
postTargetManagement.createOrUpdate(targetName, guild.getIdLong(), channel.getIdLong());
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
}
private MessageToSend getMessageToSendForPosttargetDisplay(Guild guild) {
AServer server = serverManagementService.loadServer(guild);
List<PostTarget> postTargets = postTargetService.getPostTargets(server);
ArrayList<PostTargetModelEntry> postTargetEntries = new ArrayList<>();
postTargets.forEach(target -> {
Optional<GuildMessageChannel> channelFromAChannel = channelService.getGuildMessageChannelFromAChannelOptional(target.getChannelReference());
PostTargetModelEntry targetEntry = PostTargetModelEntry
.builder()
.channel(channelFromAChannel.orElse(null))
.disabled(target.getDisabled())
.postTarget(target).build();
postTargetEntries.add(targetEntry);
});
PostTargetDisplayModel posttargetDisplayModel = PostTargetDisplayModel
.builder()
.postTargets(postTargetEntries)
.build();
List<String> postTargetConfigs = postTargetService.getPostTargetsOfEnabledFeatures(server);
postTargetConfigs.forEach(postTargetName -> {
if(postTargetEntries.stream().noneMatch(postTargetModelEntry -> postTargetModelEntry.getPostTarget().getName().equalsIgnoreCase(postTargetName))) {
PostTarget fakeEntry = PostTarget
.builder()
.name(postTargetName)
.build();
PostTargetModelEntry postTargetEntry = PostTargetModelEntry
.builder()
.postTarget(fakeEntry)
.disabled(false)
.build();
postTargetEntries.add(postTargetEntry);
}
});
return templateService.renderEmbedTemplate(POST_TARGET_SHOW_TARGETS, posttargetDisplayModel, guild.getIdLong());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
if(!slashCommandParameterService.hasCommandOption(CHANNEL_PARAMETER, event) && !slashCommandParameterService.hasCommandOption(NAME_PARAMETER, event)) {
MessageToSend messageToSend = getMessageToSendForPosttargetDisplay(event.getGuild());
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
} else {
if(!slashCommandParameterService.hasCommandOption(NAME_PARAMETER, event)) {
throw new SlashCommandParameterMissingException(NAME_PARAMETER);
}
if(!slashCommandParameterService.hasCommandOption(CHANNEL_PARAMETER, event)) {
throw new SlashCommandParameterMissingException(CHANNEL_PARAMETER);
}
String postTargetName = slashCommandParameterService.getCommandOption(NAME_PARAMETER, event, String.class);
GuildChannel channel = slashCommandParameterService.getCommandOption(CHANNEL_PARAMETER, event, TextChannel.class, GuildChannel.class);
validateAndCreatePosttarget(event.getGuild(), postTargetName, channel);
return interactionService.replyEmbed(POSTTARGET_RESPONSE_TEMPLATE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
}
@Override
public CommandConfiguration getConfiguration() {
Parameter postTargetName = Parameter.builder().name("name").type(String.class).optional(true).templated(true).build();
Parameter channel = Parameter.builder().name("channel").type(TextChannel.class).optional(true).templated(true).build();
Parameter postTargetName = Parameter
.builder()
.name(NAME_PARAMETER)
.type(String.class)
.optional(true)
.templated(true)
.build();
Parameter channel = Parameter
.builder()
.name(CHANNEL_PARAMETER)
.type(TextChannel.class)
.optional(true)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(postTargetName, channel);
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.POST_TARGET)
.commandName(POSTTARGET_COMMAND)
.build();
return CommandConfiguration.builder()
.name("posttarget")
.name(POSTTARGET_COMMAND)
.module(ChannelsModuleDefinition.CHANNELS)
.parameters(parameters)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.templated(true)

View File

@@ -1,32 +1,51 @@
package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.exception.SlashCommandParameterMissingException;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class RemoveFromChannelGroup extends AbstractConditionableCommand {
private static final String REMOVE_FROM_CHANNEL_GROUP_COMMAND = "removeFromChannelGroup";
private static final String CHANNEL_PARAMETER = "channel";
private static final String NAME_PARAMETER = "name";
private static final String REMOVE_FROM_CHANNEL_GROUP_RESPONSE = "removeFromChannelGroup_response";
@Autowired
private ChannelGroupService channelGroupService;
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
String name = (String) commandContext.getParameters().getParameters().get(0);
@@ -39,18 +58,57 @@ public class RemoveFromChannelGroup extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String channelGroupName = slashCommandParameterService.getCommandOption(NAME_PARAMETER, event, String.class);
AChannel actualChannel;
if(slashCommandParameterService.hasCommandOptionWithFullType(CHANNEL_PARAMETER, event, OptionType.CHANNEL)) {
GuildMessageChannel guildChannel = slashCommandParameterService.getCommandOption(CHANNEL_PARAMETER, event, AChannel.class, GuildMessageChannel.class);
actualChannel = channelManagementService.loadChannel(guildChannel.getIdLong());
} else if(slashCommandParameterService.hasCommandOptionWithFullType(CHANNEL_PARAMETER, event, OptionType.STRING)) {
String channelId = slashCommandParameterService.getCommandOption(CHANNEL_PARAMETER, event, AChannel.class, String.class);
actualChannel = channelManagementService.loadChannel(Long.parseLong(channelId));
} else {
throw new SlashCommandParameterMissingException(CHANNEL_PARAMETER);
}
channelGroupService.removeChannelFromChannelGroup(channelGroupName, actualChannel);
return interactionService.replyEmbed(REMOVE_FROM_CHANNEL_GROUP_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter channelGroupName = Parameter.builder().name("name").type(String.class).build();
Parameter channelToAdd = Parameter.builder().name("channel").type(AChannel.class).build();
Parameter channelGroupName = Parameter
.builder()
.name(NAME_PARAMETER)
.type(String.class)
.build();
Parameter channelToAdd = Parameter
.builder()
.name(CHANNEL_PARAMETER)
.type(AChannel.class)
.build();
List<Parameter> parameters = Arrays.asList(channelGroupName, channelToAdd);
List<String> aliases = Arrays.asList("rmChChgrp", "chGrpCh-");
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CHANNELS)
.commandName(REMOVE_FROM_CHANNEL_GROUP_COMMAND)
.build();
return CommandConfiguration.builder()
.name("removeFromChannelGroup")
.name(REMOVE_FROM_CHANNEL_GROUP_COMMAND)
.module(ChannelsModuleDefinition.CHANNELS)
.aliases(aliases)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.templated(true)
.help(helpInfo)
.supportsEmbedException(true)

View File

@@ -1,40 +1,71 @@
package dev.sheldan.abstracto.core.commands.config;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.CacheServiceBean;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class ClearCache extends AbstractConditionableCommand {
private static final String CLEAR_CACHE_COMMAND = "clearCache";
private static final String RESPONSE_TEMPLATE = "clearCache_response";
@Autowired
private CacheServiceBean cacheServiceBean;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
cacheServiceBean.clearCaches();
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
cacheServiceBean.clearCaches();
return interactionService.replyEmbed(RESPONSE_TEMPLATE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.INTERNAL)
.commandName(CLEAR_CACHE_COMMAND)
.build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("clearCache")
.name(CLEAR_CACHE_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.templated(true)

View File

@@ -1,17 +1,22 @@
package dev.sheldan.abstracto.core.commands.config;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.service.management.FeatureManagementService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.ConfigurationKeyNotFoundException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.management.DefaultConfigManagementService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -31,6 +36,16 @@ public class ResetConfig extends AbstractConditionableCommand {
@Autowired
private FeatureManagementService featureManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
private static final String RESPONSE_TEMPLATE = "resetConfig_response";
private static final String KEY_PARAMETER = "key";
private static final String RESET_CONFIG_COMMAND = "resetConfig";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
Long serverId = commandContext.getGuild().getIdLong();
@@ -49,16 +64,53 @@ public class ResetConfig extends AbstractConditionableCommand {
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
long serverId = event.getGuild().getIdLong();
if(!slashCommandParameterService.hasCommandOption("key", event)) {
configService.resetConfigForServer(serverId);
} else {
String key = slashCommandParameterService.getCommandOption(KEY_PARAMETER, event, String.class);
if(featureManagementService.featureExists(key)) {
configService.resetConfigForFeature(key, serverId);
} else if(defaultConfigManagementService.configKeyExists(key)) {
configService.resetConfigForKey(key, serverId);
} else {
throw new ConfigurationKeyNotFoundException(key);
}
}
return interactionService.replyEmbed(RESPONSE_TEMPLATE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter keyToChange = Parameter.builder().name("key").type(String.class).optional(true).templated(true).build();
Parameter keyToChange = Parameter
.builder()
.name("key")
.type(String.class)
.optional(true)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(keyToChange);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CONFIG)
.commandName("reset")
.build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("resetConfig")
.name(RESET_CONFIG_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.supportsEmbedException(true)
.help(helpInfo)

View File

@@ -1,26 +1,43 @@
package dev.sheldan.abstracto.core.commands.config;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class SetConfig extends AbstractConditionableCommand {
private static final String KEY_PARAMETER = "key";
private static final String VALUE_PARAMETER = "value";
private static final String SET_CONFIG_COMMAND = "setConfig";
@Autowired
private ConfigService configService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
private static final String RESPONSE_TEMPLATE = "setConfig_response";
@Override
public CommandResult execute(CommandContext commandContext) {
String key = (String) commandContext.getParameters().getParameters().get(0);
@@ -30,16 +47,48 @@ public class SetConfig extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String key = slashCommandParameterService.getCommandOption(KEY_PARAMETER, event, String.class);
String value = slashCommandParameterService.getCommandOption(VALUE_PARAMETER, event, String.class);
configService.setOrCreateConfigValue(key, event.getGuild().getIdLong(), value);
return interactionService.replyEmbed(RESPONSE_TEMPLATE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter keyToChange = Parameter.builder().name("key").type(String.class).templated(true).build();
Parameter valueToSet = Parameter.builder().name("value").type(String.class).templated(true).build();
Parameter keyToChange = Parameter
.builder()
.name(KEY_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter valueToSet = Parameter
.builder()
.name(VALUE_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(keyToChange, valueToSet);
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CONFIG)
.commandName("set")
.build();
return CommandConfiguration.builder()
.name("setConfig")
.name(SET_CONFIG_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.templated(true)
.supportsEmbedException(true)
.help(helpInfo)

View File

@@ -16,7 +16,6 @@ import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.models.database.AFeature;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -37,9 +36,6 @@ public class Allow extends AbstractConditionableCommand {
@Autowired
private CommandService commandService;
@Autowired
private RoleManagementService roleManagementService;
@Autowired
private TemplateService templateService;

View File

@@ -1,26 +1,30 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.condition.CommandCondition;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.template.commands.FeatureSwitchModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -33,6 +37,7 @@ import java.util.stream.Collectors;
@Component
public class DisableFeature extends AbstractConditionableCommand {
public static final String DISABLE_FEATURE_COMMAND = "disableFeature";
@Autowired
private FeatureConfigService featureConfigService;
@@ -47,61 +52,114 @@ public class DisableFeature extends AbstractConditionableCommand {
@Autowired
private ServerManagementService serverManagementService;
private static final String DISABLE_FEATURE_ALL_FEATURES_RESPONSE_TEMPLATE_KEY = "disableFeature_all_features_response";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
private static final String DISABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY = "disableFeature_feature_dependencies_response";
private static final String DISABLE_FEATURE_RESPONSE_TEMPLATE_KEY = "disableFeature_response";
private static final String FEATURE_NAME_PARAMETER = "featureName";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
if (commandContext.getParameters().getParameters().isEmpty()) {
FeatureSwitchModel model = (FeatureSwitchModel) ContextConverter.fromCommandContext(commandContext, FeatureSwitchModel.class);
model.setFeatures(featureConfigService.getAllFeatures());
MessageToSend messageToSend = templateService.renderEmbedTemplate(DISABLE_FEATURE_ALL_FEATURES_RESPONSE_TEMPLATE_KEY, model, commandContext.getGuild().getIdLong());
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(flagKey);
Long serverId = commandContext.getGuild().getIdLong();
List<FeatureConfig> featureDependencies = disableFeature(feature, serverId);
if (featureDependencies.isEmpty()) {
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
} else {
List<String> additionalFeatures = featureDependencies
.stream()
.map(featureDef -> featureDef.getFeature().getKey()).
collect(Collectors.toList());
FeatureSwitchModel model = FeatureSwitchModel
.builder()
.features(additionalFeatures)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(DISABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY,
model, serverId);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(message -> CommandResult.fromIgnored());
} else {
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(flagKey);
featureFlagService.disableFeature(feature, commandContext.getGuild().getIdLong());
List<FeatureConfig> featureDependencies = new ArrayList<>();
if (feature.getDependantFeatures() != null) {
AServer server = serverManagementService.loadServer(commandContext.getGuild());
feature.getDependantFeatures().forEach(featureDisplay -> {
if (featureFlagService.isFeatureEnabled(featureDisplay, server)) {
featureFlagService.disableFeature(featureDisplay, server);
featureDependencies.add(featureDisplay);
}
}
);
}
if (featureDependencies.isEmpty()) {
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
} else {
List<String> additionalFeatures = featureDependencies
.stream()
.map(featureDef -> featureDef.getFeature().getKey()).
collect(Collectors.toList());
FeatureSwitchModel model = (FeatureSwitchModel) ContextConverter.fromCommandContext(commandContext, FeatureSwitchModel.class);
model.setFeatures(additionalFeatures);
MessageToSend messageToSend = templateService.renderEmbedTemplate(DISABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY,
model, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(message -> CommandResult.fromIgnored());
}
}
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String featureName = slashCommandParameterService.getCommandOption(FEATURE_NAME_PARAMETER, event, String.class);
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(featureName);
Long serverId = event.getGuild().getIdLong();
List<FeatureConfig> featureDependencies = disableFeature(feature, serverId);
if (featureDependencies.isEmpty()) {
return interactionService.replyEmbed(DISABLE_FEATURE_RESPONSE_TEMPLATE_KEY, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
} else {
List<String> additionalFeatures = featureDependencies
.stream()
.map(featureDef -> featureDef.getFeature().getKey()).
collect(Collectors.toList());
FeatureSwitchModel model = FeatureSwitchModel
.builder()
.features(additionalFeatures)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(DISABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY,
model, serverId);
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
}
private List<FeatureConfig> disableFeature(FeatureConfig feature, Long serverId) {
featureFlagService.disableFeature(feature, serverId);
List<FeatureConfig> featureDependencies = new ArrayList<>();
if (feature.getDependantFeatures() != null) {
AServer server = serverManagementService.loadServer(serverId);
feature.getDependantFeatures().forEach(featureDisplay -> {
if (featureFlagService.isFeatureEnabled(featureDisplay, server)) {
featureFlagService.disableFeature(featureDisplay, server);
featureDependencies.add(featureDisplay);
}
}
);
}
return featureDependencies;
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("featureName").templated(true).type(String.class).optional(true).build();
Parameter featureName = Parameter
.builder()
.name(FEATURE_NAME_PARAMETER)
.templated(true)
.type(String.class)
.build();
List<Parameter> parameters = Arrays.asList(featureName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.FEATURE)
.commandName("disable")
.build();
return CommandConfiguration.builder()
.name("disableFeature")
.name(DISABLE_FEATURE_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.async(true)
.help(helpInfo)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(true)
.build();

View File

@@ -1,33 +1,34 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.service.management.FeatureManagementService;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.service.management.FeatureModeManagementService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class DisableMode extends AbstractConditionableCommand {
@Autowired
private FeatureManagementService featureManagementService;
@Autowired
private FeatureConfigService featureConfigService;
@@ -35,10 +36,18 @@ public class DisableMode extends AbstractConditionableCommand {
private FeatureModeService featureModeService;
@Autowired
private FeatureModeManagementService featureModeManagementService;
private ServerManagementService serverManagementService;
@Autowired
private ServerManagementService serverManagementService;
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
private static final String DISABLE_MODE_RESPONSE_KEY = "disableMode_response";
private static final String FEATURE_PARAMETER = "feature";
private static final String MODE_PARAMETER = "mode";
private static final String DISABLE_MODE_COMMAND = "disableMode";
@Override
public CommandResult execute(CommandContext commandContext) {
@@ -51,17 +60,53 @@ public class DisableMode extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String featureName = slashCommandParameterService.getCommandOption(FEATURE_PARAMETER, event, String.class);
String modeName = slashCommandParameterService.getCommandOption(MODE_PARAMETER, event, String.class);
FeatureDefinition featureDefinition = featureConfigService.getFeatureEnum(featureName);
FeatureMode featureMode = featureModeService.getFeatureModeForKey(featureName, modeName);
AServer server = serverManagementService.loadServer(event.getGuild());
featureModeService.disableFeatureModeForFeature(featureDefinition, server, featureMode);
return interactionService.replyEmbed(DISABLE_MODE_RESPONSE_KEY, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("feature").type(String.class).templated(true).build();
Parameter mode = Parameter.builder().name("mode").type(String.class).templated(true).build();
Parameter featureName = Parameter
.builder()
.name(FEATURE_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter mode = Parameter
.builder()
.name(MODE_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(featureName, mode);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.FEATURE)
.commandName(DISABLE_MODE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("disableMode")
.name(DISABLE_MODE_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.causesReaction(true)

View File

@@ -1,28 +1,32 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.condition.CommandCondition;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.FeatureValidationResult;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.template.commands.FeatureSwitchModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -51,65 +55,119 @@ public class EnableFeature extends AbstractConditionableCommand {
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
private static final String ENABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY = "enableFeature_feature_dependencies_response";
private static final String ENABLE_FEATURE_ALL_FEATURES_RESPONSE = "enableFeature_all_features_response";
private static final String ENABLE_FEATURE_RESPONSE_TEMPLATE_KEY = "enableFeature_response";
private static final String FEATURE_NAME_PARAMETER = "featureName";
private static final String ENABLE_FEATURE_COMMAND = "enableFeature";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
if(commandContext.getParameters().getParameters().isEmpty()) {
FeatureSwitchModel model = (FeatureSwitchModel) ContextConverter.fromCommandContext(commandContext, FeatureSwitchModel.class);
model.setFeatures(featureConfigService.getAllFeatures());
MessageToSend messageToSend = templateService.renderEmbedTemplate(ENABLE_FEATURE_ALL_FEATURES_RESPONSE, model, commandContext.getGuild().getIdLong());
Long serverId = commandContext.getGuild().getIdLong();
String featureKey = (String) commandContext.getParameters().getParameters().get(0);
EnableFeatureResult result = enableFeature(serverId, featureKey);
if(result.featureDependencies.isEmpty()) {
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
} else {
List<String> additionalFeatures = result.featureDependencies
.stream()
.map(featureDef -> featureDef.getFeature().getKey()).
collect(Collectors.toList());
FeatureSwitchModel model = FeatureSwitchModel
.builder()
.features(additionalFeatures)
.validationText(result.validationResult.getValidationText())
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(ENABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY,
model, serverId);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(message -> CommandResult.fromIgnored());
} else {
String featureKey = (String) commandContext.getParameters().getParameters().get(0);
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(featureKey);
AServer server = serverManagementService.loadServer(commandContext.getUserInitiatedContext().getGuild().getIdLong());
FeatureValidationResult featureSetup = featureConfigService.validateFeatureSetup(feature, server);
if(Boolean.FALSE.equals(featureSetup.getValidationResult())) {
log.info("Feature {} has failed the setup validation. Notifying user.", featureKey);
channelService.sendTextToChannelNotAsync(templateService.renderTemplatable(featureSetup, commandContext.getGuild().getIdLong()),
commandContext.getChannel());
}
featureFlagService.enableFeature(feature, server);
List<FeatureConfig> featureDependencies = new ArrayList<>();
if(feature.getRequiredFeatures() != null) {
feature.getRequiredFeatures().forEach(featureDisplay -> {
log.info("Also enabling required feature {}.", featureDisplay.getFeature().getKey());
if(!featureFlagService.isFeatureEnabled(featureDisplay, server)) {
featureFlagService.enableFeature(featureDisplay, server);
featureDependencies.add(featureDisplay);
}
});
}
if(featureDependencies.isEmpty()) {
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
} else {
List<String> additionalFeatures = featureDependencies
.stream()
.map(featureDef -> featureDef.getFeature().getKey()).
collect(Collectors.toList());
FeatureSwitchModel model = (FeatureSwitchModel) ContextConverter.fromCommandContext(commandContext, FeatureSwitchModel.class);
model.setFeatures(additionalFeatures);
MessageToSend messageToSend = templateService.renderEmbedTemplate(ENABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY,
model, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(message -> CommandResult.fromIgnored());
}
}
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String featureName = slashCommandParameterService.getCommandOption(FEATURE_NAME_PARAMETER, event, String.class);
EnableFeatureResult enableFeatureResult = enableFeature(event.getGuild().getIdLong(), featureName);
if(enableFeatureResult.featureDependencies.isEmpty()) {
return interactionService.replyEmbed(ENABLE_FEATURE_RESPONSE_TEMPLATE_KEY, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
} else {
List<String> additionalFeatures = enableFeatureResult.featureDependencies
.stream()
.map(featureDef -> featureDef.getFeature().getKey()).
collect(Collectors.toList());
FeatureSwitchModel model = FeatureSwitchModel
.builder()
.validationText(enableFeatureResult.validationResult.getValidationText())
.features(additionalFeatures)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(ENABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY,
model, event.getGuild().getIdLong());
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
}
private EnableFeatureResult enableFeature(Long serverId, String featureKey) {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(featureKey);
AServer server = serverManagementService.loadServer(serverId);
FeatureValidationResult validationResult = featureConfigService.validateFeatureSetup(feature, server);
if(Boolean.FALSE.equals(validationResult.getValidationResult())) {
log.info("Feature {} has failed the setup validation. Notifying user.", featureKey);
validationResult.setValidationText(templateService.renderTemplatable(validationResult, serverId));
}
featureFlagService.enableFeature(feature, server);
List<FeatureConfig> featureDependencies = new ArrayList<>();
if(feature.getRequiredFeatures() != null) {
feature.getRequiredFeatures().forEach(featureDisplay -> {
log.info("Also enabling required feature {}.", featureDisplay.getFeature().getKey());
if(!featureFlagService.isFeatureEnabled(featureDisplay, server)) {
featureFlagService.enableFeature(featureDisplay, server);
featureDependencies.add(featureDisplay);
}
});
}
EnableFeatureResult result = new EnableFeatureResult();
result.featureDependencies = featureDependencies;
result.validationResult = validationResult;
return result;
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("featureName").type(String.class).optional(true).templated(true).build();
Parameter featureName = Parameter
.builder()
.name(FEATURE_NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(featureName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.FEATURE)
.commandName("enable")
.build();
return CommandConfiguration.builder()
.name("enableFeature")
.name(ENABLE_FEATURE_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.templated(true)
.help(helpInfo)
@@ -128,4 +186,9 @@ public class EnableFeature extends AbstractConditionableCommand {
conditions.remove(featureEnabledCondition);
return conditions;
}
private static class EnableFeatureResult {
private FeatureValidationResult validationResult;
private List<FeatureConfig> featureDependencies;
}
}

View File

@@ -1,33 +1,34 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.service.management.FeatureManagementService;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.service.management.FeatureModeManagementService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class EnableMode extends AbstractConditionableCommand {
@Autowired
private FeatureManagementService featureManagementService;
@Autowired
private FeatureConfigService featureConfigService;
@@ -35,11 +36,19 @@ public class EnableMode extends AbstractConditionableCommand {
private FeatureModeService featureModeService;
@Autowired
private FeatureModeManagementService featureModeManagementService;
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private ServerManagementService serverManagementService;
private static final String ENABLE_MODE_RESPONSE_KEY = "enableMode_response";
private static final String FEATURE_PARAMETER = "feature";
private static final String MODE_PARAMETER = "mode";
private static final String ENABLE_MODE_COMMAND = "enableMode";
@Override
public CommandResult execute(CommandContext commandContext) {
String featureName = (String) commandContext.getParameters().getParameters().get(0);
@@ -51,17 +60,51 @@ public class EnableMode extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String featureName = slashCommandParameterService.getCommandOption(FEATURE_PARAMETER, event, String.class);
String modeName = slashCommandParameterService.getCommandOption(MODE_PARAMETER, event, String.class);
FeatureDefinition featureDefinition = featureConfigService.getFeatureEnum(featureName);
FeatureMode featureMode = featureModeService.getFeatureModeForKey(featureName, modeName);
AServer server = serverManagementService.loadServer(event.getGuild().getIdLong());
featureModeService.enableFeatureModeForFeature(featureDefinition, server, featureMode);
return interactionService.replyEmbed(ENABLE_MODE_RESPONSE_KEY, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("feature").type(String.class).templated(true).build();
Parameter mode = Parameter.builder().name("mode").type(String.class).templated(true).build();
Parameter featureName = Parameter
.builder()
.name(FEATURE_PARAMETER)
.type(String.class)
.templated(true).build();
Parameter mode = Parameter
.builder().name(MODE_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(featureName, mode);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.FEATURE)
.commandName(ENABLE_MODE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("enableMode")
.name(ENABLE_MODE_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.causesReaction(true)

View File

@@ -1,15 +1,18 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.service.management.FeatureManagementService;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AFeature;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.template.commands.FeatureModeDisplay;
@@ -17,8 +20,10 @@ import dev.sheldan.abstracto.core.models.template.commands.FeatureModesModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -30,6 +35,8 @@ import java.util.concurrent.CompletableFuture;
public class FeatureModes extends AbstractConditionableCommand {
public static final String FEATURE_MODES_RESPONSE_TEMPLATE_KEY = "feature_modes_response";
private static final String FEATURE_PARAMETER = "feature";
private static final String FEATURE_MODES_COMMAND = "featureModes";
@Autowired
private FeatureConfigService featureConfigService;
@@ -45,6 +52,12 @@ public class FeatureModes extends AbstractConditionableCommand {
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private InteractionService interactionService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<FeatureModeDisplay> featureModes;
@@ -62,17 +75,55 @@ public class FeatureModes extends AbstractConditionableCommand {
.thenApply(aVoid -> CommandResult.fromIgnored());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
List<FeatureModeDisplay> featureModes;
AServer server = serverManagementService.loadServer(event.getGuild());
if(!slashCommandParameterService.hasCommandOption(FEATURE_PARAMETER, event)) {
featureModes = featureModeService.getEffectiveFeatureModes(server);
} else {
String featureName = slashCommandParameterService.getCommandOption(FEATURE_PARAMETER, event, String.class);
FeatureDefinition featureDefinition = featureConfigService.getFeatureEnum(featureName);
AFeature feature = featureManagementService.getFeature(featureDefinition.getKey());
featureModes = featureModeService.getEffectiveFeatureModes(server, feature);
}
FeatureModesModel model = FeatureModesModel
.builder()
.featureModes(featureModes)
.build();
return interactionService.replyEmbed(FEATURE_MODES_RESPONSE_TEMPLATE_KEY, model, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("feature").type(String.class).optional(true).templated(true).build();
Parameter featureName = Parameter
.builder()
.name(FEATURE_PARAMETER)
.type(String.class)
.optional(true)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(featureName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.FEATURE)
.commandName(FEATURE_MODES_COMMAND)
.build();
return CommandConfiguration.builder()
.name("featureModes")
.name(FEATURE_MODES_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.templated(true)
.supportsEmbedException(true)
.slashCommandConfig(slashCommandConfig)
.help(helpInfo)
.async(true)
.causesReaction(true)

View File

@@ -1,15 +1,17 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.converter.FeatureFlagConverter;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AFeatureFlag;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.property.FeatureFlagProperty;
@@ -21,6 +23,7 @@ import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -32,6 +35,7 @@ import java.util.stream.Collectors;
@Component
public class Features extends AbstractConditionableCommand {
private static final String FEATURES_COMMAND = "features";
@Autowired
private FeatureFlagManagementService featureFlagManagementService;
@@ -50,14 +54,28 @@ public class Features extends AbstractConditionableCommand {
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
AServer server = serverManagementService.loadServer(commandContext.getGuild());
MessageToSend messageToSend = getMessageToSend(commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
MessageToSend messageToSend = getMessageToSend(event.getGuild().getIdLong());
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
private MessageToSend getMessageToSend(Long serverId) {
AServer server = serverManagementService.loadServer(serverId);
List<AFeatureFlag> features = featureFlagManagementService.getFeatureFlagsOfServer(server);
List<FeatureFlagProperty> defaultFeatureFlagProperties = defaultFeatureFlagManagementService.getAllDefaultFeatureFlags();
FeaturesModel featuresModel = (FeaturesModel) ContextConverter.fromCommandContext(commandContext, FeaturesModel.class);
features.sort(Comparator.comparing(o -> o.getFeature().getKey()));
featuresModel.setFeatures(featureFlagConverter.fromFeatureFlags(features));
defaultFeatureFlagProperties = defaultFeatureFlagProperties
.stream()
.filter(featureFlagProperty ->
@@ -68,20 +86,34 @@ public class Features extends AbstractConditionableCommand {
.equals(featureFlagProperty.getFeatureName())))
.collect(Collectors.toList());
defaultFeatureFlagProperties.sort(Comparator.comparing(FeatureFlagProperty::getFeatureName));
featuresModel.setDefaultFeatures(featureFlagConverter.fromFeatureFlagProperties(defaultFeatureFlagProperties));
MessageToSend messageToSend = templateService.renderEmbedTemplate("features_response", featuresModel, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
FeaturesModel featuresModel = FeaturesModel
.builder()
.features(featureFlagConverter.fromFeatureFlags(features))
.defaultFeatures(featureFlagConverter.fromFeatureFlagProperties(defaultFeatureFlagProperties))
.build();
return templateService.renderEmbedTemplate("features_response", featuresModel, serverId);
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.FEATURE)
.commandName("list")
.build();
return CommandConfiguration.builder()
.name("features")
.name(FEATURES_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.causesReaction(true)

View File

@@ -1,57 +1,112 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.service.RoleImmunityService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class MakeAffected extends AbstractConditionableCommand {
private static final String EFFECT_PARAMETER = "effect";
private static final String ROLE_PARAMETER = "role";
private static final String MAKE_AFFECTED_RESPONSE = "makeAffected_response";
private static final String MAKE_AFFECTED_COMMAND_NAME = "makeAffected";
@Autowired
private RoleManagementService roleManagementService;
@Autowired
private RoleImmunityService roleImmunityService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
String name = (String) commandContext.getParameters().getParameters().get(0);
ARole role = (ARole) commandContext.getParameters().getParameters().get(1);
ARole actualRole = roleManagementService.findRole(role.getId());
if(!actualRole.getServer().getId().equals(commandContext.getGuild().getIdLong())) {
Role role = (Role) commandContext.getParameters().getParameters().get(1);
if(!role.getGuild().getId().equals(commandContext.getGuild().getId())) {
throw new EntityGuildMismatchException();
}
roleImmunityService.makeRoleAffected(actualRole, name);
ARole aRole = roleManagementService.findRole(role.getIdLong());
roleImmunityService.makeRoleAffected(aRole, name);
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String name = slashCommandParameterService.getCommandOption(EFFECT_PARAMETER, event, String.class);
Role role = slashCommandParameterService.getCommandOption(ROLE_PARAMETER, event, Role.class);
if(!role.getGuild().getId().equals(event.getGuild().getId())) {
throw new EntityGuildMismatchException();
}
ARole aRole = roleManagementService.findRole(role.getIdLong());
roleImmunityService.makeRoleAffected(aRole, name);
return interactionService.replyEmbed(MAKE_AFFECTED_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("effect").type(String.class).templated(true).build();
Parameter role = Parameter.builder().name("role").type(ARole.class).templated(true).build();
Parameter featureName = Parameter
.builder()
.name(EFFECT_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter role = Parameter
.builder()
.name(ROLE_PARAMETER)
.type(Role.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(featureName, role);
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CONFIG)
.commandName(MAKE_AFFECTED_COMMAND_NAME)
.build();
return CommandConfiguration.builder()
.name("makeAffected")
.name(MAKE_AFFECTED_COMMAND_NAME)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.supportsEmbedException(true)
.help(helpInfo)
.slashCommandConfig(slashCommandConfig)
.templated(true)
.causesReaction(true)
.build();

View File

@@ -1,29 +1,46 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.RoleImmunityService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class MakeImmune extends AbstractConditionableCommand {
private static final String ROLE_PARAMETER = "role";
private static final String EFFECT_PARAMETER = "effect";
private static final String MAKE_IMMUNE_COMMAND = "makeImmune";
private static final String MAKE_IMMUNE_RESPONSE = "makeImmune_response";
@Autowired
private RoleImmunityService roleImmunityService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
String name = (String) commandContext.getParameters().getParameters().get(0);
@@ -35,18 +52,54 @@ public class MakeImmune extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String name = slashCommandParameterService.getCommandOption(EFFECT_PARAMETER, event, String.class);
Role role = slashCommandParameterService.getCommandOption(ROLE_PARAMETER, event, Role.class);
if(!role.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
roleImmunityService.makeRoleImmune(role, name);
return interactionService.replyEmbed(MAKE_IMMUNE_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("effect").type(String.class).templated(true).build();
Parameter role = Parameter.builder().name("role").type(Role.class).templated(true).build();
Parameter featureName = Parameter
.builder()
.name(EFFECT_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter role = Parameter
.builder()
.name(ROLE_PARAMETER)
.type(Role.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(featureName, role);
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CONFIG)
.commandName(MAKE_IMMUNE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("makeImmune")
.name(MAKE_IMMUNE_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.templated(true)
.supportsEmbedException(true)
.slashCommandConfig(slashCommandConfig)
.help(helpInfo)
.causesReaction(true)
.build();

View File

@@ -1,18 +1,22 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.EffectType;
import dev.sheldan.abstracto.core.models.template.commands.ShowEffectsModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.EffectTypeManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -23,33 +27,62 @@ import java.util.stream.Collectors;
@Component
public class ShowEffects extends AbstractConditionableCommand {
private static final String SHOW_EFFECTS_COMMAND = "showEffects";
private static final String SHOW_EFFECTS_RESPONSE_TEMPLATE = "showEffects_response";
@Autowired
private EffectTypeManagementService effectTypeManagementService;
@Autowired
private ChannelService channelService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<EffectType> allEffects = effectTypeManagementService.getAllEffects();
List<String> effectKeys = allEffects.stream().map(EffectType::getEffectTypeKey).collect(Collectors.toList());
ShowEffectsModel model = ShowEffectsModel
.builder()
.effects(effectKeys)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList("showEffects_response",
ShowEffectsModel model = getModel();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(SHOW_EFFECTS_RESPONSE_TEMPLATE,
model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
ShowEffectsModel model = getModel();
return interactionService.replyEmbed(SHOW_EFFECTS_RESPONSE_TEMPLATE, model, event)
.thenApply(unused -> CommandResult.fromSuccess());
}
private ShowEffectsModel getModel() {
List<EffectType> allEffects = effectTypeManagementService.getAllEffects();
List<String> effectKeys = allEffects.stream().map(EffectType::getEffectTypeKey).collect(Collectors.toList());
return ShowEffectsModel
.builder()
.effects(effectKeys)
.build();
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CONFIG)
.commandName(SHOW_EFFECTS_COMMAND)
.build();
return CommandConfiguration.builder()
.name("showEffects")
.name(SHOW_EFFECTS_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.help(helpInfo)
.build();
}

View File

@@ -1,27 +1,46 @@
package dev.sheldan.abstracto.core.commands.config.profanity;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ProfanityService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class AddProfanityRegex extends AbstractConditionableCommand {
private static final String PROFANITY_GROUP_PARAMETER = "profanityGroup";
private static final String PROFANITY_NAME_PARAMETER = "profanityName";
private static final String REGEX_PARAMETER = "regex";
private static final String REPLACEMENT_PARAMETER = "replacement";
private static final String ADD_PROFANITY_REGEX_RESPONSE = "addProfanityRegex_response";
private static final String ADD_PROFANITY_REGEX_COMMAND = "addProfanityRegex";
@Autowired
private ProfanityService profanityService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
@@ -38,19 +57,68 @@ public class AddProfanityRegex extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String profanityGroup = slashCommandParameterService.getCommandOption(PROFANITY_GROUP_PARAMETER, event, String.class);
String profanityName = slashCommandParameterService.getCommandOption(PROFANITY_NAME_PARAMETER, event, String.class);
String regex = slashCommandParameterService.getCommandOption(REGEX_PARAMETER, event, String.class);
Long serverId = event.getGuild().getIdLong();
if(slashCommandParameterService.hasCommandOption(REPLACEMENT_PARAMETER, event)) {
String replacement = slashCommandParameterService.getCommandOption(REPLACEMENT_PARAMETER, event, String.class);
profanityService.createProfanityRegex(serverId, profanityGroup, profanityName, regex, replacement);
} else {
profanityService.createProfanityRegex(serverId, profanityGroup, profanityName, regex);
}
return interactionService.replyEmbed(ADD_PROFANITY_REGEX_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter profanityGroupParameter = Parameter.builder().name("profanityGroup").type(String.class).templated(true).build();
Parameter nameParameter = Parameter.builder().name("profanityName").type(String.class).templated(true).build();
Parameter regexParameter = Parameter.builder().name("regex").type(String.class).templated(true).build();
Parameter replacement = Parameter.builder().name("replacement").type(String.class).optional(true).templated(true).build();
Parameter profanityGroupParameter = Parameter
.builder()
.name(PROFANITY_GROUP_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter nameParameter = Parameter
.builder()
.name(PROFANITY_NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter regexParameter = Parameter
.builder()
.name(REGEX_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter replacement = Parameter
.builder()
.name(REPLACEMENT_PARAMETER)
.type(String.class)
.optional(true)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(profanityGroupParameter, nameParameter, regexParameter, replacement);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.PROFANITY)
.commandName(ADD_PROFANITY_REGEX_COMMAND)
.build();
return CommandConfiguration.builder()
.name("addProfanityRegex")
.name(ADD_PROFANITY_REGEX_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.causesReaction(true)

View File

@@ -1,27 +1,43 @@
package dev.sheldan.abstracto.core.commands.config.profanity;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ProfanityService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class CreateProfanityGroup extends AbstractConditionableCommand {
private static final String PROFANITY_GROUP_NAME_PARAMETER = "profanityGroupName";
private static final String CREATE_PROFANITY_GROUP_RESPONSE = "createProfanityGroup_response";
private static final String CREATE_PROFANITY_GROUP_COMMAND = "createProfanityGroup";
@Autowired
private ProfanityService profanityService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
String profanityGroupName = (String) commandContext.getParameters().getParameters().get(0);
@@ -29,16 +45,40 @@ public class CreateProfanityGroup extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String profanityGroup = slashCommandParameterService.getCommandOption(PROFANITY_GROUP_NAME_PARAMETER, event, String.class);
profanityService.createProfanityGroup(event.getGuild().getIdLong(), profanityGroup);
return interactionService.replyEmbed(CREATE_PROFANITY_GROUP_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter profanityGroupName = Parameter.builder().name("profanityGroupName").type(String.class).templated(true).build();
Parameter profanityGroupName = Parameter
.builder()
.name(PROFANITY_GROUP_NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(profanityGroupName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.PROFANITY)
.commandName(CREATE_PROFANITY_GROUP_COMMAND)
.build();
return CommandConfiguration.builder()
.name("createProfanityGroup")
.name(CREATE_PROFANITY_GROUP_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.causesReaction(true)

View File

@@ -1,27 +1,43 @@
package dev.sheldan.abstracto.core.commands.config.profanity;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ProfanityService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class DeleteProfanityGroup extends AbstractConditionableCommand {
private static final String PROFANITY_GROUP_NAME_PARAMETER = "profanityGroupName";
private static final String DELETE_PROFANITY_GROUP_COMMAND = "deleteProfanityGroup";
private static final String DELETE_PROFANITY_GROUP_RESPONSE = "deleteProfanityGroup_response";
@Autowired
private ProfanityService profanityService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
String profanityGroupName = (String) commandContext.getParameters().getParameters().get(0);
@@ -29,15 +45,40 @@ public class DeleteProfanityGroup extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String profanityGroup = slashCommandParameterService.getCommandOption(PROFANITY_GROUP_NAME_PARAMETER, event, String.class);
profanityService.deleteProfanityGroup(event.getGuild().getIdLong(), profanityGroup);
return interactionService.replyEmbed(DELETE_PROFANITY_GROUP_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter profanityGroupParameter = Parameter.builder().name("profanityGroupName").type(String.class).templated(true).build();
Parameter profanityGroupParameter = Parameter
.builder()
.name(PROFANITY_GROUP_NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(profanityGroupParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.PROFANITY)
.commandName(DELETE_PROFANITY_GROUP_COMMAND)
.build();
return CommandConfiguration.builder()
.name("deleteProfanityGroup")
.name(DELETE_PROFANITY_GROUP_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.templated(true)
.supportsEmbedException(true)
.help(helpInfo)

View File

@@ -1,27 +1,44 @@
package dev.sheldan.abstracto.core.commands.config.profanity;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ProfanityService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class RemoveProfanityRegex extends AbstractConditionableCommand {
private static final String REMOVE_PROFANITY_REGEX_COMMAND = "removeProfanityRegex";
private static final String PROFANITY_NAME_PARAMETER = "profanityName";
private static final String PROFANITY_GROUP_NAME_PARAMETER = "profanityGroupName";
private static final String REMOVE_PROFANITY_REGEX_RESPONSE = "removeProfanityRegex_response";
@Autowired
private ProfanityService profanityService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
@@ -31,17 +48,47 @@ public class RemoveProfanityRegex extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String profanityGroup = slashCommandParameterService.getCommandOption(PROFANITY_GROUP_NAME_PARAMETER, event, String.class);
profanityService.deleteProfanityGroup(event.getGuild().getIdLong(), profanityGroup);
return interactionService.replyEmbed(REMOVE_PROFANITY_REGEX_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter profanityGroupParameter = Parameter.builder().name("profanityGroupName").type(String.class).templated(true).build();
Parameter profanityNameParameter = Parameter.builder().name("profanityName").type(String.class).templated(true).build();
Parameter profanityGroupParameter = Parameter
.builder()
.name(PROFANITY_GROUP_NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter profanityNameParameter = Parameter
.builder()
.name(PROFANITY_NAME_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(profanityGroupParameter, profanityNameParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.PROFANITY)
.commandName(REMOVE_PROFANITY_REGEX_COMMAND)
.build();
return CommandConfiguration.builder()
.name("removeProfanityRegex")
.name(REMOVE_PROFANITY_REGEX_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.causesReaction(true)

View File

@@ -1,14 +1,16 @@
package dev.sheldan.abstracto.core.commands.config.profanity;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.ProfanityGroup;
import dev.sheldan.abstracto.core.models.template.commands.ProfanityConfigModel;
import dev.sheldan.abstracto.core.service.ChannelService;
@@ -16,6 +18,7 @@ import dev.sheldan.abstracto.core.service.management.ProfanityGroupManagementSer
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -26,6 +29,7 @@ import java.util.concurrent.CompletableFuture;
public class ShowProfanityConfig extends AbstractConditionableCommand {
public static final String SHOW_PROFANITY_CONFIG_RESPONSE_TEMPLATE_KEY = "showProfanityConfig_response";
public static final String SHOW_PROFANITY_CONFIG_COMMAND = "showProfanityConfig";
@Autowired
private ProfanityGroupManagementService profanityGroupManagementService;
@@ -35,25 +39,55 @@ public class ShowProfanityConfig extends AbstractConditionableCommand {
@Autowired
private ChannelService channelService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
ProfanityConfigModel model = (ProfanityConfigModel) ContextConverter.slimFromCommandContext(commandContext, ProfanityConfigModel.class);
Long serverId = commandContext.getGuild().getIdLong();
List<ProfanityGroup> groups = profanityGroupManagementService.getAllForServer(serverId);
model.setProfanityGroups(groups);
MessageToSend message = templateService.renderEmbedTemplate(SHOW_PROFANITY_CONFIG_RESPONSE_TEMPLATE_KEY, model, serverId);
MessageToSend message = getMessageToSend(serverId);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(message, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long serverId = event.getGuild().getIdLong();
MessageToSend message = getMessageToSend(serverId);
return interactionService.replyMessageToSend(message, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
private MessageToSend getMessageToSend(Long serverId) {
List<ProfanityGroup> groups = profanityGroupManagementService.getAllForServer(serverId);
ProfanityConfigModel model = ProfanityConfigModel
.builder()
.profanityGroups(groups)
.build();
return templateService.renderEmbedTemplate(SHOW_PROFANITY_CONFIG_RESPONSE_TEMPLATE_KEY, model, serverId);
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.PROFANITY)
.commandName(SHOW_PROFANITY_CONFIG_COMMAND)
.build();
return CommandConfiguration.builder()
.name("showProfanityConfig")
.name(SHOW_PROFANITY_CONFIG_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.help(helpInfo)
.causesReaction(true)

View File

@@ -1,78 +1,165 @@
package dev.sheldan.abstracto.core.commands.config.template;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.exception.CustomTemplateNotFoundException;
import dev.sheldan.abstracto.core.exception.UploadFileTooLargeException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.template.commands.GetCustomTemplateModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.model.database.CustomTemplate;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.management.CustomTemplateManagementService;
import dev.sheldan.abstracto.core.utils.FileService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class GetCustomTemplate extends AbstractConditionableCommand {
private static final String GET_CUSTOM_TEMPLATE_COMMAND = "getCustomTemplate";
private static final String TEMPLATE_KEY_PARAMETER = "templateKey";
private static final String GET_CUSTOM_TEMPLATE_RESPONSE_TEMPLATE_KEY = "getCustomTemplate_response";
@Autowired
private CustomTemplateManagementService customTemplateManagementService;
@Autowired
private ChannelService channelService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private FileService fileService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
private static final String GET_CUSTOM_TEMPLATE_FILE_NAME_TEMPLATE_KEY = "getCustomTemplate_file_name";
private static final String GET_CUSTOM_TEMPLATE_RESPONSE_TEMPLATE_KEY = "getCustomTemplate_response";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
String templateKey = (String) commandContext.getParameters().getParameters().get(0);
Optional<CustomTemplate> templateOptional = customTemplateManagementService.getCustomTemplate(templateKey, commandContext.getGuild().getIdLong());
if(templateOptional.isPresent()) {
CustomTemplate template = templateOptional.get();
GetCustomTemplateModel model = (GetCustomTemplateModel) ContextConverter.slimFromCommandContext(commandContext, GetCustomTemplateModel.class);
model.setCreated(template.getCreated());
model.setLastModified(template.getLastModified());
model.setTemplateContent(template.getContent());
model.setTemplateKey(templateKey);
return FutureUtils.toSingleFutureGeneric(channelService.sendFileToChannel(template.getContent(),
GET_CUSTOM_TEMPLATE_FILE_NAME_TEMPLATE_KEY, GET_CUSTOM_TEMPLATE_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
GetCustomTemplateModel model = getModel(templateKey, commandContext.getGuild());
File tempFile = fileService.createTempFile(templateKey + ".ftl");
try {
fileService.writeContentToFile(tempFile, model.getTemplateContent());
long maxFileSize = commandContext.getGuild().getIdLong();
// in this case, we cannot upload the file, so we need to fail
if(tempFile.length() > maxFileSize) {
throw new UploadFileTooLargeException(tempFile.length(), maxFileSize);
}
MessageToSend messageToSend = templateService.renderEmbedTemplate(GET_CUSTOM_TEMPLATE_RESPONSE_TEMPLATE_KEY, model, commandContext.getGuild().getIdLong());
messageToSend.getAttachedFiles().get(0).setFile(tempFile);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannelList(GET_CUSTOM_TEMPLATE_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
} catch (IOException e) {
throw new AbstractoRunTimeException(e);
} finally {
try {
fileService.safeDelete(tempFile);
} catch (IOException e) {
log.error("Failed to delete temporary get custom template file {}.", tempFile.getAbsoluteFile(), e);
}
}
}
private GetCustomTemplateModel getModel(String templateKey, Guild guild) {
Optional<CustomTemplate> templateOptional = customTemplateManagementService.getCustomTemplate(templateKey, guild.getIdLong());
return templateOptional.map(customTemplate -> {
CustomTemplate template = templateOptional.get();
return GetCustomTemplateModel
.builder()
.created(template.getCreated())
.lastModified(template.getLastModified())
.templateContent(template.getContent())
.templateKey(templateKey)
.build();
}).orElseThrow(() -> new CustomTemplateNotFoundException(templateKey, guild));
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String templateKey = slashCommandParameterService.getCommandOption(TEMPLATE_KEY_PARAMETER, event, String.class);
GetCustomTemplateModel model = getModel(templateKey, event.getGuild());
File tempFile = fileService.createTempFile(templateKey + ".ftl");
try {
fileService.writeContentToFile(tempFile, model.getTemplateContent());
long maxFileSize = event.getGuild().getMaxFileSize();
// in this case, we cannot upload the file, so we need to fail
if(tempFile.length() > maxFileSize) {
throw new UploadFileTooLargeException(tempFile.length(), maxFileSize);
}
MessageToSend messageToSend = templateService.renderEmbedTemplate(GET_CUSTOM_TEMPLATE_RESPONSE_TEMPLATE_KEY, model, event.getGuild().getIdLong());
messageToSend.getAttachedFiles().get(0).setFile(tempFile);
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
} catch (IOException e) {
throw new AbstractoRunTimeException(e);
} finally {
try {
fileService.safeDelete(tempFile);
} catch (IOException e) {
log.error("Failed to delete temporary get custom template file {}.", tempFile.getAbsoluteFile(), e);
}
}
throw new CustomTemplateNotFoundException(templateKey, commandContext.getGuild());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter templateKeyParameter = Parameter.builder().name("templateKey").type(String.class).templated(true).build();
Parameter templateKeyParameter = Parameter
.builder()
.name(TEMPLATE_KEY_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(templateKeyParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.INTERNAL)
.commandName(GET_CUSTOM_TEMPLATE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("getCustomTemplate")
.name(GET_CUSTOM_TEMPLATE_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.supportsEmbedException(true)
.parameters(parameters)
.async(true)
.slashCommandConfig(slashCommandConfig)
.help(helpInfo)
.templated(true)
.causesReaction(true)

View File

@@ -1,78 +1,145 @@
package dev.sheldan.abstracto.core.commands.config.template;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.exception.TemplateNotFoundException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.template.commands.GetTemplateModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.templating.model.database.Template;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.management.TemplateManagementService;
import dev.sheldan.abstracto.core.utils.FileService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class GetTemplate extends AbstractConditionableCommand {
private static final String GET_TEMPLATE_COMMAND = "getTemplate";
private static final String TEMPLATE_KEY_PARAMETER = "templateKey";
private static final String GET_TEMPLATE_RESPONSE_TEMPLATE_KEY = "getTemplate_response";
@Autowired
private TemplateManagementService templateManagementService;
@Autowired
private FileService fileService;
private ChannelService channelService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
private static final String GET_TEMPLATE_FILE_NAME_TEMPLATE_KEY = "getTemplate_file_name";
private static final String GET_TEMPLATE_RESPONSE_TEMPLATE_KEY = "getTemplate_response";
private FileService fileService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
String templateKey = (String) commandContext.getParameters().getParameters().get(0);
Optional<Template> templateOptional = templateManagementService.getTemplateByKey(templateKey);
if(templateOptional.isPresent()) {
Template template = templateOptional.get();
GetTemplateModel model = (GetTemplateModel) ContextConverter.slimFromCommandContext(commandContext, GetTemplateModel.class);
model.setCreated(template.getCreated());
model.setLastModified(template.getLastModified());
model.setTemplateContent(template.getContent());
model.setTemplateKey(templateKey);
return FutureUtils.toSingleFutureGeneric(channelService.sendFileToChannel(template.getContent(),
GET_TEMPLATE_FILE_NAME_TEMPLATE_KEY, GET_TEMPLATE_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
GetTemplateModel model = getModel(templateKey);
File tempFile = fileService.createTempFile(templateKey + ".ftl");
try {
fileService.writeContentToFile(tempFile, model.getTemplateContent());
MessageToSend messageToSend = templateService.renderEmbedTemplate(GET_TEMPLATE_RESPONSE_TEMPLATE_KEY, model, commandContext.getGuild().getIdLong());
messageToSend.getAttachedFiles().get(0).setFile(tempFile);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
} catch (IOException e) {
throw new AbstractoRunTimeException(e);
} finally {
try {
fileService.safeDelete(tempFile);
} catch (IOException e) {
log.error("Failed to delete temporary get template file {}.", tempFile.getAbsoluteFile(), e);
}
}
throw new TemplateNotFoundException(templateKey);
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String templateKey = slashCommandParameterService.getCommandOption(TEMPLATE_KEY_PARAMETER, event, String.class);
GetTemplateModel model = getModel(templateKey);
File tempFile = fileService.createTempFile(templateKey + ".ftl");
try {
fileService.writeContentToFile(tempFile, model.getTemplateContent());
MessageToSend messageToSend = templateService.renderEmbedTemplate(GET_TEMPLATE_RESPONSE_TEMPLATE_KEY, model, event.getGuild().getIdLong());
messageToSend.getAttachedFiles().get(0).setFile(tempFile);
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
} catch (IOException e) {
throw new AbstractoRunTimeException(e);
} finally {
try {
fileService.safeDelete(tempFile);
} catch (IOException e) {
log.error("Failed to delete temporary get template file {}.", tempFile.getAbsoluteFile(), e);
}
}
}
private GetTemplateModel getModel(String templateKey) {
return templateManagementService.getTemplateByKey(templateKey)
.map(template -> GetTemplateModel
.builder()
.created(template.getCreated())
.lastModified(template.getLastModified())
.templateContent(template.getContent())
.templateKey(templateKey)
.build()).orElseThrow(() -> new TemplateNotFoundException(templateKey));
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter templateKeyParameter = Parameter.builder().name("templateKey").type(String.class).templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
Parameter templateKeyParameter = Parameter
.builder()
.name(TEMPLATE_KEY_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(templateKeyParameter);
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.INTERNAL)
.commandName(GET_TEMPLATE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("getTemplate")
.name(GET_TEMPLATE_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.supportsEmbedException(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -1,34 +1,50 @@
package dev.sheldan.abstracto.core.commands.config.template;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.CustomTemplateNotFoundException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.templating.model.database.CustomTemplate;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.management.CustomTemplateManagementService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
public class ResetTemplate extends AbstractConditionableCommand {
private static final String RESET_TEMPLATE_COMMAND = "resetTemplate";
private static final String TEMPLATE_KEY_PARAMETER = "templateKey";
private static final String RESET_TEMPLATE_RESPONSE_TEMPLATE_KEY = "resetTemplate_response";
@Autowired
private CustomTemplateManagementService customTemplateManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
String templateKey = (String) commandContext.getParameters().getParameters().get(0);
@@ -41,16 +57,47 @@ public class ResetTemplate extends AbstractConditionableCommand {
throw new CustomTemplateNotFoundException(templateKey, commandContext.getGuild());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String templateKey = slashCommandParameterService.getCommandOption(TEMPLATE_KEY_PARAMETER, event, String.class);
Optional<CustomTemplate> templateOptional = customTemplateManagementService.getCustomTemplate(templateKey, event.getGuild().getIdLong());
if (templateOptional.isPresent()) {
customTemplateManagementService.deleteCustomTemplate(templateOptional.get());
templateService.clearCache();
return interactionService.replyEmbed(RESET_TEMPLATE_RESPONSE_TEMPLATE_KEY, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
throw new CustomTemplateNotFoundException(templateKey, event.getGuild());
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter templateKeyParameter = Parameter.builder().name("templateKey").type(String.class).templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
Parameter templateKeyParameter = Parameter
.builder()
.name(TEMPLATE_KEY_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(templateKeyParameter);
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.INTERNAL)
.commandName(RESET_TEMPLATE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("resetTemplate")
.name(RESET_TEMPLATE_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.supportsEmbedException(true)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.help(helpInfo)
.templated(true)
.causesReaction(true)

View File

@@ -1,19 +1,25 @@
package dev.sheldan.abstracto.core.commands.config.template;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.exception.AbstractoTemplatedException;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.management.CustomTemplateManagementService;
import dev.sheldan.abstracto.core.utils.FileService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -23,11 +29,17 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class SetTemplate extends AbstractConditionableCommand {
private static final String TEMPLATE_KEY_PARAMETER = "templateKey";
private static final String FILE_PARAMETER = "file";
private static final String SET_TEMPLATE_COMMAND = "setTemplate";
private static final String SET_TEMPLATE_RESPONSE_TEMPLATE = "setTemplate_response";
@Autowired
private CustomTemplateManagementService customTemplateManagementService;
@@ -37,6 +49,12 @@ public class SetTemplate extends AbstractConditionableCommand {
@Autowired
private TemplateService templateService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameter = commandContext.getParameters().getParameters();
@@ -59,6 +77,37 @@ public class SetTemplate extends AbstractConditionableCommand {
}
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String templateKey = slashCommandParameterService.getCommandOption(TEMPLATE_KEY_PARAMETER, event, String.class);
Message.Attachment templateAttachment = slashCommandParameterService.getCommandOption(FILE_PARAMETER, event, File.class, Message.Attachment.class);
File templateFile = fileService.createTempFile(Math.random() + "");
return templateAttachment.downloadToFile(templateFile).thenCompose(file -> {
try {
return updateTemplate(event, templateFile, templateKey);
} catch (IOException e) {
log.error("IO Exception when loading input file.", e);
throw new AbstractoTemplatedException("Failed to set template.", "failed_to_set_template_exception", e);
} finally {
try {
if(templateFile != null) {
fileService.safeDelete(templateFile);
}
} catch (IOException e) {
log.error("Failed to delete downloaded template file.", e);
}
}
});
}
private CompletableFuture<CommandResult> updateTemplate(SlashCommandInteractionEvent event, File templateFile, String templateKey) throws IOException {
String templateContent = FileUtils.readFileToString(templateFile, StandardCharsets.UTF_8);
customTemplateManagementService.createOrUpdateCustomTemplate(templateKey, templateContent, event.getGuild().getIdLong());
templateService.clearCache();
return interactionService.replyEmbed(SET_TEMPLATE_RESPONSE_TEMPLATE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public FeatureDefinition getFeature() {
return CoreFeatureDefinition.CORE_FEATURE;
@@ -66,15 +115,37 @@ public class SetTemplate extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
Parameter templateKeyParameter = Parameter.builder().name("templateKey").type(String.class).templated(true).build();
Parameter fileAttachmentParameter = Parameter.builder().name("file").type(File.class).templated(true).build();
Parameter templateKeyParameter = Parameter
.builder()
.name(TEMPLATE_KEY_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter fileAttachmentParameter = Parameter
.builder()
.name(FILE_PARAMETER)
.type(File.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(templateKeyParameter, fileAttachmentParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.INTERNAL)
.commandName(SET_TEMPLATE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("setTemplate")
.name(SET_TEMPLATE_COMMAND)
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.supportsEmbedException(true)
.slashCommandConfig(slashCommandConfig)
.help(helpInfo)
.templated(true)
.causesReaction(true)

View File

@@ -1,14 +1,18 @@
package dev.sheldan.abstracto.core.commands.help;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -16,11 +20,14 @@ import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
@Component
public class Documentation implements Command {
public class Documentation extends AbstractConditionableCommand {
@Autowired
private ChannelService channelService;
@Autowired
private InteractionService interactionService;
private static final String DOCUMENTATION_RESPONSE_TEMPLATE_KEY = "documentation_response";
@Override
@@ -29,15 +36,33 @@ public class Documentation implements Command {
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
return interactionService.replyEmbed(DOCUMENTATION_RESPONSE_TEMPLATE_KEY, new Object(), event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.INFO)
.commandName("documentation")
.build();
return CommandConfiguration.builder()
.name("documentation")
.async(true)
.aliases(Arrays.asList("docu", "docs"))
.module(SupportModuleDefinition.SUPPORT)
.help(helpInfo)
.slashCommandConfig(slashCommandConfig)
.templated(true)
.causesReaction(true)
.build();

View File

@@ -1,12 +1,12 @@
package dev.sheldan.abstracto.core.commands.help;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.condition.ConditionResult;
import dev.sheldan.abstracto.core.command.config.*;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.command.model.database.ACommand;
import dev.sheldan.abstracto.core.command.model.database.ACommandInAServer;
import dev.sheldan.abstracto.core.command.service.*;
@@ -38,7 +38,7 @@ import java.util.stream.Collectors;
@Service
@Slf4j
public class Help implements Command {
public class Help extends AbstractConditionableCommand {
@Autowired
private ModuleRegistry moduleService;
@@ -136,9 +136,11 @@ public class Help implements Command {
}));
module.setCommands(filteredCommand);
List<ModuleDefinition> subModules = moduleService.getSubModules(moduleDefinition);
HelpModuleDetailsModel model = (HelpModuleDetailsModel) ContextConverter.fromCommandContext(commandContext, HelpModuleDetailsModel.class);
model.setModule(module);
model.setSubModules(subModules);
HelpModuleDetailsModel model = HelpModuleDetailsModel
.builder()
.subModules(subModules)
.module(module)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate("help_module_details_response", model, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
@@ -157,10 +159,14 @@ public class Help implements Command {
ACommand aCommand = commandManagementService.findCommandByName(command.getConfiguration().getName());
List<String> aliases = commandInServerAliasService.getAliasesForCommand(commandContext.getGuild().getIdLong(), command.getConfiguration().getName());
ACommandInAServer aCommandInAServer = commandInServerManagementService.getCommandForServer(aCommand, commandContext.getGuild().getIdLong());
HelpCommandDetailsModel model = (HelpCommandDetailsModel) ContextConverter.fromCommandContext(commandContext, HelpCommandDetailsModel.class);
model.setServerSpecificAliases(aliases);
CommandCoolDownConfig coolDownConfig = getCoolDownConfig(command, contextIds);
model.setCooldowns(coolDownConfig);
HelpCommandDetailsModel model = HelpCommandDetailsModel
.builder()
.serverSpecificAliases(aliases)
.cooldowns(coolDownConfig)
.usage(commandService.generateUsage(command))
.command(command.getConfiguration())
.build();
if(Boolean.TRUE.equals(aCommandInAServer.getRestricted())) {
model.setAllowedRoles(roleService.getRolesFromGuild(aCommandInAServer.getAllowedRoles()));
model.setRestricted(true);
@@ -174,8 +180,6 @@ public class Help implements Command {
if(!effects.isEmpty()) {
model.setEffects(effects);
}
model.setUsage(commandService.generateUsage(command));
model.setCommand(command.getConfiguration());
MessageToSend messageToSend = templateService.renderEmbedTemplate("help_command_details_response", model, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
@@ -209,8 +213,10 @@ public class Help implements Command {
log.debug("Displaying help overview response.");
ModuleDefinition moduleDefinition = moduleService.getDefaultModule();
List<ModuleDefinition> subModules = moduleService.getSubModules(moduleDefinition);
HelpModuleOverviewModel model = (HelpModuleOverviewModel) ContextConverter.fromCommandContext(commandContext, HelpModuleOverviewModel.class);
model.setModules(subModules);
HelpModuleOverviewModel model = HelpModuleOverviewModel
.builder()
.modules(subModules)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate("help_module_overview_response", model, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());

View File

@@ -5,23 +5,28 @@ 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.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.template.commands.EchoModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Service
public class Echo extends AbstractConditionableCommand {
private static final String TEMPLATE_NAME = "echo_response";
public static final String ECHO_COMMAND = "echo";
@Autowired
private TemplateService templateService;
@@ -29,29 +34,57 @@ public class Echo extends AbstractConditionableCommand {
@Autowired
private ChannelService channelService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
StringBuilder sb = new StringBuilder();
commandContext.getParameters().getParameters().forEach(o ->
sb.append(o.toString())
);
EchoModel model = EchoModel.builder().text(sb.toString()).build();
EchoModel model = EchoModel
.builder()
.text(sb.toString())
.build();
String textToSend = templateService.renderTemplate(TEMPLATE_NAME, model, commandContext.getGuild().getIdLong());
channelService.sendTextToChannel(textToSend, commandContext.getChannel());
return CommandResult.fromIgnored();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
EchoModel model = EchoModel
.builder()
.text(event.getOption("input").getAsString())
.build();
return interactionService.replyMessage(TEMPLATE_NAME, model, event)
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("input").type(String.class).templated(true).remainder(true).build());
parameters.add(Parameter
.builder()
.name("input")
.type(String.class)
.templated(true)
.remainder(true)
.build());
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ECHO_COMMAND)
.build();
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("echo")
.name(ECHO_COMMAND)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.supportsEmbedException(true)
.causesReaction(false)
.slashCommandConfig(slashCommandConfig)
.parameters(parameters)
.help(helpInfo)
.build();

View File

@@ -1,49 +1,74 @@
package dev.sheldan.abstracto.core.commands.utility;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.template.commands.PingModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MessageService;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class Ping implements Command {
public class Ping extends AbstractConditionableCommand {
public static final String PING_TEMPLATE = "ping_response";
@Autowired
private MessageService messageService;
private static final String PING_COMMAND = "ping";
@Autowired
private ChannelService channelService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
long ping = commandContext.getJda().getGatewayPing();
PingModel model = PingModel.builder().latency(ping).build();
PingModel model = buildModel(commandContext.getJda());
return channelService.sendTextTemplateInTextChannel(PING_TEMPLATE, model, commandContext.getChannel())
.thenApply(message -> CommandResult.fromIgnored());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
PingModel model = buildModel(event.getJDA());
return interactionService.replyMessage(PING_TEMPLATE, model, event)
.thenApply(unused -> CommandResult.fromSuccess());
}
private PingModel buildModel(JDA jda) {
long ping = jda.getGatewayPing();
return PingModel
.builder()
.latency(ping)
.build();
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(PING_COMMAND)
.build();
return CommandConfiguration.builder()
.name("ping")
.name(PING_COMMAND)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.async(true)
.help(helpInfo)
.slashCommandConfig(slashCommandConfig)
.causesReaction(false)
.build();
}

View File

@@ -1,33 +1,41 @@
package dev.sheldan.abstracto.core.commands.utility;
import dev.sheldan.abstracto.core.command.CoreSlashCommandNames;
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.config.ParameterValidator;
import dev.sheldan.abstracto.core.command.config.*;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.config.validator.MaxStringLengthValidator;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class SetEmote extends AbstractConditionableCommand {
private static final String EMOTE_KEY_PARAMETER = "emoteKey";
private static final String EMOTE_PARAMETER = "emote";
private static final String RESPONSE_TEMPLATE = "setEmote_response";
@Autowired
private EmoteManagementService emoteManagementService;
@Autowired
private EmoteService emoteService;
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
@@ -38,19 +46,50 @@ public class SetEmote extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String emoteKey = slashCommandParameterService.getCommandOption(EMOTE_KEY_PARAMETER, event, String.class);
String emote = slashCommandParameterService.getCommandOption(EMOTE_PARAMETER, event, String.class);
AEmote aEmote = slashCommandParameterService.loadAEmoteFromString(emote, event);
emoteManagementService.setEmoteToAEmote(emoteKey, aEmote, event.getGuild().getIdLong());
return interactionService.replyEmbed(RESPONSE_TEMPLATE, new Object(), event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<ParameterValidator> emoteKeyValidators = Arrays.asList(MaxStringLengthValidator.max(255L));
Parameter emoteKey = Parameter.builder().name("emoteKey").validators(emoteKeyValidators).type(String.class).templated(true).build();
Parameter emote = Parameter.builder().name("emote").type(AEmote.class).templated(true).build();
Parameter emoteKey = Parameter
.builder()
.name(EMOTE_KEY_PARAMETER)
.validators(emoteKeyValidators)
.type(String.class)
.templated(true)
.build();
Parameter emote = Parameter
.builder()
.name(EMOTE_PARAMETER)
.type(AEmote.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(emoteKey, emote);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(CoreSlashCommandNames.CONFIG)
.commandName("setEmote")
.build();
return CommandConfiguration.builder()
.name("setEmote")
.module(ConfigModuleDefinition.CONFIG)
.parameters(parameters)
.supportsEmbedException(true)
.help(helpInfo)
.slashCommandConfig(slashCommandConfig)
.templated(true)
.causesReaction(true)
.build();

View File

@@ -4,15 +4,18 @@ import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.SystemInfo;
import dev.sheldan.abstracto.core.models.template.commands.UptimeModel;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -22,33 +25,60 @@ import java.util.concurrent.CompletableFuture;
public class Uptime extends AbstractConditionableCommand {
public static final String UPTIME_RESPONSE_TEMPLATE_KEY = "uptime_response";
private static final String UPTIME_COMMAND = "uptime";
@Autowired
private BotService botService;
@Autowired
private ChannelService channelService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
SystemInfo systemInfo = botService.getSystemInfo();
UptimeModel model = UptimeModel
.builder()
.uptime(systemInfo.getUptime())
.startDate(systemInfo.getStartTime())
.build();
UptimeModel model = getModel();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(UPTIME_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
UptimeModel model = getModel();
return interactionService.replyEmbed(UPTIME_RESPONSE_TEMPLATE_KEY, model, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
private UptimeModel getModel() {
SystemInfo systemInfo = botService.getSystemInfo();
return UptimeModel
.builder()
.uptime(systemInfo.getUptime())
.startDate(systemInfo.getStartTime())
.build();
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(UPTIME_COMMAND)
.build();
return CommandConfiguration.builder()
.name("uptime")
.name(UPTIME_COMMAND)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.async(true)
.help(helpInfo)
.slashCommandConfig(slashCommandConfig)
.causesReaction(false)
.build();
}

View File

@@ -67,6 +67,11 @@ public class ListenerExecutorConfig {
return executorService.setupExecutorFor("messageContextCommandListener");
}
@Bean(name = "slashCommandExecutor")
public TaskExecutor slashCommandExecutor() {
return executorService.setupExecutorFor("slashCommandListener");
}
@Bean(name = "emoteDeletedExecutor")
public TaskExecutor emoteDeletedExecutor() {
return executorService.setupExecutorFor("emoteDeletedListener");

View File

@@ -10,7 +10,6 @@ import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.PaginatorServiceBean;
import dev.sheldan.abstracto.core.templating.model.MessageConfiguration;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.templating.service.TemplateServiceBean;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
@@ -33,17 +32,11 @@ public class PaginatorButtonListener implements ButtonClickedListener {
@Autowired
private TemplateServiceBean templateServiceBean;
@Autowired
private TemplateService templateService;
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
PaginatorButtonPayload payload = (PaginatorButtonPayload) model.getDeserializedPayload();
Message originalMessage = model.getEvent().getMessage();
if(originalMessage == null) {
return ButtonClickedListenerResult.IGNORED;
}
if(payload.getAllowedUser() != null && model.getEvent().getUser().getIdLong() != payload.getAllowedUser()) {
return ButtonClickedListenerResult.IGNORED;
}

View File

@@ -44,9 +44,6 @@ public class SyncButtonClickedListenerBean extends ListenerAdapter {
@Qualifier("buttonClickedExecutor")
private TaskExecutor buttonClickedExecutor;
@Autowired
private ListenerService listenerService;
@Autowired
private FeatureConfigService featureConfigService;
@@ -68,6 +65,7 @@ public class SyncButtonClickedListenerBean extends ListenerAdapter {
@Override
public void onButtonInteraction(@NotNull ButtonInteractionEvent event) {
if(listenerList == null) return;
// TODO remove this and make this configurable
event.deferEdit().queue();
CompletableFuture.runAsync(() -> self.executeListenerLogic(event), buttonClickedExecutor).exceptionally(throwable -> {
log.error("Failed to execute listener logic in async button event.", throwable);

View File

@@ -17,6 +17,7 @@ import dev.sheldan.abstracto.core.service.management.ChannelGroupManagementServi
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.GuildChannel;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
@@ -65,7 +66,7 @@ public class ChannelGroupServiceBean implements ChannelGroupService {
}
@Override
public void addChannelToChannelGroup(String channelGroupName, TextChannel textChannel) {
public void addChannelToChannelGroup(String channelGroupName, GuildChannel textChannel) {
addChannelToChannelGroup(channelGroupName, textChannel.getIdLong(), textChannel.getGuild().getIdLong());
}

View File

@@ -8,6 +8,7 @@ import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ComponentPayloadManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.AttachedFile;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
@@ -19,6 +20,7 @@ import net.dv8tion.jda.api.interactions.components.ActionComponent;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.ItemComponent;
import net.dv8tion.jda.api.requests.restaction.MessageAction;
import net.dv8tion.jda.api.utils.AttachmentOption;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -256,13 +258,28 @@ public class ChannelServiceBean implements ChannelService {
}
}
if(messageToSend.hasFileToSend()) {
if(messageToSend.hasFilesToSend()) {
if(!allMessageActions.isEmpty()) {
// in case there has not been a message, we need to increment it
allMessageActions.set(0, allMessageActions.get(0).addFile(messageToSend.getFileToSend()));
messageToSend.getAttachedFiles().forEach(attachedFile -> {
String fileNameToUse = attachedFile.getFileName() != null ? attachedFile.getFileName() : attachedFile.getFile().getName();
allMessageActions.set(0, allMessageActions.get(0)
.addFile(attachedFile.getFile(), fileNameToUse, attachedFile.getOptions().toArray(new AttachmentOption[0])));
});
} else {
metricService.incrementCounter(MESSAGE_SEND_METRIC);
allMessageActions.add(textChannel.sendFile(messageToSend.getFileToSend()));
messageToSend.getAttachedFiles().forEach(attachedFile -> allMessageActions.set(0, allMessageActions.get(0)
.addFile(attachedFile.getFile(), attachedFile.getFileName(), attachedFile.getOptions().toArray(new AttachmentOption[0]))));
AttachedFile firstAttachment = messageToSend.getAttachedFiles().get(0);
String fileNameToUse = firstAttachment.getFileName() != null ? firstAttachment.getFileName() : firstAttachment.getFile().getName();
allMessageActions.add(textChannel.sendFile(firstAttachment.getFile(), fileNameToUse, firstAttachment.getOptions().toArray(new AttachmentOption[0])));
if(messageToSend.getAttachedFiles().size() > 1) {
messageToSend.getAttachedFiles().stream().skip(1).forEach(attachedFile -> {
String innerFileNameToUse = attachedFile.getFileName() != null ? attachedFile.getFileName() : attachedFile.getFile().getName();
allMessageActions.set(0, allMessageActions.get(0).addFile(attachedFile.getFile(), innerFileNameToUse, attachedFile.getOptions().toArray(new AttachmentOption[0])));
});
}
}
}
Set<Message.MentionType> allowedMentions = allowedMentionService.getAllowedMentionsFor(textChannel, messageToSend);
@@ -604,7 +621,12 @@ public class ChannelServiceBean implements ChannelService {
} else {
messageToSend = templateService.renderEmbedTemplate(messageTemplate, model);
}
messageToSend.setFileToSend(tempFile);
AttachedFile file = AttachedFile
.builder()
.file(tempFile)
.fileName(fileName)
.build();
messageToSend.setAttachedFiles(Arrays.asList(file));
return sendMessageToSendToChannel(messageToSend, channel);
} catch (IOException e) {
log.error("Failed to write local temporary file for template download.", e);
@@ -630,9 +652,14 @@ public class ChannelServiceBean implements ChannelService {
throw new UploadFileTooLargeException(tempFile.length(), maxFileSize);
}
}
AttachedFile attachedFile = AttachedFile
.builder()
.fileName(tempFile.getName())
.file(tempFile)
.build();
MessageToSend messageToSend = MessageToSend
.builder()
.fileToSend(tempFile)
.attachedFiles(Arrays.asList(attachedFile))
.build();
return sendMessageToSendToChannel(messageToSend, channel);
} catch (IOException e) {

View File

@@ -11,6 +11,7 @@ import dev.sheldan.abstracto.core.service.management.DefaultEmoteManagementServi
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emoji;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.MessageReaction;
@@ -164,7 +165,12 @@ public class EmoteServiceBean implements EmoteService {
public AEmote getFakeEmoteFromEmote(Emote emote) {
AServer server = null;
if(emote.getGuild() != null) {
server = AServer.builder().id(emote.getGuild().getIdLong()).fake(true).build();
server = AServer
.builder()
.id(emote.getGuild()
.getIdLong())
.fake(true)
.build();
}
return AEmote
.builder()
@@ -177,6 +183,18 @@ public class EmoteServiceBean implements EmoteService {
.build();
}
@Override
public AEmote getFakeEmoteFromEmoji(Emoji emoji) {
return AEmote
.builder()
.fake(true)
.emoteKey(emoji.getName())
.custom(emoji.isCustom())
.animated(emoji.isAnimated())
.emoteId(emoji.getIdLong())
.build();
}
@Override
public boolean emoteIsFromGuild(Emote emote, Guild guild) {
return guild.getEmoteById(emote.getId()) != null;

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService;
@@ -7,6 +8,7 @@ import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ComponentPayloadManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.AttachedFile;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
@@ -14,9 +16,13 @@ import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.interactions.Interaction;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
import net.dv8tion.jda.api.interactions.components.ActionComponent;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.requests.restaction.WebhookMessageAction;
import net.dv8tion.jda.api.requests.restaction.WebhookMessageUpdateAction;
import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction;
import net.dv8tion.jda.api.utils.AttachmentOption;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -57,7 +63,7 @@ public class InteractionServiceBean implements InteractionService {
.build();
@Override
public List<CompletableFuture<Message>> sendMessageToInteraction(MessageToSend messageToSend, InteractionHook interactionHook){
public List<CompletableFuture<Message>> sendMessageToInteraction(MessageToSend messageToSend, InteractionHook interactionHook) {
List<CompletableFuture<Message>> futures = new ArrayList<>();
List<WebhookMessageAction<Message>> allMessageActions = new ArrayList<>();
int iterations = Math.min(messageToSend.getMessages().size(), messageToSend.getEmbeds().size());
@@ -105,13 +111,21 @@ public class InteractionServiceBean implements InteractionService {
metricService.incrementCounter(EPHEMERAL_MESSAGES_SEND);
}
if(messageToSend.hasFileToSend()) {
if(messageToSend.hasFilesToSend()) {
if(!allMessageActions.isEmpty()) {
// in case there has not been a message, we need to increment it
allMessageActions.set(0, allMessageActions.get(0).addFile(messageToSend.getFileToSend()));
messageToSend.getAttachedFiles().forEach(attachedFile ->
allMessageActions.set(0, allMessageActions.get(0).addFile(attachedFile.getFile(), attachedFile.getFileName(), attachedFile.getOptions().toArray(new AttachmentOption[0]))));
} else {
metricService.incrementCounter(MESSAGE_SEND_METRIC);
allMessageActions.add(interactionHook.sendFile(messageToSend.getFileToSend()));
List<AttachedFile> attachedFiles = messageToSend.getAttachedFiles();
AttachedFile firstFile = attachedFiles.get(0);
allMessageActions.add(interactionHook.sendFile(firstFile.getFile(), firstFile.getFileName(), firstFile.getOptions().toArray(new AttachmentOption[0])));
attachedFiles
.stream()
.skip(1)
.forEach(attachedFile -> allMessageActions
.add(interactionHook.sendFile(attachedFile.getFile(), attachedFile.getFileName(), attachedFile.getOptions().toArray(new AttachmentOption[0]))));
}
}
Set<Message.MentionType> allowedMentions = allowedMentionService.getAllowedMentionsFor(interactionHook.getInteraction().getMessageChannel(), messageToSend);
@@ -125,6 +139,188 @@ public class InteractionServiceBean implements InteractionService {
return sendMessageToInteraction(messageToSend, interactionHook);
}
@Override
public CompletableFuture<InteractionHook> replyEmbed(String templateKey, Object model, IReplyCallback callback) {
Long serverId = callback.getGuild().getIdLong();
MessageToSend messageToSend = templateService.renderEmbedTemplate(templateKey, model, serverId);
return replyMessageToSend(messageToSend, callback);
}
@Override
public CompletableFuture<InteractionHook> replyEmbed(String templateKey, IReplyCallback callback) {
return replyEmbed(templateKey, new Object(), callback);
}
@Override
public CompletableFuture<Message> editOriginal(MessageToSend messageToSend, InteractionHook interactionHook) {
Long serverId = interactionHook.getInteraction().getGuild().getIdLong();
if(messageToSend.getEphemeral()) {
Interaction interaction = interactionHook.getInteraction();
log.info("Sending ephemeral message to interaction in guild {} in channel {} for user {}.",
interaction.getGuild().getIdLong(), interaction.getChannel().getId(),
interaction.getMember().getIdLong());
metricService.incrementCounter(EPHEMERAL_MESSAGES_SEND);
interactionHook.setEphemeral(true);
}
WebhookMessageUpdateAction<Message> action = null;
if(messageToSend.getMessages() != null && !messageToSend.getMessages().isEmpty()) {
metricService.incrementCounter(MESSAGE_SEND_METRIC);
action = interactionHook.editOriginal(messageToSend.getMessages().get(0));
}
if(messageToSend.getEmbeds() != null && !messageToSend.getEmbeds().isEmpty()) {
if(action != null) {
action = action.setEmbeds(messageToSend.getEmbeds().subList(0, 10));
} else {
action = interactionHook.editOriginalEmbeds(messageToSend.getEmbeds());
}
}
if(messageToSend.hasFilesToSend()) {
if(action != null) {
List<AttachedFile> attachedFiles = messageToSend.getAttachedFiles();
AttachedFile firstFile = attachedFiles.get(0);
action = action.addFile(firstFile.getFile(), firstFile.getFileName(), firstFile.getOptions().toArray(new AttachmentOption[0]));
boolean first = true;
for (AttachedFile attachedFile : attachedFiles) {
if (first) {
first = false;
continue;
}
action = action.addFile(attachedFile.getFile(), attachedFile.getFileName(), attachedFile.getOptions().toArray(new AttachmentOption[0]));
}
} else {
metricService.incrementCounter(MESSAGE_SEND_METRIC);
List<AttachedFile> attachedFiles = messageToSend.getAttachedFiles();
AttachedFile firstFile = attachedFiles.get(0);
action = interactionHook.editOriginal(firstFile.getFile(), firstFile.getFileName(), firstFile.getOptions().toArray(new AttachmentOption[0]));
boolean first = true;
for (AttachedFile attachedFile : attachedFiles) {
if (first) {
first = false;
continue;
}
action = action.addFile(attachedFile.getFile(), attachedFile.getFileName(), attachedFile.getOptions().toArray(new AttachmentOption[0]));
}
}
}
// this should be last, because we are "faking" a message, by inserting a ., in case there has not been a reply yet
// we could also throw an exception, but we are allowing this to go through
List<ActionRow> actionRows = messageToSend.getActionRows();
if(actionRows != null && !actionRows.isEmpty()) {
if(action == null) {
action = interactionHook.editOriginal(".");
}
action = action.setActionRows(actionRows);
AServer server = serverManagementService.loadServer(serverId);
actionRows.forEach(components -> components.forEach(component -> {
if(component instanceof ActionComponent) {
String id = ((ActionComponent)component).getId();
MessageToSend.ComponentConfig payload = messageToSend.getComponentPayloads().get(id);
if(payload.getPersistCallback()) {
componentPayloadManagementService.createPayload(id, payload.getPayload(), payload.getPayloadType(), payload.getComponentOrigin(), server, payload.getComponentType());
}
}
}));
}
if(action == null) {
throw new AbstractoRunTimeException("The callback did not result in any message.");
}
return action.submit();
}
public CompletableFuture<InteractionHook> replyMessageToSend(MessageToSend messageToSend, IReplyCallback callback) {
Long serverId = callback.getGuild().getIdLong();
ReplyCallbackAction action = null;
if(messageToSend.getMessages() != null && !messageToSend.getMessages().isEmpty()) {
metricService.incrementCounter(MESSAGE_SEND_METRIC);
action = callback.reply(messageToSend.getMessages().get(0));
}
if(messageToSend.getEmbeds() != null && !messageToSend.getEmbeds().isEmpty()) {
if(action != null) {
action = action.addEmbeds(messageToSend.getEmbeds().subList(0, 10));
} else {
action = callback.replyEmbeds(messageToSend.getEmbeds());
}
}
if(messageToSend.hasFilesToSend()) {
if(action != null) {
List<AttachedFile> attachedFiles = messageToSend.getAttachedFiles();
AttachedFile firstFile = attachedFiles.get(0);
action = action.addFile(firstFile.getFile(), firstFile.getFileName(), firstFile.getOptions().toArray(new AttachmentOption[0]));
boolean first = true;
for (AttachedFile attachedFile : attachedFiles) {
if (first) {
first = false;
continue;
}
action = action.addFile(attachedFile.getFile(), attachedFile.getFileName(), attachedFile.getOptions().toArray(new AttachmentOption[0]));
}
} else {
metricService.incrementCounter(MESSAGE_SEND_METRIC);
List<AttachedFile> attachedFiles = messageToSend.getAttachedFiles();
AttachedFile firstFile = attachedFiles.get(0);
action = callback.replyFile(firstFile.getFile(), firstFile.getFileName(), firstFile.getOptions().toArray(new AttachmentOption[0]));
boolean first = true;
for (AttachedFile attachedFile : attachedFiles) {
if (first) {
first = false;
continue;
}
action = action.addFile(attachedFile.getFile(), attachedFile.getFileName(), attachedFile.getOptions().toArray(new AttachmentOption[0]));
}
}
}
// this should be last, because we are "faking" a message, by inserting a ., in case there has not been a reply yet
// we could also throw an exception, but we are allowing this to go through
List<ActionRow> actionRows = messageToSend.getActionRows();
if(actionRows != null && !actionRows.isEmpty()) {
if(action == null) {
action = callback.reply(".");
}
action = action.addActionRows(actionRows);
AServer server = serverManagementService.loadServer(serverId);
actionRows.forEach(components -> components.forEach(component -> {
if(component instanceof ActionComponent) {
String id = ((ActionComponent)component).getId();
MessageToSend.ComponentConfig payload = messageToSend.getComponentPayloads().get(id);
if(payload.getPersistCallback()) {
componentPayloadManagementService.createPayload(id, payload.getPayload(), payload.getPayloadType(), payload.getComponentOrigin(), server, payload.getComponentType());
}
}
}));
}
if(messageToSend.getEphemeral()) {
log.info("Sending ephemeral message to interaction in guild {} in channel {} for user {}.",
callback.getGuild().getIdLong(), callback.getChannel().getId(),
callback.getMember().getIdLong());
metricService.incrementCounter(EPHEMERAL_MESSAGES_SEND);
if(action != null) {
action = action.setEphemeral(messageToSend.getEphemeral());
}
}
if(action == null) {
throw new AbstractoRunTimeException("The callback did not result in any message.");
}
return action.submit();
}
@Override
public CompletableFuture<InteractionHook> replyMessage(String templateKey, Object model, IReplyCallback callback) {
Long serverId = callback.getGuild().getIdLong();
MessageToSend messageToSend = templateService.renderTemplateToMessageToSend(templateKey, model, serverId);
return replyMessageToSend(messageToSend, callback);
}
@PostConstruct
public void postConstruct() {
metricService.registerCounter(EPHEMERAL_MESSAGES_SEND, "Ephemeral messages send");

View File

@@ -73,6 +73,12 @@ public class MessageServiceBean implements MessageService {
.tagList(Arrays.asList(MetricTag.getTag(INTERACTION_TYPE, "message.delete")))
.build();
public static final CounterMetric MESSAGE_PIN_METRIC = CounterMetric
.builder()
.name(DISCORD_API_INTERACTION_METRIC)
.tagList(Arrays.asList(MetricTag.getTag(INTERACTION_TYPE, "message.pin")))
.build();
@Override
public CompletableFuture<Void> deleteMessageInChannelInServer(Long serverId, Long channelId, Long messageId) {
metricService.incrementCounter(MESSAGE_DELETE_METRIC);
@@ -251,11 +257,18 @@ public class MessageServiceBean implements MessageService {
return message.editMessage(message).setActionRows(rows).submit();
}
@Override
public CompletableFuture<Void> pinMessage(Message message) {
metricService.incrementCounter(MESSAGE_PIN_METRIC);
return message.pin().submit();
}
@PostConstruct
public void postConstruct() {
metricService.registerCounter(MESSAGE_SEND_METRIC, "Messages send to discord");
metricService.registerCounter(MESSAGE_EDIT_METRIC, "Messages edited in discord");
metricService.registerCounter(MESSAGE_LOAD_METRIC, "Messages loaded from discord");
metricService.registerCounter(MESSAGE_DELETE_METRIC, "Messages deleted from discord");
metricService.registerCounter(MESSAGE_PIN_METRIC, "Messages pinned in discord");
}
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.service;
import com.google.gson.Gson;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.model.PaginatorButtonPayload;
import dev.sheldan.abstracto.core.model.PaginatorConfiguration;
import dev.sheldan.abstracto.core.model.PaginatorFooterModel;
@@ -22,7 +23,7 @@ import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -38,9 +39,6 @@ import java.util.concurrent.locks.ReentrantLock;
@Slf4j
public class PaginatorServiceBean implements PaginatorService {
@Autowired
private BotService botService;
@Autowired
private TemplateService templateService;
@@ -68,6 +66,9 @@ public class PaginatorServiceBean implements PaginatorService {
@Autowired
private SchedulerService schedulerService;
@Autowired
private InteractionService interactionService;
private static final Map<String, PaginatorInfo> PAGINATORS = new ConcurrentHashMap<>();
public static final String PAGINATOR_BUTTON = "PAGINATOR_BUTTON";
public static final String PAGINATOR_FOOTER_TEMPLATE_KEY = "paginator_footer";
@@ -76,6 +77,15 @@ public class PaginatorServiceBean implements PaginatorService {
@Override
public CompletableFuture<Void> createPaginatorFromTemplate(String templateKey, Object model, GuildMessageChannel textChannel, Long userId) {
Long serverId = textChannel.getGuild().getIdLong();
PaginatorSetup setup = getPaginatorSetup(templateKey, model, userId, serverId);
log.info("Setting up paginator in channel {} in server {} with {} pages.", textChannel.getIdLong(),
textChannel.getGuild().getIdLong(), setup.getConfiguration().getEmbedConfigs().size());
List<CompletableFuture<Message>> paginatorFutures = channelService.sendMessageToSendToChannel(setup.getMessageToSend(), textChannel);
return FutureUtils.toSingleFutureGeneric(paginatorFutures)
.thenAccept(unused -> self.setupButtonPayloads(paginatorFutures.get(0).join(), setup, serverId));
}
private PaginatorSetup getPaginatorSetup(String templateKey, Object model, Long userId, Long serverId) {
String exitButtonId = componentService.generateComponentId(serverId);
String startButtonId = componentService.generateComponentId(serverId);
String previousButtonId = componentService.generateComponentId(serverId);
@@ -92,8 +102,6 @@ public class PaginatorServiceBean implements PaginatorService {
.build();
String embedConfig = templateService.renderTemplate(templateKey + "_paginator", wrapperModel, serverId);
PaginatorConfiguration configuration = gson.fromJson(embedConfig, PaginatorConfiguration.class);
log.info("Setting up paginator in channel {} in server {} with {} pages.", textChannel.getIdLong(),
textChannel.getGuild().getIdLong(), configuration.getEmbedConfigs().size());
setupFooters(configuration);
configuration.setPaginatorId(componentService.generateComponentId());
@@ -113,9 +121,21 @@ public class PaginatorServiceBean implements PaginatorService {
MessageConfiguration messageConfiguration = configuration.getEmbedConfigs().get(0);
MessageToSend messageToSend = templateServiceBean.convertEmbedConfigurationToMessageToSend(messageConfiguration);
List<CompletableFuture<Message>> paginatorFutures = channelService.sendMessageToSendToChannel(messageToSend, textChannel);
return FutureUtils.toSingleFutureGeneric(paginatorFutures)
.thenAccept(unused -> self.setupButtonPayloads(paginatorFutures.get(0).join(), configuration, serverId, buttonPayload));
return PaginatorSetup
.builder()
.messageToSend(messageToSend)
.configuration(configuration)
.payload(buttonPayload)
.build();
}
@Override
public CompletableFuture<Void> createPaginatorFromTemplate(String templateKey, Object model, IReplyCallback callback) {
Long serverId = callback.getGuild().getIdLong();
PaginatorSetup setup = getPaginatorSetup(templateKey, model, callback.getMember().getIdLong(), serverId);
return interactionService.replyMessageToSend(setup.getMessageToSend(), callback)
.thenCompose(interactionHook -> interactionHook.retrieveOriginal().submit())
.thenAccept(message -> self.setupButtonPayloads(message, setup, serverId));
}
private void setupFooters(PaginatorConfiguration configuration) {
@@ -219,7 +239,9 @@ public class PaginatorServiceBean implements PaginatorService {
}
@Transactional
public void setupButtonPayloads(Message paginatorMessage, PaginatorConfiguration configuration, Long serverId, PaginatorButtonPayload payload) {
public void setupButtonPayloads(Message paginatorMessage, PaginatorSetup setup, Long serverId) {
PaginatorConfiguration configuration = setup.getConfiguration();
PaginatorButtonPayload payload = setup.getPayload();
savePayload(configuration.getExitButton(), serverId);
if(!configuration.getSinglePage()) {
savePayload(configuration.getStartButton(), serverId);
@@ -274,4 +296,12 @@ public class PaginatorServiceBean implements PaginatorService {
private List<String> payloadIds;
}
@Getter
@Builder
public static class PaginatorSetup {
private PaginatorConfiguration configuration;
private PaginatorButtonPayload payload;
private MessageToSend messageToSend;
}
}

View File

@@ -0,0 +1,70 @@
package dev.sheldan.abstracto.core.startup;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.slash.SlashCommandService;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.listener.AsyncStartupListener;
import dev.sheldan.abstracto.core.command.SlashCommandListenerBean;
import dev.sheldan.abstracto.core.service.*;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
@Slf4j
public class SlashCommandLoaderListener implements AsyncStartupListener {
@Autowired
private SlashCommandListenerBean slashCommandListenerBean;
@Autowired
private SlashCommandService slashCommandService;
@Autowired
private BotService botService;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private FeatureModeService featureModeService;
@Override
public void execute() {
List<Command> incomingSlashCommands = slashCommandListenerBean.getSlashCommands();
JDA jda = botService.getInstance();
List<Guild> onlineGuilds = jda.getGuilds();
onlineGuilds.forEach(guild -> {
log.info("Updating slash commands for guild {}.", guild.getIdLong());
guild.retrieveCommands().queue(commands -> {
log.info("Loaded {} slash commands for guild {}.", commands.size(), guild.getIdLong());
List<Pair<List<CommandConfiguration>, SlashCommandData>> commandsToUpDate = new ArrayList<>();
incomingSlashCommands.forEach(command -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(command.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, guild.getIdLong())) {
return;
}
if(!featureModeService.necessaryFeatureModesMet(command, guild.getIdLong())) {
return;
}
log.info("Updating slash command {} in guild {}.", command.getConfiguration().getName(), guild.getId());
slashCommandService.convertCommandConfigToCommandData(command.getConfiguration(), commandsToUpDate);
});
slashCommandService.updateGuildSlashCommand(guild, commandsToUpDate)
.thenAccept(commands1 -> log.info("Updated {} commands in guild {}.", commands1.size(), guild.getIdLong()));
},
throwable -> log.error("Failed to load commands for guild {}.", guild.getIdLong(), throwable));
});
}
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.core.templating.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class FileConfig {
private String fileName;
private Boolean spoiler;
}

View File

@@ -22,4 +22,5 @@ public class MessageConfiguration {
private String additionalMessage;
private MetaMessageConfiguration messageConfig;
private List<ButtonConfig> buttons;
private List<FileConfig> files;
}

View File

@@ -23,6 +23,8 @@ import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.dv8tion.jda.api.utils.AttachmentOption;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -166,6 +168,22 @@ public class TemplateServiceBean implements TemplateService {
isEphemeral = Boolean.TRUE.equals(messageConfiguration.getMessageConfig().isEphemeral());
}
List<AttachedFile> files = new ArrayList<>();
if(messageConfiguration.getFiles() != null && !messageConfiguration.getFiles().isEmpty()) {
messageConfiguration.getFiles().forEach(fileToAttach -> {
List<AttachmentOption> options = new ArrayList<>();
if(fileToAttach.getSpoiler() != null && fileToAttach.getSpoiler()) {
options.add(AttachmentOption.SPOILER);
}
AttachedFile attachedFile = AttachedFile
.builder()
.fileName(fileToAttach.getFileName() != null ? fileToAttach.getFileName() : RandomStringUtils.randomAlphabetic(5))
.options(options)
.build();
files.add(attachedFile);
});
}
String additionalMessage = messageConfiguration.getAdditionalMessage();
if(additionalMessage != null) {
Long segmentLimit = messageConfiguration.getMessageConfig() != null
@@ -212,6 +230,7 @@ public class TemplateServiceBean implements TemplateService {
.messageConfig(createMessageConfig(messageConfiguration.getMessageConfig()))
.messages(messages)
.ephemeral(isEphemeral)
.attachedFiles(!files.isEmpty() ? files : null)
.actionRows(buttons)
.componentPayloads(componentPayloads)
.referencedMessageId(referencedMessageId)

View File

@@ -1,23 +0,0 @@
package dev.sheldan.abstracto.core.utils;
import dev.sheldan.abstracto.core.models.SnowFlake;
import net.dv8tion.jda.api.entities.ISnowflake;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class SnowflakeUtils {
private SnowflakeUtils() {
}
public static Set<Long> getOwnItemsIds(List<? extends SnowFlake> elements){
return elements.stream().map(SnowFlake::getId).collect(Collectors.toSet());
}
public static Set<Long> getSnowflakeIds(List<? extends ISnowflake> elements){
return elements.stream().map(ISnowflake::getIdLong).collect(Collectors.toSet());
}
}

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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="command_in_server-add-slash-command-id">
<addColumn tableName="command_in_server" >
<column name="slash_command_id" type="BIGINT">
<constraints nullable="true" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@@ -8,4 +8,5 @@
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="channel.xml" relativeToChangelogFile="true"/>
<include file="component_payload.xml" relativeToChangelogFile="true"/>
<include file="command_in_server.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>