diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelType.java b/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelType.java index 7fce6cdcc1..6d1ff37d34 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelType.java +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/ChannelType.java @@ -171,6 +171,17 @@ public boolean isThread() } } + /** + * All the channel types for a {@link net.dv8tion.jda.api.entities.Guild Guild}. + * + * @return {@link EnumSet} of {@link ChannelType} + */ + @Nonnull + public static EnumSet guildTypes() + { + return EnumSet.complementOf(EnumSet.of(PRIVATE, GROUP, UNKNOWN)); + } + /** * Static accessor for retrieving a channel type based on its Discord id key. * diff --git a/src/main/java/net/dv8tion/jda/api/events/interaction/component/EntitySelectInteractionEvent.java b/src/main/java/net/dv8tion/jda/api/events/interaction/component/EntitySelectInteractionEvent.java new file mode 100644 index 0000000000..c2a25cf77c --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/interaction/component/EntitySelectInteractionEvent.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.interaction.component; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.IMentionable; +import net.dv8tion.jda.api.entities.Mentions; +import net.dv8tion.jda.api.interactions.components.selections.EntitySelectInteraction; +import net.dv8tion.jda.api.interactions.components.selections.EntitySelectMenu; + +import javax.annotation.Nonnull; + +/** + * Indicates that a custom {@link EntitySelectMenu} on one of the bots messages was used by a user. + * + *

This fires when a user selects the options on one of the custom select menus attached to a bot or webhook message. + * Use {@link #getMentions()} or {@link #getValues()} to handle the selected entities. + * + *

Requirements
+ * To receive these events, you must unset the Interactions Endpoint URL in your application dashboard. + * You can simply remove the URL for this endpoint in your settings at the Discord Developers Portal. + * + * @see StringSelectInteractionEvent + */ +public class EntitySelectInteractionEvent extends GenericSelectMenuInteractionEvent implements EntitySelectInteraction +{ + private final EntitySelectInteraction interaction; + + public EntitySelectInteractionEvent(@Nonnull JDA api, long responseNumber, @Nonnull EntitySelectInteraction interaction) + { + super(api, responseNumber, interaction); + this.interaction = interaction; + } + + @Nonnull + @Override + public EntitySelectInteraction getInteraction() + { + return this.interaction; + } + + @Nonnull + @Override + public Mentions getMentions() + { + return interaction.getMentions(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/interaction/component/SelectMenuInteractionEvent.java b/src/main/java/net/dv8tion/jda/api/events/interaction/component/GenericSelectMenuInteractionEvent.java similarity index 76% rename from src/main/java/net/dv8tion/jda/api/events/interaction/component/SelectMenuInteractionEvent.java rename to src/main/java/net/dv8tion/jda/api/events/interaction/component/GenericSelectMenuInteractionEvent.java index 6b80804c8e..da8ee9c5be 100644 --- a/src/main/java/net/dv8tion/jda/api/events/interaction/component/SelectMenuInteractionEvent.java +++ b/src/main/java/net/dv8tion/jda/api/events/interaction/component/GenericSelectMenuInteractionEvent.java @@ -31,12 +31,17 @@ *

Requirements
* To receive these events, you must unset the Interactions Endpoint URL in your application dashboard. * You can simply remove the URL for this endpoint in your settings at the Discord Developers Portal. + * + * @param + * The select menu value type + * @param + * The type of select menu */ -public class SelectMenuInteractionEvent extends GenericComponentInteractionCreateEvent implements SelectMenuInteraction +public class GenericSelectMenuInteractionEvent extends GenericComponentInteractionCreateEvent implements SelectMenuInteraction { - private final SelectMenuInteraction menuInteraction; + private final SelectMenuInteraction menuInteraction; - public SelectMenuInteractionEvent(@Nonnull JDA api, long responseNumber, @Nonnull SelectMenuInteraction interaction) + public GenericSelectMenuInteractionEvent(@Nonnull JDA api, long responseNumber, @Nonnull SelectMenuInteraction interaction) { super(api, responseNumber, interaction); this.menuInteraction = interaction; @@ -44,21 +49,21 @@ public SelectMenuInteractionEvent(@Nonnull JDA api, long responseNumber, @Nonnul @Nonnull @Override - public SelectMenuInteraction getInteraction() + public SelectMenuInteraction getInteraction() { return menuInteraction; } @Nonnull @Override - public SelectMenu getComponent() + public S getComponent() { return menuInteraction.getComponent(); } @Nonnull @Override - public List getValues() + public List getValues() { return menuInteraction.getValues(); } diff --git a/src/main/java/net/dv8tion/jda/api/events/interaction/component/StringSelectInteractionEvent.java b/src/main/java/net/dv8tion/jda/api/events/interaction/component/StringSelectInteractionEvent.java new file mode 100644 index 0000000000..ba6c7af4c8 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/interaction/component/StringSelectInteractionEvent.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.interaction.component; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.interactions.components.selections.StringSelectInteraction; +import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu; + +import javax.annotation.Nonnull; + +/** + * Indicates that a custom {@link StringSelectMenu} on one of the bots messages was used by a user. + * + *

This fires when a user selects the options on one of the custom select menus attached to a bot or webhook message. + * Use {@link #getValues()} or {@link #getSelectedOptions()} to handle the selected options. + * + *

Requirements
+ * To receive these events, you must unset the Interactions Endpoint URL in your application dashboard. + * You can simply remove the URL for this endpoint in your settings at the Discord Developers Portal. + * + * @see EntitySelectInteractionEvent + */ +public class StringSelectInteractionEvent extends GenericSelectMenuInteractionEvent implements StringSelectInteraction +{ + private final StringSelectInteraction interaction; + + public StringSelectInteractionEvent(@Nonnull JDA api, long responseNumber, @Nonnull StringSelectInteraction interaction) + { + super(api, responseNumber, interaction); + this.interaction = interaction; + } + + @Nonnull + @Override + public StringSelectInteraction getInteraction() + { + return interaction; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java index 5290a68b23..86519063b5 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java @@ -55,9 +55,7 @@ import net.dv8tion.jda.api.events.interaction.GenericInteractionCreateEvent; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.*; -import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; -import net.dv8tion.jda.api.events.interaction.component.GenericComponentInteractionCreateEvent; -import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.*; import net.dv8tion.jda.api.events.message.*; import net.dv8tion.jda.api.events.message.react.*; import net.dv8tion.jda.api.events.role.GenericRoleEvent; @@ -147,9 +145,10 @@ public void onSlashCommandInteraction(@Nonnull SlashCommandInteractionEvent even public void onUserContextInteraction(@Nonnull UserContextInteractionEvent event) {} public void onMessageContextInteraction(@Nonnull MessageContextInteractionEvent event) {} public void onButtonInteraction(@Nonnull ButtonInteractionEvent event) {} - public void onSelectMenuInteraction(@Nonnull SelectMenuInteractionEvent event) {} public void onCommandAutoCompleteInteraction(@Nonnull CommandAutoCompleteInteractionEvent event) {} public void onModalInteraction(@Nonnull ModalInteractionEvent event) {} + public void onStringSelectInteraction(@Nonnull StringSelectInteractionEvent event) {} + public void onEntitySelectInteraction(@Nonnull EntitySelectInteractionEvent event) {} //User Events public void onUserUpdateName(@Nonnull UserUpdateNameEvent event) {} @@ -359,6 +358,7 @@ public void onGenericAutoCompleteInteraction(@Nonnull GenericAutoCompleteInterac public void onGenericComponentInteractionCreate(@Nonnull GenericComponentInteractionCreateEvent event) {} public void onGenericCommandInteraction(@Nonnull GenericCommandInteractionEvent event) {} public void onGenericContextInteraction(@Nonnull GenericContextInteractionEvent event) {} + public void onGenericSelectMenuInteraction(@Nonnull GenericSelectMenuInteractionEvent event) {} public void onGenericMessage(@Nonnull GenericMessageEvent event) {} public void onGenericMessageReaction(@Nonnull GenericMessageReactionEvent event) {} public void onGenericUser(@Nonnull GenericUserEvent event) {} diff --git a/src/main/java/net/dv8tion/jda/api/interactions/components/ActionRow.java b/src/main/java/net/dv8tion/jda/api/interactions/components/ActionRow.java index f4fb2a2b83..efc958deb5 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/components/ActionRow.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/components/ActionRow.java @@ -19,7 +19,8 @@ import net.dv8tion.jda.api.utils.data.DataArray; import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.internal.interactions.component.ButtonImpl; -import net.dv8tion.jda.internal.interactions.component.SelectMenuImpl; +import net.dv8tion.jda.internal.interactions.component.EntitySelectMenuImpl; +import net.dv8tion.jda.internal.interactions.component.StringSelectMenuImpl; import net.dv8tion.jda.internal.interactions.component.TextInputImpl; import net.dv8tion.jda.internal.utils.Checks; import net.dv8tion.jda.internal.utils.EntityString; @@ -69,10 +70,15 @@ public static ActionRow fromData(@Nonnull DataObject data) { case BUTTON: return new ButtonImpl(obj); - case SELECT_MENU: - return new SelectMenuImpl(obj); + case STRING_SELECT: + return new StringSelectMenuImpl(obj); case TEXT_INPUT: return new TextInputImpl(obj); + case USER_SELECT: + case ROLE_SELECT: + case CHANNEL_SELECT: + case MENTIONABLE_SELECT: + return new EntitySelectMenuImpl(obj); default: return null; } diff --git a/src/main/java/net/dv8tion/jda/api/interactions/components/Component.java b/src/main/java/net/dv8tion/jda/api/interactions/components/Component.java index 035943c023..58afe48279 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/components/Component.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/components/Component.java @@ -75,10 +75,18 @@ enum Type ACTION_ROW(1, 0, true, true), /** A button */ BUTTON(2, 5, true, false), - /** A select menu */ - SELECT_MENU(3, 1, true, false), + /** A select menu of strings */ + STRING_SELECT(3, 1, true, false), /** A text input field */ - TEXT_INPUT(4, 1, false, true) + TEXT_INPUT(4, 1, false, true), + /** A select menu of users */ + USER_SELECT(5, 1, true, false), + /** A select menu of roles */ + ROLE_SELECT(6, 1, true, false), + /** A select menu of users and roles */ + MENTIONABLE_SELECT(7, 1, true, false), + /** A select menu of channels */ + CHANNEL_SELECT(8, 1, true, false), ; private final int key; diff --git a/src/main/java/net/dv8tion/jda/api/interactions/components/selections/EntitySelectInteraction.java b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/EntitySelectInteraction.java new file mode 100644 index 0000000000..9c6efb29e5 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/EntitySelectInteraction.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.interactions.components.selections; + +import net.dv8tion.jda.api.entities.IMentionable; +import net.dv8tion.jda.api.entities.Mentions; +import net.dv8tion.jda.api.events.interaction.component.EntitySelectInteractionEvent; + +import javax.annotation.Nonnull; + +/** + * Component Interaction for a {@link EntitySelectMenu}. + * + * @see EntitySelectInteractionEvent + */ +public interface EntitySelectInteraction extends SelectMenuInteraction +{ + /** + * The resolved {@link Mentions} for this selection. + *
This supports {@link Mentions#getRoles() roles}, {@link Mentions#getUsers() users}, and {@link Mentions#getChannels() channels}. + * + * @return The mentions + */ + @Nonnull + Mentions getMentions(); +} diff --git a/src/main/java/net/dv8tion/jda/api/interactions/components/selections/EntitySelectMenu.java b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/EntitySelectMenu.java new file mode 100644 index 0000000000..501c04ec92 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/EntitySelectMenu.java @@ -0,0 +1,329 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.interactions.components.selections; + +import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.interactions.components.ActionComponent; +import net.dv8tion.jda.api.interactions.components.Component; +import net.dv8tion.jda.internal.interactions.component.EntitySelectMenuImpl; +import net.dv8tion.jda.internal.utils.Checks; +import net.dv8tion.jda.internal.utils.Helpers; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; + +/** + * Specialized {@link SelectMenu} for selecting Discord entities. + * + *

Unlike {@link StringSelectMenu}, these entity select menus do not support custom choices. + * A user will get suggested inputs based on what they write into the select menu. + * + *

This is an interactive component and usually located within an {@link net.dv8tion.jda.api.interactions.components.ActionRow ActionRow}. + * One select menu fills up an entire action row by itself. You cannot have an action row with other components if a select menu is present in the same row. + * + *

The selections a user makes are only visible within their current client session. + * Other users cannot see the choices selected, and they will disappear when the client restarts or the message is reloaded. + * + *

Examples
+ *

{@code
+ * public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
+ *   if (!event.getName().equals("class")) return;
+ *
+ *   EntitySelectMenu menu = EntitySelectMenu.create("menu:class", SelectTarget.ROLE)
+ *     .setPlaceholder("Choose your class") // shows the placeholder indicating what this menu is for
+ *     .setRequireRange(1, 1) // must select exactly one
+ *     .build();
+ *
+ *   event.reply("Please pick your class below")
+ *     .setEphemeral(true)
+ *     .addActionRow(menu)
+ *     .queue();
+ * }
+ * }
+ * + * @see SelectTarget + * @see EntitySelectInteraction + * @see StringSelectMenu + */ +public interface EntitySelectMenu extends SelectMenu +{ + @Nonnull + @Override + default EntitySelectMenu asDisabled() + { + return (EntitySelectMenu) SelectMenu.super.asDisabled(); + } + + @Nonnull + @Override + default EntitySelectMenu asEnabled() + { + return (EntitySelectMenu) SelectMenu.super.asEnabled(); + } + + @Nonnull + @Override + default EntitySelectMenu withDisabled(boolean disabled) + { + return createCopy().setDisabled(disabled).build(); + } + + /** + * The {@link SelectTarget SelectTargets} supported by this menu. + *
If the targets include {@link SelectTarget#CHANNEL}, then they are also filtered by {@link #getChannelTypes()}. + * + *

Modifying the returned {@link EnumSet} will not affect this menu. + * + * @return {@link EnumSet} of {@link SelectTarget} + */ + @Nonnull + EnumSet getEntityTypes(); + + /** + * The allowed {@link ChannelType ChannelTypes} for this menu. + *
This is only relevant if the {@link SelectTarget SelectTargets} include {@link SelectTarget#CHANNEL}. + * The returned set is empty if all types are supported, or {@link #getEntityTypes()} does not include {@link SelectTarget#CHANNEL}. + * + *

Modifying the returned {@link EnumSet} will not affect this menu. + * + * @return {@link EnumSet} of {@link ChannelType} + */ + @Nonnull + EnumSet getChannelTypes(); + + /** + * Creates a new preconfigured {@link Builder} with the same settings used for this select menu. + *
This can be useful to create an updated version of this menu without needing to rebuild it from scratch. + * + * @return The {@link Builder} used to create the select menu + */ + @Nonnull + @CheckReturnValue + default Builder createCopy() + { + //noinspection ConstantConditions + Builder builder = create(getId(), getEntityTypes()); + EnumSet channelTypes = getChannelTypes(); + if (!channelTypes.isEmpty()) + builder.setChannelTypes(channelTypes); + builder.setRequiredRange(getMinValues(), getMaxValues()); + builder.setPlaceholder(getPlaceholder()); + builder.setDisabled(isDisabled()); + return builder; + } + + /** + * Creates a new {@link Builder} for a select menu with the provided custom id. + * + * @param customId + * The id used to identify this menu with {@link ActionComponent#getId()} for component interactions + * @param types + * The supported {@link SelectTarget SelectTargets} + * + * @throws IllegalArgumentException + *

+ * + * @return The {@link Builder} used to create the select menu + */ + @Nonnull + @CheckReturnValue + static Builder create(@Nonnull String customId, @Nonnull Collection types) + { + return new Builder(customId).setEntityTypes(types); + } + + /** + * Creates a new {@link Builder} for a select menu with the provided custom id. + * + * @param customId + * The id used to identify this menu with {@link ActionComponent#getId()} for component interactions + * @param type + * The first supported {@link SelectTarget} + * @param types + * Other supported {@link SelectTarget SelectTargets} + * + * @throws IllegalArgumentException + *
    + *
  • If the provided id is null, empty, or longer than {@value ID_MAX_LENGTH} characters.
  • + *
  • If the provided types are null or invalid.
  • + *
+ * + * @return The {@link Builder} used to create the select menu + */ + @Nonnull + @CheckReturnValue + static Builder create(@Nonnull String customId, @Nonnull SelectTarget type, @Nonnull SelectTarget... types) + { + Checks.notNull(type, "Type"); + Checks.noneNull(types, "Types"); + return create(customId, EnumSet.of(type, types)); + } + + /** + * Supported entity types for a EntitySelectMenu. + *
Note that some combinations are unsupported by Discord, due to the restrictive API design. + * + *

The only combination that is currently supported is {@link #USER} + {@link #ROLE} (often referred to as "mentionables"). + * Combinations such as {@link #ROLE} + {@link #CHANNEL} are currently not supported. + */ + enum SelectTarget + { + USER, + ROLE, + CHANNEL + } + + /** + * A preconfigured builder for the creation of entity select menus. + */ + class Builder extends SelectMenu.Builder + { + protected Component.Type componentType; + protected EnumSet channelTypes = EnumSet.noneOf(ChannelType.class); + + protected Builder(@Nonnull String customId) + { + super(customId); + } + + /** + * The {@link SelectTarget SelectTargets} that should be supported by this menu. + * + * @param types + * The supported {@link SelectTarget SelectTargets} (1-2) + * + * @throws IllegalArgumentException + * If the provided targets are null, empty, or invalid. + * + * @return The current Builder instance + */ + @Nonnull + public Builder setEntityTypes(@Nonnull Collection types) + { + Checks.notEmpty(types, "Types"); + Checks.noneNull(types, "Types"); + + EnumSet set = Helpers.copyEnumSet(SelectTarget.class, types); + if (set.size() == 1) + { + if (set.contains(SelectTarget.CHANNEL)) + this.componentType = Component.Type.CHANNEL_SELECT; + else if (set.contains(SelectTarget.ROLE)) + this.componentType = Component.Type.ROLE_SELECT; + else if (set.contains(SelectTarget.USER)) + this.componentType = Component.Type.USER_SELECT; + } + else if (set.size() == 2) + { + if (set.contains(SelectTarget.USER) && set.contains(SelectTarget.ROLE)) + this.componentType = Type.MENTIONABLE_SELECT; + else + throw new IllegalArgumentException("The provided combination of select targets is not supported. Provided: " + set); + } + else + { + throw new IllegalArgumentException("The provided combination of select targets is not supported. Provided: " + set); + } + + return this; + } + + /** + * The {@link SelectTarget SelectTargets} that should be supported by this menu. + * + * @param type + * The first supported {@link SelectTarget} + * @param types + * Additional supported {@link SelectTarget SelectTargets} + * + * @throws IllegalArgumentException + * If the provided targets are null or invalid. + * + * @return The current Builder instance + */ + @Nonnull + public Builder setEntityTypes(@Nonnull SelectTarget type, @Nonnull SelectTarget... types) + { + Checks.notNull(type, "Type"); + Checks.noneNull(types, "Types"); + return setEntityTypes(EnumSet.of(type, types)); + } + + /** + * The {@link ChannelType ChannelTypes} that should be supported by this menu. + *
This is only relevant for menus that allow {@link SelectTarget#CHANNEL CHANNEL} targets. + * + * @param types + * The supported {@link ChannelType ChannelTypes} (empty to allow all types) + * + * @throws IllegalArgumentException + * If the provided types are null or not guild types + * + * @return The current Builder instance + */ + @Nonnull + public Builder setChannelTypes(@Nonnull Collection types) + { + Checks.noneNull(types, "Types"); + for (ChannelType type : types) + Checks.check(type.isGuild(), "Only guild channel types are allowed! Provided: %s", type); + this.channelTypes = Helpers.copyEnumSet(ChannelType.class, types); + return this; + } + + /** + * The {@link ChannelType ChannelTypes} that should be supported by this menu. + *
This is only relevant for menus that allow {@link SelectTarget#CHANNEL CHANNEL} targets. + * + * @param types + * The supported {@link ChannelType ChannelTypes} (empty to allow all types) + * + * @throws IllegalArgumentException + * If the provided types are null or not guild types + * + * @return The current Builder instance + */ + @Nonnull + public Builder setChannelTypes(@Nonnull ChannelType... types) + { + return setChannelTypes(Arrays.asList(types)); + } + + /** + * Creates a new {@link EntitySelectMenu} instance if all requirements are satisfied. + * + * @throws IllegalArgumentException + * Throws if {@link #getMinValues()} is greater than {@link #getMaxValues()} + * + * @return The new {@link EntitySelectMenu} instance + */ + @Nonnull + @Override + public EntitySelectMenu build() + { + Checks.check(minValues <= maxValues, "Min values cannot be greater than max values!"); + EnumSet channelTypes = componentType == Type.CHANNEL_SELECT ? this.channelTypes : EnumSet.noneOf(ChannelType.class); + return new EntitySelectMenuImpl(customId, placeholder, minValues, maxValues, disabled, componentType, channelTypes); + } + } +} diff --git a/src/main/java/net/dv8tion/jda/api/interactions/components/selections/SelectMenu.java b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/SelectMenu.java index 617e504f5f..b89239d392 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/components/selections/SelectMenu.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/SelectMenu.java @@ -16,17 +16,12 @@ package net.dv8tion.jda.api.interactions.components.selections; -import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.interactions.components.ActionComponent; -import net.dv8tion.jda.api.utils.data.DataObject; -import net.dv8tion.jda.internal.interactions.component.SelectMenuImpl; import net.dv8tion.jda.internal.utils.Checks; -import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.*; -import java.util.stream.Collectors; +import java.util.Collection; /** * Represents a select menu in a message. @@ -36,26 +31,12 @@ *

The selections a user makes are only visible within their current client session. * Other users cannot see the choices selected, and they will disappear when the client restarts or the message is reloaded. * - *

Examples
- *

{@code
- * public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
- *   if (!event.getName().equals("class")) return;
- *
- *   SelectMenu menu = SelectMenu.create("menu:class")
- *     .setPlaceholder("Choose your class") // shows the placeholder indicating what this menu is for
- *     .setRequireRange(1, 1) // only one can be selected
- *     .addOption("Arcane Mage", "mage-arcane")
- *     .addOption("Fire Mage", "mage-fire")
- *     .addOption("Frost Mage", "mage-frost")
- *     .build();
- *
- *   event.reply("Please pick your class below")
- *     .setEphemeral(true)
- *     .addActionRow(menu)
- *     .queue();
- * }
- * }
+ *

This is a generic interface for all types of select menus. + *
You can use {@link EntitySelectMenu#create(String, Collection)} to create a select menu of Discord entities such as {@link net.dv8tion.jda.api.interactions.components.selections.EntitySelectMenu.SelectTarget#USER users}. + *
Alternatively, you can use {@link StringSelectMenu#create(String)} to create a select menu of up to {@value #OPTIONS_MAX_AMOUNT} pre-defined strings to pick from. * + * @see StringSelectMenu + * @see EntitySelectMenu * @see SelectMenuInteraction */ public interface SelectMenu extends ActionComponent @@ -97,108 +78,21 @@ public interface SelectMenu extends ActionComponent */ int getMaxValues(); - /** - * An unmodifiable list of up to {@value #OPTIONS_MAX_AMOUNT} available options to choose from. - * - * @return The {@link SelectOption SelectOptions} this menu provides - * - * @see Builder#getOptions() - */ - @Nonnull - List getOptions(); - - @Nonnull - @Override - @CheckReturnValue - default SelectMenu asDisabled() - { - return withDisabled(true); - } - - @Nonnull - @Override - @CheckReturnValue - default SelectMenu asEnabled() - { - return withDisabled(false); - } - - @Nonnull - @Override - @CheckReturnValue - default SelectMenu withDisabled(boolean disabled) - { - return createCopy().setDisabled(disabled).build(); - } - - /** - * Creates a new preconfigured {@link Builder} with the same settings used for this select menu. - *
This can be useful to create an updated version of this menu without needing to rebuild it from scratch. - * - * @return The {@link Builder} used to create the select menu - */ - @Nonnull - @CheckReturnValue - default Builder createCopy() - { - //noinspection ConstantConditions - Builder builder = create(getId()); - builder.setRequiredRange(getMinValues(), getMaxValues()); - builder.setPlaceholder(getPlaceholder()); - builder.addOptions(getOptions()); - builder.setDisabled(isDisabled()); - return builder; - } - - /** - * Creates a new {@link Builder} for a select menu with the provided custom id. - * - * @param customId - * The id used to identify this menu with {@link ActionComponent#getId()} for component interactions - * - * @throws IllegalArgumentException - * If the provided id is null, empty, or longer than {@value #ID_MAX_LENGTH} characters - * - * @return The {@link Builder} used to create the select menu - */ - @Nonnull - @CheckReturnValue - static Builder create(@Nonnull String customId) - { - return new Builder(customId); - } - - /** - * Inverse function for {@link #toData()} which parses the serialized select menu data. - *
Returns a {@link Builder} which allows for further configuration. - * - * @param data - * The serialized select menu data - * - * @throws net.dv8tion.jda.api.exceptions.ParsingException - * If the data representation is invalid - * @throws IllegalArgumentException - * If some part of the data has an invalid length or null is provided - * - * @return The parsed SelectMenu Builder instance - */ - @Nonnull - @CheckReturnValue - static Builder fromData(@Nonnull DataObject data) - { - return new SelectMenuImpl(data).createCopy(); - } - /** * A preconfigured builder for the creation of select menus. + * + * @param + * The output type + * @param + * The builder type (used for fluent interface) */ - class Builder + @SuppressWarnings("unchecked") + abstract class Builder> { - private String customId; - private String placeholder; - private int minValues = 1, maxValues = 1; - private boolean disabled = false; - private final List options = new ArrayList<>(); + protected String customId; + protected String placeholder; + protected int minValues = 1, maxValues = 1; + protected boolean disabled = false; protected Builder(@Nonnull String customId) { @@ -217,12 +111,12 @@ protected Builder(@Nonnull String customId) * @return The same builder instance for chaining */ @Nonnull - public Builder setId(@Nonnull String customId) + public B setId(@Nonnull String customId) { Checks.notEmpty(customId, "Component ID"); Checks.notLonger(customId, ID_MAX_LENGTH, "Component ID"); this.customId = customId; - return this; + return (B) this; } /** @@ -237,7 +131,7 @@ public Builder setId(@Nonnull String customId) * @return The same builder instance for chaining */ @Nonnull - public Builder setPlaceholder(@Nullable String placeholder) + public B setPlaceholder(@Nullable String placeholder) { if (placeholder != null) { @@ -245,7 +139,7 @@ public Builder setPlaceholder(@Nullable String placeholder) Checks.notLonger(placeholder, PLACEHOLDER_MAX_LENGTH, "Placeholder"); } this.placeholder = placeholder; - return this; + return (B) this; } /** @@ -263,12 +157,12 @@ public Builder setPlaceholder(@Nullable String placeholder) * @return The same builder instance for chaining */ @Nonnull - public Builder setMinValues(int minValues) + public B setMinValues(int minValues) { Checks.notNegative(minValues, "Min Values"); Checks.check(minValues <= OPTIONS_MAX_AMOUNT, "Min Values may not be greater than %d! Provided: %d", OPTIONS_MAX_AMOUNT, minValues); this.minValues = minValues; - return this; + return (B) this; } /** @@ -286,12 +180,12 @@ public Builder setMinValues(int minValues) * @return The same builder instance for chaining */ @Nonnull - public Builder setMaxValues(int maxValues) + public B setMaxValues(int maxValues) { Checks.positive(maxValues, "Max Values"); Checks.check(maxValues <= OPTIONS_MAX_AMOUNT, "Max Values may not be greater than %d! Provided: %d", OPTIONS_MAX_AMOUNT, maxValues); this.maxValues = maxValues; - return this; + return (B) this; } /** @@ -311,7 +205,7 @@ public Builder setMaxValues(int maxValues) * @return The same builder instance for chaining */ @Nonnull - public Builder setRequiredRange(int min, int max) + public B setRequiredRange(int min, int max) { Checks.check(min <= max, "Min Values should be less than or equal to Max Values! Provided: [%d, %d]", min, max); return setMinValues(min).setMaxValues(max); @@ -327,199 +221,10 @@ public Builder setRequiredRange(int min, int max) * @return The same builder instance for chaining */ @Nonnull - public Builder setDisabled(boolean disabled) + public B setDisabled(boolean disabled) { this.disabled = disabled; - return this; - } - - /** - * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. - * - * @param options - * The {@link SelectOption SelectOptions} to add - * - * @throws IllegalArgumentException - * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT} or null is provided - * - * @return The same builder instance for chaining - * - * @see SelectOption#of(String, String) - */ - @Nonnull - public Builder addOptions(@Nonnull SelectOption... options) - { - Checks.noneNull(options, "Options"); - Checks.check(this.options.size() + options.length <= OPTIONS_MAX_AMOUNT, "Cannot have more than %d options for a select menu!", OPTIONS_MAX_AMOUNT); - Collections.addAll(this.options, options); - return this; - } - - /** - * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. - * - * @param options - * The {@link SelectOption SelectOptions} to add - * - * @throws IllegalArgumentException - * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT} or null is provided - * - * @return The same builder instance for chaining - * - * @see SelectOption#of(String, String) - */ - @Nonnull - public Builder addOptions(@Nonnull Collection options) - { - Checks.noneNull(options, "Options"); - Checks.check(this.options.size() + options.size() <= OPTIONS_MAX_AMOUNT, "Cannot have more than %d options for a select menu!", OPTIONS_MAX_AMOUNT); - this.options.addAll(options); - return this; - } - - /** - * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. - * - * @param label - * The label for the option, up to {@value SelectOption#LABEL_MAX_LENGTH} characters - * @param value - * The value for the option used to indicate which option was selected with {@link SelectMenuInteraction#getValues()}, - * up to {@value SelectOption#VALUE_MAX_LENGTH} characters - * - * @throws IllegalArgumentException - * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT}, invalid null is provided, - * or any of the individual parameter requirements are violated. - * - * @return The same builder instance for chaining - */ - @Nonnull - public Builder addOption(@Nonnull String label, @Nonnull String value) - { - return addOptions(new SelectOption(label, value)); - } - - /** - * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. - * - * @param label - * The label for the option, up to {@value SelectOption#LABEL_MAX_LENGTH} characters - * @param value - * The value for the option used to indicate which option was selected with {@link SelectMenuInteraction#getValues()}, - * up to {@value SelectOption#VALUE_MAX_LENGTH} characters - * @param emoji - * The {@link Emoji} shown next to this option, or null - * - * @throws IllegalArgumentException - * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT}, invalid null is provided, or any of the individual parameter requirements are violated. - * - * @return The same builder instance for chaining - */ - @Nonnull - public Builder addOption(@Nonnull String label, @Nonnull String value, @Nonnull Emoji emoji) - { - return addOption(label, value, null, emoji); - } - - /** - * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. - * - * @param label - * The label for the option, up to {@value SelectOption#LABEL_MAX_LENGTH} characters - * @param value - * The value for the option used to indicate which option was selected with {@link SelectMenuInteraction#getValues()}, - * up to {@value SelectOption#VALUE_MAX_LENGTH} characters - * @param description - * The description explaining the meaning of this option in more detail, up to 50 characters - * - * @throws IllegalArgumentException - * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT}, invalid null is provided, - * or any of the individual parameter requirements are violated. - * - * @return The same builder instance for chaining - */ - @Nonnull - public Builder addOption(@Nonnull String label, @Nonnull String value, @Nonnull String description) - { - return addOption(label, value, description, null); - } - - /** - * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. - * - * @param label - * The label for the option, up to {@value SelectOption#LABEL_MAX_LENGTH} characters - * @param value - * The value for the option used to indicate which option was selected with {@link SelectMenuInteraction#getValues()}, - * up to {@value SelectOption#VALUE_MAX_LENGTH} characters - * @param description - * The description explaining the meaning of this option in more detail, up to 50 characters - * @param emoji - * The {@link Emoji} shown next to this option, or null - * - * @throws IllegalArgumentException - * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT}, invalid null is provided, - * or any of the individual parameter requirements are violated. - * - * @return The same builder instance for chaining - */ - @Nonnull - public Builder addOption(@Nonnull String label, @Nonnull String value, @Nullable String description, @Nullable Emoji emoji) - { - return addOptions(new SelectOption(label, value, description, false, emoji)); - } - - /** - * Modifiable list of options currently configured in this builder. - * - * @return The list of {@link SelectOption SelectOptions} - */ - @Nonnull - public List getOptions() - { - return options; - } - - /** - * Configures which of the currently applied {@link #getOptions() options} should be selected by default. - * - * @param values - * The {@link SelectOption#getValue() option values} - * - * @throws IllegalArgumentException - * If null is provided - * - * @return The same builder instance for chaining - */ - @Nonnull - public Builder setDefaultValues(@Nonnull Collection values) - { - Checks.noneNull(values, "Values"); - Set set = new HashSet<>(values); - for (ListIterator it = getOptions ().listIterator(); it.hasNext();) - { - SelectOption option = it.next(); - it.set(option.withDefault(set.contains(option.getValue()))); - } - return this; - } - - - /** - * Configures which of the currently applied {@link #getOptions() options} should be selected by default. - * - * @param values - * The {@link SelectOption SelectOptions} - * - * @throws IllegalArgumentException - * If null is provided - * - * @return The same builder instance for chaining - */ - @Nonnull - public Builder setDefaultOptions(@Nonnull Collection values) - { - Checks.noneNull(values, "Values"); - return setDefaultValues(values.stream().map(SelectOption::getValue).collect(Collectors.toSet())); + return (B) this; } /** @@ -576,24 +281,13 @@ public boolean isDisabled() /** * Creates a new {@link SelectMenu} instance if all requirements are satisfied. - *
A select menu may not have more than {@value #OPTIONS_MAX_AMOUNT} options at once. - * - *

The values for {@link #setMinValues(int)} and {@link #setMaxValues(int)} are bounded by the length of {@link #getOptions()}. - * This means they will automatically be adjusted to not be greater than {@code getOptions().size()}. * * @throws IllegalArgumentException - * Throws if {@link #getMinValues()} is greater than {@link #getMaxValues()} or more than {@value #OPTIONS_MAX_AMOUNT} options are provided + * Throws if {@link #getMinValues()} is greater than {@link #getMaxValues()} * * @return The new {@link SelectMenu} instance */ @Nonnull - public SelectMenu build() - { - Checks.check(minValues <= maxValues, "Min values cannot be greater than max values!"); - Checks.check(options.size() <= OPTIONS_MAX_AMOUNT, "Cannot build a select menu with more than %d options.", OPTIONS_MAX_AMOUNT); - int min = Math.min(minValues, options.size()); - int max = Math.min(maxValues, options.size()); - return new SelectMenuImpl(customId, placeholder, min, max, disabled, options); - } + public abstract T build(); } } diff --git a/src/main/java/net/dv8tion/jda/api/interactions/components/selections/SelectMenuInteraction.java b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/SelectMenuInteraction.java index 58256eff31..4bfc7b1b0b 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/components/selections/SelectMenuInteraction.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/SelectMenuInteraction.java @@ -17,7 +17,7 @@ package net.dv8tion.jda.api.interactions.components.selections; import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.GenericSelectMenuInteractionEvent; import net.dv8tion.jda.api.interactions.components.ActionRow; import net.dv8tion.jda.api.interactions.components.ComponentInteraction; import net.dv8tion.jda.api.interactions.components.LayoutComponent; @@ -29,18 +29,24 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.stream.Collectors; /** * Component Interaction for a {@link SelectMenu}. * - * @see SelectMenuInteractionEvent + * @param + * The select menu value type + * @param + * The type of select menu + * + * @see GenericSelectMenuInteractionEvent + * @see EntitySelectInteraction + * @see StringSelectInteraction */ -public interface SelectMenuInteraction extends ComponentInteraction +public interface SelectMenuInteraction extends ComponentInteraction { @Nonnull @Override - SelectMenu getComponent(); + S getComponent(); /** * The {@link SelectMenu} this interaction belongs to. @@ -50,35 +56,18 @@ public interface SelectMenuInteraction extends ComponentInteraction * @see #getComponentId() */ @Nonnull - default SelectMenu getSelectMenu() + default S getSelectMenu() { return getComponent(); } /** - * If available, this will resolve the selected {@link #getValues() values} to the representative {@link SelectOption SelectOption} instances. - *
This is null if the message is ephemeral. + * The provided selection. * - * @return {@link List} of the selected options or null if this message is ephemeral + * @return {@link List} of {@link T} */ @Nonnull - default List getSelectedOptions() - { - SelectMenu menu = getComponent(); - List values = getValues(); - return menu.getOptions() - .stream() - .filter(it -> values.contains(it.getValue())) - .collect(Collectors.toList()); - } - - /** - * The selected values. These are defined in the individual {@link SelectOption SelectOptions}. - * - * @return {@link List} of {@link SelectOption#getValue()} - */ - @Nonnull - List getValues(); + List getValues(); /** * Update the select menu with a new select menu instance. @@ -90,9 +79,6 @@ default List getSelectedOptions() * The new select menu to use, or null to remove this menu from the message entirely * * @return {@link RestAction} - * - * @see SelectMenu#createCopy() - * @see SelectMenu#create(String) */ @Nonnull @CheckReturnValue diff --git a/src/main/java/net/dv8tion/jda/api/interactions/components/selections/StringSelectInteraction.java b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/StringSelectInteraction.java new file mode 100644 index 0000000000..d85bbab88c --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/StringSelectInteraction.java @@ -0,0 +1,57 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.interactions.components.selections; + +import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Component Interaction for a {@link StringSelectMenu}. + * + * @see StringSelectInteractionEvent + */ +public interface StringSelectInteraction extends SelectMenuInteraction +{ + /** + * The selected values. + *
These are defined in the individual {@link SelectOption SelectOptions}. + * + * @return {@link List} of {@link SelectOption#getValue()} + */ + @Nonnull + List getValues(); + + /** + * This resolves the selected {@link #getValues() values} to the representative {@link SelectOption SelectOption} instances. + *
It is recommended to check {@link #getValues()} directly instead of using the options. + * + * @return {@link List} of the selected options + */ + @Nonnull + default List getSelectedOptions() + { + StringSelectMenu menu = getComponent(); + List values = getValues(); + return menu.getOptions() + .stream() + .filter(it -> values.contains(it.getValue())) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/interactions/components/selections/StringSelectMenu.java b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/StringSelectMenu.java new file mode 100644 index 0000000000..ebf3422b67 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/interactions/components/selections/StringSelectMenu.java @@ -0,0 +1,396 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.interactions.components.selections; + +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.interactions.components.ActionComponent; +import net.dv8tion.jda.internal.interactions.component.StringSelectMenuImpl; +import net.dv8tion.jda.internal.utils.Checks; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Represents a select menu in a message. + *
This is an interactive component and usually located within an {@link net.dv8tion.jda.api.interactions.components.ActionRow ActionRow}. + * One select menu fills up an entire action row by itself. You cannot have an action row with other components if a select menu is present in the same row. + * + *

The selections a user makes are only visible within their current client session. + * Other users cannot see the choices selected, and they will disappear when the client restarts or the message is reloaded. + * + *

Examples
+ *

{@code
+ * public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
+ *   if (!event.getName().equals("class")) return;
+ *
+ *   StringSelectMenu menu = StringSelectMenu.create("menu:class")
+ *     .setPlaceholder("Choose your class") // shows the placeholder indicating what this menu is for
+ *     .setRequireRange(1, 1) // exactly one must be selected
+ *     .addOption("Arcane Mage", "mage-arcane")
+ *     .addOption("Fire Mage", "mage-fire")
+ *     .addOption("Frost Mage", "mage-frost")
+ *     .setDefaultValues("mage-fire") // default to fire mage
+ *     .build();
+ *
+ *   event.reply("Please pick your class below")
+ *     .setEphemeral(true)
+ *     .addActionRow(menu)
+ *     .queue();
+ * }
+ * }
+ * + * @see StringSelectInteraction + * @see EntitySelectMenu + */ +public interface StringSelectMenu extends SelectMenu +{ + @Nonnull + @Override + default StringSelectMenu asDisabled() + { + return withDisabled(true); + } + + @Nonnull + @Override + default StringSelectMenu asEnabled() + { + return withDisabled(false); + } + + @Nonnull + @Override + default StringSelectMenu withDisabled(boolean disabled) + { + return createCopy().setDisabled(disabled).build(); + } + + /** + * An unmodifiable list of up to {@value #OPTIONS_MAX_AMOUNT} available options to choose from. + * + * @return The {@link SelectOption SelectOptions} this menu provides + * + * @see Builder#getOptions() + */ + @Nonnull + List getOptions(); + + /** + * Creates a new preconfigured {@link Builder} with the same settings used for this select menu. + *
This can be useful to create an updated version of this menu without needing to rebuild it from scratch. + * + * @return The {@link Builder} used to create the select menu + */ + @Nonnull + @CheckReturnValue + default Builder createCopy() + { + //noinspection ConstantConditions + Builder builder = create(getId()); + builder.setRequiredRange(getMinValues(), getMaxValues()); + builder.setPlaceholder(getPlaceholder()); + builder.addOptions(getOptions()); + builder.setDisabled(isDisabled()); + return builder; + } + + /** + * Creates a new {@link Builder} for a select menu with the provided custom id. + * + * @param customId + * The id used to identify this menu with {@link ActionComponent#getId()} for component interactions + * + * @throws IllegalArgumentException + * If the provided id is null, empty, or longer than {@value #ID_MAX_LENGTH} characters + * + * @return The {@link Builder} used to create the select menu + */ + @Nonnull + @CheckReturnValue + static Builder create(@Nonnull String customId) + { + return new Builder(customId); + } + + /** + * A preconfigured builder for the creation of string select menus. + */ + class Builder extends SelectMenu.Builder + { + private final List options = new ArrayList<>(); + + protected Builder(@Nonnull String customId) + { + super(customId); + } + + /** + * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. + * + * @param options + * The {@link SelectOption SelectOptions} to add + * + * @throws IllegalArgumentException + * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT} or null is provided + * + * @return The same builder instance for chaining + * + * @see SelectOption#of(String, String) + */ + @Nonnull + public Builder addOptions(@Nonnull SelectOption... options) + { + Checks.noneNull(options, "Options"); + Checks.check(this.options.size() + options.length <= OPTIONS_MAX_AMOUNT, "Cannot have more than %d options for a select menu!", OPTIONS_MAX_AMOUNT); + Collections.addAll(this.options, options); + return this; + } + + /** + * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. + * + * @param options + * The {@link SelectOption SelectOptions} to add + * + * @throws IllegalArgumentException + * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT} or null is provided + * + * @return The same builder instance for chaining + * + * @see SelectOption#of(String, String) + */ + @Nonnull + public Builder addOptions(@Nonnull Collection options) + { + Checks.noneNull(options, "Options"); + Checks.check(this.options.size() + options.size() <= OPTIONS_MAX_AMOUNT, "Cannot have more than %d options for a select menu!", OPTIONS_MAX_AMOUNT); + this.options.addAll(options); + return this; + } + + /** + * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. + * + * @param label + * The label for the option, up to {@value SelectOption#LABEL_MAX_LENGTH} characters + * @param value + * The value for the option used to indicate which option was selected with {@link SelectMenuInteraction#getValues()}, + * up to {@value SelectOption#VALUE_MAX_LENGTH} characters + * + * @throws IllegalArgumentException + * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT}, invalid null is provided, + * or any of the individual parameter requirements are violated. + * + * @return The same builder instance for chaining + */ + @Nonnull + public Builder addOption(@Nonnull String label, @Nonnull String value) + { + return addOptions(new SelectOption(label, value)); + } + + /** + * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. + * + * @param label + * The label for the option, up to {@value SelectOption#LABEL_MAX_LENGTH} characters + * @param value + * The value for the option used to indicate which option was selected with {@link SelectMenuInteraction#getValues()}, + * up to {@value SelectOption#VALUE_MAX_LENGTH} characters + * @param emoji + * The {@link Emoji} shown next to this option, or null + * + * @throws IllegalArgumentException + * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT}, invalid null is provided, or any of the individual parameter requirements are violated. + * + * @return The same builder instance for chaining + */ + @Nonnull + public Builder addOption(@Nonnull String label, @Nonnull String value, @Nonnull Emoji emoji) + { + return addOption(label, value, null, emoji); + } + + /** + * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. + * + * @param label + * The label for the option, up to {@value SelectOption#LABEL_MAX_LENGTH} characters + * @param value + * The value for the option used to indicate which option was selected with {@link SelectMenuInteraction#getValues()}, + * up to {@value SelectOption#VALUE_MAX_LENGTH} characters + * @param description + * The description explaining the meaning of this option in more detail, up to 50 characters + * + * @throws IllegalArgumentException + * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT}, invalid null is provided, + * or any of the individual parameter requirements are violated. + * + * @return The same builder instance for chaining + */ + @Nonnull + public Builder addOption(@Nonnull String label, @Nonnull String value, @Nonnull String description) + { + return addOption(label, value, description, null); + } + + /** + * Adds up to {@value #OPTIONS_MAX_AMOUNT} possible options to this select menu. + * + * @param label + * The label for the option, up to {@value SelectOption#LABEL_MAX_LENGTH} characters + * @param value + * The value for the option used to indicate which option was selected with {@link SelectMenuInteraction#getValues()}, + * up to {@value SelectOption#VALUE_MAX_LENGTH} characters + * @param description + * The description explaining the meaning of this option in more detail, up to 50 characters + * @param emoji + * The {@link Emoji} shown next to this option, or null + * + * @throws IllegalArgumentException + * If the total amount of options is greater than {@value #OPTIONS_MAX_AMOUNT}, invalid null is provided, + * or any of the individual parameter requirements are violated. + * + * @return The same builder instance for chaining + */ + @Nonnull + public Builder addOption(@Nonnull String label, @Nonnull String value, @Nullable String description, @Nullable Emoji emoji) + { + return addOptions(new SelectOption(label, value, description, false, emoji)); + } + + /** + * Modifiable list of options currently configured in this builder. + * + * @return The list of {@link SelectOption SelectOptions} + */ + @Nonnull + public List getOptions() + { + return options; + } + + /** + * Configures which of the currently applied {@link #getOptions() options} should be selected by default. + * + * @param values + * The {@link SelectOption#getValue() option values} + * + * @throws IllegalArgumentException + * If null is provided + * + * @return The same builder instance for chaining + */ + @Nonnull + public Builder setDefaultValues(@Nonnull Collection values) + { + Checks.noneNull(values, "Values"); + Set set = new HashSet<>(values); + for (ListIterator it = getOptions ().listIterator(); it.hasNext();) + { + SelectOption option = it.next(); + it.set(option.withDefault(set.contains(option.getValue()))); + } + return this; + } + + /** + * Configures which of the currently applied {@link #getOptions() options} should be selected by default. + * + * @param values + * The {@link SelectOption#getValue() option values} + * + * @throws IllegalArgumentException + * If null is provided + * + * @return The same builder instance for chaining + */ + @Nonnull + public Builder setDefaultValues(@Nonnull String... values) + { + Checks.noneNull(values, "Values"); + return setDefaultValues(Arrays.asList(values)); + } + + /** + * Configures which of the currently applied {@link #getOptions() options} should be selected by default. + * + * @param values + * The {@link SelectOption SelectOptions} + * + * @throws IllegalArgumentException + * If null is provided + * + * @return The same builder instance for chaining + */ + @Nonnull + public Builder setDefaultOptions(@Nonnull Collection values) + { + Checks.noneNull(values, "Values"); + return setDefaultValues(values.stream().map(SelectOption::getValue).collect(Collectors.toSet())); + } + + /** + * Configures which of the currently applied {@link #getOptions() options} should be selected by default. + * + * @param values + * The {@link SelectOption SelectOptions} + * + * @throws IllegalArgumentException + * If null is provided + * + * @return The same builder instance for chaining + */ + @Nonnull + public Builder setDefaultOptions(@Nonnull SelectOption... values) + { + Checks.noneNull(values, "Values"); + return setDefaultOptions(Arrays.asList(values)); + } + + /** + * Creates a new {@link StringSelectMenu} instance if all requirements are satisfied. + *
A select menu may not have more than {@value #OPTIONS_MAX_AMOUNT} options at once. + * + *

The values for {@link #setMinValues(int)} and {@link #setMaxValues(int)} are bounded by the length of {@link #getOptions()}. + * This means they will automatically be adjusted to not be greater than {@code getOptions().size()}. + * You can use this to your advantage to easily make a select menu with unlimited options by setting it to {@link #OPTIONS_MAX_AMOUNT}. + * + * @throws IllegalArgumentException + *

    + *
  • If {@link #getMinValues()} is greater than {@link #getMaxValues()}
  • + *
  • If no options are provided
  • + *
  • If more than {@value #OPTIONS_MAX_AMOUNT} options are provided
  • + *
+ * + * @return The new {@link StringSelectMenu} instance + */ + @Nonnull + public StringSelectMenu build() + { + Checks.check(minValues <= maxValues, "Min values cannot be greater than max values!"); + Checks.check(!options.isEmpty(), "Cannot build a select menu without options. Add at least one option!"); + Checks.check(options.size() <= OPTIONS_MAX_AMOUNT, "Cannot build a select menu with more than %d options.", OPTIONS_MAX_AMOUNT); + int min = Math.min(minValues, options.size()); + int max = Math.min(maxValues, options.size()); + return new StringSelectMenuImpl(customId, placeholder, min, max, disabled, options); + } + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/entities/SelectMenuMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/SelectMenuMentions.java new file mode 100644 index 0000000000..308d3eb2f4 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/SelectMenuMentions.java @@ -0,0 +1,289 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.entities; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.entities.emoji.CustomEmoji; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.utils.Checks; +import net.dv8tion.jda.internal.utils.Helpers; +import org.apache.commons.collections4.Bag; +import org.apache.commons.collections4.BagUtils; +import org.apache.commons.collections4.bag.HashBag; + +import javax.annotation.Nonnull; +import java.util.*; +import java.util.stream.Collectors; + +public class SelectMenuMentions implements Mentions +{ + private final DataObject resolved; + private final JDAImpl jda; + private final GuildImpl guild; + private final List values; + + private List cachedUsers; + private List cachedMembers; + private List cachedRoles; + private List cachedChannels; + + public SelectMenuMentions(JDAImpl jda, GuildImpl guild, DataObject resolved, DataArray values) + { + this.jda = jda; + this.guild = guild; + this.resolved = resolved; + this.values = values.stream(DataArray::getString).collect(Collectors.toList()); + } + + @Nonnull + @Override + public JDA getJDA() + { + return jda; + } + + @Override + public boolean mentionsEveryone() + { + return false; + } + + @Nonnull + @Override + public List getUsers() + { + if (cachedUsers != null) + return cachedUsers; + + DataObject userMap = resolved.optObject("users").orElseGet(DataObject::empty); + EntityBuilder builder = jda.getEntityBuilder(); + + return cachedUsers = values.stream() + .map(id -> userMap.optObject(id).orElse(null)) + .filter(Objects::nonNull) + .map(builder::createUser) + .collect(Helpers.toUnmodifiableList()); + } + + @Nonnull + @Override + public Bag getUsersBag() + { + return new HashBag<>(getUsers()); + } + + @Nonnull + @Override + public List getChannels() + { + if (cachedChannels != null) + return cachedChannels; + + DataObject channelMap = resolved.optObject("channels").orElseGet(DataObject::empty); + + return cachedChannels = values.stream() + .map(id -> channelMap.optObject(id).orElse(null)) + .filter(Objects::nonNull) + .map(json -> jda.getGuildChannelById(ChannelType.fromId(json.getInt("type", -1)), json.getUnsignedLong("id"))) + .filter(Objects::nonNull) + .collect(Helpers.toUnmodifiableList()); + } + + @Nonnull + @Override + public Bag getChannelsBag() + { + return new HashBag<>(getChannels()); + } + + @Nonnull + @Override + public List getChannels(@Nonnull Class clazz) + { + return getChannels().stream() + .filter(clazz::isInstance) + .map(clazz::cast) + .collect(Helpers.toUnmodifiableList()); + } + + @Nonnull + @Override + public Bag getChannelsBag(@Nonnull Class clazz) + { + return new HashBag<>(getChannels(clazz)); + } + + @Nonnull + @Override + public List getRoles() + { + if (cachedRoles != null) + return cachedRoles; + + DataObject roleMap = resolved.optObject("roles").orElseGet(DataObject::empty); + + return cachedRoles = values.stream() + .filter(roleMap::hasKey) + .map(jda::getRoleById) + .filter(Objects::nonNull) + .collect(Helpers.toUnmodifiableList()); + } + + @Nonnull + @Override + public Bag getRolesBag() + { + return new HashBag<>(getRoles()); + } + + @Nonnull + @Override + public List getCustomEmojis() + { + return Collections.emptyList(); + } + + @Nonnull + @Override + public Bag getCustomEmojisBag() + { + return BagUtils.emptyBag(); + } + + @Nonnull + @Override + public List getMembers() + { + if (cachedMembers != null) + return cachedMembers; + + DataObject memberMap = resolved.optObject("members").orElseGet(DataObject::empty); + DataObject userMap = resolved.optObject("users").orElseGet(DataObject::empty); + EntityBuilder builder = jda.getEntityBuilder(); + + return cachedMembers = values.stream() + .map(id -> memberMap.optObject(id).map(m -> m.put("id", id)).orElse(null)) + .filter(Objects::nonNull) + .map(json -> json.put("user", userMap.getObject(json.getString("id")))) + .map(json -> builder.createMember(guild, json)) + .filter(Objects::nonNull) + .filter(member -> { + builder.updateMemberCache(member); + return true; + }) + .collect(Helpers.toUnmodifiableList()); + } + + @Nonnull + @Override + public Bag getMembersBag() + { + return new HashBag<>(getMembers()); + } + + @Nonnull + @Override + public List getMentions(@Nonnull Message.MentionType... types) + { + if (types.length == 0) + return getMentions(Message.MentionType.values()); + List mentions = new ArrayList<>(); + // Convert to set to avoid duplicates + EnumSet set = EnumSet.of(types[0], types); + for (Message.MentionType type : set) + { + switch (type) + { + case USER: + List members = getMembers(); + List users = getUsers(); + mentions.addAll(members); + users.stream() + .filter(u -> members.stream().noneMatch(m -> m.getIdLong() == u.getIdLong())) + .forEach(mentions::add); + break; + case ROLE: + mentions.addAll(getRoles()); + break; + case CHANNEL: + mentions.addAll(getChannels()); + break; + } + } + + mentions.sort(Comparator.comparingInt(it -> values.indexOf(it.getId()))); + return Collections.unmodifiableList(mentions); + } + + @Override + public boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull Message.MentionType... types) + { + Checks.notNull(types, "Mention Types"); + if (types.length == 0) + return isMentioned(mentionable, Message.MentionType.values()); + + String id = mentionable.getId(); + for (Message.MentionType type : types) + { + switch (type) + { + case USER: + if (mentionable instanceof UserSnowflake) + { + boolean mentioned = resolved.optObject("users").map(obj -> obj.hasKey(id)).orElse(false); + if (mentioned) + return true; + } + break; + case ROLE: + if (mentionable instanceof Member) + { + boolean mentioned = ((Member) mentionable).getRoles().stream().anyMatch(role -> isMentioned(role, Message.MentionType.ROLE)); + if (mentioned) + return true; + } + else if (mentionable instanceof User) + { + boolean mentioned = getMembers().stream() + .filter(it -> it.getIdLong() == mentionable.getIdLong()) + .findFirst() + .map(member -> isMentioned(member, Message.MentionType.ROLE)) + .orElse(false); + if (mentioned) + return true; + } + else if (mentionable instanceof Role) + { + boolean mentioned = resolved.optObject("roles").map(obj -> obj.hasKey(id)).orElse(false); + if (mentioned) + return true; + } + break; + case CHANNEL: + if (mentionable instanceof GuildChannel && getChannels().contains(mentionable)) + return true; + break; + } + } + return false; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/InteractionCreateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/InteractionCreateHandler.java index b319d14e57..85770a37a8 100644 --- a/src/main/java/net/dv8tion/jda/internal/handle/InteractionCreateHandler.java +++ b/src/main/java/net/dv8tion/jda/internal/handle/InteractionCreateHandler.java @@ -25,7 +25,8 @@ import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; -import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.EntitySelectInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; import net.dv8tion.jda.api.interactions.InteractionType; import net.dv8tion.jda.api.interactions.commands.Command; import net.dv8tion.jda.api.interactions.components.Component; @@ -38,7 +39,8 @@ import net.dv8tion.jda.internal.interactions.command.SlashCommandInteractionImpl; import net.dv8tion.jda.internal.interactions.command.UserContextInteractionImpl; import net.dv8tion.jda.internal.interactions.component.ButtonInteractionImpl; -import net.dv8tion.jda.internal.interactions.component.SelectMenuInteractionImpl; +import net.dv8tion.jda.internal.interactions.component.EntitySelectInteractionImpl; +import net.dv8tion.jda.internal.interactions.component.StringSelectInteractionImpl; import net.dv8tion.jda.internal.requests.WebSocketClient; public class InteractionCreateHandler extends SocketHandler @@ -133,10 +135,18 @@ private void handleAction(DataObject content) new ButtonInteractionEvent(api, responseNumber, new ButtonInteractionImpl(api, content))); break; - case SELECT_MENU: + case STRING_SELECT: api.handleEvent( - new SelectMenuInteractionEvent(api, responseNumber, - new SelectMenuInteractionImpl(api, content))); + new StringSelectInteractionEvent(api, responseNumber, + new StringSelectInteractionImpl(api, content))); + break; + case USER_SELECT: + case ROLE_SELECT: + case MENTIONABLE_SELECT: + case CHANNEL_SELECT: + api.handleEvent( + new EntitySelectInteractionEvent(api, responseNumber, + new EntitySelectInteractionImpl(api, content))); break; } } diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/component/EntitySelectInteractionImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/component/EntitySelectInteractionImpl.java new file mode 100644 index 0000000000..e736781ece --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/interactions/component/EntitySelectInteractionImpl.java @@ -0,0 +1,60 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.interactions.component; + +import net.dv8tion.jda.api.entities.IMentionable; +import net.dv8tion.jda.api.entities.Mentions; +import net.dv8tion.jda.api.interactions.components.selections.EntitySelectInteraction; +import net.dv8tion.jda.api.interactions.components.selections.EntitySelectMenu; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.entities.SelectMenuMentions; + +import javax.annotation.Nonnull; +import java.util.List; + +public class EntitySelectInteractionImpl extends SelectMenuInteractionImpl implements EntitySelectInteraction +{ + private final Mentions mentions; + + public EntitySelectInteractionImpl(JDAImpl jda, DataObject data) + { + super(jda, EntitySelectMenu.class, data); + DataObject content = data.getObject("data"); + this.mentions = new SelectMenuMentions( + jda, + (GuildImpl) getGuild(), + content.getObject("resolved"), + content.getArray("values") + ); + } + + @Nonnull + @Override + public Mentions getMentions() + { + return mentions; + } + + @Nonnull + @Override + public List getValues() + { + return mentions.getMentions(); + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/component/EntitySelectMenuImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/component/EntitySelectMenuImpl.java new file mode 100644 index 0000000000..5da192976c --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/interactions/component/EntitySelectMenuImpl.java @@ -0,0 +1,118 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.interactions.component; + +import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.interactions.components.Component; +import net.dv8tion.jda.api.interactions.components.selections.EntitySelectMenu; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.utils.Helpers; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nonnull; +import java.util.EnumSet; +import java.util.Objects; +import java.util.stream.Collectors; + +public class EntitySelectMenuImpl extends SelectMenuImpl implements EntitySelectMenu +{ + protected final Component.Type type; + protected final EnumSet channelTypes; + + public EntitySelectMenuImpl(DataObject data) + { + super(data); + this.type = Component.Type.fromKey(data.getInt("type")); + this.channelTypes = Helpers.copyEnumSet(ChannelType.class, data.optArray("channel_types").map( + arr -> arr.stream(DataArray::getInt).map(ChannelType::fromId).collect(Collectors.toList()) + ).orElse(null)); + } + + public EntitySelectMenuImpl(String id, String placeholder, int minValues, int maxValues, boolean disabled, Type type, EnumSet channelTypes) + { + super(id, placeholder, minValues, maxValues, disabled); + this.type = type; + this.channelTypes = channelTypes; + } + + @Nonnull + @Override + public Type getType() + { + return type; + } + + @Nonnull + @Override + public EnumSet getEntityTypes() + { + switch (type) + { + case ROLE_SELECT: + return EnumSet.of(SelectTarget.ROLE); + case USER_SELECT: + return EnumSet.of(SelectTarget.USER); + case CHANNEL_SELECT: + return EnumSet.of(SelectTarget.CHANNEL); + case MENTIONABLE_SELECT: + return EnumSet.of(SelectTarget.ROLE, SelectTarget.USER); + } + // Ideally this never happens, so its undocumented + throw new IllegalStateException("Unsupported type: " + type); + } + + @Nonnull + @Override + public EnumSet getChannelTypes() + { + return channelTypes; + } + + @NotNull + @Override + public DataObject toData() + { + DataObject json = super.toData().put("type", type.getKey()); + if (type == Type.CHANNEL_SELECT && !channelTypes.isEmpty()) + json.put("channel_types", DataArray.fromCollection(channelTypes.stream().map(ChannelType::getId).collect(Collectors.toList()))); + return json; + } + + @Override + public int hashCode() + { + return Objects.hash(id, placeholder, minValues, maxValues, disabled, type, channelTypes); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + return true; + if (!(obj instanceof EntitySelectMenu)) + return false; + EntitySelectMenu other = (EntitySelectMenu) obj; + return Objects.equals(id, other.getId()) + && Objects.equals(placeholder, other.getPlaceholder()) + && minValues == other.getMinValues() + && maxValues == other.getMaxValues() + && disabled == other.isDisabled() + && type == other.getType() + && channelTypes.equals(other.getChannelTypes()); + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/component/SelectMenuImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/component/SelectMenuImpl.java index e15b01fbdd..157b42e5e3 100644 --- a/src/main/java/net/dv8tion/jda/internal/interactions/component/SelectMenuImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/interactions/component/SelectMenuImpl.java @@ -17,24 +17,17 @@ package net.dv8tion.jda.internal.interactions.component; import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; -import net.dv8tion.jda.api.interactions.components.selections.SelectOption; -import net.dv8tion.jda.api.utils.data.DataArray; import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.internal.utils.EntityString; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -public class SelectMenuImpl implements SelectMenu +public abstract class SelectMenuImpl implements SelectMenu { - private final String id, placeholder; - private final int minValues, maxValues; - private final boolean disabled; - private final List options; + protected final String id, placeholder; + protected final int minValues, maxValues; + protected final boolean disabled; public SelectMenuImpl(DataObject data) { @@ -43,35 +36,17 @@ public SelectMenuImpl(DataObject data) data.getString("placeholder", null), data.getInt("min_values", 1), data.getInt("max_values", 1), - data.getBoolean("disabled"), - parseOptions(data.getArray("options")) + data.getBoolean("disabled") ); } - public SelectMenuImpl(String id, String placeholder, int minValues, int maxValues, boolean disabled, List options) + public SelectMenuImpl(String id, String placeholder, int minValues, int maxValues, boolean disabled) { this.id = id; this.placeholder = placeholder; this.minValues = minValues; this.maxValues = maxValues; this.disabled = disabled; - this.options = Collections.unmodifiableList(options); - } - - private static List parseOptions(DataArray array) - { - List options = new ArrayList<>(array.length()); - array.stream(DataArray::getObject) - .map(SelectOption::fromData) - .forEach(options::add); - return options; - } - - @Nonnull - @Override - public Type getType() - { - return Type.SELECT_MENU; } @Nullable @@ -100,13 +75,6 @@ public int getMaxValues() return maxValues; } - @Nonnull - @Override - public List getOptions() - { - return options; - } - @Override public boolean isDisabled() { @@ -118,12 +86,10 @@ public boolean isDisabled() public DataObject toData() { DataObject data = DataObject.empty(); - data.put("type", 3); data.put("custom_id", id); data.put("min_values", minValues); data.put("max_values", maxValues); data.put("disabled", disabled); - data.put("options", DataArray.fromCollection(options)); if (placeholder != null) data.put("placeholder", placeholder); return data; @@ -132,31 +98,10 @@ public DataObject toData() @Override public String toString() { - return new EntityString(this) + return new EntityString(SelectMenu.class) + .setType(getType()) .addMetadata("id", id) .addMetadata("placeholder", placeholder) .toString(); } - - @Override - public int hashCode() - { - return Objects.hash(id, placeholder, minValues, maxValues, disabled, options); - } - - @Override - public boolean equals(Object obj) - { - if (obj == this) - return true; - if (!(obj instanceof SelectMenu)) - return false; - SelectMenu other = (SelectMenu) obj; - return Objects.equals(id, other.getId()) - && Objects.equals(placeholder, other.getPlaceholder()) - && minValues == other.getMinValues() - && maxValues == other.getMaxValues() - && disabled == other.isDisabled() - && Objects.equals(options, other.getOptions()); - } } diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/component/SelectMenuInteractionImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/component/SelectMenuInteractionImpl.java index d37d079dd0..2d9f546557 100644 --- a/src/main/java/net/dv8tion/jda/internal/interactions/component/SelectMenuInteractionImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/interactions/component/SelectMenuInteractionImpl.java @@ -19,33 +19,25 @@ import net.dv8tion.jda.api.interactions.components.Component; import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; import net.dv8tion.jda.api.interactions.components.selections.SelectMenuInteraction; -import net.dv8tion.jda.api.utils.data.DataArray; import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.internal.JDAImpl; import javax.annotation.Nonnull; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -public class SelectMenuInteractionImpl extends ComponentInteractionImpl implements SelectMenuInteraction +public abstract class SelectMenuInteractionImpl extends ComponentInteractionImpl implements SelectMenuInteraction { - private final List values; - private final SelectMenu menu; + private final S menu; - public SelectMenuInteractionImpl(JDAImpl jda, DataObject data) + public SelectMenuInteractionImpl(JDAImpl jda, Class type, DataObject data) { super(jda, data); - values = Collections.unmodifiableList(data.getObject("data").getArray("values") - .stream(DataArray::getString) - .collect(Collectors.toList())); if (message != null) { menu = message.getActionRows() .stream() .flatMap(row -> row.getComponents().stream()) - .filter(SelectMenu.class::isInstance) - .map(SelectMenu.class::cast) + .filter(type::isInstance) + .map(type::cast) .filter(c -> customId.equals(c.getId())) .findFirst() .orElse(null); @@ -58,7 +50,7 @@ public SelectMenuInteractionImpl(JDAImpl jda, DataObject data) @Nonnull @Override - public SelectMenu getComponent() + public S getComponent() { return menu; } @@ -67,13 +59,6 @@ public SelectMenu getComponent() @Override public Component.Type getComponentType() { - return Component.Type.SELECT_MENU; - } - - @Nonnull - @Override - public List getValues() - { - return values; + return menu.getType(); } } diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/component/StringSelectInteractionImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/component/StringSelectInteractionImpl.java new file mode 100644 index 0000000000..5e17e170eb --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/interactions/component/StringSelectInteractionImpl.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.interactions.component; + +import net.dv8tion.jda.api.interactions.components.selections.StringSelectInteraction; +import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class StringSelectInteractionImpl extends SelectMenuInteractionImpl implements StringSelectInteraction +{ + private final List values; + + public StringSelectInteractionImpl(JDAImpl jda, DataObject data) + { + super(jda, StringSelectMenu.class, data); + this.values = Collections.unmodifiableList(parseValues(data.getObject("data"))); + } + + protected List parseValues(DataObject data) + { + return data.getArray("values") + .stream(DataArray::getString) + .collect(Collectors.toList()); + } + + @Nonnull + @Override + public List getValues() + { + return values; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/component/StringSelectMenuImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/component/StringSelectMenuImpl.java new file mode 100644 index 0000000000..fc3a08d374 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/interactions/component/StringSelectMenuImpl.java @@ -0,0 +1,98 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.interactions.component; + +import net.dv8tion.jda.api.interactions.components.selections.SelectOption; +import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class StringSelectMenuImpl extends SelectMenuImpl implements StringSelectMenu +{ + private final List options; + + public StringSelectMenuImpl(DataObject data) + { + super(data); + this.options = parseOptions(data.getArray("options")); + } + + public StringSelectMenuImpl(String id, String placeholder, int minValues, int maxValues, boolean disabled, List options) + { + super(id, placeholder, minValues, maxValues, disabled); + this.options = options; + } + + private static List parseOptions(DataArray array) + { + List options = new ArrayList<>(array.length()); + array.stream(DataArray::getObject) + .map(SelectOption::fromData) + .forEach(options::add); + return options; + } + + @Nonnull + @Override + public Type getType() + { + return Type.STRING_SELECT; + } + + @Nonnull + @Override + public List getOptions() + { + return options; + } + + @Nonnull + @Override + public DataObject toData() + { + return super.toData() + .put("type", Type.STRING_SELECT.getKey()) + .put("options", DataArray.fromCollection(options)); + } + + @Override + public int hashCode() + { + return Objects.hash(id, placeholder, minValues, maxValues, disabled, options); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + return true; + if (!(obj instanceof StringSelectMenu)) + return false; + StringSelectMenu other = (StringSelectMenu) obj; + return Objects.equals(id, other.getId()) + && Objects.equals(placeholder, other.getPlaceholder()) + && minValues == other.getMinValues() + && maxValues == other.getMaxValues() + && disabled == other.isDisabled() + && Objects.equals(options, other.getOptions()); + } +}