diff --git a/build.gradle.kts b/build.gradle.kts
index 2ac4c6d..71bd606 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -12,8 +12,8 @@ plugins {
}
val major = "0"
-val minor = "8"
-val patch = "2"
+val minor = "9"
+val patch = "0"
group = "club.minnced"
version = "$major.$minor.$patch"
@@ -46,7 +46,7 @@ repositories {
val versions = mapOf(
"slf4j" to "1.7.32",
"okhttp" to "4.10.0",
- "json" to "20210307",
+ "json" to "20220320",
"jda" to "5.0.0-alpha.13",
"discord4j" to "3.2.2",
"javacord" to "3.4.0",
diff --git a/src/main/java/club/minnced/discord/webhook/LibraryInfo.java b/src/main/java/club/minnced/discord/webhook/LibraryInfo.java
index 7a7a077..869991f 100644
--- a/src/main/java/club/minnced/discord/webhook/LibraryInfo.java
+++ b/src/main/java/club/minnced/discord/webhook/LibraryInfo.java
@@ -17,7 +17,7 @@
package club.minnced.discord.webhook;
public class LibraryInfo {
- public static final int DISCORD_API_VERSION = 9;
+ public static final int DISCORD_API_VERSION = 10;
public static final String VERSION_MAJOR = "@MAJOR@";
public static final String VERSION_MINOR = "@MINOR@";
public static final String VERSION_PATCH = "@PATCH@";
diff --git a/src/main/java/club/minnced/discord/webhook/receive/ReadonlyAttachment.java b/src/main/java/club/minnced/discord/webhook/receive/ReadonlyAttachment.java
index d076ca5..7994d28 100644
--- a/src/main/java/club/minnced/discord/webhook/receive/ReadonlyAttachment.java
+++ b/src/main/java/club/minnced/discord/webhook/receive/ReadonlyAttachment.java
@@ -16,6 +16,7 @@
package club.minnced.discord.webhook.receive;
+import club.minnced.discord.webhook.send.MessageAttachment;
import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;
import org.json.JSONPropertyName;
@@ -26,7 +27,7 @@
*
This does not actually contain the file but only meta-data
* useful to retrieve the actual attachment.
*/
-public class ReadonlyAttachment implements JSONString {
+public class ReadonlyAttachment extends MessageAttachment implements JSONString {
private final String url;
private final String proxyUrl;
private final String fileName;
@@ -37,6 +38,7 @@ public class ReadonlyAttachment implements JSONString {
public ReadonlyAttachment(
@NotNull String url, @NotNull String proxyUrl, @NotNull String fileName,
int width, int height, int size, long id) {
+ super(id, fileName);
this.url = url;
this.proxyUrl = proxyUrl;
this.fileName = fileName;
diff --git a/src/main/java/club/minnced/discord/webhook/send/MessageAttachment.java b/src/main/java/club/minnced/discord/webhook/send/MessageAttachment.java
index c3528ca..4e32724 100644
--- a/src/main/java/club/minnced/discord/webhook/send/MessageAttachment.java
+++ b/src/main/java/club/minnced/discord/webhook/send/MessageAttachment.java
@@ -18,6 +18,8 @@
import club.minnced.discord.webhook.IOUtil;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
@@ -28,15 +30,25 @@
* Internal representation of attachments for outgoing messages
*/
public class MessageAttachment {
+ private static final byte[] empty = new byte[0];
+ private final long id;
private final String name;
private final byte[] data;
+ protected MessageAttachment(long id, @NotNull String name) {
+ this.id = id;
+ this.name = name;
+ this.data = empty;
+ }
+
MessageAttachment(@NotNull String name, @NotNull byte[] data) {
+ this.id = 0;
this.name = name;
this.data = data;
}
MessageAttachment(@NotNull String name, @NotNull InputStream stream) throws IOException {
+ this.id = 0;
this.name = name;
try (InputStream data = stream) {
this.data = IOUtil.readAllBytes(data);
@@ -47,6 +59,50 @@ public class MessageAttachment {
this(name, new FileInputStream(file));
}
+ /**
+ * Create an instance of this class with the provided ID and name.
+ *
+ *
This can be used in {@link club.minnced.discord.webhook.send.WebhookMessageBuilder#addFile(MessageAttachment)} + * to retain existing attachments on a message for an edit request. + * The name parameter can also be used to rename the attachment. + * + * @param id + * The snowflake ID for this attachment (must exist on the message you edit9 + * @param name + * The new name for the attachment, or null to keep existing name + * + * @throws java.lang.IllegalArgumentException + * If the provided ID is not a valid snowflake + * + * @return The attachment instance + */ + @NotNull + public static MessageAttachment fromId(long id, @Nullable String name) { + if (id > WebhookMessage.MAX_FILES) + throw new IllegalArgumentException("MessageAttachment ID must be higher than " + WebhookMessage.MAX_FILES + "."); + return new MessageAttachment(id, name == null ? "" : name); + } + + + /** + * Create an instance of this class with the provided ID and name. + * + *
This can be used in {@link club.minnced.discord.webhook.send.WebhookMessageBuilder#addFile(MessageAttachment)}
+ * to retain existing attachments on a message for an edit request.
+ *
+ * @param id
+ * The snowflake ID for this attachment (must exist on the message you edit9
+ *
+ * @throws java.lang.IllegalArgumentException
+ * If the provided ID is not a valid snowflake
+ *
+ * @return The attachment instance
+ */
+ @NotNull
+ public static MessageAttachment fromId(long id) {
+ return fromId(id, null);
+ }
+
@NotNull
public String getName() {
return name;
@@ -56,4 +112,18 @@ public String getName() {
public byte[] getData() {
return data;
}
+
+ public long getId() {
+ return id;
+ }
+
+ @NotNull
+ public JSONObject toJSON() {
+ JSONObject json = new JSONObject();
+ if (!name.isEmpty())
+ json.put("name", name);
+ if (id != 0)
+ json.put("id", id);
+ return json;
+ }
}
diff --git a/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java b/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java
index b412643..4e03d16 100644
--- a/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java
+++ b/src/main/java/club/minnced/discord/webhook/send/WebhookMessage.java
@@ -233,6 +233,33 @@ public static WebhookMessage embeds(@NotNull Collection This can be used inplace of {@link #addFile(MessageAttachment)} overloads to entirely remove files.
+ *
+ * @param attachments
+ * The attachments to retain (or empty to remove all)
+ *
+ * @throws java.lang.NullPointerException
+ * If provided with null
+ * @throws java.lang.IllegalStateException
+ * If more than {@value WebhookMessage#MAX_FILES} are added
+ *
+ * @return This builder for chaining convenience
+ *
+ * @see MessageAttachment#fromId(long)
+ * @see MessageAttachment#fromId(long, String)
+ */
+ @NotNull
+ public WebhookMessageBuilder retainAttachments(@NotNull Collection extends MessageAttachment> attachments) {
+ Objects.requireNonNull(attachments, "Attachments");
+ if (fileIndex + attachments.size() >= WebhookMessage.MAX_FILES)
+ throw new IllegalStateException("Cannot add more than " + WebhookMessage.MAX_FILES + " attachments to a message");
+ hasFiles = true;
+ for (MessageAttachment attachment : attachments) {
+ Objects.requireNonNull(attachment, "MessageAttachment");
+ files[fileIndex++] = attachment;
+ }
+ return this;
+ }
+
/**
* Adds the provided file as an attachment to this message.
* As of recent API updates, adding new files also requires specifying all existing files to keep attached.
+ * If the existing attachments are not provided in addition to new files, they will instead be removed from the message.
+ *
+ * @param attachment
+ * The file attachment to add
+ *
+ * @throws java.lang.NullPointerException
+ * If provided with null
+ * @throws java.lang.IllegalStateException
+ * If more than {@value WebhookMessage#MAX_FILES} are added
*
* @return This builder for chaining convenience
+ */
+ @NotNull
+ public WebhookMessageBuilder addFile(@NotNull MessageAttachment attachment) {
+ Objects.requireNonNull(attachment, "MessageAttachment");
+ if (fileIndex >= WebhookMessage.MAX_FILES)
+ throw new IllegalStateException("Cannot add more than " + WebhookMessage.MAX_FILES + " attachments to a message");
+ files[fileIndex++] = attachment;
+ return this;
+ }
+
+ /**
+ * Adds the provided file as an attachment to this message.
+ * As of recent API updates, adding new files also requires specifying all existing files to keep attached.
+ * If the existing attachments are not provided in addition to new files, they will instead be removed from the message.
+ * Use {@link #retainAttachments(java.util.Collection)} or {@link #addFile(MessageAttachment)} to add all existing attachments.
+ *
+ * @param file
+ * The file to attach
*
* @throws java.lang.NullPointerException
* If provided with null
+ * @throws java.lang.IllegalStateException
+ * If more than {@value WebhookMessage#MAX_FILES} are added
+ *
+ * @return This builder for chaining convenience
*/
@NotNull
public WebhookMessageBuilder addFile(@NotNull File file) {
@@ -314,6 +379,10 @@ public WebhookMessageBuilder addFile(@NotNull File file) {
* Adds the provided file as an attachment to this message.
* As of recent API updates, adding new files also requires specifying all existing files to keep attached.
+ * If the existing attachments are not provided in addition to new files, they will instead be removed from the message.
+ * Use {@link #retainAttachments(java.util.Collection)} or {@link #addFile(MessageAttachment)} to add all existing attachments.
+ *
* @param name
* The alternative name that should be used instead
* @param file
@@ -321,6 +390,8 @@ public WebhookMessageBuilder addFile(@NotNull File file) {
*
* @throws java.lang.NullPointerException
* If provided with null
+ * @throws java.lang.IllegalStateException
+ * If more than {@value WebhookMessage#MAX_FILES} are added
*
* @return This builder for chaining convenience
*/
@@ -346,6 +417,10 @@ public WebhookMessageBuilder addFile(@NotNull String name, @NotNull File file) {
* Adds the provided data as a file attachment to this message.
* As of recent API updates, adding new files also requires specifying all existing files to keep attached.
+ * If the existing attachments are not provided in addition to new files, they will instead be removed from the message.
+ * Use {@link #retainAttachments(java.util.Collection)} or {@link #addFile(MessageAttachment)} to add all existing attachments.
+ *
* @param name
* The alternative name that should be used
* @param data
@@ -372,6 +447,10 @@ public WebhookMessageBuilder addFile(@NotNull String name, @NotNull byte[] data)
* Adds the provided data as a file attachment to this message.
* As of recent API updates, adding new files also requires specifying all existing files to keep attached.
+ * If the existing attachments are not provided in addition to new files, they will instead be removed from the message.
+ * Use {@link #retainAttachments(java.util.Collection)} or {@link #addFile(MessageAttachment)} to add all existing attachments.
+ *
* @param name
* The alternative name that should be used
* @param data
@@ -379,6 +458,8 @@ public WebhookMessageBuilder addFile(@NotNull String name, @NotNull byte[] data)
*
* @throws java.lang.NullPointerException
* If provided with null
+ * @throws java.lang.IllegalStateException
+ * If more than {@value WebhookMessage#MAX_FILES} are added
*
* @return This builder for chaining convenience
*/
@@ -410,7 +491,7 @@ public WebhookMessage build() {
if (isEmpty())
throw new IllegalStateException("Cannot build an empty message!");
return new WebhookMessage(username, avatarUrl, content.toString(), embeds, isTTS,
- fileIndex == 0 ? null : Arrays.copyOf(files, fileIndex), allowedMentions, flags);
+ fileIndex == 0 && !hasFiles ? null : Arrays.copyOf(files, fileIndex), allowedMentions, flags);
}
diff --git a/src/test/java/root/send/MessageTest.java b/src/test/java/root/send/MessageTest.java
index 11b96d2..aa2c792 100644
--- a/src/test/java/root/send/MessageTest.java
+++ b/src/test/java/root/send/MessageTest.java
@@ -301,17 +301,21 @@ public void checkMultipart() throws IOException {
.put("allowed_mentions", allowedMentions)
.put("content", "CONTENT!")
.put("embeds", new JSONArray())
+ .put("attachments", new JSONArray()
+ .put(new JSONObject()
+ .put("id", 0)
+ .put("name", "myFile.txt")))
.put("tts", false)
.put("flags", 0)
.toMap(),
new JSONObject((String) multiPart.get("payload_json")).toMap()
);
- Assert.assertTrue("Multipart doesn't contain file", multiPart.containsKey("file0"));
- Assert.assertTrue("Multipart file is not of correct type", multiPart.get("file0") instanceof IOTestUtil.MultiPartFile);
+ Assert.assertTrue("Multipart doesn't contain file", multiPart.containsKey("files[0]"));
+ Assert.assertTrue("Multipart file is not of correct type", multiPart.get("files[0]") instanceof IOTestUtil.MultiPartFile);
Assert.assertEquals("Multipart file mismatches",
fileContent,
- new String(((IOTestUtil.MultiPartFile) multiPart.get("file0")).content, StandardCharsets.UTF_8)
+ new String(((IOTestUtil.MultiPartFile) multiPart.get("files[0]")).content, StandardCharsets.UTF_8)
);
}
A message can hold up to {@value #MAX_FILES} attachments
+ * and a total of 8MiB of data.
+ *
+ * @param attachments
+ * The attachments to add, keys are the alternative names
+ * for each attachment
+ *
+ * @throws java.lang.NullPointerException
+ * If provided with null
+ * @throws java.lang.IllegalArgumentException
+ * If no attachments are provided or more than {@value #MAX_FILES}
+ *
+ * @return A WebhookMessage for the attachments
+ */
+ @NotNull
+ public static WebhookMessage files(@NotNull Collection extends MessageAttachment> attachments) {
+ Objects.requireNonNull(attachments, "Attachments");
+
+ int fileAmount = attachments.size();
+ if (fileAmount > WebhookMessage.MAX_FILES)
+ throw new IllegalArgumentException("Cannot add more than " + WebhookMessage.MAX_FILES + " files to a message");
+ MessageAttachment[] files = attachments.toArray(new MessageAttachment[0]);
+ return new WebhookMessage(null, null, null, null, false, files, AllowedMentions.all(), 0);
+ }
+
/**
* Creates a WebhookMessage from the provided attachments.
*
A message can hold up to {@value #MAX_FILES} attachments
@@ -254,8 +281,6 @@ public static WebhookMessage files(@NotNull Map
A single message can have up to {@value WebhookMessage#MAX_FILES} attachments.
*
- * @param file
- * The file to attach
+ *
A single message can have up to {@value WebhookMessage#MAX_FILES} attachments.
+ *
+ *
A single message can have up to {@value WebhookMessage#MAX_FILES} attachments.
*
+ *
A single message can have up to {@value WebhookMessage#MAX_FILES} attachments.
*
+ *
A single message can have up to {@value WebhookMessage#MAX_FILES} attachments.
*
+ *