All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.dv8tion.jda.api.interactions.components.selections.EntitySelectMenu Maven / Gradle / Ivy

Go to download

Java wrapper for the popular chat & VOIP service: Discord https://discord.com

There is a newer version: 5.1.0
Show newest version
/*
 * 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.ISnowflake;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.UserSnowflake;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.events.interaction.component.EntitySelectInteractionEvent;
import net.dv8tion.jda.api.interactions.components.ActionComponent;
import net.dv8tion.jda.api.interactions.components.Component;
import net.dv8tion.jda.api.utils.MiscUtil;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.api.utils.data.SerializableData;
import net.dv8tion.jda.internal.interactions.component.EntitySelectMenuImpl;
import net.dv8tion.jda.internal.utils.Checks;
import net.dv8tion.jda.internal.utils.EntityString;
import net.dv8tion.jda.internal.utils.Helpers;

import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import java.util.*;

/**
 * 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
 *     .setRequiredRange(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(); /** * Default selected values. *
These are shown until the user customizes the selected values, * which then fires a {@link EntitySelectInteractionEvent}. * * @return Immutable list of {@link DefaultValue default values} */ @Nonnull List getDefaultValues(); /** * 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 *

    *
  • If the provided id is null, empty, or longer than {@value ID_MAX_LENGTH} characters.
  • *
  • If the provided types are null, empty, or invalid.
  • *
* * @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 } /** * Represents the default values used in {@link #getDefaultValues()}. *
The value is {@link #getType() typed} correspondingly to the menu {@link EntitySelectMenu#getEntityTypes() entity types}. * *

The value is represented by the {@link #getId() ID}, corresponding to the entity of that ID. */ class DefaultValue implements ISnowflake, SerializableData { private final long id; private final SelectTarget type; protected DefaultValue(long id, @Nonnull SelectTarget type) { this.id = id; this.type = type; } /** * Parses the provided {@link DataObject} into the default value. * * @param object * The serialized default value, with a valid type and id * * @throws IllegalArgumentException * If the provided object is invalid or missing required keys * * @return Parsed default value */ @Nonnull public static DefaultValue fromData(@Nonnull DataObject object) { Checks.notNull(object, "DataObject"); long id = object.getUnsignedLong("id"); switch (object.getString("type")) { case "role": return role(id); case "user": return user(id); case "channel": return channel(id); } throw new IllegalArgumentException("Unknown value type '" + object.getString("type", null) + "'"); } /** * Creates a default value of type {@link SelectTarget#USER} for the provided user. * * @param user * The corresponding user * * @throws IllegalArgumentException * If null is provided * * @return The default value */ @Nonnull public static DefaultValue from(@Nonnull UserSnowflake user) { Checks.notNull(user, "User"); return user(user.getIdLong()); } /** * Creates a default value of type {@link SelectTarget#ROLE} for the provided role. * * @param role * The corresponding role * * @throws IllegalArgumentException * If null is provided * * @return The default value */ @Nonnull public static DefaultValue from(@Nonnull Role role) { Checks.notNull(role, "Role"); return role(role.getIdLong()); } /** * Creates a default value of type {@link SelectTarget#CHANNEL} for the provided channel. * * @param channel * The corresponding channel * * @throws IllegalArgumentException * If null is provided * * @return The default value */ @Nonnull public static DefaultValue from(@Nonnull GuildChannel channel) { Checks.notNull(channel, "Channel"); return channel(channel.getIdLong()); } /** * Creates a default value of type {@link SelectTarget#ROLE} with the provided id. * * @param id * The role id * * @return The default value */ @Nonnull public static DefaultValue role(long id) { return new DefaultValue(id, SelectTarget.ROLE); } /** * Creates a default value of type {@link SelectTarget#ROLE} with the provided id. * * @param id * The role id * * @throws IllegalArgumentException * If the provided id is not a valid snowflake * * @return The default value */ @Nonnull public static DefaultValue role(@Nonnull String id) { return new DefaultValue(MiscUtil.parseSnowflake(id), SelectTarget.ROLE); } /** * Creates a default value of type {@link SelectTarget#USER} with the provided id. * * @param id * The role id * * @return The default value */ @Nonnull public static DefaultValue user(long id) { return new DefaultValue(id, SelectTarget.USER); } /** * Creates a default value of type {@link SelectTarget#USER} with the provided id. * * @param id * The role id * * @throws IllegalArgumentException * If the provided id is not a valid snowflake * * @return The default value */ @Nonnull public static DefaultValue user(@Nonnull String id) { return new DefaultValue(MiscUtil.parseSnowflake(id), SelectTarget.USER); } /** * Creates a default value of type {@link SelectTarget#CHANNEL} with the provided id. * * @param id * The role id * * @return The default value */ @Nonnull public static DefaultValue channel(long id) { return new DefaultValue(id, SelectTarget.CHANNEL); } /** * Creates a default value of type {@link SelectTarget#CHANNEL} with the provided id. * * @param id * The role id * * @throws IllegalArgumentException * If the provided id is not a valid snowflake * * @return The default value */ @Nonnull public static DefaultValue channel(@Nonnull String id) { return new DefaultValue(MiscUtil.parseSnowflake(id), SelectTarget.CHANNEL); } @Override public long getIdLong() { return id; } @Nonnull public SelectTarget getType() { return type; } @Nonnull @Override public DataObject toData() { return DataObject.empty() .put("type", type.name().toLowerCase(Locale.ROOT)) .put("id", getId()); } @Override public int hashCode() { return Objects.hash(type, id); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof DefaultValue)) return false; DefaultValue other = (DefaultValue) obj; return id == other.id && type == other.type; } @Override public String toString() { return new EntityString(this) .setType(type) .toString(); } } /** * 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 List defaultValues = new ArrayList<>(); 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)); } /** * The {@link #getDefaultValues() default values} that will be shown to the user. * * @param values * The default values (up to {@value #OPTIONS_MAX_AMOUNT}) * * @throws IllegalArgumentException * If null is provided, more than {@value #OPTIONS_MAX_AMOUNT} values are provided, * or any of the value types is incompatible with the configured {@link #setEntityTypes(Collection) entity types}. * * @return The current Builder instance */ @Nonnull public Builder setDefaultValues(@Nonnull DefaultValue... values) { Checks.noneNull(values, "Default Values"); return setDefaultValues(Arrays.asList(values)); } /** * The {@link #getDefaultValues() default values} that will be shown to the user. * * @param values * The default values (up to {@value #OPTIONS_MAX_AMOUNT}) * * @throws IllegalArgumentException * If null is provided, more than {@value #OPTIONS_MAX_AMOUNT} values are provided, * or any of the value types is incompatible with the configured {@link #setEntityTypes(Collection) entity types}. * * @return The current Builder instance */ @Nonnull public Builder setDefaultValues(@Nonnull Collection values) { Checks.noneNull(values, "Default Values"); Checks.check(values.size() <= SelectMenu.OPTIONS_MAX_AMOUNT, "Cannot add more than %d default values to a select menu!", SelectMenu.OPTIONS_MAX_AMOUNT); for (DefaultValue value : values) { SelectTarget type = value.getType(); String error = "The select menu supports types %s, but provided default value has type SelectTarget.%s!"; switch (componentType) { case ROLE_SELECT: Checks.check(type == SelectTarget.ROLE, error, "SelectTarget.ROLE", type); break; case USER_SELECT: Checks.check(type == SelectTarget.USER, error, "SelectTarget.USER", type); break; case CHANNEL_SELECT: Checks.check(type == SelectTarget.CHANNEL, error, "SelectTarget.CHANNEL", type); break; case MENTIONABLE_SELECT: Checks.check(type == SelectTarget.ROLE || type == SelectTarget.USER, error, "SelectTarget.ROLE and SelectTarget.USER", type); break; } } this.defaultValues.clear(); this.defaultValues.addAll(values); return this; } /** * 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); List defaultValues = new ArrayList<>(this.defaultValues); return new EntitySelectMenuImpl(customId, placeholder, minValues, maxValues, disabled, componentType, channelTypes, defaultValues); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy