mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-25 20:04:01 +00:00
[AB-90] adding poll functionality
adding select menu functionality not automatically acknowledging button interactions adding ability to define positions for components adding method to remove components to channel service always replacing message contents with edit message in a channel adding ability to reply a modal to a button interaction moving post target specific methods from server management service to post target management
This commit is contained in:
@@ -72,6 +72,11 @@ public class ListenerExecutorConfig {
|
||||
return executorService.setupExecutorFor("buttonClickedListener");
|
||||
}
|
||||
|
||||
@Bean(name = "stringSelectMenuExecutor")
|
||||
public TaskExecutor stringSelectMenuExecutor() {
|
||||
return executorService.setupExecutorFor("stringSelectMenuListener");
|
||||
}
|
||||
|
||||
@Bean(name = "modalInteractionExecutor")
|
||||
public TaskExecutor modalInteractionExecutor() {
|
||||
return executorService.setupExecutorFor("modalInteractionListener");
|
||||
|
||||
@@ -101,7 +101,7 @@ public class InteractionServiceBean implements InteractionService {
|
||||
if(component instanceof ActionComponent) {
|
||||
String id = ((ActionComponent)component).getId();
|
||||
MessageToSend.ComponentConfig payload = messageToSend.getComponentPayloads().get(id);
|
||||
if(payload.getPersistCallback()) {
|
||||
if(payload != null && payload.getPersistCallback()) {
|
||||
componentPayloadManagementService.createPayload(id, payload.getPayload(), payload.getPayloadType(), payload.getComponentOrigin(), server, payload.getComponentType());
|
||||
}
|
||||
}
|
||||
@@ -217,7 +217,7 @@ public class InteractionServiceBean implements InteractionService {
|
||||
if(component instanceof ActionComponent) {
|
||||
String id = ((ActionComponent)component).getId();
|
||||
MessageToSend.ComponentConfig payload = messageToSend.getComponentPayloads().get(id);
|
||||
if(payload.getPersistCallback()) {
|
||||
if(payload != null && payload.getPersistCallback()) {
|
||||
componentPayloadManagementService.createPayload(id, payload.getPayload(), payload.getPayloadType(), payload.getComponentOrigin(), server, payload.getComponentType());
|
||||
}
|
||||
}
|
||||
@@ -273,7 +273,7 @@ public class InteractionServiceBean implements InteractionService {
|
||||
if(component instanceof ActionComponent) {
|
||||
String id = ((ActionComponent)component).getId();
|
||||
MessageToSend.ComponentConfig payload = messageToSend.getComponentPayloads().get(id);
|
||||
if(payload.getPersistCallback()) {
|
||||
if(payload != null && payload.getPersistCallback()) {
|
||||
componentPayloadManagementService.createPayload(id, payload.getPayload(), payload.getPayloadType(), payload.getComponentOrigin(), server, payload.getComponentType());
|
||||
}
|
||||
}
|
||||
@@ -303,6 +303,11 @@ public class InteractionServiceBean implements InteractionService {
|
||||
return replyMessageToSend(messageToSend, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Message> replyString(String text, InteractionHook interactionHook) {
|
||||
return interactionHook.sendMessage(text).submit();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
metricService.registerCounter(EPHEMERAL_MESSAGES_SEND, "Ephemeral messages send");
|
||||
|
||||
@@ -62,8 +62,6 @@ public class SyncButtonClickedListenerBean extends ListenerAdapter {
|
||||
@Override
|
||||
public void onButtonInteraction(@Nonnull 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);
|
||||
return null;
|
||||
@@ -83,6 +81,9 @@ public class SyncButtonClickedListenerBean extends ListenerAdapter {
|
||||
if(listenerOptional.isPresent()) {
|
||||
listener = listenerOptional.get();
|
||||
log.info("Executing button listener {} for event for id {}.", listener.getClass().getSimpleName(), event.getComponentId());
|
||||
if(listener.autoAcknowledgeEvent()) {
|
||||
event.deferEdit().queue();
|
||||
}
|
||||
listener.execute(model);
|
||||
InteractionResult result = InteractionResult.fromSuccess();
|
||||
for (ButtonPostInteractionExecution postInteractionExecution : postInteractionExecutions) {
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package dev.sheldan.abstracto.core.interaction.menu;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import dev.sheldan.abstracto.core.config.FeatureConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentPayloadManagementService;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionResult;
|
||||
import dev.sheldan.abstracto.core.interaction.menu.listener.StringSelectMenuListener;
|
||||
import dev.sheldan.abstracto.core.interaction.menu.listener.StringSelectMenuListenerModel;
|
||||
import dev.sheldan.abstracto.core.models.database.ComponentPayload;
|
||||
import dev.sheldan.abstracto.core.service.FeatureConfigService;
|
||||
import dev.sheldan.abstracto.core.service.FeatureFlagService;
|
||||
import dev.sheldan.abstracto.core.service.FeatureModeService;
|
||||
import dev.sheldan.abstracto.core.utils.BeanUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
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.Nonnull;
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class StringSelectMenuListenerBean extends ListenerAdapter {
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<StringSelectMenuListener> listenerList;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("buttonClickedExecutor")
|
||||
private TaskExecutor buttonClickedExecutor;
|
||||
|
||||
@Autowired
|
||||
private StringSelectMenuListenerBean self;
|
||||
|
||||
@Autowired
|
||||
private FeatureConfigService featureConfigService;
|
||||
|
||||
@Autowired
|
||||
private FeatureFlagService featureFlagService;
|
||||
|
||||
@Autowired
|
||||
private ComponentPayloadManagementService componentPayloadManagementService;
|
||||
|
||||
@Autowired
|
||||
private FeatureModeService featureModeService;
|
||||
|
||||
@Autowired
|
||||
private Gson gson;
|
||||
|
||||
@Override
|
||||
public void onStringSelectInteraction(@Nonnull StringSelectInteractionEvent event) {
|
||||
if(listenerList == null) return;
|
||||
event.deferEdit().queue();
|
||||
CompletableFuture.runAsync(() -> self.executeListenerLogic(event), buttonClickedExecutor).exceptionally(throwable -> {
|
||||
log.error("Failed to execute listener logic in async button event.", throwable);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void executeListenerLogic(StringSelectInteractionEvent event) {
|
||||
StringSelectMenuListenerModel model = null;
|
||||
StringSelectMenuListener listener = null;
|
||||
try {
|
||||
Optional<ComponentPayload> callbackInformation = componentPayloadManagementService.findPayload(event.getComponentId());
|
||||
if(callbackInformation.isPresent()) {
|
||||
model = getModel(event, callbackInformation.get());
|
||||
List<StringSelectMenuListener> validListener = filterFeatureAwareListener(listenerList, model);
|
||||
Optional<StringSelectMenuListener> listenerOptional = findListener(validListener, model);
|
||||
if(listenerOptional.isPresent()) {
|
||||
listener = listenerOptional.get();
|
||||
log.info("Executing string select menu listener {} for event for id {}.", listener.getClass().getSimpleName(), event.getComponentId());
|
||||
listener.execute(model);
|
||||
} else {
|
||||
log.warn("No listener found for string select menu event for id {}.", event.getComponentId());
|
||||
}
|
||||
} else {
|
||||
log.warn("No callback found for id {}.", event.getComponentId());
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
if(event.isFromGuild()) {
|
||||
log.error("String select menu listener failed with exception in server {} and channel {}.", event.getGuild().getIdLong(),
|
||||
event.getGuildChannel().getIdLong(), exception);
|
||||
} else {
|
||||
log.error("String select menu listener failed with exception outside of a guild.", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Optional<StringSelectMenuListener> findListener(List<StringSelectMenuListener> featureAwareListeners, StringSelectMenuListenerModel model) {
|
||||
return featureAwareListeners.stream().filter(asyncButtonClickedListener -> asyncButtonClickedListener.handlesEvent(model)).findFirst();
|
||||
}
|
||||
|
||||
private List<StringSelectMenuListener> filterFeatureAwareListener(List<StringSelectMenuListener> featureAwareListeners, StringSelectMenuListenerModel model) {
|
||||
return featureAwareListeners.stream().filter(trFeatureAwareListener -> {
|
||||
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(trFeatureAwareListener.getFeature());
|
||||
if(!model.getEvent().isFromGuild()) {
|
||||
return true;
|
||||
}
|
||||
if (!featureFlagService.isFeatureEnabled(feature, model.getServerId())) {
|
||||
return false;
|
||||
}
|
||||
return featureModeService.necessaryFeatureModesMet(trFeatureAwareListener, model.getServerId());
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private StringSelectMenuListenerModel getModel(StringSelectInteractionEvent event, ComponentPayload componentPayload) throws ClassNotFoundException {
|
||||
SelectMenuPayload payload = null;
|
||||
if(componentPayload.getPayloadType() != null && componentPayload.getPayload() != null) {
|
||||
payload = (SelectMenuPayload) gson.fromJson(componentPayload.getPayload(), Class.forName(componentPayload.getPayloadType()));
|
||||
}
|
||||
return StringSelectMenuListenerModel
|
||||
.builder()
|
||||
.event(event)
|
||||
.deserializedPayload(payload)
|
||||
.payload(componentPayload.getPayload())
|
||||
.origin(componentPayload.getOrigin())
|
||||
.build();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
BeanUtils.sortPrioritizedListeners(listenerList);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import dev.sheldan.abstracto.core.interaction.modal.config.TextInputComponent;
|
||||
import dev.sheldan.abstracto.core.interaction.modal.config.TextInputComponentStyle;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import net.dv8tion.jda.api.events.interaction.command.GenericCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||
import net.dv8tion.jda.api.interactions.components.ItemComponent;
|
||||
import net.dv8tion.jda.api.interactions.components.text.TextInput;
|
||||
@@ -34,6 +35,12 @@ public class ModalServiceBean implements ModalService {
|
||||
return event.replyModal(modal).submit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> replyModal(ButtonInteractionEvent event, String templateKey, Object model) {
|
||||
Modal modal = createModalFromTemplate(templateKey, model, event.getGuild().getIdLong());
|
||||
return event.replyModal(modal).submit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modal createModalFromTemplate(String templateKey, Object model, Long serverId) {
|
||||
String modalConfigString = templateService.renderTemplate(templateKey + "_modal", model, serverId);
|
||||
@@ -44,7 +51,7 @@ public class ModalServiceBean implements ModalService {
|
||||
.sorted(Comparator.comparing(ModalComponent::getPosition))
|
||||
.collect(Collectors.toList());
|
||||
return Modal.create(modalConfig.getId(), modalConfig.getTitle())
|
||||
.addActionRows(convertToActionRows(components))
|
||||
.addComponents(convertToActionRows(components))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -123,12 +123,12 @@ public class SlashCommandParameterServiceBean implements SlashCommandParameterSe
|
||||
|
||||
@Override
|
||||
public Object getCommandOption(String name, SlashCommandInteractionEvent event) {
|
||||
return event.getOption(name);
|
||||
return event.getOption(name.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean hasCommandOption(String name, SlashCommandInteractionEvent event) {
|
||||
return event.getOption(name) != null;
|
||||
return event.getOption(name.toLowerCase(Locale.ROOT)) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -350,7 +350,7 @@ public class ChannelServiceBean implements ChannelService {
|
||||
.collect(Collectors.toList());
|
||||
messageAction = messageAction.setFiles(files);
|
||||
}
|
||||
messageAction = messageAction.setComponents(messageToSend.getActionRows());
|
||||
messageAction = messageAction.setComponents(messageToSend.getActionRows()).setReplace(true);
|
||||
metricService.incrementCounter(MESSAGE_EDIT_METRIC);
|
||||
return messageAction.submit();
|
||||
}
|
||||
@@ -411,6 +411,11 @@ public class ChannelServiceBean implements ChannelService {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Message> removeComponents(MessageChannel channel, Long messageId) {
|
||||
return channel.editMessageComponentsById(messageId, new ArrayList<>()).submit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteTextChannel(AChannel channel) {
|
||||
return deleteTextChannel(channel.getServer().getId(), channel.getId());
|
||||
|
||||
@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.core.service.management;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentPayloadManagementService;
|
||||
import dev.sheldan.abstracto.core.interaction.menu.SelectMenuConfigModel;
|
||||
import dev.sheldan.abstracto.core.interaction.modal.ModalConfigPayload;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.ComponentPayload;
|
||||
@@ -59,6 +60,18 @@ public class ComponentPayloadManagementServiceBean implements ComponentPayloadMa
|
||||
return createButtonPayload(buttonConfigModel, server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentPayload createStringSelectMenuPayload(SelectMenuConfigModel selectMenuConfigModel, Long serverId) {
|
||||
AServer server = serverManagementService.loadOrCreate(serverId);
|
||||
return createStringSelectMenuPayload(selectMenuConfigModel, server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentPayload createStringSelectMenuPayload(SelectMenuConfigModel selectMenuConfigModel, AServer server) {
|
||||
String payload = gson.toJson(selectMenuConfigModel.getSelectMenuPayload());
|
||||
return createPayload(selectMenuConfigModel.getSelectMenuId(), payload, selectMenuConfigModel.getPayloadType(), selectMenuConfigModel.getOrigin(), server, ComponentType.SELECTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentPayload createModalPayload(ModalConfigPayload payloadConfig, Long serverId) {
|
||||
String payload = gson.toJson(payloadConfig.getModalPayload());
|
||||
|
||||
@@ -130,4 +130,27 @@ public class PostTargetManagementBean implements PostTargetManagement {
|
||||
return postTargetRepository.findByServerReference(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AChannel getPostTarget(Long serverId, String name) {
|
||||
AServer server = serverManagementService.loadOrCreate(serverId);
|
||||
return getPostTarget(server, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AChannel getPostTarget(Long serverId, PostTarget target) {
|
||||
AServer server = serverManagementService.loadOrCreate(serverId);
|
||||
return getPostTarget(server, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AChannel getPostTarget(AServer server, PostTarget target) {
|
||||
return target.getChannelReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AChannel getPostTarget(AServer server, String name) {
|
||||
PostTarget target = getPostTarget(name, server);
|
||||
return getPostTarget(server, target);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@ public class ServerManagementServiceBean implements ServerManagementService {
|
||||
@Autowired
|
||||
private ServerRepository repository;
|
||||
|
||||
@Autowired
|
||||
private PostTargetManagement postTargetManagement;
|
||||
|
||||
@Autowired
|
||||
private UserManagementService userManagementService;
|
||||
|
||||
@@ -93,29 +90,6 @@ public class ServerManagementServiceBean implements ServerManagementService {
|
||||
return aUserInAServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AChannel getPostTarget(Long serverId, String name) {
|
||||
AServer server = this.loadOrCreate(serverId);
|
||||
return getPostTarget(server, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AChannel getPostTarget(Long serverId, PostTarget target) {
|
||||
AServer server = this.loadOrCreate(serverId);
|
||||
return getPostTarget(server, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AChannel getPostTarget(AServer server, PostTarget target) {
|
||||
return target.getChannelReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AChannel getPostTarget(AServer server, String name) {
|
||||
PostTarget target = postTargetManagement.getPostTarget(name, server);
|
||||
return getPostTarget(server, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AServer> getAllServers() {
|
||||
return repository.findAll();
|
||||
|
||||
@@ -17,4 +17,5 @@ public class ButtonConfig {
|
||||
private String buttonPayload;
|
||||
private String payloadType;
|
||||
private ButtonMetaConfig metaConfig;
|
||||
private Integer position;
|
||||
}
|
||||
|
||||
@@ -22,5 +22,6 @@ public class MessageConfiguration {
|
||||
private String additionalMessage;
|
||||
private MetaMessageConfiguration messageConfig;
|
||||
private List<ButtonConfig> buttons;
|
||||
private List<SelectionMenuConfig> selectionMenus;
|
||||
private List<FileConfig> files;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.sheldan.abstracto.core.templating.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public enum SelectionMenuChannelType {
|
||||
@SerializedName("TEXT")
|
||||
TEXT,
|
||||
@SerializedName("VOICE")
|
||||
VOICE
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package dev.sheldan.abstracto.core.templating.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class SelectionMenuConfig {
|
||||
private String id;
|
||||
private SelectionMenuType type;
|
||||
private List<SelectionMenuTarget> targets;
|
||||
private List<SelectionMenuChannelType> channelTypes;
|
||||
private List<SelectionMenuEntry> menuEntries;
|
||||
private Integer position;
|
||||
private Integer minValues;
|
||||
private Integer maxValues;
|
||||
private Boolean disabled;
|
||||
private String placeholder;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.core.templating.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class SelectionMenuEntry {
|
||||
private String value;
|
||||
private String label;
|
||||
private Boolean isDefault;
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package dev.sheldan.abstracto.core.templating.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public enum SelectionMenuTarget {
|
||||
@SerializedName("USER")
|
||||
USER,
|
||||
@SerializedName("ROLE")
|
||||
ROLE,
|
||||
@SerializedName("CHANNEL")
|
||||
CHANNEL
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.sheldan.abstracto.core.templating.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public enum SelectionMenuType {
|
||||
@SerializedName("STRING")
|
||||
STRING,
|
||||
@SerializedName("ENTITY")
|
||||
ENTITY
|
||||
}
|
||||
@@ -21,10 +21,16 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
||||
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||
import net.dv8tion.jda.api.interactions.components.ItemComponent;
|
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||
import net.dv8tion.jda.api.interactions.components.selections.EntitySelectMenu;
|
||||
import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
|
||||
import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -99,54 +105,74 @@ public class TemplateServiceBean implements TemplateService {
|
||||
convertEmbeds(messageConfiguration, embedBuilders);
|
||||
}
|
||||
|
||||
List<ActionRow> buttons = new ArrayList<>();
|
||||
List<ActionRow> actionRows = new ArrayList<>();
|
||||
Map<String, MessageToSend.ComponentConfig> componentPayloads = new HashMap<>();
|
||||
if(messageConfiguration.getButtons() != null) {
|
||||
ActionRow currentRow = null;
|
||||
for (ButtonConfig buttonConfig : messageConfiguration.getButtons()) {
|
||||
ButtonMetaConfig metaConfig = buttonConfig.getMetaConfig() != null ? buttonConfig.getMetaConfig() : null;
|
||||
String id = metaConfig != null && Boolean.TRUE.equals(metaConfig.getGenerateRandomUUID()) ?
|
||||
UUID.randomUUID().toString() : buttonConfig.getId();
|
||||
String componentOrigin = metaConfig != null ? metaConfig.getButtonOrigin() : null;
|
||||
MessageToSend.ComponentConfig componentConfig = null;
|
||||
try {
|
||||
componentConfig = MessageToSend.ComponentConfig
|
||||
.builder()
|
||||
.componentOrigin(componentOrigin)
|
||||
.componentType(ComponentType.BUTTON)
|
||||
.persistCallback(metaConfig != null && Boolean.TRUE.equals(metaConfig.getPersistCallback()))
|
||||
.payload(buttonConfig.getButtonPayload())
|
||||
.payloadType(buttonConfig.getPayloadType() != null ? Class.forName(buttonConfig.getPayloadType()) : null)
|
||||
.build();
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new AbstractoRunTimeException("Referenced class in button config could not be found: " + buttonConfig.getPayloadType(), e);
|
||||
}
|
||||
componentPayloads.put(id, componentConfig);
|
||||
String idOrUl = buttonConfig.getUrl() == null ? buttonConfig.getId() : buttonConfig.getUrl();
|
||||
Button createdButton = Button.of(ButtonStyleConfig.getStyle(buttonConfig.getButtonStyle()), idOrUl, buttonConfig.getLabel());
|
||||
if (buttonConfig.getDisabled() != null) {
|
||||
createdButton = createdButton.withDisabled(buttonConfig.getDisabled());
|
||||
}
|
||||
if (buttonConfig.getEmoteMarkdown() != null) {
|
||||
createdButton = createdButton.withEmoji(Emoji.fromFormatted(buttonConfig.getEmoteMarkdown()));
|
||||
}
|
||||
if(currentRow == null) {
|
||||
currentRow = ActionRow.of(createdButton);
|
||||
} else if (
|
||||
(
|
||||
metaConfig != null &&
|
||||
Boolean.TRUE.equals(metaConfig.getForceNewRow())
|
||||
)
|
||||
|| currentRow.getComponents().size() == ComponentServiceBean.MAX_BUTTONS_PER_ROW) {
|
||||
buttons.add(currentRow);
|
||||
currentRow = ActionRow.of(createdButton);
|
||||
if(messageConfiguration.getButtons() != null || messageConfiguration.getSelectionMenus() != null) {
|
||||
// this basically preprocesses the buttons and select menus
|
||||
// by getting the positions of the items first
|
||||
// we only need this, because the current message config does not have them in the same item
|
||||
// they are two distinct lists, but map to the same concept in discord: components
|
||||
Set<Integer> positions = new HashSet<>();
|
||||
HashMap<Integer, ButtonConfig> buttonPositions = new HashMap<>();
|
||||
List<ButtonConfig> buttonsWithoutPosition = new ArrayList<>();
|
||||
HashMap<Integer, SelectionMenuConfig> selectionMenuPositions = new HashMap<>();
|
||||
List<SelectionMenuConfig> selectionMenusWithoutPosition = new ArrayList<>();
|
||||
// we do this by getting all positions which are part of the config
|
||||
// we also track which positions are buttons and which are select menus
|
||||
if(messageConfiguration.getButtons() != null) {
|
||||
messageConfiguration.getButtons().forEach(buttonConfig -> {
|
||||
if(buttonConfig.getPosition() != null) {
|
||||
positions.add(buttonConfig.getPosition());
|
||||
buttonPositions.put(buttonConfig.getPosition(), buttonConfig);
|
||||
} else {
|
||||
buttonsWithoutPosition.add(buttonConfig);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(messageConfiguration.getSelectionMenus() != null) {
|
||||
messageConfiguration.getSelectionMenus().forEach(selectionMenuConfig -> {
|
||||
if(selectionMenuConfig.getPosition() != null) {
|
||||
positions.add(selectionMenuConfig.getPosition());
|
||||
selectionMenuPositions.put(selectionMenuConfig.getPosition(), selectionMenuConfig);
|
||||
} else {
|
||||
selectionMenusWithoutPosition.add(selectionMenuConfig);
|
||||
}
|
||||
});
|
||||
}
|
||||
List<Integer> positionsSorted = new ArrayList<>(positions);
|
||||
Collections.sort(positionsSorted);
|
||||
List<ButtonConfig> currentButtons = new ArrayList<>();
|
||||
// we go over all positions, and if its part of the buttons, we only add it to a list of buttons
|
||||
// this will then mean, that all buttons are processed as a group
|
||||
// this is necessary, because we can only add buttons as part of an action row
|
||||
// and in order to make it easier, we process the whole chunk of buttons at once, producing
|
||||
// at least one or more action rows
|
||||
for (Integer position : positionsSorted) {
|
||||
if (buttonPositions.containsKey(position)) {
|
||||
currentButtons.add(buttonPositions.get(position));
|
||||
} else {
|
||||
currentRow.getComponents().add(createdButton);
|
||||
// if we get interrupted by a selection menu, we process the buttons we have so far
|
||||
// because those should be handled as a group
|
||||
// and then process the selection menu, the selection menu will always represent one full action row
|
||||
// it is not possible to have a button and a menu in the same row
|
||||
if(!currentButtons.isEmpty()) {
|
||||
addButtons(actionRows, componentPayloads, currentButtons);
|
||||
currentButtons.clear();
|
||||
}
|
||||
addSelectionMenu(actionRows, selectionMenuPositions.get(position));
|
||||
}
|
||||
}
|
||||
if(currentRow != null) {
|
||||
buttons.add(currentRow);
|
||||
if(!currentButtons.isEmpty()) {
|
||||
addButtons(actionRows, componentPayloads, currentButtons);
|
||||
currentButtons.clear();
|
||||
}
|
||||
// all the rest without positions will be processed at the end (probably default case for most cases)
|
||||
addButtons(actionRows, componentPayloads, buttonsWithoutPosition);
|
||||
// selection menus are handled afterwards, that is just implied logic
|
||||
// to have a select menu before a button, one would need to set accordingly, or only
|
||||
// set the position for the selection menu, and not for the button
|
||||
selectionMenusWithoutPosition.forEach(selectionMenuConfig -> addSelectionMenu(actionRows, selectionMenuConfig));
|
||||
}
|
||||
|
||||
setPagingFooters(embedBuilders);
|
||||
@@ -244,12 +270,129 @@ public class TemplateServiceBean implements TemplateService {
|
||||
.messages(messages)
|
||||
.ephemeral(isEphemeral)
|
||||
.attachedFiles(files)
|
||||
.actionRows(buttons)
|
||||
.actionRows(actionRows)
|
||||
.componentPayloads(componentPayloads)
|
||||
.referencedMessageId(referencedMessageId)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void addSelectionMenu(List<ActionRow> actionRows, SelectionMenuConfig selectionMenuConfig) {
|
||||
ItemComponent selectionMenu;
|
||||
if (selectionMenuConfig.getType() == SelectionMenuType.STRING) {
|
||||
List<SelectOption> selectOptions = selectionMenuConfig.getMenuEntries().stream().map(selectionMenuEntry -> {
|
||||
SelectOption option = SelectOption.of(selectionMenuEntry.getLabel(), selectionMenuEntry.getValue());
|
||||
if (StringUtils.isNotBlank(selectionMenuEntry.getDescription())) {
|
||||
option = option.withDescription(selectionMenuEntry.getDescription());
|
||||
}
|
||||
if(Boolean.TRUE.equals(selectionMenuEntry.getIsDefault())) {
|
||||
option = option.withDefault(true);
|
||||
}
|
||||
return option;
|
||||
}).collect(Collectors.toList());
|
||||
StringSelectMenu.Builder builder = StringSelectMenu
|
||||
.create(selectionMenuConfig.getId())
|
||||
.addOptions(selectOptions);
|
||||
List<SelectOption> defaultOptions = selectOptions
|
||||
.stream()
|
||||
.filter(SelectOption::isDefault)
|
||||
.collect(Collectors.toList());
|
||||
builder.setDefaultOptions(defaultOptions);
|
||||
if (selectionMenuConfig.getMaxValues() != null) {
|
||||
builder.setMaxValues(selectionMenuConfig.getMaxValues());
|
||||
}
|
||||
if (selectionMenuConfig.getMinValues() != null) {
|
||||
builder.setMinValues(selectionMenuConfig.getMinValues());
|
||||
}
|
||||
if (selectionMenuConfig.getPlaceholder() != null) {
|
||||
builder.setPlaceholder(selectionMenuConfig.getPlaceholder());
|
||||
}
|
||||
selectionMenu = builder.build();
|
||||
} else {
|
||||
Set<EntitySelectMenu.SelectTarget> targets = new HashSet<>();
|
||||
if(selectionMenuConfig.getTargets() != null) {
|
||||
selectionMenuConfig.getTargets().forEach(selectionMenuTarget -> {
|
||||
switch (selectionMenuTarget) {
|
||||
case ROLE:
|
||||
targets.add(EntitySelectMenu.SelectTarget.ROLE);
|
||||
break;
|
||||
case USER:
|
||||
targets.add(EntitySelectMenu.SelectTarget.USER);
|
||||
break;
|
||||
case CHANNEL:
|
||||
targets.add(EntitySelectMenu.SelectTarget.CHANNEL);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Set<ChannelType> channelTypes = new HashSet<>();
|
||||
if(selectionMenuConfig.getChannelTypes() != null) {
|
||||
selectionMenuConfig.getChannelTypes().forEach(channelType -> {
|
||||
switch (channelType) {
|
||||
case TEXT:
|
||||
channelTypes.add(ChannelType.TEXT);
|
||||
break;
|
||||
case VOICE:
|
||||
channelTypes.add(ChannelType.VOICE);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
selectionMenu = EntitySelectMenu.create(selectionMenuConfig.getId(), targets)
|
||||
.setChannelTypes(channelTypes)
|
||||
.build();
|
||||
}
|
||||
actionRows.add(ActionRow.of(selectionMenu));
|
||||
}
|
||||
|
||||
private void addButtons(List<ActionRow> actionRows, Map<String, MessageToSend.ComponentConfig> componentPayloads, List<ButtonConfig> buttonConfigs) {
|
||||
ActionRow currentRow = null;
|
||||
for (ButtonConfig buttonConfig : buttonConfigs) {
|
||||
ButtonMetaConfig metaConfig = buttonConfig.getMetaConfig() != null ? buttonConfig.getMetaConfig() : null;
|
||||
String id = metaConfig != null && Boolean.TRUE.equals(metaConfig.getGenerateRandomUUID()) ?
|
||||
UUID.randomUUID().toString() : buttonConfig.getId();
|
||||
String componentOrigin = metaConfig != null ? metaConfig.getButtonOrigin() : null;
|
||||
MessageToSend.ComponentConfig componentConfig = null;
|
||||
try {
|
||||
componentConfig = MessageToSend.ComponentConfig
|
||||
.builder()
|
||||
.componentOrigin(componentOrigin)
|
||||
.componentType(ComponentType.BUTTON)
|
||||
.persistCallback(metaConfig != null && Boolean.TRUE.equals(metaConfig.getPersistCallback()))
|
||||
.payload(buttonConfig.getButtonPayload())
|
||||
.payloadType(buttonConfig.getPayloadType() != null ? Class.forName(buttonConfig.getPayloadType()) : null)
|
||||
.build();
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new AbstractoRunTimeException("Referenced class in button config could not be found: " + buttonConfig.getPayloadType(), e);
|
||||
}
|
||||
componentPayloads.put(id, componentConfig);
|
||||
String idOrUl = buttonConfig.getUrl() == null ? buttonConfig.getId() : buttonConfig.getUrl();
|
||||
Button createdButton = Button.of(ButtonStyleConfig.getStyle(buttonConfig.getButtonStyle()), idOrUl, buttonConfig.getLabel());
|
||||
if (buttonConfig.getDisabled() != null) {
|
||||
createdButton = createdButton.withDisabled(buttonConfig.getDisabled());
|
||||
}
|
||||
if (buttonConfig.getEmoteMarkdown() != null) {
|
||||
createdButton = createdButton.withEmoji(Emoji.fromFormatted(buttonConfig.getEmoteMarkdown()));
|
||||
}
|
||||
if(currentRow == null) {
|
||||
currentRow = ActionRow.of(createdButton);
|
||||
} else if (
|
||||
(
|
||||
metaConfig != null &&
|
||||
Boolean.TRUE.equals(metaConfig.getForceNewRow())
|
||||
)
|
||||
|| currentRow.getComponents().size() == ComponentServiceBean.MAX_BUTTONS_PER_ROW) {
|
||||
actionRows.add(currentRow);
|
||||
currentRow = ActionRow.of(createdButton);
|
||||
} else {
|
||||
currentRow.getComponents().add(createdButton);
|
||||
}
|
||||
}
|
||||
if(currentRow != null) {
|
||||
actionRows.add(currentRow);
|
||||
}
|
||||
}
|
||||
|
||||
private void convertEmbeds(MessageConfiguration messageConfiguration, List<EmbedBuilder> embedBuilders) {
|
||||
int currentEffectiveEmbed;
|
||||
for (int embedIndex = 0; embedIndex < messageConfiguration.getEmbeds().size(); embedIndex++) {
|
||||
|
||||
Reference in New Issue
Block a user