From 385d77925fc167b62b7cabc83a53adb942c4c24d Mon Sep 17 00:00:00 2001 From: Patrick Bassner Date: Wed, 4 Aug 2021 17:31:34 +0200 Subject: [PATCH] Add message updater fix allowed mentions checkstyle Fixes add fixes --- .../api/entity/message/MessageBuilder.java | 522 +---------------- .../entity/message/MessageBuilderBase.java | 540 ++++++++++++++++++ .../api/entity/message/MessageUpdater.java | 42 ++ .../InteractionMessageBuilderDelegate.java | 2 +- ...e.java => MessageBuilderBaseDelegate.java} | 11 +- .../WebhookMessageBuilderDelegate.java | 2 +- .../api/util/internal/DelegateFactory.java | 8 +- .../internal/DelegateFactoryDelegate.java | 4 +- ...InteractionMessageBuilderDelegateImpl.java | 2 +- ...va => MessageBuilderBaseDelegateImpl.java} | 248 +++++--- .../WebhookMessageBuilderDelegateImpl.java | 2 +- .../util/DelegateFactoryDelegateImpl.java | 8 +- 12 files changed, 778 insertions(+), 613 deletions(-) create mode 100644 javacord-api/src/main/java/org/javacord/api/entity/message/MessageBuilderBase.java create mode 100644 javacord-api/src/main/java/org/javacord/api/entity/message/MessageUpdater.java rename javacord-api/src/main/java/org/javacord/api/entity/message/internal/{MessageBuilderDelegate.java => MessageBuilderBaseDelegate.java} (96%) rename javacord-core/src/main/java/org/javacord/core/entity/message/{MessageBuilderDelegateImpl.java => MessageBuilderBaseDelegateImpl.java} (72%) diff --git a/javacord-api/src/main/java/org/javacord/api/entity/message/MessageBuilder.java b/javacord-api/src/main/java/org/javacord/api/entity/message/MessageBuilder.java index 4af9fa385f..7e56f62882 100644 --- a/javacord-api/src/main/java/org/javacord/api/entity/message/MessageBuilder.java +++ b/javacord-api/src/main/java/org/javacord/api/entity/message/MessageBuilder.java @@ -1,35 +1,25 @@ package org.javacord.api.entity.message; import org.javacord.api.DiscordApi; -import org.javacord.api.entity.Icon; -import org.javacord.api.entity.Mentionable; import org.javacord.api.entity.channel.TextChannel; -import org.javacord.api.entity.message.component.HighLevelComponent; -import org.javacord.api.entity.message.component.LowLevelComponent; -import org.javacord.api.entity.message.embed.EmbedBuilder; -import org.javacord.api.entity.message.internal.MessageBuilderDelegate; -import org.javacord.api.entity.message.mention.AllowedMentions; import org.javacord.api.entity.user.User; import org.javacord.api.entity.webhook.IncomingWebhook; import org.javacord.api.util.DiscordRegexPattern; -import org.javacord.api.util.internal.DelegateFactory; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.InputStream; -import java.net.URL; import java.util.concurrent.CompletableFuture; import java.util.regex.Matcher; /** * This class can help you to create messages. */ -public class MessageBuilder { +public class MessageBuilder extends MessageBuilderBase { /** - * The message delegate used by this instance. + * Class constructor. */ - protected final MessageBuilderDelegate delegate = DelegateFactory.createMessageBuilderDelegate(); + public MessageBuilder() { + super(MessageBuilder.class); + } /** * Creates a message builder from a message. @@ -54,133 +44,6 @@ public MessageBuilder copy(Message message) { return this; } - /** - * Add multiple high level components to the message. - * - * @param components The high level components. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addComponents(HighLevelComponent... components) { - delegate.addComponents(components); - return this; - } - - /** - * Add multiple low level components, wrapped in an ActionRow, to the message. - * - * @param components The low level components. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addActionRow(LowLevelComponent... components) { - delegate.addActionRow(components); - return this; - } - - /** - * Appends code to the message. - * - * @param language The language, e.g. "java". - * @param code The code. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder appendCode(String language, String code) { - delegate.appendCode(language, code); - return this; - } - - /** - * Appends a sting with or without decoration to the message. - * - * @param message The string to append. - * @param decorations The decorations of the string. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder append(String message, MessageDecoration... decorations) { - delegate.append(message, decorations); - return this; - } - - /** - * Appends a mentionable entity (usually a user or channel) to the message. - * - * @param entity The entity to mention. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder append(Mentionable entity) { - delegate.append(entity); - return this; - } - - /** - * Appends the string representation of the object (calling {@link String#valueOf(Object)} method) to the message. - * - * @param object The object to append. - * @return The current instance in order to chain call methods. - * @see StringBuilder#append(Object) - */ - public MessageBuilder append(Object object) { - delegate.append(object); - return this; - } - - /** - * Appends a new line to the message. - * - * @return The current instance in order to chain call methods. - */ - public MessageBuilder appendNewLine() { - delegate.appendNewLine(); - return this; - } - - /** - * Sets the content of the message. - * This method overwrites all previous content changes - * (using {@link #append(String, MessageDecoration...)} for example). - * - * @param content The new content of the message. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder setContent(String content) { - delegate.setContent(content); - return this; - } - - /** - * Sets the embed of the message (overrides all existing embeds). - * - * @param embed The embed to set. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder setEmbed(EmbedBuilder embed) { - delegate.removeAllEmbeds(); - delegate.addEmbed(embed); - return this; - } - - /** - * Sets multiple embeds of the message (overrides all existing embeds). - * - * @param embeds The embed to set. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder setEmbeds(EmbedBuilder... embeds) { - delegate.removeAllEmbeds(); - delegate.addEmbeds(embeds); - return this; - } - - /** - * Adds an embed to the message. - * - * @param embed The embed to add. - * @return The current isntance in order to chain call methods. - */ - public MessageBuilder addEmbed(EmbedBuilder embed) { - delegate.addEmbed(embed); - return this; - } - /** * Sets if the message should be text to speech. * @@ -192,305 +55,6 @@ public MessageBuilder setTts(boolean tts) { return this; } - /** - * Adds a file to the message. - * - * @param image The image to add as an attachment. - * @param fileName The file name of the image. - * @return The current instance in order to chain call methods. - * @see #addAttachment(BufferedImage, String) - */ - public MessageBuilder addFile(BufferedImage image, String fileName) { - delegate.addFile(image, fileName); - return this; - } - - /** - * Adds a file to the message. - * - * @param file The file to add as an attachment. - * @return The current instance in order to chain call methods. - * @see #addAttachment(File) - */ - public MessageBuilder addFile(File file) { - delegate.addFile(file); - return this; - } - - /** - * Adds a file to the message. - * - * @param icon The icon to add as an attachment. - * @return The current instance in order to chain call methods. - * @see #addAttachment(Icon) - */ - public MessageBuilder addFile(Icon icon) { - delegate.addFile(icon); - return this; - } - - /** - * Adds a file to the message and marks it as a spoiler. - * - * @param url The url of the attachment. - * @return The current instance in order to chain call methods. - * @see #addAttachment(URL) - */ - public MessageBuilder addFile(URL url) { - delegate.addFile(url); - return this; - } - - /** - * Adds a file to the message. - * - * @param bytes The bytes of the file. - * @param fileName The name of the file. - * @return The current instance in order to chain call methods. - * @see #addAttachment(byte[], String) - */ - public MessageBuilder addFile(byte[] bytes, String fileName) { - delegate.addFile(bytes, fileName); - return this; - } - - /** - * Adds a file to the message. - * - * @param stream The stream of the file. - * @param fileName The name of the file. - * @return The current instance in order to chain call methods. - * @see #addAttachment(InputStream, String) - */ - public MessageBuilder addFile(InputStream stream, String fileName) { - delegate.addFile(stream, fileName); - return this; - } - - /** - * Adds a file to the message and marks it as spoiler. - * - * @param image The image to add as an attachment. - * @param fileName The file name of the image. - * @return The current instance in order to chain call methods. - * @see #addAttachmentAsSpoiler(BufferedImage, String) - */ - public MessageBuilder addFileAsSpoiler(BufferedImage image, String fileName) { - delegate.addFile(image, "SPOILER_" + fileName); - return this; - } - - /** - * Adds a file to the message and marks it as spoiler. - * - * @param file The file to add as an attachment. - * @return The current instance in order to chain call methods. - * @see #addAttachmentAsSpoiler(File) - */ - public MessageBuilder addFileAsSpoiler(File file) { - delegate.addFileAsSpoiler(file); - return this; - } - - /** - * Adds a file to the message and marks it as spoiler. - * - * @param icon The icon to add as an attachment. - * @return The current instance in order to chain call methods. - * @see #addAttachmentAsSpoiler(Icon) - */ - public MessageBuilder addFileAsSpoiler(Icon icon) { - delegate.addFileAsSpoiler(icon); - return this; - } - - /** - * Adds a file to the message and marks it as a spoiler. - * - * @param url The url of the attachment. - * @return The current instance in order to chain call methods. - * @see #addAttachment(URL) - */ - public MessageBuilder addFileAsSpoiler(URL url) { - delegate.addFileAsSpoiler(url); - return this; - } - - /** - * Adds a file to the message and marks it as spoiler. - * - * @param bytes The bytes of the file. - * @param fileName The name of the file. - * @return The current instance in order to chain call methods. - * @see #addAttachmentAsSpoiler(byte[], String) - */ - public MessageBuilder addFileAsSpoiler(byte[] bytes, String fileName) { - delegate.addFile(bytes, "SPOILER_" + fileName); - return this; - } - - /** - * Adds a file to the message and marks it as spoiler. - * - * @param stream The stream of the file. - * @param fileName The name of the file. - * @return The current instance in order to chain call methods. - * @see #addAttachment(InputStream, String) - */ - public MessageBuilder addFileAsSpoiler(InputStream stream, String fileName) { - delegate.addFile(stream, "SPOILER_" + fileName); - return this; - } - - /** - * Adds an attachment to the message. - * - * @param image The image to add as an attachment. - * @param fileName The file name of the image. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachment(BufferedImage image, String fileName) { - delegate.addAttachment(image, fileName); - return this; - } - - /** - * Adds an attachment to the message. - * - * @param file The file to add as an attachment. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachment(File file) { - delegate.addAttachment(file); - return this; - } - - /** - * Adds an attachment to the message. - * - * @param icon The icon to add as an attachment. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachment(Icon icon) { - delegate.addAttachment(icon); - return this; - } - - /** - * Adds an attachment to the message. - * - * @param url The url of the attachment. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachment(URL url) { - delegate.addAttachment(url); - return this; - } - - /** - * Adds an attachment to the message. - * - * @param bytes The bytes of the file. - * @param fileName The name of the file. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachment(byte[] bytes, String fileName) { - delegate.addAttachment(bytes, fileName); - return this; - } - - /** - * Adds an attachment to the message. - * - * @param stream The stream of the file. - * @param fileName The name of the file. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachment(InputStream stream, String fileName) { - delegate.addAttachment(stream, fileName); - return this; - } - - /** - * Adds an attachment to the message and marks it as spoiler. - * - * @param image The image to add as an attachment. - * @param fileName The file name of the image. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachmentAsSpoiler(BufferedImage image, String fileName) { - delegate.addAttachment(image, "SPOILER_" + fileName); - return this; - } - - /** - * Adds an attachment to the message and marks it as spoiler. - * - * @param file The file to add as an attachment. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachmentAsSpoiler(File file) { - delegate.addAttachmentAsSpoiler(file); - return this; - } - - /** - * Adds an attachment to the message and marks it as spoiler. - * - * @param icon The icon to add as an attachment. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachmentAsSpoiler(Icon icon) { - delegate.addAttachmentAsSpoiler(icon); - return this; - } - - /** - * Adds an attachment to the message and marks it as spoiler. - * - * @param url The url of the attachment. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachmentAsSpoiler(URL url) { - delegate.addAttachmentAsSpoiler(url); - return this; - } - - /** - * Adds an attachment to the message and marks it as spoiler. - * - * @param bytes The bytes of the file. - * @param fileName The name of the file. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachmentAsSpoiler(byte[] bytes, String fileName) { - delegate.addAttachment(bytes, "SPOILER_" + fileName); - return this; - } - - /** - * Adds an attachment to the message and marks it as spoiler. - * - * @param stream The stream of the file. - * @param fileName The name of the file. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addAttachmentAsSpoiler(InputStream stream, String fileName) { - delegate.addAttachment(stream, "SPOILER_" + fileName); - return this; - } - - /** - * Controls who will be mentioned if mentions exist in the message. - * - * @param allowedMentions The mention object. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder setAllowedMentions(AllowedMentions allowedMentions) { - delegate.setAllowedMentions(allowedMentions); - return this; - } - /** * Sets the message to reply to. * @@ -513,79 +77,6 @@ public MessageBuilder replyTo(long messageId) { return this; } - /** - * Remove all high-level components from the message. - * - * @return The current instance in order to chain call methods. - */ - public MessageBuilder removeAllComponents() { - delegate.removeAllComponents(); - return this; - } - - /** - * Adds the embeds to the message. - * - * @param embeds The embeds to add. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder addEmbeds(EmbedBuilder... embeds) { - delegate.addEmbeds(embeds); - return this; - } - - /** - * Removes the embed from the message. - * - * @param embed The embed to remove. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder removeEmbed(EmbedBuilder embed) { - delegate.removeEmbed(embed); - return this; - } - - /** - * Removes the embeds from the message. - * - * @param embeds The embeds to remove. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder removeEmbeds(EmbedBuilder... embeds) { - delegate.removeEmbeds(embeds); - return this; - } - - /** - * Removes all embeds from the message. - * - * @return The current instance in order to chain call methods. - */ - public MessageBuilder removeAllEmbeds() { - delegate.removeAllEmbeds(); - return this; - } - - /** - * Sets the nonce of the message. - * - * @param nonce The nonce to set. - * @return The current instance in order to chain call methods. - */ - public MessageBuilder setNonce(String nonce) { - delegate.setNonce(nonce); - return this; - } - - /** - * Gets the {@link StringBuilder} which is used to build the message. - * - * @return The StringBuilder which is used to build the message. - */ - public StringBuilder getStringBuilder() { - return delegate.getStringBuilder(); - } - /** * Sends the message. * @@ -660,7 +151,7 @@ public CompletableFuture sendWithWebhook(DiscordApi api, String webhook * @throws IllegalArgumentException If the link isn't valid. */ public CompletableFuture sendWithWebhook(DiscordApi api, String webhookUrl) - throws IllegalArgumentException { + throws IllegalArgumentException { Matcher matcher = DiscordRegexPattern.WEBHOOK_URL.matcher(webhookUrl); if (!matcher.matches()) { @@ -669,5 +160,4 @@ public CompletableFuture sendWithWebhook(DiscordApi api, String webhook return sendWithWebhook(api, matcher.group("id"), matcher.group("token")); } - } diff --git a/javacord-api/src/main/java/org/javacord/api/entity/message/MessageBuilderBase.java b/javacord-api/src/main/java/org/javacord/api/entity/message/MessageBuilderBase.java new file mode 100644 index 0000000000..3b52cd8882 --- /dev/null +++ b/javacord-api/src/main/java/org/javacord/api/entity/message/MessageBuilderBase.java @@ -0,0 +1,540 @@ +package org.javacord.api.entity.message; + +import org.javacord.api.entity.Icon; +import org.javacord.api.entity.Mentionable; +import org.javacord.api.entity.message.component.HighLevelComponent; +import org.javacord.api.entity.message.component.LowLevelComponent; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.entity.message.internal.MessageBuilderBaseDelegate; +import org.javacord.api.entity.message.mention.AllowedMentions; +import org.javacord.api.util.internal.DelegateFactory; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.InputStream; +import java.net.URL; + +abstract class MessageBuilderBase { + private final Class myClass; + + public MessageBuilderBase(Class myClass) { + this.myClass = myClass; + } + + /** + * The message delegate used by this instance. + */ + protected final MessageBuilderBaseDelegate delegate = DelegateFactory.createMessageBuilderBaseDelegate(); + + /** + * Add multiple high level components to the message. + * + * @param components The high level components. + * @return The current instance in order to chain call methods. + */ + public T addComponents(HighLevelComponent... components) { + delegate.addComponents(components); + return myClass.cast(this); + } + + /** + * Add multiple low level components, wrapped in an ActionRow, to the message. + * + * @param components The low level components. + * @return The current instance in order to chain call methods. + */ + public T addActionRow(LowLevelComponent... components) { + delegate.addActionRow(components); + return myClass.cast(this); + } + + /** + * Appends code to the message. + * + * @param language The language, e.g. "java". + * @param code The code. + * @return The current instance in order to chain call methods. + */ + public T appendCode(String language, String code) { + delegate.appendCode(language, code); + return myClass.cast(this); + } + + /** + * Appends a sting with or without decoration to the message. + * + * @param message The string to append. + * @param decorations The decorations of the string. + * @return The current instance in order to chain call methods. + */ + public T append(String message, MessageDecoration... decorations) { + delegate.append(message, decorations); + return myClass.cast(this); + } + + /** + * Appends a mentionable entity (usually a user or channel) to the message. + * + * @param entity The entity to mention. + * @return The current instance in order to chain call methods. + */ + public T append(Mentionable entity) { + delegate.append(entity); + return myClass.cast(this); + } + + /** + * Appends the string representation of the object (calling {@link String#valueOf(Object)} method) to the message. + * + * @param object The object to append. + * @return The current instance in order to chain call methods. + * @see StringBuilder#append(Object) + */ + public T append(Object object) { + delegate.append(object); + return myClass.cast(this); + } + + /** + * Appends a new line to the message. + * + * @return The current instance in order to chain call methods. + */ + public T appendNewLine() { + delegate.appendNewLine(); + return myClass.cast(this); + } + + /** + * Sets the content of the message. + * This method overwrites all previous content changes + * (using {@link #append(String, MessageDecoration...)} for example). + * + * @param content The new content of the message. + * @return The current instance in order to chain call methods. + */ + public T setContent(String content) { + delegate.setContent(content); + return myClass.cast(this); + } + + /** + * Removes the content of the message. + * This method overwrites all previous content changes + * (using {@link #append(String, MessageDecoration...)} for example). + * + * @return The current instance in order to chain call methods. + */ + public T removeContent() { + delegate.setContent(null); + return myClass.cast(this); + } + + + /** + * Sets the embed of the message (overrides all existing embeds). + * + * @param embed The embed to set. + * @return The current instance in order to chain call methods. + */ + public T setEmbed(EmbedBuilder embed) { + delegate.removeAllEmbeds(); + delegate.addEmbed(embed); + return myClass.cast(this); + } + + /** + * Sets multiple embeds of the message (overrides all existing embeds). + * + * @param embeds The embed to set. + * @return The current instance in order to chain call methods. + */ + public T setEmbeds(EmbedBuilder... embeds) { + delegate.removeAllEmbeds(); + delegate.addEmbeds(embeds); + return myClass.cast(this); + } + + /** + * Adds an embed to the message. + * + * @param embed The embed to add. + * @return The current isntance in order to chain call methods. + */ + public T addEmbed(EmbedBuilder embed) { + delegate.addEmbed(embed); + return myClass.cast(this); + } + + /** + * Adds a file to the message. + * + * @param image The image to add as an attachment. + * @param fileName The file name of the image. + * @return The current instance in order to chain call methods. + * @see #addAttachment(BufferedImage, String) + */ + public T addFile(BufferedImage image, String fileName) { + delegate.addFile(image, fileName); + return myClass.cast(this); + } + + /** + * Adds a file to the message. + * + * @param file The file to add as an attachment. + * @return The current instance in order to chain call methods. + * @see #addAttachment(File) + */ + public T addFile(File file) { + delegate.addFile(file); + return myClass.cast(this); + } + + /** + * Adds a file to the message. + * + * @param icon The icon to add as an attachment. + * @return The current instance in order to chain call methods. + * @see #addAttachment(Icon) + */ + public T addFile(Icon icon) { + delegate.addFile(icon); + return myClass.cast(this); + } + + /** + * Adds a file to the message and marks it as a spoiler. + * + * @param url The url of the attachment. + * @return The current instance in order to chain call methods. + * @see #addAttachment(URL) + */ + public T addFile(URL url) { + delegate.addFile(url); + return myClass.cast(this); + } + + /** + * Adds a file to the message. + * + * @param bytes The bytes of the file. + * @param fileName The name of the file. + * @return The current instance in order to chain call methods. + * @see #addAttachment(byte[], String) + */ + public T addFile(byte[] bytes, String fileName) { + delegate.addFile(bytes, fileName); + return myClass.cast(this); + } + + /** + * Adds a file to the message. + * + * @param stream The stream of the file. + * @param fileName The name of the file. + * @return The current instance in order to chain call methods. + * @see #addAttachment(InputStream, String) + */ + public T addFile(InputStream stream, String fileName) { + delegate.addFile(stream, fileName); + return myClass.cast(this); + } + + /** + * Adds a file to the message and marks it as spoiler. + * + * @param image The image to add as an attachment. + * @param fileName The file name of the image. + * @return The current instance in order to chain call methods. + * @see #addAttachmentAsSpoiler(BufferedImage, String) + */ + public T addFileAsSpoiler(BufferedImage image, String fileName) { + delegate.addFile(image, "SPOILER_" + fileName); + return myClass.cast(this); + } + + /** + * Adds a file to the message and marks it as spoiler. + * + * @param file The file to add as an attachment. + * @return The current instance in order to chain call methods. + * @see #addAttachmentAsSpoiler(File) + */ + public T addFileAsSpoiler(File file) { + delegate.addFileAsSpoiler(file); + return myClass.cast(this); + } + + /** + * Adds a file to the message and marks it as spoiler. + * + * @param icon The icon to add as an attachment. + * @return The current instance in order to chain call methods. + * @see #addAttachmentAsSpoiler(Icon) + */ + public T addFileAsSpoiler(Icon icon) { + delegate.addFileAsSpoiler(icon); + return myClass.cast(this); + } + + /** + * Adds a file to the message and marks it as a spoiler. + * + * @param url The url of the attachment. + * @return The current instance in order to chain call methods. + * @see #addAttachment(URL) + */ + public T addFileAsSpoiler(URL url) { + delegate.addFileAsSpoiler(url); + return myClass.cast(this); + } + + /** + * Adds a file to the message and marks it as spoiler. + * + * @param bytes The bytes of the file. + * @param fileName The name of the file. + * @return The current instance in order to chain call methods. + * @see #addAttachmentAsSpoiler(byte[], String) + */ + public T addFileAsSpoiler(byte[] bytes, String fileName) { + delegate.addFile(bytes, "SPOILER_" + fileName); + return myClass.cast(this); + } + + /** + * Adds a file to the message and marks it as spoiler. + * + * @param stream The stream of the file. + * @param fileName The name of the file. + * @return The current instance in order to chain call methods. + * @see #addAttachment(InputStream, String) + */ + public T addFileAsSpoiler(InputStream stream, String fileName) { + delegate.addFile(stream, "SPOILER_" + fileName); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message. + * + * @param image The image to add as an attachment. + * @param fileName The file name of the image. + * @return The current instance in order to chain call methods. + */ + public T addAttachment(BufferedImage image, String fileName) { + delegate.addAttachment(image, fileName); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message. + * + * @param file The file to add as an attachment. + * @return The current instance in order to chain call methods. + */ + public T addAttachment(File file) { + delegate.addAttachment(file); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message. + * + * @param icon The icon to add as an attachment. + * @return The current instance in order to chain call methods. + */ + public T addAttachment(Icon icon) { + delegate.addAttachment(icon); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message. + * + * @param url The url of the attachment. + * @return The current instance in order to chain call methods. + */ + public T addAttachment(URL url) { + delegate.addAttachment(url); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message. + * + * @param bytes The bytes of the file. + * @param fileName The name of the file. + * @return The current instance in order to chain call methods. + */ + public T addAttachment(byte[] bytes, String fileName) { + delegate.addAttachment(bytes, fileName); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message. + * + * @param stream The stream of the file. + * @param fileName The name of the file. + * @return The current instance in order to chain call methods. + */ + public T addAttachment(InputStream stream, String fileName) { + delegate.addAttachment(stream, fileName); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message and marks it as spoiler. + * + * @param image The image to add as an attachment. + * @param fileName The file name of the image. + * @return The current instance in order to chain call methods. + */ + public T addAttachmentAsSpoiler(BufferedImage image, String fileName) { + delegate.addAttachment(image, "SPOILER_" + fileName); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message and marks it as spoiler. + * + * @param file The file to add as an attachment. + * @return The current instance in order to chain call methods. + */ + public T addAttachmentAsSpoiler(File file) { + delegate.addAttachmentAsSpoiler(file); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message and marks it as spoiler. + * + * @param icon The icon to add as an attachment. + * @return The current instance in order to chain call methods. + */ + public T addAttachmentAsSpoiler(Icon icon) { + delegate.addAttachmentAsSpoiler(icon); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message and marks it as spoiler. + * + * @param url The url of the attachment. + * @return The current instance in order to chain call methods. + */ + public T addAttachmentAsSpoiler(URL url) { + delegate.addAttachmentAsSpoiler(url); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message and marks it as spoiler. + * + * @param bytes The bytes of the file. + * @param fileName The name of the file. + * @return The current instance in order to chain call methods. + */ + public T addAttachmentAsSpoiler(byte[] bytes, String fileName) { + delegate.addAttachment(bytes, "SPOILER_" + fileName); + return myClass.cast(this); + } + + /** + * Adds an attachment to the message and marks it as spoiler. + * + * @param stream The stream of the file. + * @param fileName The name of the file. + * @return The current instance in order to chain call methods. + */ + public T addAttachmentAsSpoiler(InputStream stream, String fileName) { + delegate.addAttachment(stream, "SPOILER_" + fileName); + return myClass.cast(this); + } + + /** + * Controls who will be mentioned if mentions exist in the message. + * + * @param allowedMentions The mention object. + * @return The current instance in order to chain call methods. + */ + public T setAllowedMentions(AllowedMentions allowedMentions) { + delegate.setAllowedMentions(allowedMentions); + return myClass.cast(this); + } + + /** + * Remove all high-level components from the message. + * + * @return The current instance in order to chain call methods. + */ + public T removeAllComponents() { + delegate.removeAllComponents(); + return myClass.cast(this); + } + + /** + * Adds the embeds to the message. + * + * @param embeds The embeds to add. + * @return The current instance in order to chain call methods. + */ + public T addEmbeds(EmbedBuilder... embeds) { + delegate.addEmbeds(embeds); + return myClass.cast(this); + } + + /** + * Removes the embed from the message. + * + * @param embed The embed to remove. + * @return The current instance in order to chain call methods. + */ + public T removeEmbed(EmbedBuilder embed) { + delegate.removeEmbed(embed); + return myClass.cast(this); + } + + /** + * Removes the embeds from the message. + * + * @param embeds The embeds to remove. + * @return The current instance in order to chain call methods. + */ + public T removeEmbeds(EmbedBuilder... embeds) { + delegate.removeEmbeds(embeds); + return myClass.cast(this); + } + + /** + * Removes all embeds from the message. + * + * @return The current instance in order to chain call methods. + */ + public T removeAllEmbeds() { + delegate.removeAllEmbeds(); + return myClass.cast(this); + } + + /** + * Sets the nonce of the message. + * + * @param nonce The nonce to set. + * @return The current instance in order to chain call methods. + */ + public T setNonce(String nonce) { + delegate.setNonce(nonce); + return myClass.cast(this); + } + + /** + * Gets the {@link StringBuilder} which is used to build the message. + * + * @return The StringBuilder which is used to build the message. + */ + public StringBuilder getStringBuilder() { + return delegate.getStringBuilder(); + } +} diff --git a/javacord-api/src/main/java/org/javacord/api/entity/message/MessageUpdater.java b/javacord-api/src/main/java/org/javacord/api/entity/message/MessageUpdater.java new file mode 100644 index 0000000000..7bcc6c1240 --- /dev/null +++ b/javacord-api/src/main/java/org/javacord/api/entity/message/MessageUpdater.java @@ -0,0 +1,42 @@ +package org.javacord.api.entity.message; + +import java.util.concurrent.CompletableFuture; + +public class MessageUpdater extends MessageBuilderBase { + /** + * The message to update. + */ + private final Message message; + + /** + * Class constructor. + * + * @param m The message to update. + */ + public MessageUpdater(Message m) { + super(MessageUpdater.class); + delegate.copy(m); + this.message = m; + } + + /** + * Edits the message, updating all fields that have been changed by a method call. + * For example, the content of the message will only be patched if you called a method on this updater + * that affects the content. + * + * @return The edited message. + */ + public CompletableFuture applyChanges() { + return delegate.edit(message, true); + } + + /** + * Edits the message, updating all fields. + * Fields that have not been set will be dropped and removed from the actual message on Discord. + * + * @return The edited message. + */ + public CompletableFuture replaceMessage() { + return delegate.edit(message, false); + } +} diff --git a/javacord-api/src/main/java/org/javacord/api/entity/message/internal/InteractionMessageBuilderDelegate.java b/javacord-api/src/main/java/org/javacord/api/entity/message/internal/InteractionMessageBuilderDelegate.java index e355bd719b..784870ba8d 100644 --- a/javacord-api/src/main/java/org/javacord/api/entity/message/internal/InteractionMessageBuilderDelegate.java +++ b/javacord-api/src/main/java/org/javacord/api/entity/message/internal/InteractionMessageBuilderDelegate.java @@ -7,7 +7,7 @@ import java.util.EnumSet; import java.util.concurrent.CompletableFuture; -public interface InteractionMessageBuilderDelegate extends MessageBuilderDelegate { +public interface InteractionMessageBuilderDelegate extends MessageBuilderBaseDelegate { /** * Sets the message flags of the message. diff --git a/javacord-api/src/main/java/org/javacord/api/entity/message/internal/MessageBuilderDelegate.java b/javacord-api/src/main/java/org/javacord/api/entity/message/internal/MessageBuilderBaseDelegate.java similarity index 96% rename from javacord-api/src/main/java/org/javacord/api/entity/message/internal/MessageBuilderDelegate.java rename to javacord-api/src/main/java/org/javacord/api/entity/message/internal/MessageBuilderBaseDelegate.java index 25a2695746..1cef24ddc9 100644 --- a/javacord-api/src/main/java/org/javacord/api/entity/message/internal/MessageBuilderDelegate.java +++ b/javacord-api/src/main/java/org/javacord/api/entity/message/internal/MessageBuilderBaseDelegate.java @@ -25,7 +25,7 @@ * This class is internally used by the {@link MessageBuilder} to create messages. * You usually don't want to interact with this object. */ -public interface MessageBuilderDelegate { +public interface MessageBuilderBaseDelegate { /** * Add high-level components to the message. @@ -350,6 +350,15 @@ public interface MessageBuilderDelegate { */ CompletableFuture send(Messageable messageable); + /** + * Edits the message. + * + * @param message The message to edit. + * @param onlyChangedFields True if only changed fields should be updated + * @return The edited message. + */ + CompletableFuture edit(Message message, boolean onlyChangedFields); + /** * Sends the message. * diff --git a/javacord-api/src/main/java/org/javacord/api/entity/message/internal/WebhookMessageBuilderDelegate.java b/javacord-api/src/main/java/org/javacord/api/entity/message/internal/WebhookMessageBuilderDelegate.java index 61ec855acc..49f63cb4be 100644 --- a/javacord-api/src/main/java/org/javacord/api/entity/message/internal/WebhookMessageBuilderDelegate.java +++ b/javacord-api/src/main/java/org/javacord/api/entity/message/internal/WebhookMessageBuilderDelegate.java @@ -15,7 +15,7 @@ * This class is internally used by the {@link WebhookMessageBuilder} to create messages. * You usually don't want to interact with this object. */ -public interface WebhookMessageBuilderDelegate extends MessageBuilderDelegate { +public interface WebhookMessageBuilderDelegate extends MessageBuilderBaseDelegate { /** * Sets the display name of the webhook. diff --git a/javacord-api/src/main/java/org/javacord/api/util/internal/DelegateFactory.java b/javacord-api/src/main/java/org/javacord/api/util/internal/DelegateFactory.java index 4237b837e9..ced7edff3b 100644 --- a/javacord-api/src/main/java/org/javacord/api/util/internal/DelegateFactory.java +++ b/javacord-api/src/main/java/org/javacord/api/util/internal/DelegateFactory.java @@ -22,7 +22,7 @@ import org.javacord.api.entity.message.component.internal.SelectMenuOptionBuilderDelegate; import org.javacord.api.entity.message.embed.internal.EmbedBuilderDelegate; import org.javacord.api.entity.message.internal.InteractionMessageBuilderDelegate; -import org.javacord.api.entity.message.internal.MessageBuilderDelegate; +import org.javacord.api.entity.message.internal.MessageBuilderBaseDelegate; import org.javacord.api.entity.message.internal.WebhookMessageBuilderDelegate; import org.javacord.api.entity.message.mention.internal.AllowedMentionsBuilderDelegate; import org.javacord.api.entity.permission.Permissions; @@ -157,11 +157,11 @@ public static AllowedMentionsBuilderDelegate createAllowedMentionsBuilderDelegat } /** - * Creates a new message builder delegate. + * Creates a new message builder base delegate. * - * @return A new message builder delegate. + * @return A new message builder base delegate. */ - public static MessageBuilderDelegate createMessageBuilderDelegate() { + public static MessageBuilderBaseDelegate createMessageBuilderBaseDelegate() { return delegateFactoryDelegate.createMessageBuilderDelegate(); } diff --git a/javacord-api/src/main/java/org/javacord/api/util/internal/DelegateFactoryDelegate.java b/javacord-api/src/main/java/org/javacord/api/util/internal/DelegateFactoryDelegate.java index 801a6394e1..3aea711e46 100644 --- a/javacord-api/src/main/java/org/javacord/api/util/internal/DelegateFactoryDelegate.java +++ b/javacord-api/src/main/java/org/javacord/api/util/internal/DelegateFactoryDelegate.java @@ -22,7 +22,7 @@ import org.javacord.api.entity.message.component.internal.SelectMenuOptionBuilderDelegate; import org.javacord.api.entity.message.embed.internal.EmbedBuilderDelegate; import org.javacord.api.entity.message.internal.InteractionMessageBuilderDelegate; -import org.javacord.api.entity.message.internal.MessageBuilderDelegate; +import org.javacord.api.entity.message.internal.MessageBuilderBaseDelegate; import org.javacord.api.entity.message.internal.WebhookMessageBuilderDelegate; import org.javacord.api.entity.message.mention.internal.AllowedMentionsBuilderDelegate; import org.javacord.api.entity.permission.Permissions; @@ -80,7 +80,7 @@ public interface DelegateFactoryDelegate { * * @return A new message builder delegate. */ - MessageBuilderDelegate createMessageBuilderDelegate(); + MessageBuilderBaseDelegate createMessageBuilderDelegate(); /** * Creates a new interaction message builder delegate. diff --git a/javacord-core/src/main/java/org/javacord/core/entity/message/InteractionMessageBuilderDelegateImpl.java b/javacord-core/src/main/java/org/javacord/core/entity/message/InteractionMessageBuilderDelegateImpl.java index 16ecc9ceff..389ba2f860 100644 --- a/javacord-core/src/main/java/org/javacord/core/entity/message/InteractionMessageBuilderDelegateImpl.java +++ b/javacord-core/src/main/java/org/javacord/core/entity/message/InteractionMessageBuilderDelegateImpl.java @@ -20,7 +20,7 @@ import java.util.NoSuchElementException; import java.util.concurrent.CompletableFuture; -public class InteractionMessageBuilderDelegateImpl extends MessageBuilderDelegateImpl +public class InteractionMessageBuilderDelegateImpl extends MessageBuilderBaseDelegateImpl implements InteractionMessageBuilderDelegate { /** diff --git a/javacord-core/src/main/java/org/javacord/core/entity/message/MessageBuilderDelegateImpl.java b/javacord-core/src/main/java/org/javacord/core/entity/message/MessageBuilderBaseDelegateImpl.java similarity index 72% rename from javacord-core/src/main/java/org/javacord/core/entity/message/MessageBuilderDelegateImpl.java rename to javacord-core/src/main/java/org/javacord/core/entity/message/MessageBuilderBaseDelegateImpl.java index 2f750bb3f3..fd3fbdfc27 100644 --- a/javacord-core/src/main/java/org/javacord/core/entity/message/MessageBuilderDelegateImpl.java +++ b/javacord-core/src/main/java/org/javacord/core/entity/message/MessageBuilderBaseDelegateImpl.java @@ -22,7 +22,7 @@ import org.javacord.api.entity.message.component.HighLevelComponent; import org.javacord.api.entity.message.component.LowLevelComponent; import org.javacord.api.entity.message.embed.EmbedBuilder; -import org.javacord.api.entity.message.internal.MessageBuilderDelegate; +import org.javacord.api.entity.message.internal.MessageBuilderBaseDelegate; import org.javacord.api.entity.message.mention.AllowedMentions; import org.javacord.api.entity.user.User; import org.javacord.api.entity.webhook.IncomingWebhook; @@ -49,25 +49,35 @@ import java.util.concurrent.CompletableFuture; /** - * The implementation of {@link MessageBuilderDelegate}. + * The implementation of {@link MessageBuilderBaseDelegate}. */ -public class MessageBuilderDelegateImpl implements MessageBuilderDelegate { +public class MessageBuilderBaseDelegateImpl implements MessageBuilderBaseDelegate { /** * The logger of this class. */ - private static final Logger logger = LoggerUtil.getLogger(MessageBuilderDelegateImpl.class); + private static final Logger logger = LoggerUtil.getLogger(MessageBuilderBaseDelegateImpl.class); /** * The string builder used to create the message. */ protected final StringBuilder strBuilder = new StringBuilder(); + /** + * True if the content has been changed by the user. + */ + protected boolean contentChanged = false; + /** * The list of embeds of the message. */ protected List embeds = new ArrayList<>(); + /** + * True if embeds have been changed by the user. + */ + protected boolean embedsChanged = false; + /** * If the message should be text to speech or not. */ @@ -83,11 +93,21 @@ public class MessageBuilderDelegateImpl implements MessageBuilderDelegate { */ protected final List attachments = new ArrayList<>(); + /** + * True if the attachments have been changed by the user. + */ + protected boolean attachmentsChanged = false; + /** * A list with all the components which should be added to the message. */ protected final List components = new ArrayList<>(); + /** + * True if the components have been changed by the user. + */ + protected boolean componentsChanged = false; + /** * The MentionsBuilder used to control mention behavior. */ @@ -102,22 +122,25 @@ public class MessageBuilderDelegateImpl implements MessageBuilderDelegate { @Override public void addComponents(HighLevelComponent... highLevelComponents) { this.components.addAll(Arrays.asList(highLevelComponents)); + componentsChanged = true; } @Override public void addActionRow(LowLevelComponent... lowLevelComponents) { this.addComponents(ActionRow.of(lowLevelComponents)); + componentsChanged = true; } @Override public void appendCode(String language, String code) { strBuilder - .append("\n") - .append(MessageDecoration.CODE_LONG.getPrefix()) - .append(language) - .append("\n") - .append(code) - .append(MessageDecoration.CODE_LONG.getSuffix()); + .append("\n") + .append(MessageDecoration.CODE_LONG.getPrefix()) + .append(language) + .append("\n") + .append(code) + .append(MessageDecoration.CODE_LONG.getSuffix()); + contentChanged = true; } @Override @@ -129,33 +152,39 @@ public void append(String message, MessageDecoration... decorations) { for (int i = decorations.length - 1; i >= 0; i--) { strBuilder.append(decorations[i].getSuffix()); } + contentChanged = true; } @Override public void append(Mentionable entity) { strBuilder.append(entity.getMentionTag()); + contentChanged = true; } @Override public void append(Object object) { strBuilder.append(object); + contentChanged = true; } @Override public void appendNewLine() { strBuilder.append("\n"); + contentChanged = true; } @Override public void setContent(String content) { strBuilder.setLength(0); strBuilder.append(content); + contentChanged = true; } @Override public void addEmbed(EmbedBuilder embed) { if (embed != null) { embeds.add(embed); + embedsChanged = true; } } @@ -184,41 +213,53 @@ public void copy(Message message) { this.addComponents(builder.build()); } } + + contentChanged = false; + componentsChanged = false; + attachmentsChanged = false; + embedsChanged = false; } @Override public void removeAllEmbeds() { embeds.clear(); + embedsChanged = true; } @Override public void addEmbeds(EmbedBuilder... embeds) { this.embeds.addAll(Arrays.asList(embeds)); + embedsChanged = true; } @Override public void removeEmbed(EmbedBuilder embed) { this.embeds.remove(embed); + embedsChanged = true; } @Override public void removeEmbeds(EmbedBuilder... embeds) { this.embeds.removeAll(Arrays.asList(embeds)); + embedsChanged = true; } @Override public void removeComponent(int index) { components.remove(index); + componentsChanged = true; } @Override public void removeComponent(HighLevelComponent component) { components.remove(component); + componentsChanged = true; } @Override public void removeAllComponents() { components.clear(); + componentsChanged = true; } @Override @@ -277,6 +318,7 @@ public void addAttachment(BufferedImage image, String fileName) { throw new IllegalArgumentException("image and fileName cannot be null!"); } attachments.add(new FileContainer(image, fileName)); + attachmentsChanged = true; } @Override @@ -285,6 +327,7 @@ public void addAttachment(File file) { throw new IllegalArgumentException("file cannot be null!"); } attachments.add(new FileContainer(file)); + attachmentsChanged = true; } @Override @@ -293,6 +336,7 @@ public void addAttachment(Icon icon) { throw new IllegalArgumentException("icon cannot be null!"); } attachments.add(new FileContainer(icon)); + attachmentsChanged = true; } @Override @@ -301,6 +345,7 @@ public void addAttachment(URL url) { throw new IllegalArgumentException("url cannot be null!"); } attachments.add(new FileContainer(url)); + attachmentsChanged = true; } @Override @@ -309,6 +354,7 @@ public void addAttachment(byte[] bytes, String fileName) { throw new IllegalArgumentException("bytes and fileName cannot be null!"); } attachments.add(new FileContainer(bytes, fileName)); + attachmentsChanged = true; } @Override @@ -317,6 +363,7 @@ public void addAttachment(InputStream stream, String fileName) { throw new IllegalArgumentException("stream and fileName cannot be null!"); } attachments.add(new FileContainer(stream, fileName)); + attachmentsChanged = true; } @Override @@ -325,6 +372,7 @@ public void addAttachmentAsSpoiler(File file) { throw new IllegalArgumentException("file cannot be null!"); } attachments.add(new FileContainer(file, true)); + attachmentsChanged = true; } @Override @@ -333,6 +381,7 @@ public void addAttachmentAsSpoiler(Icon icon) { throw new IllegalArgumentException("icon cannot be null!"); } attachments.add(new FileContainer(icon, true)); + attachmentsChanged = true; } @Override @@ -341,6 +390,7 @@ public void addAttachmentAsSpoiler(URL url) { throw new IllegalArgumentException("url cannot be null!"); } attachments.add(new FileContainer(url, true)); + attachmentsChanged = true; } @Override @@ -399,20 +449,11 @@ public CompletableFuture send(TextChannel channel) { .put("tts", tts); body.putArray("mentions"); - if (allowedMentions != null) { - ((AllowedMentionsImpl) allowedMentions).toJsonNode(body.putObject("allowed_mentions")); - } + prepareAllowedMentions(body); - if (embeds.size() != 0) { - // Discord now supports multiple embeds for Discord bots. - ArrayNode embedsNode = JsonNodeFactory.instance.objectNode().arrayNode(); - for (int i = 0; i < embeds.size() && i < 10; i++) { - embedsNode.add(((EmbedBuilderDelegateImpl) embeds.get(i).getDelegate()).toJsonNode()); - } - body.set("embeds", embedsNode); - } + prepareEmbeds(body, false); - prepareComponents(body); + prepareComponents(body, false); if (nonce != null) { body.put("nonce", nonce); @@ -424,55 +465,24 @@ public CompletableFuture send(TextChannel channel) { RestRequest request = new RestRequest(channel.getApi(), RestMethod.POST, RestEndpoint.MESSAGE) .setUrlParameters(channel.getIdAsString()); - if (!attachments.isEmpty() || (embeds.size() > 0 && embeds.get(0).requiresAttachments())) { - CompletableFuture future = new CompletableFuture<>(); - // We access files etc. so this should be async - channel.getApi().getThreadPool().getExecutorService().submit(() -> { - try { - List tempAttachments = new ArrayList<>(attachments); - // Add the attachments required for the embeds - for (EmbedBuilder embed : embeds) { - tempAttachments.addAll( - ((EmbedBuilderDelegateImpl) embed.getDelegate()).getRequiredAttachments()); - } - - addMultipartBodyToRequest(request, body, tempAttachments, channel.getApi()); - - request.execute(result -> ((DiscordApiImpl) channel.getApi()) - .getOrCreateMessage(channel, result.getJsonBody())) - .whenComplete((message, throwable) -> { - if (throwable != null) { - future.completeExceptionally(throwable); - } else { - future.complete(message); - } - }); - } catch (Throwable t) { - future.completeExceptionally(t); - } - }); - return future; - } else { - request.setBody(body); - return request.execute(result -> ((DiscordApiImpl) channel.getApi()) - .getOrCreateMessage(channel, result.getJsonBody())); - } + return checkForAttachmentsAndExecuteRequest(channel, body, request, false); } @Override public CompletableFuture send(IncomingWebhook webhook) { - return send(webhook.getIdAsString(), webhook.getToken(), null, null, true, webhook.getApi()); + return send(webhook.getIdAsString(), webhook.getToken(), + null, null, true, webhook.getApi()); } /** * Send a message to an incoming webhook. * - * @param webhookId The id of the webhook to send the message to + * @param webhookId The id of the webhook to send the message to * @param webhookToken The token of the webhook to send the message to - * @param displayName The display name the webhook should use - * @param avatarUrl The avatar the webhook should use - * @param wait If the completable future will be completed - * @param api The api instance needed to send and return the message + * @param displayName The display name the webhook should use + * @param avatarUrl The avatar the webhook should use + * @param wait If the completable future will be completed + * @param api The api instance needed to send and return the message * @return The sent message */ protected CompletableFuture send(String webhookId, String webhookToken, String displayName, URL avatarUrl, @@ -487,14 +497,6 @@ protected CompletableFuture send(String webhookId, String webhookToken, body.put("avatar_url", avatarUrl.toExternalForm()); } - if (embeds.size() != 0) { - ArrayNode embedsNode = JsonNodeFactory.instance.objectNode().arrayNode(); - for (int i = 0; i < embeds.size() && i < 10; i++) { - embedsNode.add(((EmbedBuilderDelegateImpl) embeds.get(i).getDelegate()).toJsonNode()); - } - body.set("embeds", embedsNode); - } - prepareComponents(body); if (strBuilder.length() != 0) { @@ -502,9 +504,9 @@ protected CompletableFuture send(String webhookId, String webhookToken, } RestRequest request = - new RestRequest(api, RestMethod.POST, RestEndpoint.WEBHOOK_SEND) - .addQueryParameter("wait", Boolean.toString(wait)) - .setUrlParameters(webhookId, webhookToken); + new RestRequest(api, RestMethod.POST, RestEndpoint.WEBHOOK_SEND) + .addQueryParameter("wait", Boolean.toString(wait)) + .setUrlParameters(webhookId, webhookToken); CompletableFuture future = new CompletableFuture<>(); if (!attachments.isEmpty() || (embeds.size() > 0 && embeds.get(0).requiresAttachments())) { // We access files etc. so this should be async @@ -540,9 +542,9 @@ public CompletableFuture sendWithWebhook(DiscordApi api, String webhook * Method which executes the webhook rest request. * * @param request The rest request to execute - * @param wait If discord sends us a response - * @param future The future to complete - * @param api The api instance needed to create the message + * @param wait If discord sends us a response + * @param future The future to complete + * @param api The api instance needed to create the message */ private static void executeWebhookRest(RestRequest request, boolean wait, CompletableFuture future, DiscordApi api) { @@ -567,17 +569,97 @@ private static void executeWebhookRest(RestRequest request, boolean wai }); } + @Override + public CompletableFuture edit(Message message, boolean updateAll) { + ObjectNode body = JsonNodeFactory.instance.objectNode(); + + if (updateAll || contentChanged) { + body.put("content", strBuilder.toString()); + } + + prepareAllowedMentions(body); + + prepareEmbeds(body, updateAll || embedsChanged); + + prepareComponents(body, updateAll || componentsChanged); + + RestRequest request = new RestRequest(message.getApi(), + RestMethod.PATCH, RestEndpoint.MESSAGE) + .setUrlParameters(Long.toUnsignedString(message.getChannel().getId()), + Long.toUnsignedString(message.getId())); + + if (updateAll || attachmentsChanged) { + return checkForAttachmentsAndExecuteRequest(message.getChannel(), body, request, true); + } else { + return executeRequestWithoutAttachments(message.getChannel(), body, request); + } + } + //////////////////////////////////////////////////////////////////////////////// // Internal MessageBuilder utility methods //////////////////////////////////////////////////////////////////////////////// + private CompletableFuture checkForAttachmentsAndExecuteRequest(TextChannel channel, + ObjectNode body, + RestRequest request, + boolean clearAttachmentsIfAppropriate) { + if (attachments.isEmpty() && (embeds.size() == 0 || !embeds.get(0).requiresAttachments())) { + if (clearAttachmentsIfAppropriate) { + body.set("attachments", JsonNodeFactory.instance.objectNode().arrayNode()); + } + return executeRequestWithoutAttachments(channel, body, request); + } + + CompletableFuture future = new CompletableFuture<>(); + // We access files etc. so this should be async + channel.getApi().getThreadPool().getExecutorService().submit(() -> { + try { + List tempAttachments = new ArrayList<>(attachments); + // Add the attachments required for the embeds + for (EmbedBuilder embed : embeds) { + tempAttachments.addAll( + ((EmbedBuilderDelegateImpl) embed.getDelegate()).getRequiredAttachments()); + } + + addMultipartBodyToRequest(request, body, tempAttachments, channel.getApi()); + + request.execute(result -> ((DiscordApiImpl) channel.getApi()) + .getOrCreateMessage(channel, result.getJsonBody())) + .whenComplete((newMessage, throwable) -> { + if (throwable != null) { + future.completeExceptionally(throwable); + } else { + future.complete(newMessage); + } + }); + } catch (Throwable t) { + future.completeExceptionally(t); + } + }); + return future; + } + + private CompletableFuture executeRequestWithoutAttachments(TextChannel channel, + ObjectNode body, + RestRequest request) { + request.setBody(body); + return request.execute(result -> ((DiscordApiImpl) channel.getApi()) + .getOrCreateMessage(channel, result.getJsonBody())); + } + + private void prepareAllowedMentions(ObjectNode body) { + if (allowedMentions != null) { + ((AllowedMentionsImpl) allowedMentions).toJsonNode(body.putObject("allowed_mentions")); + } + } + /** * Method which creates and adds a MultipartBody to a RestRequest. * - * @param request The RestRequest to add the MultipartBody to - * @param body The body to use as base for the MultipartBody + * @param request The RestRequest to add the MultipartBody to + * @param body The body to use as base for the MultipartBody * @param attachments The List of FileContainers to add as attachments - * @param api The api instance needed to add the attachments + * @param api The api instance needed to add the attachments */ protected void addMultipartBodyToRequest(RestRequest request, ObjectNode body, List attachments, DiscordApi api) { @@ -606,10 +688,12 @@ protected void prepareCommonWebhookMessageBodyParts(ObjectNode body) { if (strBuilder.length() != 0) { body.put("content", strBuilder.toString()); } - if (allowedMentions != null) { - ((AllowedMentionsImpl) allowedMentions).toJsonNode(body.putObject("allowed_mentions")); - } - if (embeds.size() != 0) { + prepareAllowedMentions(body); + prepareEmbeds(body, false); + } + + private void prepareEmbeds(ObjectNode body, boolean evenIfEmpty) { + if (embeds.size() != 0 || evenIfEmpty) { ArrayNode embedsNode = JsonNodeFactory.instance.objectNode().arrayNode(); for (EmbedBuilder embed : embeds) { embedsNode.add(((EmbedBuilderDelegateImpl) embed.getDelegate()).toJsonNode()); diff --git a/javacord-core/src/main/java/org/javacord/core/entity/message/WebhookMessageBuilderDelegateImpl.java b/javacord-core/src/main/java/org/javacord/core/entity/message/WebhookMessageBuilderDelegateImpl.java index fe46fd72c6..683fed7408 100644 --- a/javacord-core/src/main/java/org/javacord/core/entity/message/WebhookMessageBuilderDelegateImpl.java +++ b/javacord-core/src/main/java/org/javacord/core/entity/message/WebhookMessageBuilderDelegateImpl.java @@ -14,7 +14,7 @@ /** * The implementation of {@link WebhookMessageBuilderDelegate}. */ -public class WebhookMessageBuilderDelegateImpl extends MessageBuilderDelegateImpl +public class WebhookMessageBuilderDelegateImpl extends MessageBuilderBaseDelegateImpl implements WebhookMessageBuilderDelegate { /** diff --git a/javacord-core/src/main/java/org/javacord/core/util/DelegateFactoryDelegateImpl.java b/javacord-core/src/main/java/org/javacord/core/util/DelegateFactoryDelegateImpl.java index ddd4d966b2..834780381c 100644 --- a/javacord-core/src/main/java/org/javacord/core/util/DelegateFactoryDelegateImpl.java +++ b/javacord-core/src/main/java/org/javacord/core/util/DelegateFactoryDelegateImpl.java @@ -22,7 +22,7 @@ import org.javacord.api.entity.message.component.internal.SelectMenuOptionBuilderDelegate; import org.javacord.api.entity.message.embed.internal.EmbedBuilderDelegate; import org.javacord.api.entity.message.internal.InteractionMessageBuilderDelegate; -import org.javacord.api.entity.message.internal.MessageBuilderDelegate; +import org.javacord.api.entity.message.internal.MessageBuilderBaseDelegate; import org.javacord.api.entity.message.internal.WebhookMessageBuilderDelegate; import org.javacord.api.entity.message.mention.internal.AllowedMentionsBuilderDelegate; import org.javacord.api.entity.permission.Permissions; @@ -62,7 +62,7 @@ import org.javacord.core.entity.emoji.CustomEmojiBuilderDelegateImpl; import org.javacord.core.entity.emoji.CustomEmojiUpdaterDelegateImpl; import org.javacord.core.entity.message.InteractionMessageBuilderDelegateImpl; -import org.javacord.core.entity.message.MessageBuilderDelegateImpl; +import org.javacord.core.entity.message.MessageBuilderBaseDelegateImpl; import org.javacord.core.entity.message.WebhookMessageBuilderDelegateImpl; import org.javacord.core.entity.message.component.internal.ActionRowBuilderDelegateImpl; import org.javacord.core.entity.message.component.internal.ButtonBuilderDelegateImpl; @@ -109,8 +109,8 @@ public AllowedMentionsBuilderDelegate createAllowedMentionsBuilderDelegate() { } @Override - public MessageBuilderDelegate createMessageBuilderDelegate() { - return new MessageBuilderDelegateImpl(); + public MessageBuilderBaseDelegate createMessageBuilderDelegate() { + return new MessageBuilderBaseDelegateImpl(); } @Override