From 8251074aabad90dbb87298102258a7eae77ab2f0 Mon Sep 17 00:00:00 2001 From: Sheldan <5037282+Sheldan@users.noreply.github.com> Date: Sun, 28 Dec 2025 23:46:36 +0100 Subject: [PATCH] [AB-xxx] adding command to view system configuration per server adding auto complete to setConfig command key parameter --- .../core/commands/config/GetConfig.java | 226 ++++++++++++++++++ .../core/commands/config/SetConfig.java | 25 ++ .../core/repository/ConfigRepository.java | 2 + .../ConfigManagementServiceBean.java | 6 + .../DefaultConfigManagementServiceBean.java | 6 + .../migrations/1.6.18/seedData/command.xml | 14 ++ .../migrations/1.6.18/seedData/data.xml | 1 + .../template/commands/GetConfigModel.java | 25 ++ .../management/ConfigManagementService.java | 2 + .../DefaultConfigManagementService.java | 3 + 10 files changed, 310 insertions(+) create mode 100644 abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/GetConfig.java create mode 100644 abstracto-application/core/core-impl/src/main/resources/migrations/1.6.18/seedData/command.xml create mode 100644 abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/template/commands/GetConfigModel.java diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/GetConfig.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/GetConfig.java new file mode 100644 index 000000000..32af75ab4 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/GetConfig.java @@ -0,0 +1,226 @@ +package dev.sheldan.abstracto.core.commands.config; + +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.features.CoreFeatureDefinition; +import dev.sheldan.abstracto.core.command.execution.CommandResult; +import dev.sheldan.abstracto.core.config.FeatureConfig; +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.interaction.slash.CoreSlashCommandNames; +import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig; +import dev.sheldan.abstracto.core.interaction.slash.SlashCommandPrivilegeLevels; +import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandAutoCompleteService; +import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService; +import dev.sheldan.abstracto.core.models.database.AConfig; +import dev.sheldan.abstracto.core.models.property.SystemConfigProperty; +import dev.sheldan.abstracto.core.models.template.commands.GetConfigModel; +import dev.sheldan.abstracto.core.service.FeatureConfigService; +import dev.sheldan.abstracto.core.service.PaginatorService; +import dev.sheldan.abstracto.core.service.management.ConfigManagementService; +import dev.sheldan.abstracto.core.service.management.DefaultConfigManagementService; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.stream.Collectors; +import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class GetConfig extends AbstractConditionableCommand { + + private static final String GET_CONFIG_COMMAND = "getConfig"; + private static final String KEY_PARAMETER = "key"; + private static final String FEATURE_PARAMETER = "feature"; + + private static final String GET_CONFIG_RESPONSE_TEMPLATE_KEY = "getConfig_response"; + private static final String NO_CONFIGS_TEMPLATE_KEY = "getConfig_no_configs_found"; + + @Autowired + private SlashCommandParameterService slashCommandParameterService; + + @Autowired + private FeatureConfigService featureConfigService; + + @Autowired + private SlashCommandAutoCompleteService slashCommandAutoCompleteService; + + @Autowired + private DefaultConfigManagementService defaultConfigManagementService; + + @Autowired + private ConfigManagementService configManagementService; + + @Autowired + private PaginatorService paginatorService; + + @Autowired + private InteractionService interactionService; + + @Override + public CompletableFuture executeSlash(SlashCommandInteractionEvent event) { + Long serverId = event.getGuild().getIdLong(); + List configValues; + if(slashCommandParameterService.hasCommandOption(KEY_PARAMETER, event)) { + String key = slashCommandParameterService.getCommandOption(KEY_PARAMETER, event, String.class); + if(!defaultConfigManagementService.configKeyExists(key)) { + throw new ConfigurationKeyNotFoundException(key); + } + + GetConfigModel.ConfigValue.ConfigValueBuilder configValueBuilder = GetConfigModel.ConfigValue.builder(); + if(configManagementService.configExists(serverId, key)) { + AConfig aConfig = configManagementService.loadConfig(serverId, key); + configValueBuilder + .doubleValue(aConfig.getDoubleValue()) + .hasConcreateValue(true) + .longValue(aConfig.getLongValue()) + .stringValue(aConfig.getStringValue()); + } + SystemConfigProperty defaultConfig = defaultConfigManagementService.getDefaultConfig(key); + GetConfigModel.ConfigValue.ConfigValueBuilder defaultConfigValueBuilder = GetConfigModel + .ConfigValue + .builder() + .stringValue(defaultConfig.getStringValue()) + .doubleValue(defaultConfig.getDoubleValue()) + .longValue(defaultConfig.getLongValue()) + .key(defaultConfig.getName()); + configValueBuilder + .key(defaultConfig.getName()) + .defaultValue(defaultConfigValueBuilder.build()); + configValues = List.of(configValueBuilder.build()); + + } else if(slashCommandParameterService.hasCommandOption(FEATURE_PARAMETER, event)) { + String featureKey = slashCommandParameterService.getCommandOption(FEATURE_PARAMETER, event, String.class); + FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(featureKey); + List configKeys = feature.getRequiredSystemConfigKeys(); + configValues = getConfigValuesForKeys(serverId, configKeys); + } else { + List configKeys = defaultConfigManagementService.getConfigKeys(); + configValues = getConfigValuesForKeys(serverId, configKeys); + } + + if(configValues.isEmpty()) { + return interactionService.replyEmbed(NO_CONFIGS_TEMPLATE_KEY, new Object() , event) + .thenApply(interactionHook -> CommandResult.fromSuccess()); + } + + configValues = new ArrayList<>(configValues); + configValues.sort(Comparator.comparing(GetConfigModel.ConfigValue::getKey)); + GetConfigModel model = GetConfigModel.builder() + .values(configValues) + .build(); + + return paginatorService.createPaginatorFromTemplate(GET_CONFIG_RESPONSE_TEMPLATE_KEY, model, event) + .thenApply(unused -> CommandResult.fromSuccess()); + } + + private List getConfigValuesForKeys(Long serverId, List configKeys) { + Map allExistingConfigs = configManagementService + .loadForServer(serverId) + .stream() + .collect(Collectors.toMap(aConfig -> aConfig.getName().toLowerCase(), Function.identity())); + return configKeys.stream().map(key -> { + GetConfigModel.ConfigValue.ConfigValueBuilder configValueBuilder = GetConfigModel.ConfigValue.builder(); + if(allExistingConfigs.containsKey(key.toLowerCase())) { + AConfig aConfig = allExistingConfigs.get(key.toLowerCase()); + configValueBuilder + .doubleValue(aConfig.getDoubleValue()) + .hasConcreateValue(true) + .longValue(aConfig.getLongValue()) + .stringValue(aConfig.getStringValue()); + } + SystemConfigProperty defaultConfig = defaultConfigManagementService.getDefaultConfig(key); + GetConfigModel.ConfigValue.ConfigValueBuilder defaultConfigValueBuilder = GetConfigModel + .ConfigValue + .builder() + .stringValue(defaultConfig.getStringValue()) + .doubleValue(defaultConfig.getDoubleValue()) + .longValue(defaultConfig.getLongValue()) + .key(defaultConfig.getName()); + configValueBuilder + .key(defaultConfig.getName()) + .defaultValue(defaultConfigValueBuilder.build()); + return configValueBuilder.build(); + }).toList(); + } + + @Override + public List performAutoComplete(CommandAutoCompleteInteractionEvent event) { + String input = event.getFocusedOption().getValue().toLowerCase(); + if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), KEY_PARAMETER)) { + return defaultConfigManagementService + .getConfigKeys() + .stream() + .map(String::toLowerCase) + .filter(key -> key.startsWith(input)) + .toList(); + } else if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), FEATURE_PARAMETER)) { + return featureConfigService.getAllFeatures() + .stream() + .map(String::toLowerCase) + .filter(lowerCase -> lowerCase.startsWith(input)) + .toList(); + } + return new ArrayList<>(); + } + + @Override + public CommandConfiguration getConfiguration() { + Parameter keyToGet = Parameter + .builder() + .name(KEY_PARAMETER) + .type(String.class) + .supportsAutoComplete(true) + .templated(true) + .optional(true) + .build(); + Parameter featureToGet = Parameter + .builder() + .name(FEATURE_PARAMETER) + .type(String.class) + .supportsAutoComplete(true) + .templated(true) + .optional(true) + .build(); + List parameters = Arrays.asList(keyToGet, featureToGet); + HelpInfo helpInfo = HelpInfo + .builder() + .templated(true) + .hasExample(true) + .build(); + + SlashCommandConfig slashCommandConfig = SlashCommandConfig + .builder() + .enabled(true) + .defaultPrivilege(SlashCommandPrivilegeLevels.INVITER) + .rootCommandName(CoreSlashCommandNames.CONFIG) + .commandName("get") + .build(); + + return CommandConfiguration.builder() + .name(GET_CONFIG_COMMAND) + .module(ConfigModuleDefinition.CONFIG) + .parameters(parameters) + .slashCommandOnly(true) + .slashCommandConfig(slashCommandConfig) + .templated(true) + .supportsEmbedException(true) + .help(helpInfo) + .causesReaction(true) + .build(); + } + + @Override + public FeatureDefinition getFeature() { + return CoreFeatureDefinition.CORE_FEATURE; + } +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/SetConfig.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/SetConfig.java index 07ffeb2bb..ff397abc7 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/SetConfig.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/SetConfig.java @@ -11,8 +11,12 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.interaction.InteractionService; import dev.sheldan.abstracto.core.interaction.slash.SlashCommandPrivilegeLevels; +import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandAutoCompleteService; import dev.sheldan.abstracto.core.service.ConfigService; import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService; +import dev.sheldan.abstracto.core.service.management.DefaultConfigManagementService; +import java.util.ArrayList; +import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -36,6 +40,12 @@ public class SetConfig extends AbstractConditionableCommand { @Autowired private InteractionService interactionService; + @Autowired + private SlashCommandAutoCompleteService slashCommandAutoCompleteService; + + @Autowired + private DefaultConfigManagementService defaultConfigManagementService; + private static final String RESPONSE_TEMPLATE = "setConfig_response"; @Override @@ -47,11 +57,26 @@ public class SetConfig extends AbstractConditionableCommand { .thenApply(interactionHook -> CommandResult.fromSuccess()); } + @Override + public List performAutoComplete(CommandAutoCompleteInteractionEvent event) { + if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), KEY_PARAMETER)) { + String input = event.getFocusedOption().getValue().toLowerCase(); + return defaultConfigManagementService + .getConfigKeys() + .stream() + .map(String::toLowerCase) + .filter(key -> key.startsWith(input)) + .toList(); + } + return new ArrayList<>(); + } + @Override public CommandConfiguration getConfiguration() { Parameter keyToChange = Parameter .builder() .name(KEY_PARAMETER) + .supportsAutoComplete(true) .type(String.class) .templated(true) .build(); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/ConfigRepository.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/ConfigRepository.java index 7d71f578a..57636cdc3 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/ConfigRepository.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/ConfigRepository.java @@ -2,6 +2,7 @@ package dev.sheldan.abstracto.core.repository; import dev.sheldan.abstracto.core.models.database.AConfig; import dev.sheldan.abstracto.core.models.database.AServer; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -11,6 +12,7 @@ public interface ConfigRepository extends JpaRepository { AConfig findAConfigByServerIdAndNameIgnoreCase(Long serverId, String name); void deleteAConfigByServerId(Long serverId); + List findByServerId(Long serverId); boolean existsAConfigByServerIdAndNameIgnoreCase(Long serverId, String name); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementServiceBean.java index 29e44f6cd..e79346187 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementServiceBean.java @@ -3,6 +3,7 @@ package dev.sheldan.abstracto.core.service.management; import dev.sheldan.abstracto.core.models.database.AConfig; import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.repository.ConfigRepository; +import java.util.List; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -98,6 +99,11 @@ public class ConfigManagementServiceBean implements ConfigManagementService { return config; } + @Override + public List loadForServer(Long serverId) { + return configRepository.findByServerId(serverId); + } + @Override public AConfig loadOrCreateIfNotExists(Long serverId, String name, Long value) { AConfig config = loadConfig(serverId, name); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/DefaultConfigManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/DefaultConfigManagementServiceBean.java index 7a29ca37a..44e0ed237 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/DefaultConfigManagementServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/DefaultConfigManagementServiceBean.java @@ -2,6 +2,7 @@ package dev.sheldan.abstracto.core.service.management; import dev.sheldan.abstracto.core.config.DefaultConfigProperties; import dev.sheldan.abstracto.core.models.property.SystemConfigProperty; +import java.util.List; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -22,4 +23,9 @@ public class DefaultConfigManagementServiceBean implements DefaultConfigManageme public boolean configKeyExists(String key) { return defaultConfigProperties.getSystemConfigs().containsKey(key.toLowerCase()); } + + @Override + public List getConfigKeys() { + return defaultConfigProperties.getSystemConfigs().keySet().stream().toList(); + } } diff --git a/abstracto-application/core/core-impl/src/main/resources/migrations/1.6.18/seedData/command.xml b/abstracto-application/core/core-impl/src/main/resources/migrations/1.6.18/seedData/command.xml new file mode 100644 index 000000000..093af70a2 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/resources/migrations/1.6.18/seedData/command.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/abstracto-application/core/core-impl/src/main/resources/migrations/1.6.18/seedData/data.xml b/abstracto-application/core/core-impl/src/main/resources/migrations/1.6.18/seedData/data.xml index 69bcd530b..a4c926134 100644 --- a/abstracto-application/core/core-impl/src/main/resources/migrations/1.6.18/seedData/data.xml +++ b/abstracto-application/core/core-impl/src/main/resources/migrations/1.6.18/seedData/data.xml @@ -3,4 +3,5 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.26.xsd" > + \ No newline at end of file diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/template/commands/GetConfigModel.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/template/commands/GetConfigModel.java new file mode 100644 index 000000000..7938339c7 --- /dev/null +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/template/commands/GetConfigModel.java @@ -0,0 +1,25 @@ +package dev.sheldan.abstracto.core.models.template.commands; + +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class GetConfigModel { + private List values; + + + @Getter + @Builder + public static class ConfigValue { + private String key; + @Builder.Default + private Boolean hasConcreateValue = false; + private Long longValue; + private String stringValue; + private Double doubleValue; + private ConfigValue defaultValue; + } + +} diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementService.java index 180da8eb8..8cc1f95c7 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementService.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementService.java @@ -2,6 +2,7 @@ package dev.sheldan.abstracto.core.service.management; import dev.sheldan.abstracto.core.models.database.AConfig; import dev.sheldan.abstracto.core.models.database.AServer; +import java.util.List; public interface ConfigManagementService { AConfig setOrCreateStringValue(Long serverId, String name, String value); @@ -11,6 +12,7 @@ public interface ConfigManagementService { AConfig createConfig(Long serverId, String name, Double value); AConfig createConfig(Long serverId, String name, Long value); AConfig loadOrCreateIfNotExists(Long serverId, String name, String value); + List loadForServer(Long serverId); AConfig loadOrCreateIfNotExists(Long serverId, String name, Long value); AConfig loadOrCreateIfNotExists(Long serverId, String name, Double value); AConfig loadConfig(Long serverId, String name); diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/DefaultConfigManagementService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/DefaultConfigManagementService.java index 2a2872c1b..caf3ec787 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/DefaultConfigManagementService.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/management/DefaultConfigManagementService.java @@ -1,8 +1,11 @@ package dev.sheldan.abstracto.core.service.management; import dev.sheldan.abstracto.core.models.property.SystemConfigProperty; +import java.util.List; public interface DefaultConfigManagementService { SystemConfigProperty getDefaultConfig(String key); boolean configKeyExists(String key); + + List getConfigKeys(); }