[AB-xxx] refactoring modmail to offer a feature mode to use threads instead of channels

adding various utilities for thread channels in core
fixing enable feature not showing validation messages
restructuring feature mode collection to be a startup listener, because postconstruct might not have the appropriate values available, and therefore not initialize the map correctly
This commit is contained in:
Sheldan
2023-12-24 23:25:03 +01:00
parent 1f0bc493d9
commit befef1f61d
16 changed files with 283 additions and 101 deletions

View File

@@ -73,7 +73,17 @@ public class EnableFeature extends AbstractConditionableCommand {
EnableFeatureResult result = enableFeature(serverId, featureKey);
if(result.featureDependencies.isEmpty()) {
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
if(!result.validationResult.getValidationResult()) {
FeatureSwitchModel model = FeatureSwitchModel
.builder()
.validationText(result.validationResult.getValidationText())
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(ENABLE_FEATURE_RESPONSE_TEMPLATE_KEY, model, serverId);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(message -> CommandResult.fromIgnored());
} else {
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
}
} else {
List<String> additionalFeatures = result.featureDependencies
.stream()
@@ -96,7 +106,11 @@ public class EnableFeature extends AbstractConditionableCommand {
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)
FeatureSwitchModel model = FeatureSwitchModel
.builder()
.validationText(enableFeatureResult.validationResult.getValidationText())
.build();
return interactionService.replyEmbed(ENABLE_FEATURE_RESPONSE_TEMPLATE_KEY, model, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
} else {
List<String> additionalFeatures = enableFeatureResult.featureDependencies

View File

@@ -19,6 +19,7 @@ import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.channel.concrete.Category;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
@@ -196,11 +197,8 @@ public class ChannelServiceBean implements ChannelService {
@Override
public List<CompletableFuture<Message>> sendMessageToSendToChannel(MessageToSend messageToSend, MessageChannel textChannel) {
if(messageToSend.getEphemeral()) {
throw new IllegalArgumentException("Ephemeral messages are only supported in interaction context.");
}
if(textChannel instanceof GuildMessageChannel) {
GuildMessageChannel guildMessageChannel = (GuildMessageChannel) textChannel;
messageToSend.setEphemeral(false);
if(textChannel instanceof GuildMessageChannel guildMessageChannel) {
long maxFileSize = guildMessageChannel.getGuild().getMaxFileSize();
// in this case, we cannot upload the file, so we need to fail
messageToSend.getAttachedFiles().forEach(attachedFile -> {
@@ -427,6 +425,16 @@ public class ChannelServiceBean implements ChannelService {
return channel.editMessageComponentsById(messageId, new ArrayList<>()).submit();
}
@Override
public ThreadChannel getThreadChannel(Long threadChannelId) {
return botService.getInstance().getThreadChannelById(threadChannelId);
}
@Override
public CompletableFuture<Void> archiveThreadChannel(ThreadChannel threadChannel) {
return threadChannel.getManager().setArchived(true).submit();
}
@Override
public CompletableFuture<Void> deleteTextChannel(AChannel channel) {
return deleteTextChannel(channel.getServer().getId(), channel.getId());
@@ -434,11 +442,11 @@ public class ChannelServiceBean implements ChannelService {
@Override
public CompletableFuture<Void> deleteTextChannel(Long serverId, Long channelId) {
TextChannel textChannelById = botService.getInstance().getTextChannelById(channelId);
if(textChannelById != null) {
GuildChannel channelById = botService.getInstance().getGuildChannelById(channelId);
if(channelById != null) {
log.info("Deleting channel {} on server {}.", channelId, serverId);
metricService.incrementCounter(CHANNEL_DELETE_METRIC);
return textChannelById.delete().submit();
return channelById.delete().submit();
}
throw new ChannelNotInGuildException(channelId);
}
@@ -508,6 +516,21 @@ public class ChannelServiceBean implements ChannelService {
throw new GuildNotFoundException(server.getId());
}
@Override
public CompletableFuture<ThreadChannel> createThread(TextChannel textChannel, String name) {
return textChannel.createThreadChannel(name).submit();
}
@Override
public CompletableFuture<ThreadChannel> createThreadWithStarterMessage(TextChannel textChannel, String name, Long messageId) {
return textChannel.createThreadChannel(name, messageId).submit();
}
@Override
public CompletableFuture<ThreadChannel> createPrivateThread(TextChannel textChannel, String name) {
return textChannel.createThreadChannel(name, true).submit();
}
@Override
public Optional<GuildChannel> getChannelFromAChannel(AChannel channel) {
return getGuildChannelFromServerOptional(channel.getServer().getId(), channel.getId());

View File

@@ -53,9 +53,12 @@ public class FeatureConfigServiceBean implements FeatureConfigService {
@Override
public FeatureConfig getFeatureDisplayForFeature(FeatureDefinition featureDefinition) {
Optional<FeatureConfig> any = getAllFeatureConfigs().stream().filter(featureDisplay -> featureDisplay.getFeature().equals(featureDefinition)).findAny();
if(any.isPresent()) {
return any.get();
List<FeatureConfig> allFeatureConfigs = getAllFeatureConfigs();
if(allFeatureConfigs != null) {
Optional<FeatureConfig> any = allFeatureConfigs.stream().filter(featureDisplay -> featureDisplay.getFeature().equals(featureDefinition)).findAny();
if(any.isPresent()) {
return any.get();
}
}
throw new FeatureNotFoundException(featureDefinition.getKey(), getFeaturesAsList());
}

View File

@@ -7,6 +7,7 @@ import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.exception.FeatureModeNotFoundException;
import dev.sheldan.abstracto.core.listener.AsyncStartupListener;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.models.property.FeatureModeProperty;
import dev.sheldan.abstracto.core.models.template.commands.AFeatureModeDisplay;
@@ -15,17 +16,18 @@ import dev.sheldan.abstracto.core.service.management.DefaultFeatureModeManagemen
import dev.sheldan.abstracto.core.service.management.FeatureFlagManagementService;
import dev.sheldan.abstracto.core.service.management.FeatureModeManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@Component
public class FeatureModeServiceBean implements FeatureModeService {
@Slf4j
public class FeatureModeServiceBean implements FeatureModeService, AsyncStartupListener {
@Autowired
private FeatureConfigService featureConfigService;
@@ -122,7 +124,10 @@ public class FeatureModeServiceBean implements FeatureModeService {
@Override
public List<FeatureMode> getAllAvailableFeatureModes() {
List<FeatureMode> fullFeatureModes = new ArrayList<>();
featureConfigService.getAllFeatureConfigs().forEach(featureConfig -> fullFeatureModes.addAll(featureConfig.getAvailableModes()));
List<FeatureConfig> allFeatureConfigs = featureConfigService.getAllFeatureConfigs();
if(allFeatureConfigs != null) {
allFeatureConfigs.forEach(featureConfig -> fullFeatureModes.addAll(featureConfig.getAvailableModes()));
}
return fullFeatureModes;
}
@@ -209,13 +214,16 @@ public class FeatureModeServiceBean implements FeatureModeService {
}
@PostConstruct
public void postConstruct() {
featureConfigService.getAllFeatureConfigs().forEach(featureConfig -> {
List<String> modes = new ArrayList<>();
featureConfig.getAvailableModes().forEach(featureMode -> modes.add(featureMode.getKey()));
featureModes.put(featureConfig.getFeature().getKey(), modes);
});
@Override
public void execute() {
List<FeatureConfig> allFeatureConfigs = featureConfigService.getAllFeatureConfigs();
log.info("Loading feature modes.");
if(allFeatureConfigs != null) {
allFeatureConfigs.forEach(featureConfig -> {
List<String> modes = new ArrayList<>();
featureConfig.getAvailableModes().forEach(featureMode -> modes.add(featureMode.getKey()));
featureModes.put(featureConfig.getFeature().getKey(), modes);
});
}
}
}

View File

@@ -289,11 +289,13 @@ public class PostTargetServiceBean implements PostTargetService {
public List<String> getPostTargetsOfEnabledFeatures(AServer server) {
List<String> postTargets = new ArrayList<>();
List<FeatureConfig> allFeatureConfigs = featureConfigService.getAllFeatureConfigs();
allFeatureConfigs.forEach(featureConfig -> {
if (featureFlagService.isFeatureEnabled(featureConfig, server)) {
featureConfig.getRequiredPostTargets().forEach(postTargetEnum -> postTargets.add(postTargetEnum.getKey()));
}
});
if(allFeatureConfigs != null) {
allFeatureConfigs.forEach(featureConfig -> {
if (featureFlagService.isFeatureEnabled(featureConfig, server)) {
featureConfig.getRequiredPostTargets().forEach(postTargetEnum -> postTargets.add(postTargetEnum.getKey()));
}
});
}
return postTargets;
}