diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/CommandData.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/CommandData.java index 9ee84f2d92..1eb6b78076 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/CommandData.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/CommandData.java @@ -40,15 +40,20 @@ public interface CommandData extends SerializableData { /** - * The maximum length the name of a command can be. + * The maximum length the name of a command can be. ({@value}) */ int MAX_NAME_LENGTH = 32; /** - * The maximum length the description of a command can be. + * The maximum length the description of a command can be. ({@value}) */ int MAX_DESCRIPTION_LENGTH = 100; + /** + * The maximum amount of options/subcommands/groups that can be added to a command or subcommand. ({@value}) + */ + int MAX_OPTIONS = 25; + /** * Sets the {@link LocalizationFunction} for this command *
This enables you to have the entirety of this command to be localized. @@ -68,10 +73,10 @@ public interface CommandData extends SerializableData * Configure the command name. * * @param name - * The name, 1-32 characters (lowercase and alphanumeric for {@link Command.Type#SLASH}) + * The name, 1-{@value #MAX_NAME_LENGTH} characters (lowercase and alphanumeric for {@link Command.Type#SLASH}) * * @throws IllegalArgumentException - * If the name is not between 1-32 characters long, or not lowercase and alphanumeric for slash commands + * If the name is not between 1-{@value #MAX_NAME_LENGTH} characters long, or not lowercase and alphanumeric for slash commands * * @return The builder instance, for chaining */ diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SlashCommandData.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SlashCommandData.java index 7c1d0bbee4..bd184de8d3 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SlashCommandData.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SlashCommandData.java @@ -33,6 +33,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.function.Predicate; /** * Extension of {@link CommandData} which allows setting slash-command specific settings such as options and subcommands. @@ -67,10 +68,10 @@ public interface SlashCommandData extends CommandData * Configure the description * * @param description - * The description, 1-100 characters + * The description, 1-{@value #MAX_DESCRIPTION_LENGTH} characters * * @throws IllegalArgumentException - * If the name is null or not between 1-100 characters + * If the name is null or not between 1-{@value #MAX_DESCRIPTION_LENGTH} characters * * @return The builder, for chaining */ @@ -133,10 +134,113 @@ public interface SlashCommandData extends CommandData @Nonnull LocalizationMap getDescriptionLocalizations(); + /** + * Removes all options that evaluate to {@code true} under the provided {@code condition}. + *
This will not affect options within subcommands. + * Use {@link SubcommandData#removeOptions(Predicate)} instead. + * + *

Example: Remove all options + *

{@code
+     * command.removeOptions(option -> true);
+     * }
+ *

Example: Remove all options that are required + *

{@code
+     * command.removeOptions(option -> option.isRequired());
+     * }
+ * + * @param condition + * The removal condition (must not throw) + * + * @throws IllegalArgumentException + * If the condition is null + * + * @return True, if any options were removed + */ + boolean removeOptions(@Nonnull Predicate condition); + + /** + * Removes options by the provided name. + *
This will not affect options within subcommands. + * Use {@link SubcommandData#removeOptionByName(String)} instead. + * + * @param name + * The case-sensitive option name + * + * @return True, if any options were removed + */ + default boolean removeOptionByName(@Nonnull String name) + { + return removeOptions(option -> option.getName().equals(name)); + } + + /** + * Removes all subcommands that evaluate to {@code true} under the provided {@code condition}. + *
This will not apply to subcommands within subcommand groups. + * Use {@link SubcommandGroupData#removeSubcommand(Predicate)} instead. + * + *

Example: Remove all subcommands + *

{@code
+     * command.removeSubcommands(subcommand -> true);
+     * }
+ * + * @param condition + * The removal condition (must not throw) + * + * @throws IllegalArgumentException + * If the condition is null + * + * @return True, if any subcommands were removed + */ + boolean removeSubcommands(@Nonnull Predicate condition); + + /** + * Removes subcommands by the provided name. + *
This will not apply to subcommands within subcommand groups. + * Use {@link SubcommandGroupData#removeSubcommandByName(String)} instead. + * + * @param name + * The case-sensitive subcommand name + * + * @return True, if any subcommands were removed + */ + default boolean removeSubcommandByName(@Nonnull String name) + { + return removeSubcommands(subcommand -> subcommand.getName().equals(name)); + } + + /** + * Removes all subcommand groups that evaluate to {@code true} under the provided {@code condition}. + * + *

Example: Remove all subcommand groups + *

{@code
+     * command.removeSubcommandGroups(group -> true);
+     * }
+ * + * @param condition + * The removal condition (must not throw) + * + * @throws IllegalArgumentException + * If the condition is null + * + * @return True, if any subcommand groups were removed + */ + boolean removeSubcommandGroups(@Nonnull Predicate condition); + + /** + * Removes subcommand groups by the provided name. + * + * @param name + * The case-sensitive subcommand group name + * + * @return True, if any subcommand groups were removed + */ + default boolean removeSubcommandGroupByName(@Nonnull String name) + { + return removeSubcommandGroups(group -> group.getName().equals(name)); + } + /** * The {@link SubcommandData Subcommands} in this command. - *
These subcommand instances are reconstructed, - * which means that any modifications will not be reflected in the backing state. * * @return Immutable list of {@link SubcommandData} */ @@ -145,8 +249,6 @@ public interface SlashCommandData extends CommandData /** * The {@link SubcommandGroupData Subcommand Groups} in this command. - *
These subcommand group instances are reconstructed, - * which means that any modifications will not be reflected in the backing state. * * @return Immutable list of {@link SubcommandGroupData} */ @@ -155,8 +257,6 @@ public interface SlashCommandData extends CommandData /** * The options for this command. - *
These option instances are reconstructed, - * which means that any modifications will not be reflected in the backing state. * * @return Immutable list of {@link OptionData} */ @@ -164,7 +264,7 @@ public interface SlashCommandData extends CommandData List getOptions(); /** - * Adds up to 25 options to this command. + * Adds up to {@value CommandData#MAX_OPTIONS} options to this command. * *

Required options must be added before non-required options! * @@ -173,10 +273,10 @@ public interface SlashCommandData extends CommandData * * @throws IllegalArgumentException *

@@ -187,7 +287,7 @@ public interface SlashCommandData extends CommandData SlashCommandData addOptions(@Nonnull OptionData... options); /** - * Adds up to 25 options to this command. + * Adds up to {@value CommandData#MAX_OPTIONS} options to this command. * *

Required options must be added before non-required options! * @@ -196,10 +296,10 @@ public interface SlashCommandData extends CommandData * * @throws IllegalArgumentException *

@@ -221,9 +321,9 @@ default SlashCommandData addOptions(@Nonnull Collection op * @param type * The {@link OptionType} * @param name - * The lowercase option name, 1-32 characters + * The lowercase option name, 1-{@value OptionData#MAX_NAME_LENGTH} characters * @param description - * The option description, 1-100 characters + * The option description, 1-{@value OptionData#MAX_DESCRIPTION_LENGTH} characters * @param required * Whether this option is required (See {@link OptionData#setRequired(boolean)}) * @param autoComplete @@ -232,12 +332,12 @@ default SlashCommandData addOptions(@Nonnull Collection op * * @throws IllegalArgumentException * @@ -260,19 +360,19 @@ default SlashCommandData addOption(@Nonnull OptionType type, @Nonnull String nam * @param type * The {@link OptionType} * @param name - * The lowercase option name, 1-32 characters + * The lowercase option name, 1-{@value OptionData#MAX_NAME_LENGTH} characters * @param description - * The option description, 1-100 characters + * The option description, 1-{@value OptionData#MAX_DESCRIPTION_LENGTH} characters * @param required * Whether this option is required (See {@link OptionData#setRequired(boolean)}) * * @throws IllegalArgumentException * @@ -294,17 +394,17 @@ default SlashCommandData addOption(@Nonnull OptionType type, @Nonnull String nam * @param type * The {@link OptionType} * @param name - * The lowercase option name, 1-32 characters + * The lowercase option name, 1-{@value OptionData#MAX_NAME_LENGTH} characters * @param description - * The option description, 1-100 characters + * The option description, 1-{@value OptionData#MAX_DESCRIPTION_LENGTH} characters * * @throws IllegalArgumentException * @@ -318,14 +418,35 @@ default SlashCommandData addOption(@Nonnull OptionType type, @Nonnull String nam } /** - * Add up to 25 {@link SubcommandData Subcommands} to this command. + * Add up to {@value CommandData#MAX_OPTIONS} {@link SubcommandData Subcommands} to this command. + *
When a subcommand or subcommand group is added, the base command itself cannot be used. + * Thus using {@link #addOptions(OptionData...)} and {@link #addSubcommands(SubcommandData...)} / {@link #addSubcommandGroups(SubcommandGroupData...)} + * for the same command, is not supported. + * + *

Valid command layouts are as follows: + *

{@code
+     * command
+     * |-- subcommand
+     * |__ subcommand group
+     *     |__ subcommand
+     *
+     * command
+     * |__ subcommand group
+     *     |__ subcommand
+     *
+     * command
+     * |-- option
+     * |__ option
+     * }
+ * + * Having an option and subcommand simultaneously is not allowed. * * @param subcommands * The subcommands to add * * @throws IllegalArgumentException - * If null, more than 25 subcommands, or duplicate subcommand names are provided. - * Also throws if you try to mix subcommands/options/groups in one command. + * If null, more than {@value CommandData#MAX_OPTIONS} subcommands, or duplicate subcommand names are provided. + * Also throws if you try adding subcommands when options are already present. * * @return The builder instance, for chaining */ @@ -333,14 +454,35 @@ default SlashCommandData addOption(@Nonnull OptionType type, @Nonnull String nam SlashCommandData addSubcommands(@Nonnull SubcommandData... subcommands); /** - * Add up to 25 {@link SubcommandData Subcommands} to this command. + * Add up to {@value CommandData#MAX_OPTIONS} {@link SubcommandData Subcommands} to this command. + *
When a subcommand or subcommand group is added, the base command itself cannot be used. + * Thus using {@link #addOptions(OptionData...)} and {@link #addSubcommands(SubcommandData...)} / {@link #addSubcommandGroups(SubcommandGroupData...)} + * for the same command, is not supported. + * + *

Valid command layouts are as follows: + *

{@code
+     * command
+     * |-- subcommand
+     * |__ subcommand group
+     *     |__ subcommand
+     *
+     * command
+     * |__ subcommand group
+     *     |__ subcommand
+     *
+     * command
+     * |-- option
+     * |__ option
+     * }
+ * + * Having an option and subcommand simultaneously is not allowed. * * @param subcommands * The subcommands to add * * @throws IllegalArgumentException - * If null, more than 25 subcommands, or duplicate subcommand names are provided. - * Also throws if you try to mix subcommands/options/groups in one command. + * If null, more than {@value CommandData#MAX_OPTIONS} subcommands, or duplicate subcommand names are provided. + * Also throws if you try adding subcommands when options are already present. * * @return The builder instance, for chaining */ @@ -352,14 +494,35 @@ default SlashCommandData addSubcommands(@Nonnull CollectionWhen a subcommand or subcommand group is added, the base command itself cannot be used. + * Thus using {@link #addOptions(OptionData...)} and {@link #addSubcommands(SubcommandData...)} / {@link #addSubcommandGroups(SubcommandGroupData...)} + * for the same command, is not supported. + * + *

Valid command layouts are as follows: + *

{@code
+     * command
+     * |-- subcommand
+     * |__ subcommand group
+     *     |__ subcommand
+     *
+     * command
+     * |__ subcommand group
+     *     |__ subcommand
+     *
+     * command
+     * |-- option
+     * |__ option
+     * }
+ * + * Having an option and subcommand simultaneously is not allowed. * * @param groups * The subcommand groups to add * * @throws IllegalArgumentException - * If null, more than 25 subcommand groups, or duplicate group names are provided. - * Also throws if you try to mix subcommands/options/groups in one command. + * If null, more than {@value CommandData#MAX_OPTIONS} subcommand groups, or duplicate group names are provided. + * Also throws if you try adding subcommand groups when options are already present. * * @return The builder instance, for chaining */ @@ -367,14 +530,35 @@ default SlashCommandData addSubcommands(@Nonnull CollectionWhen a subcommand or subcommand group is added, the base command itself cannot be used. + * Thus using {@link #addOptions(OptionData...)} and {@link #addSubcommands(SubcommandData...)} / {@link #addSubcommandGroups(SubcommandGroupData...)} + * for the same command, is not supported. + * + *

Valid command layouts are as follows: + *

{@code
+     * command
+     * |-- subcommand
+     * |__ subcommand group
+     *     |__ subcommand
+     *
+     * command
+     * |__ subcommand group
+     *     |__ subcommand
+     *
+     * command
+     * |-- option
+     * |__ option
+     * }
+ * + * Having an option and subcommand simultaneously is not allowed. * * @param groups * The subcommand groups to add * * @throws IllegalArgumentException - * If null, more than 25 subcommand groups, or duplicate group names are provided. - * Also throws if you try to mix subcommands/options/groups in one command. + * If null, more than {@value CommandData#MAX_OPTIONS} subcommand groups, or duplicate group names are provided. + * Also throws if you try adding subcommand groups when options are already present. * * @return The builder instance, for chaining */ diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandData.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandData.java index 92b2d6691e..60cfc3a3d2 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandData.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandData.java @@ -25,14 +25,11 @@ import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.api.utils.data.SerializableData; import net.dv8tion.jda.internal.utils.Checks; -import net.dv8tion.jda.internal.utils.Helpers; import net.dv8tion.jda.internal.utils.localization.LocalizationUtils; import javax.annotation.Nonnull; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.function.Predicate; import java.util.stream.Stream; /** @@ -40,7 +37,7 @@ */ public class SubcommandData implements SerializableData { - protected final DataArray options = DataArray.empty(); + protected final List options = new ArrayList<>(CommandData.MAX_OPTIONS); protected String name, description; private final LocalizationMap nameLocalizations = new LocalizationMap(this::checkName); private final LocalizationMap descriptionLocalizations = new LocalizationMap(this::checkDescription); @@ -219,7 +216,46 @@ public SubcommandData setDescriptionLocalizations(@Nonnull MapExample: Remove all options + *
{@code
+     * command.removeOptions(option -> true);
+     * }
+ *

Example: Remove all options that are required + *

{@code
+     * command.removeOptions(option -> option.isRequired());
+     * }
+ * + * @param condition + * The removal condition (must not throw) + * + * @throws IllegalArgumentException + * If the condition is null + * + * @return True, if any options were removed + */ + public boolean removeOptions(@Nonnull Predicate condition) + { + Checks.notNull(condition, "Condition"); + return options.removeIf(condition); + } + + /** + * Removes options by the provided name. + * + * @param name + * The case-sensitive option name + * + * @return True, if any options were removed + */ + public boolean removeOptionByName(@Nonnull String name) + { + return removeOptions(option -> option.getName().equals(name)); + } + + /** + * Adds up to {@value CommandData#MAX_OPTIONS} options to this subcommand. * *

Required options must be added before non-required options! * @@ -229,7 +265,7 @@ public SubcommandData setDescriptionLocalizations(@Nonnull Map *

  • If this option is required and you already added a non-required option.
  • - *
  • If more than 25 options are provided.
  • + *
  • If more than {@value CommandData#MAX_OPTIONS} options are provided.
  • *
  • If the option name is not unique
  • *
  • If null is provided
  • * @@ -240,7 +276,7 @@ public SubcommandData setDescriptionLocalizations(@Nonnull Map new Object[]{ value, count }); this.allowRequired = allowRequired; - for (OptionData option : options) - this.options.add(option); + this.options.addAll(Arrays.asList(options)); return this; } /** - * Adds up to 25 options to this subcommand. + * Adds up to {@value CommandData#MAX_OPTIONS} options to this subcommand. * *

    Required options must be added before non-required options! * @@ -272,7 +307,7 @@ public SubcommandData addOptions(@Nonnull OptionData... options) * @throws IllegalArgumentException *

      *
    • If this option is required and you already added a non-required option.
    • - *
    • If more than 25 options are provided.
    • + *
    • If more than {@value CommandData#MAX_OPTIONS} options are provided.
    • *
    • If the option name is not unique
    • *
    • If null is provided
    • *
    @@ -294,9 +329,9 @@ public SubcommandData addOptions(@Nonnull Collection optio * @param type * The {@link OptionType} * @param name - * The lowercase option name, 1-32 characters + * The lowercase option name, 1-{@value OptionData#MAX_NAME_LENGTH} characters * @param description - * The option description, 1-100 characters + * The option description, 1-{@value OptionData#MAX_DESCRIPTION_LENGTH} characters * @param required * Whether this option is required (See {@link OptionData#setRequired(boolean)}) * @param autoComplete @@ -307,7 +342,7 @@ public SubcommandData addOptions(@Nonnull Collection optio *
      *
    • If this option is required and you already added a non-required option.
    • *
    • If the provided option type does not support auto-complete
    • - *
    • If more than 25 options are provided.
    • + *
    • If more than {@value CommandData#MAX_OPTIONS} options are provided.
    • *
    • If the option name is not unique
    • *
    • If null is provided
    • *
    @@ -330,16 +365,16 @@ public SubcommandData addOption(@Nonnull OptionType type, @Nonnull String name, * @param type * The {@link OptionType} * @param name - * The lowercase option name, 1-32 characters + * The lowercase option name, 1-{@value OptionData#MAX_NAME_LENGTH} characters * @param description - * The option description, 1-100 characters + * The option description, 1-{@value OptionData#MAX_DESCRIPTION_LENGTH} characters * @param required * Whether this option is required (See {@link OptionData#setRequired(boolean)}) * * @throws IllegalArgumentException *
      *
    • If this option is required and you already added a non-required option.
    • - *
    • If more than 25 options are provided.
    • + *
    • If more than {@value CommandData#MAX_OPTIONS} options are provided.
    • *
    • If the option name is not unique
    • *
    • If null is provided
    • *
    @@ -361,14 +396,14 @@ public SubcommandData addOption(@Nonnull OptionType type, @Nonnull String name, * @param type * The {@link OptionType} * @param name - * The lowercase option name, 1-32 characters + * The lowercase option name, 1-{@value OptionData#MAX_NAME_LENGTH} characters * @param description - * The option description, 1-100 characters + * The option description, 1-{@value OptionData#MAX_DESCRIPTION_LENGTH} characters * * @throws IllegalArgumentException *
      *
    • If this option is required and you already added a non-required option.
    • - *
    • If more than 25 options are provided.
    • + *
    • If more than {@value CommandData#MAX_OPTIONS} options are provided.
    • *
    • If the option name is not unique
    • *
    • If null is provided
    • *
    @@ -389,10 +424,7 @@ public SubcommandData addOption(@Nonnull OptionType type, @Nonnull String name, @Nonnull public List getOptions() { - return options.stream(DataArray::getObject) - .map(OptionData::fromData) - .filter(it -> it.getType().getKey() > OptionType.SUB_COMMAND_GROUP.getKey()) - .collect(Helpers.toUnmodifiableList()); + return Collections.unmodifiableList(options); } /** @@ -449,7 +481,7 @@ public DataObject toData() .put("name_localizations", nameLocalizations) .put("description", description) .put("description_localizations", descriptionLocalizations) - .put("options", options); + .put("options", DataArray.fromCollection(options)); } /** diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandGroupData.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandGroupData.java index d2396c0645..36d519f964 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandGroupData.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandGroupData.java @@ -24,14 +24,11 @@ import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.api.utils.data.SerializableData; import net.dv8tion.jda.internal.utils.Checks; -import net.dv8tion.jda.internal.utils.Helpers; import net.dv8tion.jda.internal.utils.localization.LocalizationUtils; import javax.annotation.Nonnull; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.function.Predicate; import java.util.stream.Stream; /** @@ -39,7 +36,7 @@ */ public class SubcommandGroupData implements SerializableData { - private final DataArray options = DataArray.empty(); + private final List subcommands = new ArrayList<>(CommandData.MAX_OPTIONS); private String name, description; private final LocalizationMap nameLocalizations = new LocalizationMap(this::checkName); private final LocalizationMap descriptionLocalizations = new LocalizationMap(this::checkDescription); @@ -267,29 +264,60 @@ public LocalizationMap getDescriptionLocalizations() return descriptionLocalizations; } + /** + * Removes all subcommands that evaluate to {@code true} under the provided {@code condition}. + * + *

    Example: Remove all subcommands + *

    {@code
    +     * command.removeSubcommands(subcommand -> true);
    +     * }
    + * + * @param condition + * The removal condition (must not throw) + * + * @throws IllegalArgumentException + * If the condition is null + * + * @return True, if any subcommands were removed + */ + public boolean removeSubcommand(@Nonnull Predicate condition) + { + Checks.notNull(condition, "Condition"); + return subcommands.removeIf(condition); + } + + /** + * Removes subcommands by the provided name. + * + * @param name + * The case-sensitive subcommand name + * + * @return True, if any subcommands were removed + */ + public boolean removeSubcommandByName(@Nonnull String name) + { + return removeSubcommand(subcommand -> subcommand.getName().equals(name)); + } + /** * The {@link SubcommandData Subcommands} in this group. - *
    These subcommand instances are reconstructed, - * which means that any modifications will not be reflected in the backing state. * * @return Immutable list of {@link SubcommandData} */ @Nonnull public List getSubcommands() { - return options.stream(DataArray::getObject) - .map(SubcommandData::fromData) - .collect(Helpers.toUnmodifiableList()); + return Collections.unmodifiableList(subcommands); } /** - * Add up to 25 {@link SubcommandData Subcommands} to this group. + * Add up to {@value CommandData#MAX_OPTIONS} {@link SubcommandData Subcommands} to this group. * * @param subcommands * The subcommands to add * * @throws IllegalArgumentException - * If null, more than 25 subcommands, or duplicate subcommand names are provided. + * If null, more than {@value CommandData#MAX_OPTIONS} subcommands, or duplicate subcommand names are provided. * * @return The SubcommandGroupData instance, for chaining */ @@ -297,14 +325,13 @@ public List getSubcommands() public SubcommandGroupData addSubcommands(@Nonnull SubcommandData... subcommands) { Checks.noneNull(subcommands, "Subcommand"); - Checks.check(subcommands.length + options.length() <= 25, "Cannot have more than 25 subcommands in one group!"); + Checks.check(subcommands.length + this.subcommands.size() <= CommandData.MAX_OPTIONS, "Cannot have more than %d subcommands in one group!", CommandData.MAX_OPTIONS); Checks.checkUnique( Stream.concat(getSubcommands().stream(), Arrays.stream(subcommands)).map(SubcommandData::getName), "Cannot have multiple subcommands with the same name. Name: \"%s\" appeared %d times!", (count, value) -> new Object[]{ value, count } ); - for (SubcommandData subcommand : subcommands) - options.add(subcommand); + this.subcommands.addAll(Arrays.asList(subcommands)); return this; } @@ -336,7 +363,7 @@ public DataObject toData() .put("name_localizations", nameLocalizations) .put("description", description) .put("description_localizations", descriptionLocalizations) - .put("options", options); + .put("options", DataArray.fromCollection(subcommands)); } /** diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/CommandDataImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/CommandDataImpl.java index 1b03d07fb0..a3fc7560da 100644 --- a/src/main/java/net/dv8tion/jda/internal/interactions/CommandDataImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/interactions/CommandDataImpl.java @@ -20,34 +20,31 @@ import net.dv8tion.jda.api.interactions.commands.Command; import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.OptionType; -import net.dv8tion.jda.api.interactions.commands.build.OptionData; -import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; -import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; -import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData; +import net.dv8tion.jda.api.interactions.commands.build.*; import net.dv8tion.jda.api.interactions.commands.localization.LocalizationFunction; import net.dv8tion.jda.api.interactions.commands.localization.LocalizationMap; import net.dv8tion.jda.api.utils.data.DataArray; import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.api.utils.data.SerializableData; import net.dv8tion.jda.internal.interactions.command.localization.LocalizationMapper; import net.dv8tion.jda.internal.utils.Checks; import net.dv8tion.jda.internal.utils.Helpers; import javax.annotation.Nonnull; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.function.Predicate; import java.util.stream.Stream; public class CommandDataImpl implements SlashCommandData { - protected final DataArray options = DataArray.empty(); + protected final List options = new ArrayList<>(MAX_OPTIONS); + protected String name, description = ""; private LocalizationMapper localizationMapper; private final LocalizationMap nameLocalizations = new LocalizationMap(this::checkName); private final LocalizationMap descriptionLocalizations = new LocalizationMap(this::checkDescription); private boolean allowSubcommands = true; - private boolean allowGroups = true; private boolean allowOption = true; private boolean allowRequired = true; private boolean guildOnly = false; @@ -97,6 +94,8 @@ public void checkDescription(@Nonnull String description) @Override public DataObject toData() { + DataArray options = DataArray.fromCollection(this.options); + if (localizationMapper != null) localizationMapper.localizeCommand(this, options); DataObject json = DataObject.empty() @@ -107,11 +106,13 @@ public DataObject toData() .put("default_member_permissions", defaultMemberPermissions == DefaultMemberPermissions.ENABLED ? null : Long.toUnsignedString(defaultMemberPermissions.getPermissionsRaw())) - .put("name_localizations", nameLocalizations) - .put("options", options); + .put("name_localizations", nameLocalizations); + if (type == Command.Type.SLASH) + { json.put("description", description) .put("description_localizations", descriptionLocalizations); + } return json; } @@ -135,17 +136,23 @@ public boolean isGuildOnly() return guildOnly; } + @Nonnull + @Override + public List getOptions() + { + return options.stream() + .filter(OptionData.class::isInstance) + .map(OptionData.class::cast) + .collect(Helpers.toUnmodifiableList()); + } + @Nonnull @Override public List getSubcommands() { - return options.stream(DataArray::getObject) - .filter(obj -> - { - OptionType type = OptionType.fromKey(obj.getInt("type")); - return type == OptionType.SUB_COMMAND; - }) - .map(SubcommandData::fromData) + return options.stream() + .filter(SubcommandData.class::isInstance) + .map(SubcommandData.class::cast) .collect(Helpers.toUnmodifiableList()); } @@ -153,13 +160,9 @@ public List getSubcommands() @Override public List getSubcommandGroups() { - return options.stream(DataArray::getObject) - .filter(obj -> - { - OptionType type = OptionType.fromKey(obj.getInt("type")); - return type == OptionType.SUB_COMMAND_GROUP; - }) - .map(SubcommandGroupData::fromData) + return options.stream() + .filter(SubcommandGroupData.class::isInstance) + .map(SubcommandGroupData.class::cast) .collect(Helpers.toUnmodifiableList()); } @@ -188,7 +191,7 @@ public CommandDataImpl addOptions(@Nonnull OptionData... options) if (options.length == 0) return this; checkType(Command.Type.SLASH, "add options"); - Checks.check(options.length + this.options.length() <= 25, "Cannot have more than 25 options for a command!"); + Checks.check(options.length + this.options.size() <= CommandData.MAX_OPTIONS, "Cannot have more than %d options for a command!", CommandData.MAX_OPTIONS); Checks.check(allowOption, "You cannot mix options with subcommands/groups."); boolean allowRequired = this.allowRequired; for (OptionData option : options) @@ -205,10 +208,9 @@ public CommandDataImpl addOptions(@Nonnull OptionData... options) (count, value) -> new Object[]{ value, count } ); - allowSubcommands = allowGroups = false; + allowSubcommands = false; this.allowRequired = allowRequired; - for (OptionData option : options) - this.options.add(option); + Collections.addAll(this.options, options); return this; } @@ -222,7 +224,7 @@ public CommandDataImpl addSubcommands(@Nonnull SubcommandData... subcommands) checkType(Command.Type.SLASH, "add subcommands"); if (!allowSubcommands) throw new IllegalArgumentException("You cannot mix options with subcommands/groups."); - Checks.check(subcommands.length + options.length() <= 25, "Cannot have more than 25 subcommands for a command!"); + Checks.check(subcommands.length + this.options.size() <= CommandData.MAX_OPTIONS, "Cannot have more than %d subcommands for a command!", CommandData.MAX_OPTIONS); Checks.checkUnique( Stream.concat(getSubcommands().stream(), Arrays.stream(subcommands)).map(SubcommandData::getName), "Cannot have multiple subcommands with the same name. Name: \"%s\" appeared %d times!", @@ -230,8 +232,7 @@ public CommandDataImpl addSubcommands(@Nonnull SubcommandData... subcommands) ); allowOption = false; - for (SubcommandData data : subcommands) - options.add(data); + Collections.addAll(this.options, subcommands); return this; } @@ -243,9 +244,9 @@ public CommandDataImpl addSubcommandGroups(@Nonnull SubcommandGroupData... group if (groups.length == 0) return this; checkType(Command.Type.SLASH, "add subcommand groups"); - if (!allowGroups) + if (!allowSubcommands) throw new IllegalArgumentException("You cannot mix options with subcommands/groups."); - Checks.check(groups.length + options.length() <= 25, "Cannot have more than 25 subcommand groups for a command!"); + Checks.check(groups.length + this.options.size() <= CommandData.MAX_OPTIONS, "Cannot have more than %d subcommand groups for a command!", CommandData.MAX_OPTIONS); Checks.checkUnique( Stream.concat(getSubcommandGroups().stream(), Arrays.stream(groups)).map(SubcommandGroupData::getName), "Cannot have multiple subcommand groups with the same name. Name: \"%s\" appeared %d times!", @@ -253,8 +254,7 @@ public CommandDataImpl addSubcommandGroups(@Nonnull SubcommandGroupData... group ); allowOption = false; - for (SubcommandGroupData data : groups) - options.add(data); + Collections.addAll(this.options, groups); return this; } @@ -347,13 +347,48 @@ public LocalizationMap getDescriptionLocalizations() return descriptionLocalizations; } - @Nonnull @Override - public List getOptions() + public boolean removeOptions(@Nonnull Predicate condition) { - return options.stream(DataArray::getObject) - .map(OptionData::fromData) - .filter(it -> it.getType().getKey() > OptionType.SUB_COMMAND_GROUP.getKey()) - .collect(Helpers.toUnmodifiableList()); + Checks.notNull(condition, "Condition"); + boolean modified = options.removeIf((o) -> o instanceof OptionData && condition.test((OptionData) o)); + if (modified) + updateAllowedOptions(); + return modified; + } + + @Override + public boolean removeSubcommands(@Nonnull Predicate condition) + { + Checks.notNull(condition, "Condition"); + boolean modified = options.removeIf((o) -> o instanceof SubcommandData && condition.test((SubcommandData) o)); + if (modified) + updateAllowedOptions(); + return modified; + } + + @Override + public boolean removeSubcommandGroups(@Nonnull Predicate condition) + { + Checks.notNull(condition, "Condition"); + boolean modified = options.removeIf((o) -> o instanceof SubcommandGroupData && condition.test((SubcommandGroupData) o)); + if (modified) + updateAllowedOptions(); + return modified; + } + + // Update allowed conditions after removing options + private void updateAllowedOptions() + { + if (options.isEmpty()) + { + allowRequired = allowOption = allowSubcommands = true; + return; + } + + SerializableData last = options.get(options.size() - 1); + allowOption = last instanceof OptionData; + allowRequired = allowOption && ((OptionData) last).isRequired(); + allowSubcommands = !allowOption; } } diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/CommandCreateActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/CommandCreateActionImpl.java index 1593c6f687..68b26e5e3c 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/restaction/CommandCreateActionImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/CommandCreateActionImpl.java @@ -34,17 +34,19 @@ import net.dv8tion.jda.internal.requests.RestActionImpl; import net.dv8tion.jda.internal.requests.Route; import okhttp3.RequestBody; +import org.jetbrains.annotations.NotNull; import javax.annotation.Nonnull; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; +import java.util.function.Predicate; public class CommandCreateActionImpl extends RestActionImpl implements CommandCreateAction { private final Guild guild; - private CommandDataImpl data; + private final CommandDataImpl data; public CommandCreateActionImpl(JDAImpl api, CommandDataImpl command) { @@ -208,6 +210,24 @@ public LocalizationMap getDescriptionLocalizations() return data.getDescriptionLocalizations(); } + @Override + public boolean removeOptions(@NotNull Predicate condition) + { + return data.removeOptions(condition); + } + + @Override + public boolean removeSubcommands(@NotNull Predicate condition) + { + return data.removeSubcommands(condition); + } + + @Override + public boolean removeSubcommandGroups(@NotNull Predicate condition) + { + return data.removeSubcommandGroups(condition); + } + @Nonnull @Override public List getSubcommands()