added setup command which includes an interactive process in order to setup features

the current supported features are post targets and system config
the wizard includes a summary step where the user can confirm the changes
removed some unnecessary validators, which were basically just validating the system config from some features
fixed post target name
This commit is contained in:
Sheldan
2020-05-22 17:25:44 +02:00
parent e2f71ef7a3
commit 992357b2cb
81 changed files with 1074 additions and 92 deletions

View File

@@ -0,0 +1,72 @@
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.CoreFeatures;
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.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.interactive.InteractiveService;
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.SetupService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class Setup extends AbstractConditionableCommand {
@Autowired
private InteractiveService interactiveService;
@Autowired
private FeatureManagementService featureManagementService;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private SetupService setupService;
@Override
public CommandResult execute(CommandContext commandContext) {
String name = (String) commandContext.getParameters().getParameters().get(0);
if(featureManagementService.featureExists(name)) {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(name);
AServerChannelUserId initiatingUser = AServerChannelUserId
.builder()
.guildId(commandContext.getGuild().getIdLong())
.channelId(commandContext.getChannel().getIdLong())
.userId(commandContext.getAuthor().getIdLong())
.build();
setupService.performSetup(feature, initiatingUser, commandContext.getMessage().getIdLong());
}
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter newPrefixParameter = Parameter.builder().name("feature").type(String.class).build();
List<Parameter> parameters = Arrays.asList(newPrefixParameter);
HelpInfo helpInfo = HelpInfo.builder().build();
return CommandConfiguration.builder()
.name("setup")
.module(ConfigModuleInterface.CONFIG)
.parameters(parameters)
.help(helpInfo)
.causesReaction(true)
.build();
}
@Override
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -0,0 +1,38 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.exception.SetupStepException;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Getter
@Setter
public abstract class AbstractConfigSetupStep implements SetupStep {
@Autowired
private InteractiveService interactiveService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
protected SetupExecution nextStep;
@Autowired
private InteractiveUtils interactiveUtils;
protected Runnable getTimeoutRunnable(Long serverId, Long channelId) {
return () -> {
interactiveUtils.sendTimeoutMessage(serverId, channelId);
};
}
}

View File

@@ -0,0 +1,117 @@
package dev.sheldan.abstracto.core.interactive;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import com.jagrosh.jdautilities.menu.ButtonMenu;
import dev.sheldan.abstracto.core.models.FullUser;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@Component
@Slf4j
public class InteractiveServiceBean implements InteractiveService {
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
@Autowired
private EventWaiter eventWaiter;
@Autowired
private MessageService messageService;
@Autowired
private EmoteService emoteService;
@Autowired
private BotService botService;
@Override
public void createMessageWithResponse(String templateKey, AUserInAServer responder, AChannel channel, Long messageId, Consumer<MessageReceivedEvent> action, Runnable finalAction) {
String message = templateService.renderSimpleTemplate(templateKey);
channelService.sendTextToAChannel(message, channel);
eventWaiter.waitForEvent(MessageReceivedEvent.class, event -> {
if(event != null) {
return event.getAuthor().getIdLong() == responder.getUserReference().getId() && event.getMessage().getIdLong() != messageId;
}
return false;
}, action, 1, TimeUnit.MINUTES, finalAction);
}
@Override
public void createMessageWithResponse(MessageToSend messageToSend, AUserInAServer responder, AChannel channel, Long messageId, Consumer<MessageReceivedEvent> action, Runnable finalAction) {
channelService.sendMessageToSendToAChannel(messageToSend, channel);
Long userId = responder.getUserReference().getId();
eventWaiter.waitForEvent(MessageReceivedEvent.class, event -> {
if(event != null) {
return event.getAuthor().getIdLong() == userId && event.getMessage().getIdLong() != messageId;
}
return false;
}, action, 1, TimeUnit.MINUTES, finalAction);
}
@Override
public void createMessageWithConfirmation(String text, AUserInAServer responder, AChannel channel, Long messageId, Consumer<Void> confirmation, Consumer<Void> denial, Runnable finalAction) {
Long serverId = responder.getServerReference().getId();
ButtonMenu.Builder builder = new ButtonMenu.Builder();
HashMap<String, Consumer<Void>> actions = new HashMap<>();
addEmoteToBuilder("confirmation", confirmation, serverId, builder, actions);
addEmoteToBuilder("denial", denial, serverId, builder, actions);
ButtonMenu menu = builder
.setEventWaiter(eventWaiter)
.setDescription(text)
.setAction(reactionEmote -> {
if(reactionEmote.isEmoji()) {
actions.get(reactionEmote.getEmoji()).accept(null);
} else {
actions.get(reactionEmote.getEmote().getId()).accept(null);
}
})
.build();
Optional<TextChannel> textChannelInGuild = channelService.getTextChannelInGuild(serverId, channel.getId());
textChannelInGuild.ifPresent(menu::display);
}
private void addEmoteToBuilder(String key, Consumer<Void> consumer, Long serverId, ButtonMenu.Builder builder, HashMap<String, Consumer<Void>> actions) {
AEmote emoteOrFakeEmote = emoteService.getEmoteOrFakeEmote(key, serverId);
if(emoteOrFakeEmote.getCustom()){
Optional<Emote> emote = botService.getEmote(serverId, emoteOrFakeEmote);
emote.ifPresent(emote1 -> {
builder.addChoice(emote1);
actions.put(emote1.getId(), consumer);
});
} else {
builder.addChoice(emoteOrFakeEmote.getEmoteKey());
actions.put(emoteOrFakeEmote.getEmoteKey(), consumer);
}
}
}

View File

@@ -0,0 +1,29 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Component
public class InteractiveUtils {
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
@Transactional
public void sendTimeoutMessage(Long serverId, Long channelId) {
String s = templateService.renderSimpleTemplate("setup_configuration_timeout");
Optional<TextChannel> channelOptional = channelService.getTextChannelInGuild(serverId, channelId);
channelOptional.ifPresent(channel -> {
channelService.sendTextToChannelNoFuture(s, channel);
});
}
}

View File

@@ -0,0 +1,23 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class PostTargetDelayedAction implements DelayedAction {
@Autowired
private PostTargetManagement postTargetManagement;
@Override
public void execute(DelayedActionConfig delayedActionConfig) {
PostTargetDelayedActionConfig concrete = (PostTargetDelayedActionConfig) delayedActionConfig;
postTargetManagement.createOrUpdate(concrete.getPostTargetKey(), concrete.getServerId(), concrete.getChannelId());
}
@Override
public boolean handles(DelayedActionConfig delayedActionConfig) {
return delayedActionConfig instanceof PostTargetDelayedActionConfig;
}
}

View File

@@ -0,0 +1,33 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.models.template.commands.PostTargetActionModel;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.TextChannel;
@Getter
@Setter
@Builder
public class PostTargetDelayedActionConfig implements DelayedActionConfig {
private String postTargetKey;
private Long serverId;
private Long channelId;
private TextChannel textChannel;
@Override
public String getTemplateName() {
return "setup_post_target_action";
}
@Override
public Object getTemplateModel() {
return PostTargetActionModel
.builder()
.channelId(channelId)
.channel(textChannel)
.postTargetKey(postTargetKey)
.build();
}
}

View File

@@ -0,0 +1,77 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
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;
import java.util.function.Consumer;
@Component
@Slf4j
public class PostTargetSetupStep extends AbstractConfigSetupStep {
@Autowired
private ConfigService configService;
@Autowired
private InteractiveService interactiveService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private PostTargetSetupStep self;
@Override
public CompletableFuture<List<DelayedActionConfig>> execute(AServerChannelUserId user, SetupStepParameter parameter) {
PostTargetStepParameter systemConfigStepParameter = (PostTargetStepParameter) parameter;
String messageTemplateKey = "setup_posttarget_" + systemConfigStepParameter.getPostTargetKey();
Optional<AChannel> channel = channelManagementService.loadChannel(user.getChannelId());
CompletableFuture<List<DelayedActionConfig>> future = new CompletableFuture<>();
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(user.getGuildId(), user.getUserId());
if(channel.isPresent()) {
Runnable finalAction = super.getTimeoutRunnable(user.getGuildId(), user.getChannelId());
Consumer<MessageReceivedEvent> configAction = (MessageReceivedEvent event) -> {
try {
if(event.getMessage().getMentionedChannels().size() == 0) {
future.completeExceptionally(new RuntimeException());
}
TextChannel textChannel = event.getMessage().getMentionedChannels().get(0);
PostTargetDelayedActionConfig build = PostTargetDelayedActionConfig
.builder()
.postTargetKey(systemConfigStepParameter.getPostTargetKey())
.serverId(user.getGuildId())
.textChannel(textChannel)
.channelId(textChannel.getIdLong())
.build();
List<DelayedActionConfig> delayedSteps = Arrays.asList(build);
future.complete(delayedSteps);
} catch (Exception e) {
log.error("Failed to handle post target step.", e);
future.completeExceptionally(e);
}
};
interactiveService.createMessageWithResponse(messageTemplateKey, aUserInAServer, channel.get(), parameter.getPreviousMessageId(), configAction, finalAction);
} else {
future.completeExceptionally(new ChannelNotFoundException(user.getGuildId(), user.getChannelId()));
}
return future;
}
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.core.interactive;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class PostTargetStepParameter implements SetupStepParameter {
private Long previousMessageId;
private String postTargetKey;
}

View File

@@ -0,0 +1,83 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.commands.SetupSummaryModel;
import dev.sheldan.abstracto.core.service.DelayedActionService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
@Component
@Slf4j
public class SetupSummaryStep extends AbstractConfigSetupStep {
@Autowired
private InteractiveService interactiveService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private DelayedActionService delayedActionService;
@Autowired
private SetupSummaryStep self;
@Override
public CompletableFuture<List<DelayedActionConfig>> execute(AServerChannelUserId user, SetupStepParameter generalParameter) {
SetupSummaryStepParameter parameter = (SetupSummaryStepParameter) generalParameter;
SetupSummaryModel model = SetupSummaryModel
.builder()
.actionConfigs(parameter.getDelayedActionList())
.build();
String messageToSend = templateService.renderTemplate("setup_confirmation", model);
Optional<AChannel> channel = channelManagementService.loadChannel(user.getChannelId());
CompletableFuture<List<DelayedActionConfig>> future = new CompletableFuture<>();
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(user.getGuildId(), user.getUserId());
if(channel.isPresent()) {
Runnable finalAction = super.getTimeoutRunnable(user.getGuildId(), user.getChannelId());
Consumer<Void> confirmation = (Void none) -> {
try {
self.executeDelayedSteps(parameter);
future.complete(null);
} catch (Exception e) {
future.completeExceptionally(e);
}
};
Consumer<Void> denial = (Void none) -> {
log.info("Stopped wizard.");
};
interactiveService.createMessageWithConfirmation(messageToSend, aUserInAServer, channel.get(), parameter.getPreviousMessageId(), confirmation, denial, finalAction);
} else {
future.completeExceptionally(new ChannelNotFoundException(user.getGuildId(), user.getChannelId()));
}
return future;
}
@Transactional
public void executeDelayedSteps(SetupSummaryStepParameter parameter) {
delayedActionService.executeDelayedActions(parameter.getDelayedActionList());
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.core.interactive;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@Builder
public class SetupSummaryStepParameter implements SetupStepParameter {
private Long previousMessageId;
private List<DelayedActionConfig> delayedActionList;
}

View File

@@ -0,0 +1,25 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.service.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SystemConfigDelayedAction implements DelayedAction {
@Autowired
private ConfigService configService;
@Override
public void execute(DelayedActionConfig delayedActionConfig) {
SystemConfigDelayedActionConfig concrete = (SystemConfigDelayedActionConfig) delayedActionConfig;
configService.setConfigValue(concrete.getConfigKey(), concrete.getServerId(), concrete.getValue());
}
@Override
public boolean handles(DelayedActionConfig delayedActionConfig) {
return delayedActionConfig instanceof SystemConfigDelayedActionConfig;
}
}

View File

@@ -0,0 +1,29 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.models.template.commands.SystemConfigActionModel;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class SystemConfigDelayedActionConfig implements DelayedActionConfig {
private String configKey;
private Long serverId;
private String value;
@Override
public String getTemplateName() {
return "setup_system_config_action";
}
@Override
public Object getTemplateModel() {
return SystemConfigActionModel
.builder()
.configKey(this.configKey)
.newValue(this.value)
.build();
}
}

View File

@@ -0,0 +1,78 @@
package dev.sheldan.abstracto.core.interactive;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
@Component
@Slf4j
public class SystemConfigSetupStep extends AbstractConfigSetupStep {
@Autowired
private ConfigService configService;
@Autowired
private InteractiveService interactiveService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private SystemConfigSetupStep self;
@Override
public CompletableFuture<List<DelayedActionConfig>> execute(AServerChannelUserId user, SetupStepParameter parameter) {
SystemConfigStepParameter systemConfigStepParameter = (SystemConfigStepParameter) parameter;
String messageTemplateKey = "setup_config_" + systemConfigStepParameter.getConfigKey();
Optional<AChannel> channel = channelManagementService.loadChannel(user.getChannelId());
CompletableFuture<List<DelayedActionConfig>> future = new CompletableFuture<>();
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(user.getGuildId(), user.getUserId());
if(channel.isPresent()) {
Runnable finalAction = super.getTimeoutRunnable(user.getGuildId(), user.getChannelId());
Consumer<MessageReceivedEvent> configAction = (MessageReceivedEvent event) -> {
try {
self.checkValidity(user, systemConfigStepParameter, event);
SystemConfigDelayedActionConfig build = SystemConfigDelayedActionConfig
.builder()
.configKey(systemConfigStepParameter.getConfigKey())
.serverId(user.getGuildId())
.value(event.getMessage().getContentRaw())
.build();
List<DelayedActionConfig> delayedSteps = Arrays.asList(build);
future.complete(delayedSteps);
} catch (Exception e) {
future.completeExceptionally(e);
}
};
interactiveService.createMessageWithResponse(messageTemplateKey, aUserInAServer, channel.get(), parameter.getPreviousMessageId(), configAction, finalAction);
} else {
future.completeExceptionally(new ChannelNotFoundException(user.getGuildId(), user.getChannelId()));
}
return future;
}
@Transactional
public void checkValidity(AServerChannelUserId user, SystemConfigStepParameter systemConfigStepParameter, MessageReceivedEvent event) {
configService.validateConfig(systemConfigStepParameter.getConfigKey(), user.getGuildId(), event.getMessage().getContentRaw());
}
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.core.interactive;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class SystemConfigStepParameter implements SetupStepParameter {
private Long previousMessageId;
private String configKey;
}

View File

@@ -182,7 +182,7 @@ public class ChannelServiceBean implements ChannelService {
@Override
@Transactional
public List<CompletableFuture<Message>> sendTemplateInChannel(String templateKey, Object model, MessageChannel channel) {
public List<CompletableFuture<Message>> sendEmbedTemplateInChannel(String templateKey, Object model, MessageChannel channel) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(templateKey, model);
return sendMessageToSendToChannel(messageToSend, channel);
}

View File

@@ -92,4 +92,28 @@ public class ConfigServiceBean implements ConfigService{
throw new ConfigurationKeyNotFoundException(name);
}
}
@Override
public boolean configIsFitting(String name, Long serverId, String value) {
try {
validateConfig(name, serverId, value);
return true;
} catch (Exception e) {
return false;
}
}
@Override
public void validateConfig(String name, Long serverId, String value) {
if(configManagementService.configExists(serverId, name)) {
AConfig existing = configManagementService.loadConfig(serverId, name);
if(existing.getDoubleValue() != null) {
Double.parseDouble(value);
} else if(existing.getLongValue() != null) {
Long.parseLong(value);
}
} else {
throw new ConfigurationKeyNotFoundException(name);
}
}
}

View File

@@ -0,0 +1,25 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.interactive.DelayedAction;
import dev.sheldan.abstracto.core.interactive.DelayedActionConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class DelayedActionServiceBean implements DelayedActionService {
@Autowired
private List<DelayedAction> delayedActions;
@Override
public void executeDelayedActions(List<DelayedActionConfig> delayedActionConfigList) {
delayedActionConfigList.forEach(delayedActionConfig -> {
delayedActions.stream()
.filter(delayedAction -> delayedAction.handles(delayedActionConfig))
.findFirst()
.ifPresent(delayedAction -> delayedAction.execute(delayedActionConfig));
});
}
}

View File

@@ -13,6 +13,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@@ -37,27 +39,42 @@ public class MessageServiceBean implements MessageService {
@Override
public void addReactionToMessage(String emoteKey, Long serverId, Message message) {
addReactionToMessageWithFuture(emoteKey, serverId, message);
}
@Override
public CompletableFuture<Void> addReactionToMessageWithFuture(String emoteKey, Long serverId, Message message) {
Optional<Guild> guildByIdOptional = botService.getGuildById(serverId);
AEmote emote = emoteService.getEmoteOrFakeEmote(emoteKey, serverId);
if(guildByIdOptional.isPresent()) {
Guild guild = guildByIdOptional.get();
if(emote.getCustom()) {
Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId());
if(emoteById != null) {
message.addReaction(emoteById).queue();
} else {
log.error("Emote with key {} and id {} for guild {} was not found.", emoteKey, emote.getEmoteId(), guild.getId());
throw new EmoteNotDefinedException(emoteKey);
}
if(emote.getCustom()) {
Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId());
if(emoteById != null) {
return message.addReaction(emoteById).submit();
} else {
message.addReaction(emote.getEmoteKey()).queue();
log.error("Emote with key {} and id {} for guild {} was not found.", emoteKey, emote.getEmoteId(), guild.getId());
throw new EmoteNotDefinedException(emoteKey);
}
} else {
return message.addReaction(emote.getEmoteKey()).submit();
}
} else {
log.error("Cannot add reaction, guild not found {}", serverId);
throw new GuildException(serverId);
}
}
@Override
public List<CompletableFuture<Void>> addReactionsToMessageWithFuture(List<String> emoteKeys, Long serverId, Message message) {
List<CompletableFuture<Void>> futures = new ArrayList<>();
emoteKeys.forEach(s -> {
futures.add(addReactionToMessageWithFuture(s, serverId, message));
});
return futures;
}
@Override
public CompletableFuture<Void> deleteMessageInChannelInServer(Long serverId, Long channelId, Long messageId) {
return botService.deleteMessage(serverId, channelId, messageId);

View File

@@ -0,0 +1,117 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.interactive.DelayedActionConfig;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.interactive.*;
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
import dev.sheldan.abstracto.core.models.template.commands.SetupCompletedNotificationModel;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Component
@Slf4j
public class SetupServiceBean implements SetupService {
@Autowired
private SystemConfigSetupStep systemConfigSetupStep;
@Autowired
private PostTargetSetupStep postTargetSetupStep;
@Autowired
private DelayedActionService delayedActionService;
@Autowired
private SetupServiceBean self;
@Autowired
private SetupSummaryStep setupSummaryStep;
@Autowired
private ChannelService channelService;
@Autowired
private TemplateService templateService;
@Override
public void performSetup(FeatureConfig featureConfig, AServerChannelUserId user, Long initialMessageId) {
List<String> requiredSystemConfigKeys = featureConfig.getRequiredSystemConfigKeys();
List<SetupExecution> steps = new ArrayList<>();
requiredSystemConfigKeys.forEach(s -> {
SetupExecution execution = SetupExecution
.builder()
.step(systemConfigSetupStep)
.parameter(SystemConfigStepParameter.builder().configKey(s).build())
.build();
steps.add(execution);
});
featureConfig.getRequiredPostTargets().forEach(postTargetEnum -> {
SetupExecution execution = SetupExecution
.builder()
.step(postTargetSetupStep)
.parameter(PostTargetStepParameter.builder().postTargetKey(postTargetEnum.getKey()).build())
.build();
steps.add(execution);
});
for (int i = 0; i < steps.size(); i++) {
SetupExecution setupExecution = steps.get(i);
setupExecution.getParameter().setPreviousMessageId(initialMessageId);
if(i < steps.size() - 1) {
setupExecution.setNextStep(steps.get(i + 1));
}
}
executeSetup(featureConfig, steps, user, new ArrayList<>());
}
@Override
public void executeSetup(FeatureConfig featureConfig, List<SetupExecution> steps, AServerChannelUserId user, List<DelayedActionConfig> delayedActionConfigs) {
steps.stream().findFirst().ifPresent(execution -> executeStep(user, execution, delayedActionConfigs, featureConfig));
}
private void executeStep(AServerChannelUserId aUserInAServer, SetupExecution execution, List<DelayedActionConfig> delayedActionConfigs, FeatureConfig featureConfig) {
execution.getStep().execute(aUserInAServer, execution.getParameter()).thenAccept(aVoid -> {
delayedActionConfigs.addAll(aVoid);
if(execution.getNextStep() != null) {
executeStep(aUserInAServer, execution.getNextStep(), delayedActionConfigs, featureConfig);
} else {
self.executePostSetupSteps(delayedActionConfigs, aUserInAServer, execution.getParameter().getPreviousMessageId(), featureConfig);
}
}).exceptionally(throwable -> {
executeStep(aUserInAServer, execution, delayedActionConfigs, featureConfig);
return null;
});
}
@Transactional
public void executePostSetupSteps(List<DelayedActionConfig> delayedActionConfigs, AServerChannelUserId user, Long initialMessage, FeatureConfig featureConfig) {
SetupSummaryStepParameter parameter = SetupSummaryStepParameter
.builder()
.delayedActionList(delayedActionConfigs)
.previousMessageId(initialMessage)
.build();
setupSummaryStep.execute(user, parameter).thenAccept(ignored -> {
self.notifyAboutCompletion(user, featureConfig);
});
}
@Transactional
public void notifyAboutCompletion(AServerChannelUserId aServerChannelUserId, FeatureConfig featureConfig) {
String templateName = "setup_completion_notification";
SetupCompletedNotificationModel model = SetupCompletedNotificationModel
.builder()
.completedFeature(featureConfig)
.build();
String text = templateService.renderTemplate(templateName, model);
Optional<TextChannel> textChannel = channelService.getTextChannelInGuild(aServerChannelUserId.getGuildId(), aServerChannelUserId.getChannelId());
textChannel.ifPresent(channel -> channelService.sendTextToChannel(text, channel));
}
}

View File

@@ -4,4 +4,8 @@ abstracto.parameter.lowerBound=50
abstracto.features.core.enabled=true
abstracto.prefix=!
abstracto.eventWaiter.threads=10
abstracto.eventWaiter.threads=10
abstracto.emoteNames.confirmation=confirmation,denial
abstracto.defaultEmotes.confirmation=\u2705
abstracto.defaultEmotes.denial=\u274c

View File

@@ -0,0 +1 @@
<#assign featureName><#include "${completedFeature.feature.key}_feature"></#assign><#include "setup_completion_message">

View File

@@ -0,0 +1,5 @@
<#list actionConfigs as actionConfig>
<#assign param=actionConfig.templateModel>
<#include "${actionConfig.templateName}">
</#list>

View File

@@ -0,0 +1 @@
<#assign postTargetKey>${param.postTargetKey}</#assign><#assign channel>${param.channel.asMention}</#assign><#include "setup_post_target_action_display">

View File

@@ -0,0 +1 @@
<#assign configKey>${param.configKey}</#assign><#assign newValue>${param.newValue}</#assign><#include "setup_system_config_action_display">