[AB-xxx] adding command to view system configuration per server

adding auto complete to setConfig command key parameter
This commit is contained in:
Sheldan
2025-12-28 23:46:36 +01:00
parent 4180a07243
commit 8251074aab
10 changed files with 310 additions and 0 deletions

View File

@@ -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<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long serverId = event.getGuild().getIdLong();
List<GetConfigModel.ConfigValue> 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<String> configKeys = feature.getRequiredSystemConfigKeys();
configValues = getConfigValuesForKeys(serverId, configKeys);
} else {
List<String> 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<GetConfigModel.ConfigValue> getConfigValuesForKeys(Long serverId, List<String> configKeys) {
Map<String, AConfig> 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<String> 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<Parameter> 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;
}
}

View File

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

View File

@@ -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, Long> {
AConfig findAConfigByServerIdAndNameIgnoreCase(Long serverId, String name);
void deleteAConfigByServerId(Long serverId);
List<AConfig> findByServerId(Long serverId);
boolean existsAConfigByServerIdAndNameIgnoreCase(Long serverId, String name);

View File

@@ -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<AConfig> loadForServer(Long serverId) {
return configRepository.findByServerId(serverId);
}
@Override
public AConfig loadOrCreateIfNotExists(Long serverId, String name, Long value) {
AConfig config = loadConfig(serverId, name);

View File

@@ -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<String> getConfigKeys() {
return defaultConfigProperties.getSystemConfigs().keySet().stream().toList();
}
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.26.xsd" >
<property name="coreFeature" value="(SELECT id FROM feature WHERE key = 'core')"/>
<property name="configModule" value="(SELECT id FROM module WHERE name = 'config')"/>
<changeSet author="Sheldan" id="getConfig-command" >
<insert tableName="command">
<column name="name" value="getConfig"/>
<column name="module_id" valueComputed="${configModule}"/>
<column name="feature_id" valueComputed="${coreFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -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" >
<include file="channel_group_types.xml" relativeToChangelogFile="true"/>
<include file="command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

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

View File

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

View File

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