From 679d3d5a0809ce506c756e9a12d8d9a5896cc75a Mon Sep 17 00:00:00 2001
From: freya02 <41875020+freya022@users.noreply.github.com>
Date: Tue, 23 Jun 2026 20:43:40 +0200
Subject: [PATCH 01/15] Add `FileType`
---
.../jda/api/interactions/FileType.java | 100 ++++++++++++++++++
1 file changed, 100 insertions(+)
create mode 100644 src/main/java/net/dv8tion/jda/api/interactions/FileType.java
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/FileType.java b/src/main/java/net/dv8tion/jda/api/interactions/FileType.java
new file mode 100644
index 0000000000..122b738785
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/interactions/FileType.java
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+import net.dv8tion.jda.internal.utils.Checks;
+import net.dv8tion.jda.internal.utils.EntityString;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Represents a type of file, you can use presets such as {@link #IMAGE},
+ * or specify an extension using {@link #ofExtension(String)}.
+ */
+public final class FileType {
+ /** Matches any image supported by the Discord client. */
+ public static final FileType IMAGE = new FileType("image");
+ /** Matches any video supported by the Discord client. */
+ public static final FileType VIDEO = new FileType("video");
+ /** Matches any audio supported by the Discord client. */
+ public static final FileType AUDIO = new FileType("audio");
+
+ private final String value;
+
+ @ApiStatus.Internal
+ public FileType(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Creates a {@link FileType} matching the provided extension.
+ *
+ * @param extension
+ * The extension to match against.
+ *
+ * @throws IllegalArgumentException
+ *
+ *
If the extension is {@code null} or empty
+ *
If the extension isn't alphanumeric
+ *
+ *
+ * @return The new {@link FileType}
+ */
+ @Nonnull
+ public static FileType ofExtension(@Nonnull String extension) {
+ Checks.matches(extension, Checks.ALPHANUMERIC, "Extension");
+ return new FileType("." + extension);
+ }
+
+ /**
+ * The raw value of this file type.
+ *
+ * @return The raw value
+ */
+ @Nonnull
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof FileType)) {
+ return false;
+ }
+ FileType that = (FileType) o;
+ return Objects.equals(value, that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(value);
+ }
+
+ @Override
+ public String toString() {
+ return new EntityString(this)
+ .setName("FileType")
+ .addMetadata("value", value)
+ .toString();
+ }
+}
From 19cf4eb95f1438a53da925a8e1d233ac30229bd7 Mon Sep 17 00:00:00 2001
From: freya02 <41875020+freya022@users.noreply.github.com>
Date: Tue, 23 Jun 2026 21:03:16 +0200
Subject: [PATCH 02/15] Add `IFilterableFileTypes`
Sharing with attachment uploads and slash command options
---
.../interactions/IFilterableFileTypes.java | 191 ++++++++++++++++++
1 file changed, 191 insertions(+)
create mode 100644 src/main/java/net/dv8tion/jda/api/interactions/IFilterableFileTypes.java
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/IFilterableFileTypes.java b/src/main/java/net/dv8tion/jda/api/interactions/IFilterableFileTypes.java
new file mode 100644
index 0000000000..736b7849fc
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/interactions/IFilterableFileTypes.java
@@ -0,0 +1,191 @@
+/*
+ * 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;
+
+import net.dv8tion.jda.internal.utils.Checks;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Enables configuring a file type filter.
+ */
+public interface IFilterableFileTypes> {
+ /** The number of file type filters that can be applied at once. */
+ int MAX_FILE_TYPES = 10;
+
+ /**
+ * Adds up to {@value #MAX_FILE_TYPES} file types to filter for.
+ *
+ * @param fileTypes
+ * The file types, up to {@value #MAX_FILE_TYPES}
+ *
+ * @throws IllegalArgumentException
+ * If there are more than {@value #MAX_FILE_TYPES} file types
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ T addFileTypes(@Nonnull Collection fileTypes);
+
+ /**
+ * Adds up to {@value #MAX_FILE_TYPES} file types to filter for.
+ *
+ * @param fileTypes
+ * The file types, up to {@value #MAX_FILE_TYPES}
+ *
+ * @throws IllegalArgumentException
+ * If there are more than {@value #MAX_FILE_TYPES} file types
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ default T addFileTypes(@Nonnull FileType... fileTypes) {
+ Checks.noneNull(fileTypes, "File types");
+ return addFileTypes(Arrays.asList(fileTypes));
+ }
+
+ /**
+ * Adds up to {@value #MAX_FILE_TYPES} file extensions to filter for.
+ *
+ *
This is the same as {@link #addFileTypes(Collection)} with {@link FileType#ofExtension(String)}.
+ *
+ * @param extensions
+ * The extensions, up to {@value #MAX_FILE_TYPES}
+ *
+ * @throws IllegalArgumentException
+ *
+ *
If there are more than {@value #MAX_FILE_TYPES} extensions
+ *
If an extension is {@code null} or empty
+ *
If an extension isn't alphanumeric
+ *
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ default T addFileTypeExtensions(@Nonnull Collection extensions) {
+ Checks.noneNull(extensions, "Extensions");
+ return addFileTypes(extensions.stream().map(FileType::ofExtension).collect(Collectors.toList()));
+ }
+
+ /**
+ * Adds up to {@value #MAX_FILE_TYPES} file extensions to filter for.
+ *
+ *
This is the same as {@link #addFileTypes(Collection)} with {@link FileType#ofExtension(String)}.
+ *
+ * @param extensions
+ * The extensions, up to {@value #MAX_FILE_TYPES}
+ *
+ * @throws IllegalArgumentException
+ *
+ *
If there are more than {@value #MAX_FILE_TYPES} extensions
+ *
If an extension is {@code null} or empty
+ *
If an extension isn't alphanumeric
+ *
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ default T addFileTypeExtensions(@Nonnull String... extensions) {
+ Checks.noneNull(extensions, "Extensions");
+ return addFileTypeExtensions(Arrays.asList(extensions));
+ }
+
+ /**
+ * Sets up to {@value #MAX_FILE_TYPES} file types to filter for.
+ * Pass an empty collection to remove file type filtering.
+ *
+ * @param fileTypes
+ * The file types, up to {@value #MAX_FILE_TYPES}
+ *
+ * @throws IllegalArgumentException
+ * If there are more than {@value #MAX_FILE_TYPES} file types
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ T setFileTypes(@Nonnull Collection fileTypes);
+
+ /**
+ * Sets up to {@value #MAX_FILE_TYPES} file types to filter for.
+ * Leave the arguments empty to remove file type filtering.
+ *
+ * @param fileTypes
+ * The file types, up to {@value #MAX_FILE_TYPES}
+ *
+ * @throws IllegalArgumentException
+ * If there are more than {@value #MAX_FILE_TYPES} file types
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ default T setFileTypes(@Nonnull FileType... fileTypes) {
+ Checks.noneNull(fileTypes, "File types");
+ return setFileTypes(Arrays.asList(fileTypes));
+ }
+
+ /**
+ * Sets up to {@value #MAX_FILE_TYPES} file extensions to filter for.
+ * Pass an empty collection to remove file type filtering.
+ *
+ *
This is the same as {@link #addFileTypes(Collection)} with {@link FileType#ofExtension(String)}.
+ *
+ * @param extensions
+ * The extensions, up to {@value #MAX_FILE_TYPES}
+ *
+ * @throws IllegalArgumentException
+ *
+ *
If there are more than {@value #MAX_FILE_TYPES} extensions
+ *
If an extension is {@code null} or empty
+ *
If an extension isn't alphanumeric
+ *
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ default T setFileTypeExtensions(@Nonnull Collection extensions) {
+ Checks.noneNull(extensions, "Extensions");
+ return setFileTypes(extensions.stream().map(FileType::ofExtension).collect(Collectors.toList()));
+ }
+
+ /**
+ * Sets up to {@value #MAX_FILE_TYPES} file extensions to filter for.
+ * Leave the arguments empty to remove file type filtering.
+ *
+ *
This is the same as {@link #addFileTypes(Collection)} with {@link FileType#ofExtension(String)}.
+ *
+ * @param extensions
+ * The extensions, up to {@value #MAX_FILE_TYPES}
+ *
+ * @throws IllegalArgumentException
+ *
+ *
If there are more than {@value #MAX_FILE_TYPES} extensions
+ *
If an extension is {@code null} or empty
+ *
If an extension isn't alphanumeric
+ *
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ default T setFileTypeExtensions(@Nonnull String... extensions) {
+ Checks.noneNull(extensions, "Extensions");
+ return setFileTypeExtensions(Arrays.asList(extensions));
+ }
+}
From c760c3a421ab76ed486c9d911e13f2c190413fba Mon Sep 17 00:00:00 2001
From: freya02 <41875020+freya022@users.noreply.github.com>
Date: Fri, 26 Jun 2026 23:22:47 +0200
Subject: [PATCH 03/15] Add file type filtering to slash command options
---
.../api/interactions/commands/Command.java | 20 +++++++
.../api/interactions/commands/OptionType.java | 6 ++-
.../commands/build/OptionData.java | 54 ++++++++++++++++++-
3 files changed, 78 insertions(+), 2 deletions(-)
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/Command.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/Command.java
index 61718c7c19..30a84d1c94 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/commands/Command.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/Command.java
@@ -21,6 +21,7 @@
import net.dv8tion.jda.api.entities.ISnowflake;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.interactions.DiscordLocale;
+import net.dv8tion.jda.api.interactions.FileType;
import net.dv8tion.jda.api.interactions.IntegrationType;
import net.dv8tion.jda.api.interactions.InteractionContextType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
@@ -35,6 +36,7 @@
import net.dv8tion.jda.internal.interactions.command.CommandImpl;
import net.dv8tion.jda.internal.utils.Checks;
import net.dv8tion.jda.internal.utils.EntityString;
+import net.dv8tion.jda.internal.utils.Helpers;
import net.dv8tion.jda.internal.utils.localization.LocalizationUtils;
import org.jetbrains.annotations.Unmodifiable;
@@ -606,6 +608,7 @@ class Option {
private Number minValue;
private Number maxValue;
private Integer minLength, maxLength;
+ private final List fileTypes;
public Option(@Nonnull DataObject json) {
this.name = json.getString("name");
@@ -636,6 +639,10 @@ public Option(@Nonnull DataObject json) {
if (!json.isNull("max_length")) {
this.maxLength = json.getInt("max_length");
}
+ this.fileTypes = json.optArray("file_types")
+ .map(it ->
+ it.stream(DataArray::getString).map(FileType::new).collect(Helpers.toUnmodifiableList()))
+ .orElse(Collections.emptyList());
}
/**
@@ -775,6 +782,19 @@ public Integer getMaxLength() {
return maxLength;
}
+ /**
+ * The immutable list of file types accepted by this option.
+ * Returns an empty list if file types are not filtered,
+ * or this isn't an {@link OptionType#ATTACHMENT ATTACHMENT} option.
+ *
+ * @return Immutable list of file types accepted by this option
+ */
+ @Nonnull
+ @Unmodifiable
+ public List getFileTypes() {
+ return fileTypes;
+ }
+
/**
* The predefined choices available for this option.
* If no choices are defined, this returns an empty list.
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionType.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionType.java
index 911fc17240..f98a62d9f8 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionType.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionType.java
@@ -16,6 +16,7 @@
package net.dv8tion.jda.api.interactions.commands;
+import net.dv8tion.jda.api.interactions.FileType;
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;
@@ -82,7 +83,10 @@ public enum OptionType {
*/
NUMBER(10, true),
/**
- * Options which accept a file attachment
+ * Options which accept a file attachment.
+ *
+ *
File types accepted by these can be filtered, such as with {@link net.dv8tion.jda.api.interactions.commands.build.OptionData#addFileTypes(FileType...) OptionData.addFileTypes(FileType...)}
+ * or {@link net.dv8tion.jda.api.interactions.commands.build.OptionData#setFileTypes(FileType...) OptionData.setFileTypes(FileType...)}
* @see OptionMapping#getAsAttachment()
*/
ATTACHMENT(11),
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/OptionData.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/OptionData.java
index 9d8068d50c..54e10d1d70 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/OptionData.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/OptionData.java
@@ -19,6 +19,8 @@
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.interactions.DiscordLocale;
+import net.dv8tion.jda.api.interactions.FileType;
+import net.dv8tion.jda.api.interactions.IFilterableFileTypes;
import net.dv8tion.jda.api.interactions.commands.Command;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.localization.LocalizationMap;
@@ -27,8 +29,10 @@
import net.dv8tion.jda.api.utils.data.DataType;
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 org.jetbrains.annotations.Unmodifiable;
+import org.jetbrains.annotations.UnmodifiableView;
import java.util.*;
import java.util.stream.Collectors;
@@ -39,7 +43,7 @@
/**
* Builder for a Slash-Command option.
*/
-public class OptionData implements SerializableData {
+public class OptionData implements SerializableData, IFilterableFileTypes {
/**
* The highest positive amount Discord allows the {@link OptionType#NUMBER NUMBER} type to be.
*/
@@ -90,6 +94,7 @@ public class OptionData implements SerializableData {
private Number maxValue;
private Integer minLength, maxLength;
private List choices;
+ private List fileTypes;
/**
* Create an option builder.
@@ -332,6 +337,19 @@ public Integer getMaxLength() {
return maxLength;
}
+ /**
+ * The unmodifiable list view of file types to filter for.
+ * Returns an empty list if file types are not filtered,
+ * or this isn't an {@link OptionType#ATTACHMENT ATTACHMENT} option.
+ *
+ * @return Unmodifiable list view of file types
+ */
+ @Nonnull
+ @UnmodifiableView
+ public List getFileTypes() {
+ return Collections.unmodifiableList(fileTypes);
+ }
+
/**
* The choices for this option.
* This is empty by default and can only be configured for specific option types.
@@ -826,6 +844,37 @@ public OptionData setRequiredLength(int minLength, int maxLength) {
return this;
}
+ @Nonnull
+ @Override
+ public OptionData addFileTypes(@Nonnull Collection fileTypes) {
+ Checks.noneNull(fileTypes, "File types");
+ if (this.fileTypes == null) {
+ setFileTypes(fileTypes);
+ } else {
+ Checks.check(
+ this.fileTypes.size() + fileTypes.size() <= MAX_FILE_TYPES,
+ "Cannot have more than %d file types (provided: %d + %d)",
+ MAX_FILE_TYPES,
+ this.fileTypes.size(),
+ fileTypes.size());
+ this.fileTypes.addAll(fileTypes);
+ }
+ return this;
+ }
+
+ @Nonnull
+ @Override
+ public OptionData setFileTypes(@Nonnull Collection fileTypes) {
+ Checks.noneNull(fileTypes, "File types");
+ Checks.check(
+ fileTypes.size() <= MAX_FILE_TYPES,
+ "Cannot have more than %d file types (provided: %d)",
+ MAX_FILE_TYPES,
+ fileTypes.size());
+ this.fileTypes = Helpers.copyAsUnmodifiableList(fileTypes);
+ return this;
+ }
+
/**
* Add a predefined choice for this option.
* The user can only provide one of the choices and cannot specify any other value.
@@ -1041,6 +1090,9 @@ public DataObject toData() {
json.put("max_length", maxLength);
}
}
+ if (type == OptionType.ATTACHMENT && fileTypes != null) {
+ json.put("file_types", fileTypes.stream().map(FileType::getValue).collect(Collectors.toList()));
+ }
return json;
}
From 9e5f8222e55bfe3d2b8b9bea6a3b06f19b78b87c Mon Sep 17 00:00:00 2001
From: freya02 <41875020+freya022@users.noreply.github.com>
Date: Fri, 26 Jun 2026 23:23:24 +0200
Subject: [PATCH 04/15] Add some methods to test of a file type filters for a
specific media type
---
.../jda/api/interactions/FileType.java | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/FileType.java b/src/main/java/net/dv8tion/jda/api/interactions/FileType.java
index 122b738785..9cd65e855b 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/FileType.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/FileType.java
@@ -63,6 +63,33 @@ public static FileType ofExtension(@Nonnull String extension) {
return new FileType("." + extension);
}
+ /**
+ * Whether this file type accepts all image formats supported by Discord.
+ *
+ * @return {@code true} if this file type accepts all image formats supported by Discord, {@code false} if not
+ */
+ public boolean isImage() {
+ return value.equals("image");
+ }
+
+ /**
+ * Whether this file type accepts all video formats supported by Discord.
+ *
+ * @return {@code true} if this file type accepts all video formats supported by Discord, {@code false} if not
+ */
+ public boolean isVideo() {
+ return value.equals("video");
+ }
+
+ /**
+ * Whether this file type accepts all audio formats supported by Discord.
+ *
+ * @return {@code true} if this file type accepts all audio formats supported by Discord, {@code false} if not
+ */
+ public boolean isAudio() {
+ return value.equals("audio");
+ }
+
/**
* The raw value of this file type.
*
From 78abc3532a1e5e80b4a072aa2d9a67d94997ad44 Mon Sep 17 00:00:00 2001
From: freya02 <41875020+freya022@users.noreply.github.com>
Date: Fri, 26 Jun 2026 23:38:30 +0200
Subject: [PATCH 05/15] Add file type filtering for `AttachmentUpload`
---
.../attachmentupload/AttachmentUpload.java | 69 ++++++++++++++++++-
.../AttachmentUploadImpl.java | 28 +++++++-
2 files changed, 92 insertions(+), 5 deletions(-)
diff --git a/src/main/java/net/dv8tion/jda/api/components/attachmentupload/AttachmentUpload.java b/src/main/java/net/dv8tion/jda/api/components/attachmentupload/AttachmentUpload.java
index 04cf7247d9..a7b9e1ab87 100644
--- a/src/main/java/net/dv8tion/jda/api/components/attachmentupload/AttachmentUpload.java
+++ b/src/main/java/net/dv8tion/jda/api/components/attachmentupload/AttachmentUpload.java
@@ -19,8 +19,16 @@
import net.dv8tion.jda.api.components.Component;
import net.dv8tion.jda.api.components.attribute.ICustomId;
import net.dv8tion.jda.api.components.label.LabelChildComponent;
+import net.dv8tion.jda.api.interactions.FileType;
+import net.dv8tion.jda.api.interactions.IFilterableFileTypes;
import net.dv8tion.jda.internal.components.attachmentupload.AttachmentUploadImpl;
import net.dv8tion.jda.internal.utils.Checks;
+import net.dv8tion.jda.internal.utils.Helpers;
+import org.jetbrains.annotations.UnmodifiableView;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
@@ -28,7 +36,8 @@
/**
* Component accepting files from users.
*
- *
The user can send up to {@value #MAX_UPLOADS} files, the requested number of files can be adjusted or be completely optional.
+ *
The user can send up to {@value #MAX_UPLOADS} files, the requested number of files can be adjusted or be completely optional,
+ * additionally, the accepted file types can be configured.
*
*
Can only be used inside {@link net.dv8tion.jda.api.components.label.Label Labels}!
*/
@@ -92,6 +101,16 @@ static AttachmentUpload of(@Nonnull String customId) {
*/
int getMaxValues();
+ /**
+ * The unmodifiable list view of file types to filter for.
+ * Returns an empty list if file types are not filtered.
+ *
+ * @return Unmodifiable list view of file types
+ */
+ @Nonnull
+ @UnmodifiableView
+ List getFileTypes();
+
/**
* Whether the user must send attachments.
*
@@ -106,11 +125,12 @@ static AttachmentUpload of(@Nonnull String customId) {
/**
* Builder for {@link AttachmentUpload AttachmentUploads}.
*/
- class Builder {
+ class Builder implements IFilterableFileTypes {
protected int uniqueId = -1;
protected String customId;
protected int minValues = 1;
protected int maxValues = 1;
+ protected List fileTypes = null;
protected boolean required = true;
protected Builder(@Nonnull String customId) {
@@ -206,6 +226,37 @@ public Builder setRequiredRange(int min, int max) {
return setMinValues(min).setMaxValues(max);
}
+ @Nonnull
+ @Override
+ public Builder addFileTypes(@Nonnull Collection fileTypes) {
+ Checks.noneNull(fileTypes, "File types");
+ if (this.fileTypes == null) {
+ setFileTypes(fileTypes);
+ } else {
+ Checks.check(
+ this.fileTypes.size() + fileTypes.size() <= MAX_FILE_TYPES,
+ "Cannot have more than %d file types (provided: %d + %d)",
+ MAX_FILE_TYPES,
+ this.fileTypes.size(),
+ fileTypes.size());
+ this.fileTypes.addAll(fileTypes);
+ }
+ return this;
+ }
+
+ @Nonnull
+ @Override
+ public Builder setFileTypes(@Nonnull Collection fileTypes) {
+ Checks.noneNull(fileTypes, "File types");
+ Checks.check(
+ fileTypes.size() <= MAX_FILE_TYPES,
+ "Cannot have more than %d file types (provided: %d)",
+ MAX_FILE_TYPES,
+ fileTypes.size());
+ this.fileTypes = Helpers.copyAsUnmodifiableList(fileTypes);
+ return this;
+ }
+
/**
* Changes whether the user must upload files.
* Default: {@code true}
@@ -264,6 +315,18 @@ public int getMaxValues() {
return maxValues;
}
+ /**
+ * The unmodifiable list view of file types to filter for.
+ * Returns an empty list if file types are not filtered.
+ *
+ * @return Unmodifiable list view of file types
+ */
+ @Nonnull
+ @UnmodifiableView
+ public List getFileTypes() {
+ return Collections.unmodifiableList(fileTypes);
+ }
+
/**
* Whether the user must send attachments.
*
@@ -288,7 +351,7 @@ public boolean isRequired() {
@Nonnull
public AttachmentUpload build() {
Checks.check(maxValues >= minValues, "Max (%s) must be higher or equal to min (%s)", maxValues, minValues);
- return new AttachmentUploadImpl(uniqueId, customId, minValues, maxValues, required);
+ return new AttachmentUploadImpl(uniqueId, customId, minValues, maxValues, fileTypes, required);
}
}
}
diff --git a/src/main/java/net/dv8tion/jda/internal/components/attachmentupload/AttachmentUploadImpl.java b/src/main/java/net/dv8tion/jda/internal/components/attachmentupload/AttachmentUploadImpl.java
index 18f6334d59..18d9ccb3d7 100644
--- a/src/main/java/net/dv8tion/jda/internal/components/attachmentupload/AttachmentUploadImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/components/attachmentupload/AttachmentUploadImpl.java
@@ -18,12 +18,18 @@
import net.dv8tion.jda.api.components.attachmentupload.AttachmentUpload;
import net.dv8tion.jda.api.components.label.LabelChildComponentUnion;
+import net.dv8tion.jda.api.interactions.FileType;
+import net.dv8tion.jda.api.utils.data.DataArray;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.components.AbstractComponentImpl;
import net.dv8tion.jda.internal.utils.Checks;
import net.dv8tion.jda.internal.utils.EntityString;
+import net.dv8tion.jda.internal.utils.Helpers;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
@@ -32,6 +38,7 @@ public class AttachmentUploadImpl extends AbstractComponentImpl implements Attac
protected final String customId;
protected final int minValues;
protected final int maxValues;
+ protected final List fileTypes;
protected final boolean required;
public AttachmentUploadImpl(DataObject data) {
@@ -40,14 +47,21 @@ public AttachmentUploadImpl(DataObject data) {
data.getString("custom_id"),
data.getInt("min_values", 1),
data.getInt("max_values", 1),
+ data.optArray("file_types")
+ .map(it -> it.stream(DataArray::getString)
+ .map(FileType::new)
+ .collect(Helpers.toUnmodifiableList()))
+ .orElse(Collections.emptyList()),
data.getBoolean("required", true));
}
- public AttachmentUploadImpl(int uniqueId, String customId, int minValues, int maxValues, boolean required) {
+ public AttachmentUploadImpl(
+ int uniqueId, String customId, int minValues, int maxValues, List fileTypes, boolean required) {
this.uniqueId = uniqueId;
this.customId = customId;
this.minValues = minValues;
this.maxValues = maxValues;
+ this.fileTypes = Helpers.copyAsUnmodifiableList(fileTypes);
this.required = required;
}
@@ -61,7 +75,7 @@ public Type getType() {
@Override
public AttachmentUploadImpl withUniqueId(int uniqueId) {
Checks.positive(uniqueId, "Unique ID");
- return new AttachmentUploadImpl(uniqueId, customId, minValues, maxValues, required);
+ return new AttachmentUploadImpl(uniqueId, customId, minValues, maxValues, fileTypes, required);
}
@Override
@@ -85,6 +99,12 @@ public int getMaxValues() {
return maxValues;
}
+ @Nonnull
+ @Override
+ public List getFileTypes() {
+ return fileTypes;
+ }
+
@Override
public boolean isRequired() {
return required;
@@ -104,6 +124,10 @@ public DataObject toData() {
json.put("id", uniqueId);
}
+ if (fileTypes != null) {
+ json.put("file_types", fileTypes.stream().map(FileType::getValue).collect(Collectors.toList()));
+ }
+
return json;
}
From 4502e6ac390eb8df77fb242d9daeffb2e4e6d52c Mon Sep 17 00:00:00 2001
From: freya02 <41875020+freya022@users.noreply.github.com>
Date: Sat, 27 Jun 2026 00:19:37 +0200
Subject: [PATCH 06/15] Add a `FileType` container to share logic between slash
command options and attachment uploads
---
.../attachmentupload/AttachmentUpload.java | 28 +----
.../api/interactions/commands/Command.java | 13 +-
.../commands/build/OptionData.java | 31 ++---
.../AttachmentUploadImpl.java | 26 ++--
.../internal/interactions/FileTypesImpl.java | 112 ++++++++++++++++++
5 files changed, 138 insertions(+), 72 deletions(-)
create mode 100644 src/main/java/net/dv8tion/jda/internal/interactions/FileTypesImpl.java
diff --git a/src/main/java/net/dv8tion/jda/api/components/attachmentupload/AttachmentUpload.java b/src/main/java/net/dv8tion/jda/api/components/attachmentupload/AttachmentUpload.java
index a7b9e1ab87..d2d498cbd8 100644
--- a/src/main/java/net/dv8tion/jda/api/components/attachmentupload/AttachmentUpload.java
+++ b/src/main/java/net/dv8tion/jda/api/components/attachmentupload/AttachmentUpload.java
@@ -22,12 +22,11 @@
import net.dv8tion.jda.api.interactions.FileType;
import net.dv8tion.jda.api.interactions.IFilterableFileTypes;
import net.dv8tion.jda.internal.components.attachmentupload.AttachmentUploadImpl;
+import net.dv8tion.jda.internal.interactions.FileTypesImpl;
import net.dv8tion.jda.internal.utils.Checks;
-import net.dv8tion.jda.internal.utils.Helpers;
import org.jetbrains.annotations.UnmodifiableView;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import javax.annotation.CheckReturnValue;
@@ -130,7 +129,7 @@ class Builder implements IFilterableFileTypes {
protected String customId;
protected int minValues = 1;
protected int maxValues = 1;
- protected List fileTypes = null;
+ protected final FileTypesImpl fileTypes = FileTypesImpl.empty();
protected boolean required = true;
protected Builder(@Nonnull String customId) {
@@ -229,31 +228,14 @@ public Builder setRequiredRange(int min, int max) {
@Nonnull
@Override
public Builder addFileTypes(@Nonnull Collection fileTypes) {
- Checks.noneNull(fileTypes, "File types");
- if (this.fileTypes == null) {
- setFileTypes(fileTypes);
- } else {
- Checks.check(
- this.fileTypes.size() + fileTypes.size() <= MAX_FILE_TYPES,
- "Cannot have more than %d file types (provided: %d + %d)",
- MAX_FILE_TYPES,
- this.fileTypes.size(),
- fileTypes.size());
- this.fileTypes.addAll(fileTypes);
- }
+ this.fileTypes.addAll(fileTypes);
return this;
}
@Nonnull
@Override
public Builder setFileTypes(@Nonnull Collection fileTypes) {
- Checks.noneNull(fileTypes, "File types");
- Checks.check(
- fileTypes.size() <= MAX_FILE_TYPES,
- "Cannot have more than %d file types (provided: %d)",
- MAX_FILE_TYPES,
- fileTypes.size());
- this.fileTypes = Helpers.copyAsUnmodifiableList(fileTypes);
+ this.fileTypes.setAll(fileTypes);
return this;
}
@@ -324,7 +306,7 @@ public int getMaxValues() {
@Nonnull
@UnmodifiableView
public List getFileTypes() {
- return Collections.unmodifiableList(fileTypes);
+ return fileTypes.asView();
}
/**
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/Command.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/Command.java
index 30a84d1c94..d84c7697b6 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/commands/Command.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/Command.java
@@ -33,10 +33,10 @@
import net.dv8tion.jda.api.utils.data.DataArray;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.api.utils.data.DataType;
+import net.dv8tion.jda.internal.interactions.FileTypesImpl;
import net.dv8tion.jda.internal.interactions.command.CommandImpl;
import net.dv8tion.jda.internal.utils.Checks;
import net.dv8tion.jda.internal.utils.EntityString;
-import net.dv8tion.jda.internal.utils.Helpers;
import net.dv8tion.jda.internal.utils.localization.LocalizationUtils;
import org.jetbrains.annotations.Unmodifiable;
@@ -608,7 +608,7 @@ class Option {
private Number minValue;
private Number maxValue;
private Integer minLength, maxLength;
- private final List fileTypes;
+ private final FileTypesImpl fileTypes;
public Option(@Nonnull DataObject json) {
this.name = json.getString("name");
@@ -639,10 +639,8 @@ public Option(@Nonnull DataObject json) {
if (!json.isNull("max_length")) {
this.maxLength = json.getInt("max_length");
}
- this.fileTypes = json.optArray("file_types")
- .map(it ->
- it.stream(DataArray::getString).map(FileType::new).collect(Helpers.toUnmodifiableList()))
- .orElse(Collections.emptyList());
+ this.fileTypes =
+ json.optArray("file_types").map(FileTypesImpl::fromArray).orElse(FileTypesImpl.empty());
}
/**
@@ -792,7 +790,8 @@ public Integer getMaxLength() {
@Nonnull
@Unmodifiable
public List getFileTypes() {
- return fileTypes;
+ // No need for an extra copy
+ return fileTypes.asView();
}
/**
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/OptionData.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/OptionData.java
index 54e10d1d70..81945213e6 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/OptionData.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/OptionData.java
@@ -28,8 +28,8 @@
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.api.utils.data.DataType;
import net.dv8tion.jda.api.utils.data.SerializableData;
+import net.dv8tion.jda.internal.interactions.FileTypesImpl;
import net.dv8tion.jda.internal.utils.Checks;
-import net.dv8tion.jda.internal.utils.Helpers;
import net.dv8tion.jda.internal.utils.localization.LocalizationUtils;
import org.jetbrains.annotations.Unmodifiable;
import org.jetbrains.annotations.UnmodifiableView;
@@ -94,7 +94,7 @@ public class OptionData implements SerializableData, IFilterableFileTypes