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..94626efff1 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,15 @@ 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.interactions.FileTypesImpl; import net.dv8tion.jda.internal.utils.Checks; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collection; +import java.util.List; import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; @@ -28,7 +35,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 +100,16 @@ static AttachmentUpload of(@Nonnull String customId) {
*/
int getMaxValues();
+ /**
+ * The unmodifiable list view of file types to filter for.
+ * Returns an empty list if any file is accepted.
+ *
+ * @return Unmodifiable list view of file types
+ */
+ @Nonnull
+ @UnmodifiableView
+ List Remember that this only checks the extension, Discord does not check for any file signature/MIME type,
+ * and thus does not guarantee receiving a valid file.
+ */
+public final class FileType {
+ private static final Pattern EXTENSION_PATTERN = Pattern.compile("[\\w\\-.]+");
+
+ /** 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
+ * 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
+ * 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
+ * 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
+ * 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
+ * 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..3517d7c8c3 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;
@@ -26,9 +28,11 @@
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.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
Default: {@code true}
@@ -264,6 +297,18 @@ public int getMaxValues() {
return maxValues;
}
+ /**
+ * The unmodifiable list view of file types to filter for.
+ * Returns an empty list if any file is accepted.
+ *
+ * @return Unmodifiable list view of file types
+ */
+ @Nonnull
+ @UnmodifiableView
+ public List
+ *
+ *
+ * @return The new {@link FileType}
+ */
+ @Nonnull
+ public static FileType ofExtension(@Nonnull String extension) {
+ Checks.matches(extension, EXTENSION_PATTERN, "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.
+ *
+ * @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();
+ }
+}
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..7119b9064d
--- /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
+ *
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ default T addFileTypeExtensions(@Nonnull Collection
+ *
+ *
+ * @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 {@code null} is provided, or there are more than {@value #MAX_FILE_TYPES} file types
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ T setFileTypes(@Nonnull Collection
+ *
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ default T setFileTypeExtensions(@Nonnull Collection
+ *
+ *
+ * @return This instance for chaining
+ */
+ @Nonnull
+ default T setFileTypeExtensions(@Nonnull String... extensions) {
+ Checks.noneNull(extensions, "Extensions");
+ return setFileTypeExtensions(Arrays.asList(extensions));
+ }
+}
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..96139a8db7 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;
@@ -32,6 +33,7 @@
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;
@@ -606,6 +608,7 @@ class Option {
private Number minValue;
private Number maxValue;
private Integer minLength, maxLength;
+ private final FileTypesImpl fileTypes;
public Option(@Nonnull DataObject json) {
this.name = json.getString("name");
@@ -636,6 +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(FileTypesImpl::fromArray).orElse(FileTypesImpl.EMPTY_AND_IMMUTABLE);
}
/**
@@ -775,6 +780,20 @@ public Integer getMaxLength() {
return maxLength;
}
+ /**
+ * The immutable list of file types accepted by this option.
+ * Returns an empty list if any file is accepted,
+ * or this isn't an {@link OptionType#ATTACHMENT ATTACHMENT} option.
+ *
+ * @return Immutable list of file types accepted by this option
+ */
+ @Nonnull
+ @Unmodifiable
+ public List
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.
+ *
+ *
This is empty by default and can only be configured for specific option types.
@@ -826,6 +844,20 @@ public OptionData setRequiredLength(int minLength, int maxLength) {
return this;
}
+ @Nonnull
+ @Override
+ public OptionData addFileTypes(@Nonnull Collection
The user can only provide one of the choices and cannot specify any other value.
@@ -1041,6 +1073,11 @@ public DataObject toData() {
json.put("max_length", maxLength);
}
}
+ if (type == OptionType.ATTACHMENT) {
+ if (!fileTypes.isEmpty()) {
+ json.put("file_types", fileTypes.toData());
+ }
+ }
return json;
}
@@ -1097,6 +1134,12 @@ public static OptionData fromData(@Nonnull DataObject json) {
option.setMaxLength(json.getInt("max_length"));
}
}
+ if (type == OptionType.ATTACHMENT) {
+ if (!json.isNull("file_types")) {
+ option.setFileTypes(
+ FileTypesImpl.fromArray(json.getArray("file_types")).asView());
+ }
+ }
json.optArray("choices")
.ifPresent(choices1 -> option.addChoices(choices1.stream(DataArray::getObject)
.map(Command.Choice::new)
@@ -1156,6 +1199,9 @@ public static OptionData fromOption(@Nonnull Command.Option option) {
data.setMaxLength(maxLength);
}
break;
+ case ATTACHMENT:
+ data.setFileTypes(option.getFileTypes());
+ break;
default:
break;
}
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..7f9d099130 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,11 +18,14 @@
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.DataObject;
import net.dv8tion.jda.internal.components.AbstractComponentImpl;
+import net.dv8tion.jda.internal.interactions.FileTypesImpl;
import net.dv8tion.jda.internal.utils.Checks;
import net.dv8tion.jda.internal.utils.EntityString;
+import java.util.List;
import java.util.Objects;
import javax.annotation.Nonnull;
@@ -32,6 +35,7 @@ public class AttachmentUploadImpl extends AbstractComponentImpl implements Attac
protected final String customId;
protected final int minValues;
protected final int maxValues;
+ protected final FileTypesImpl fileTypes;
protected final boolean required;
public AttachmentUploadImpl(DataObject data) {
@@ -40,14 +44,17 @@ public AttachmentUploadImpl(DataObject data) {
data.getString("custom_id"),
data.getInt("min_values", 1),
data.getInt("max_values", 1),
+ data.optArray("file_types").map(FileTypesImpl::fromArray).orElse(FileTypesImpl.empty()),
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, FileTypesImpl fileTypes, boolean required) {
this.uniqueId = uniqueId;
this.customId = customId;
this.minValues = minValues;
this.maxValues = maxValues;
+ this.fileTypes = fileTypes.copy();
this.required = required;
}
@@ -61,7 +68,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 +92,12 @@ public int getMaxValues() {
return maxValues;
}
+ @Nonnull
+ @Override
+ public List