From b4117b4d603ed165c2ba6d00b400f17bfa666a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Fri, 2 Oct 2020 02:55:28 +0200 Subject: [PATCH 01/14] Implement inline replies feature --- .../net/dv8tion/jda/api/entities/Message.java | 3 +++ .../requests/restaction/MessageAction.java | 19 +++++++++++++++++++ .../internal/entities/AbstractMessage.java | 7 +++++++ .../jda/internal/entities/EntityBuilder.java | 5 ++++- .../internal/entities/ReceivedMessage.java | 10 +++++++++- .../restaction/MessageActionImpl.java | 11 +++++++++++ 6 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/entities/Message.java b/src/main/java/net/dv8tion/jda/api/entities/Message.java index 1a4d3d0ced..94b6b99289 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Message.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Message.java @@ -199,6 +199,9 @@ public interface Message extends ISnowflake, Formattable "(?:\\?\\S*)?(?:#\\S*)?", // Useless query or URN appendix Pattern.CASE_INSENSITIVE); + @Nullable + Message getReferencedMessage(); + /** * An immutable list of all mentioned {@link net.dv8tion.jda.api.entities.User Users}. *
If no user was mentioned, this list is empty. Elements are sorted in order of appearance. This only diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java index f533bc916c..f688e36152 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java @@ -22,6 +22,7 @@ import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.utils.AttachmentOption; +import net.dv8tion.jda.api.utils.MiscUtil; import net.dv8tion.jda.internal.requests.restaction.MessageActionImpl; import net.dv8tion.jda.internal.utils.Checks; @@ -181,6 +182,24 @@ static EnumSet getDefaultMentions() @CheckReturnValue MessageAction apply(@Nullable final Message message); + @Nonnull + @CheckReturnValue + MessageAction referenceById(long messageId); + + @Nonnull + @CheckReturnValue + default MessageAction referenceById(@Nonnull String messageId) + { + return referenceById(MiscUtil.parseSnowflake(messageId)); + } + + @Nonnull + @CheckReturnValue + default MessageAction reference(@Nonnull Message message) + { + return referenceById(message.getIdLong()); + } + /** * Enable/Disable Text-To-Speech for the resulting message. *
This is only relevant to MessageActions that are not {@code isEdit() == true}! diff --git a/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java b/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java index 0db4a965ee..9000caf7fe 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java @@ -106,6 +106,13 @@ protected void appendFormat(Formatter formatter, int width, int precision, boole } } + @Override + public Message getReferencedMessage() + { + unsupported(); + return null; + } + @Nonnull @Override public Bag getMentionedUsersBag() diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java index e3ea42044d..fe6c9db218 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -1139,10 +1139,13 @@ public Message createMessage(DataObject jsonObject, MessageChannel chan, boolean MessageType type = MessageType.fromId(jsonObject.getInt("type")); ReceivedMessage message; + Message referencedMessage = null; + if (!jsonObject.isNull("referenced_message")) + referencedMessage = createMessage(jsonObject.getObject("referenced_message"), chan, false); switch (type) { case DEFAULT: - message = new ReceivedMessage(id, chan, type, fromWebhook, + message = new ReceivedMessage(id, chan, type, referencedMessage, fromWebhook, mentionsEveryone, mentionedUsers, mentionedRoles, tts, pinned, content, nonce, user, member, activity, editTime, reactions, attachments, embeds, flags); break; diff --git a/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java b/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java index d67e56e1c6..8ffb3ffc6f 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java @@ -55,6 +55,7 @@ public class ReceivedMessage extends AbstractMessage protected final long id; protected final MessageType type; protected final MessageChannel channel; + protected final Message referencedMessage; protected final boolean fromWebhook; protected final boolean mentionsEveryone; protected final boolean pinned; @@ -81,7 +82,7 @@ public class ReceivedMessage extends AbstractMessage protected List invites = null; public ReceivedMessage( - long id, MessageChannel channel, MessageType type, + long id, MessageChannel channel, MessageType type, Message referencedMessage, boolean fromWebhook, boolean mentionsEveryone, TLongSet mentionedUsers, TLongSet mentionedRoles, boolean tts, boolean pinned, String content, String nonce, User author, Member member, MessageActivity activity, OffsetDateTime editTime, List reactions, List attachments, List embeds, int flags) @@ -89,6 +90,7 @@ public ReceivedMessage( super(content, nonce, tts); this.id = id; this.channel = channel; + this.referencedMessage = referencedMessage; this.type = type; this.api = (channel != null) ? (JDAImpl) channel.getJDA() : null; this.fromWebhook = fromWebhook; @@ -113,6 +115,12 @@ public JDA getJDA() return api; } + @Override + public Message getReferencedMessage() + { + return referencedMessage; + } + @Override public boolean isPinned() { diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java index a0ac3df566..4bfb5c4c77 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java @@ -62,6 +62,7 @@ public class MessageActionImpl extends RestActionImpl implements Messag protected EnumSet allowedMentions; protected Set mentionableUsers = new HashSet<>(); protected Set mentionableRoles = new HashSet<>(); + protected long messageReference; public static void setDefaultMentions(@Nullable Collection allowedMentions) { @@ -185,6 +186,14 @@ public MessageActionImpl apply(final Message message) return content(content).tts(message.isTTS()); } + @Nonnull + @Override + public MessageActionImpl referenceById(long messageId) + { + messageReference = messageId; + return this; + } + @Nonnull @Override @CheckReturnValue @@ -480,6 +489,8 @@ protected DataObject getJSON() if (nonce != null) obj.put("nonce", nonce); } + if (messageReference != 0) + obj.put("message_reference", messageReference); obj.put("tts", tts); if (allowedMentions != null || !mentionableUsers.isEmpty() || !mentionableRoles.isEmpty()) { From 4a626ff1bc4f0a46cc377d5320db002e6b61e592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Fri, 2 Oct 2020 02:58:41 +0200 Subject: [PATCH 02/14] Fix compiler error --- .../java/net/dv8tion/jda/internal/entities/SystemMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/SystemMessage.java b/src/main/java/net/dv8tion/jda/internal/entities/SystemMessage.java index db57654561..6fe514c563 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/SystemMessage.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/SystemMessage.java @@ -34,7 +34,7 @@ public SystemMessage( String content, String nonce, User author, Member member, MessageActivity activity, OffsetDateTime editTime, List reactions, List attachments, List embeds, int flags) { - super(id, channel, type, fromWebhook, mentionsEveryone, mentionedUsers, mentionedRoles, + super(id, channel, type, null, fromWebhook, mentionsEveryone, mentionedUsers, mentionedRoles, tts, pinned, content, nonce, author, member, activity, editTime, reactions, attachments, embeds, flags); } From e58d5c3f13462eba1d4799cba473647fd5a59837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Fri, 2 Oct 2020 03:11:14 +0200 Subject: [PATCH 03/14] Handle type 19 INLINE_REPLY --- src/main/java/net/dv8tion/jda/api/entities/MessageType.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/dv8tion/jda/api/entities/MessageType.java b/src/main/java/net/dv8tion/jda/api/entities/MessageType.java index ca94ba98ee..2088ac46cb 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/MessageType.java +++ b/src/main/java/net/dv8tion/jda/api/entities/MessageType.java @@ -122,6 +122,8 @@ public int getId() @Nonnull public static MessageType fromId(int id) { + if (id == 19) + return DEFAULT; // we don't use the INLINE_REPLY type because its useless (just check for message reference) for (MessageType type : values()) { if (type.id == id) From 50ce905649898d5294b9712c5476941491767906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Fri, 2 Oct 2020 03:27:53 +0200 Subject: [PATCH 04/14] Improve handling of inline_reply type --- src/main/java/net/dv8tion/jda/api/entities/MessageType.java | 2 ++ .../net/dv8tion/jda/internal/entities/EntityBuilder.java | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/net/dv8tion/jda/api/entities/MessageType.java b/src/main/java/net/dv8tion/jda/api/entities/MessageType.java index 2088ac46cb..9ac7315050 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/MessageType.java +++ b/src/main/java/net/dv8tion/jda/api/entities/MessageType.java @@ -88,6 +88,8 @@ public enum MessageType */ CHANNEL_FOLLOW_ADD(12), + INLINE_REPLY(19), + /** * Unknown MessageType. */ diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java index fe6c9db218..bdda981920 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -1141,9 +1141,14 @@ public Message createMessage(DataObject jsonObject, MessageChannel chan, boolean ReceivedMessage message; Message referencedMessage = null; if (!jsonObject.isNull("referenced_message")) + { referencedMessage = createMessage(jsonObject.getObject("referenced_message"), chan, false); + if (type == MessageType.DEFAULT) + type = MessageType.INLINE_REPLY; + } switch (type) { + case INLINE_REPLY: case DEFAULT: message = new ReceivedMessage(id, chan, type, referencedMessage, fromWebhook, mentionsEveryone, mentionedUsers, mentionedRoles, tts, pinned, From fdecb79d52cde6a0f5205003160bf25a9677ef69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Fri, 2 Oct 2020 03:37:08 +0200 Subject: [PATCH 05/14] Remove special casing --- src/main/java/net/dv8tion/jda/api/entities/MessageType.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/entities/MessageType.java b/src/main/java/net/dv8tion/jda/api/entities/MessageType.java index 9ac7315050..77faf2a49c 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/MessageType.java +++ b/src/main/java/net/dv8tion/jda/api/entities/MessageType.java @@ -124,8 +124,6 @@ public int getId() @Nonnull public static MessageType fromId(int id) { - if (id == 19) - return DEFAULT; // we don't use the INLINE_REPLY type because its useless (just check for message reference) for (MessageType type : values()) { if (type.id == id) From 6e79a3634a0220f529cf4845e2df4dc7dc4aeb9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Mon, 5 Oct 2020 01:38:33 +0200 Subject: [PATCH 06/14] Fix message_reference in MessageAction json --- .../jda/internal/requests/restaction/MessageActionImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java index 4bfb5c4c77..3e2bdc61a3 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java @@ -490,7 +490,9 @@ protected DataObject getJSON() obj.put("nonce", nonce); } if (messageReference != 0) - obj.put("message_reference", messageReference); + obj.put("message_reference", DataObject.empty() + .put("message_id", messageReference) + .put("channel_id", channel.getId())); obj.put("tts", tts); if (allowedMentions != null || !mentionableUsers.isEmpty() || !mentionableRoles.isEmpty()) { From 302c342a2f72b0a22434537e6ef407c8a119c37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Mon, 26 Oct 2020 14:49:23 +0100 Subject: [PATCH 07/14] Add Message#reply and Message#replyFormat --- .../net/dv8tion/jda/api/entities/Message.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/main/java/net/dv8tion/jda/api/entities/Message.java b/src/main/java/net/dv8tion/jda/api/entities/Message.java index 94b6b99289..3910635503 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Message.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Message.java @@ -951,6 +951,62 @@ default boolean isFromGuild() @CheckReturnValue MessageAction editMessage(@Nonnull Message newContent); + @Nonnull + @CheckReturnValue + default MessageAction reply(@Nonnull CharSequence content) + { + return getChannel().sendMessage(content).reference(this); + } + + @Nonnull + @CheckReturnValue + default MessageAction reply(@Nonnull MessageEmbed content) + { + return getChannel().sendMessage(content).reference(this); + } + + @Nonnull + @CheckReturnValue + default MessageAction reply(@Nonnull Message content) + { + return getChannel().sendMessage(content).reference(this); + } + + @Nonnull + @CheckReturnValue + default MessageAction replyFormat(@Nonnull String format, @Nonnull Object... args) + { + return getChannel().sendMessageFormat(format, args).reference(this); + } + + @Nonnull + @CheckReturnValue + default MessageAction reply(@Nonnull File file) + { + return getChannel().sendFile(file).reference(this); + } + + @Nonnull + @CheckReturnValue + default MessageAction reply(@Nonnull File data, @Nonnull String name) + { + return getChannel().sendFile(data, name).reference(this); + } + + @Nonnull + @CheckReturnValue + default MessageAction reply(@Nonnull InputStream data, @Nonnull String name) + { + return getChannel().sendFile(data, name).reference(this); + } + + @Nonnull + @CheckReturnValue + default MessageAction reply(@Nonnull byte[] data, @Nonnull String name) + { + return getChannel().sendFile(data, name).reference(this); + } + /** * Deletes this Message from Discord. *
If this Message was not sent by the currently logged in account, then this will fail unless the Message is from From 63e9ca0b8fa07a6f04115dc1022cebe843362411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sun, 15 Nov 2020 19:19:46 +0100 Subject: [PATCH 08/14] Add some documentation and support new mention flag --- .../net/dv8tion/jda/api/entities/Message.java | 138 +++++++++++++++++- .../requests/restaction/MessageAction.java | 57 ++++++++ .../internal/entities/AbstractMessage.java | 1 - .../restaction/MessageActionImpl.java | 21 ++- 4 files changed, 203 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/entities/Message.java b/src/main/java/net/dv8tion/jda/api/entities/Message.java index 3910635503..61cfa43f1b 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Message.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Message.java @@ -23,6 +23,7 @@ import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; import net.dv8tion.jda.api.requests.restaction.MessageAction; import net.dv8tion.jda.api.requests.restaction.pagination.ReactionPaginationAction; +import net.dv8tion.jda.api.utils.AttachmentOption; import net.dv8tion.jda.internal.JDAImpl; import net.dv8tion.jda.internal.requests.FunctionalCallback; import net.dv8tion.jda.internal.requests.Requester; @@ -199,6 +200,15 @@ public interface Message extends ISnowflake, Formattable "(?:\\?\\S*)?(?:#\\S*)?", // Useless query or URN appendix Pattern.CASE_INSENSITIVE); + /** + * Referenced message. + * + *

This will have different meaning depending on the {@link #getType() type} of message. + * Usually, this is a {@link MessageType#INLINE_REPLY INLINE_REPLY} reference. + * This can be null even if the type is {@link MessageType#INLINE_REPLY INLINE_REPLY}, when the message it references doesn't exist or discord wasn't able to resolve it in time. + * + * @return The referenced message, or null + */ @Nullable Message getReferencedMessage(); @@ -951,6 +961,18 @@ default boolean isFromGuild() @CheckReturnValue MessageAction editMessage(@Nonnull Message newContent); + /** + * Replies and references this message. + *
This is identical to {@code message.getChannel().sendMessage(content).reference(message)}. + * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. + * + *

For further info, see {@link MessageChannel#sendMessage(CharSequence)} and {@link MessageAction#reference(Message)}. + * + * @param content + * The content of the reply message + * + * @return {@link MessageAction} Providing the {@link Message} created from this upload. + */ @Nonnull @CheckReturnValue default MessageAction reply(@Nonnull CharSequence content) @@ -958,6 +980,18 @@ default MessageAction reply(@Nonnull CharSequence content) return getChannel().sendMessage(content).reference(this); } + /** + * Replies and references this message. + *
This is identical to {@code message.getChannel().sendMessage(content).reference(message)}. + * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. + * + *

For further info, see {@link MessageChannel#sendMessage(CharSequence)} and {@link MessageAction#reference(Message)}. + * + * @param content + * The content of the reply message + * + * @return {@link MessageAction} Providing the {@link Message} created from this upload. + */ @Nonnull @CheckReturnValue default MessageAction reply(@Nonnull MessageEmbed content) @@ -965,6 +999,18 @@ default MessageAction reply(@Nonnull MessageEmbed content) return getChannel().sendMessage(content).reference(this); } + /** + * Replies and references this message. + *
This is identical to {@code message.getChannel().sendMessage(content).reference(message)}. + * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. + * + *

For further info, see {@link MessageChannel#sendMessage(CharSequence)} and {@link MessageAction#reference(Message)}. + * + * @param content + * The content of the reply message + * + * @return {@link MessageAction} Providing the {@link Message} created from this upload. + */ @Nonnull @CheckReturnValue default MessageAction reply(@Nonnull Message content) @@ -972,6 +1018,20 @@ default MessageAction reply(@Nonnull Message content) return getChannel().sendMessage(content).reference(this); } + /** + * Replies and references this message. + *
This is identical to {@code message.getChannel().sendMessageFormat(content, args).reference(message)}. + * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. + * + *

For further info, see {@link MessageChannel#sendMessage(CharSequence)} and {@link MessageAction#reference(Message)}. + * + * @param format + * The string that should be formatted, if this is null or empty the content of the Message would be empty and cause a builder exception. + * @param args + * The arguments for your format + * + * @return {@link MessageAction} Providing the {@link Message} created from this upload. + */ @Nonnull @CheckReturnValue default MessageAction replyFormat(@Nonnull String format, @Nonnull Object... args) @@ -979,32 +1039,94 @@ default MessageAction replyFormat(@Nonnull String format, @Nonnull Object... arg return getChannel().sendMessageFormat(format, args).reference(this); } + /** + * Replies and references this message. + *
This is identical to {@code message.getChannel().sendFile(file, options).reference(message)}. + * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. + * + *

For further info, see {@link MessageChannel#sendFile(File, net.dv8tion.jda.api.utils.AttachmentOption...)} and {@link MessageAction#reference(Message)}. + * + * @param file + * The file to upload to the channel in the reply + * @param options + * Possible options to apply to this attachment, such as marking it as spoiler image + * + * @return {@link MessageAction} Providing the {@link Message} created from this upload. + */ @Nonnull @CheckReturnValue - default MessageAction reply(@Nonnull File file) + default MessageAction reply(@Nonnull File file, @Nonnull AttachmentOption... options) { - return getChannel().sendFile(file).reference(this); + return getChannel().sendFile(file, options).reference(this); } + /** + * Replies and references this message. + *
This is identical to {@code message.getChannel().sendFile(data, name, options).reference(message)}. + * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. + * + *

For further info, see {@link MessageChannel#sendFile(File, net.dv8tion.jda.api.utils.AttachmentOption...)} and {@link MessageAction#reference(Message)}. + * + * @param data + * The data to upload to the channel in the reply + * @param name + * The name that should be sent to discord + * @param options + * Possible options to apply to this attachment, such as marking it as spoiler image + * + * @return {@link MessageAction} Providing the {@link Message} created from this upload. + */ @Nonnull @CheckReturnValue - default MessageAction reply(@Nonnull File data, @Nonnull String name) + default MessageAction reply(@Nonnull File data, @Nonnull String name, @Nonnull AttachmentOption... options) { - return getChannel().sendFile(data, name).reference(this); + return getChannel().sendFile(data, name, options).reference(this); } + /** + * Replies and references this message. + *
This is identical to {@code message.getChannel().sendFile(data, name, options).reference(message)}. + * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. + * + *

For further info, see {@link MessageChannel#sendFile(File, net.dv8tion.jda.api.utils.AttachmentOption...)} and {@link MessageAction#reference(Message)}. + * + * @param data + * The data to upload to the channel in the reply + * @param name + * The name that should be sent to discord + * @param options + * Possible options to apply to this attachment, such as marking it as spoiler image + * + * @return {@link MessageAction} Providing the {@link Message} created from this upload. + */ @Nonnull @CheckReturnValue - default MessageAction reply(@Nonnull InputStream data, @Nonnull String name) + default MessageAction reply(@Nonnull InputStream data, @Nonnull String name, @Nonnull AttachmentOption... options) { - return getChannel().sendFile(data, name).reference(this); + return getChannel().sendFile(data, name, options).reference(this); } + /** + * Replies and references this message. + *
This is identical to {@code message.getChannel().sendFile(data, name, options).reference(message)}. + * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. + * + *

For further info, see {@link MessageChannel#sendFile(File, net.dv8tion.jda.api.utils.AttachmentOption...)} and {@link MessageAction#reference(Message)}. + * + * @param data + * The data to upload to the channel in the reply + * @param name + * The name that should be sent to discord + * @param options + * Possible options to apply to this attachment, such as marking it as spoiler image + * + * @return {@link MessageAction} Providing the {@link Message} created from this upload. + */ @Nonnull @CheckReturnValue - default MessageAction reply(@Nonnull byte[] data, @Nonnull String name) + default MessageAction reply(@Nonnull byte[] data, @Nonnull String name, @Nonnull AttachmentOption... options) { - return getChannel().sendFile(data, name).reference(this); + return getChannel().sendFile(data, name, options).reference(this); } /** diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java index f688e36152..85e35b3490 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java @@ -16,6 +16,7 @@ package net.dv8tion.jda.api.requests.restaction; +import net.dv8tion.jda.api.MessageBuilder; import net.dv8tion.jda.api.entities.IMentionable; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageChannel; @@ -182,10 +183,36 @@ static EnumSet getDefaultMentions() @CheckReturnValue MessageAction apply(@Nullable final Message message); + /** + * Make the message a reply to the referenced message. + *
You can only reply to messages from the same channel! + * + *

This requires {@link net.dv8tion.jda.api.Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY} in the channel! + * + * @param messageId + * The target message + * + * @return Updated MessageAction for chaining convenience + */ @Nonnull @CheckReturnValue MessageAction referenceById(long messageId); + /** + * Make the message a reply to the referenced message. + *
You can only reply to messages from the same channel! + * + *

This requires {@link net.dv8tion.jda.api.Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY} in the channel! + * You cannot reply to system messages (such as {@link net.dv8tion.jda.api.entities.MessageType#CHANNEL_PINNED_ADD CHANNEL_PINNED_ADD} and similar). + * + * @param messageId + * The target message + * + * @throws IllegalArgumentException + * If the provided ID is null or not a valid snowflake + * + * @return Updated MessageAction for chaining convenience + */ @Nonnull @CheckReturnValue default MessageAction referenceById(@Nonnull String messageId) @@ -193,13 +220,43 @@ default MessageAction referenceById(@Nonnull String messageId) return referenceById(MiscUtil.parseSnowflake(messageId)); } + /** + * Make the message a reply to the referenced message. + *
You can only reply to messages from the same channel! + * + *

This requires {@link net.dv8tion.jda.api.Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY} in the channel! + * + * @param message + * The target message + * + * @throws IllegalArgumentException + * If the provided message is null + * @throws UnsupportedOperationException + * If the provided message is from a {@link MessageBuilder} + * + * @return Updated MessageAction for chaining convenience + */ @Nonnull @CheckReturnValue default MessageAction reference(@Nonnull Message message) { + Checks.notNull(message, "Message"); return referenceById(message.getIdLong()); } + /** + * Whether to mention the used, when replying to a message. + *
This only matters in combination with {@link #reference(Message)} and {@link #referenceById(long)}! + * + * @param mention + * True, to mention the author if the referenced message + * + * @return Updated MessageAction for chaining convenience + */ + @Nonnull + @CheckReturnValue + MessageAction mentionRepliedUser(boolean mention); + /** * Enable/Disable Text-To-Speech for the resulting message. *
This is only relevant to MessageActions that are not {@code isEdit() == true}! diff --git a/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java b/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java index 9000caf7fe..b8ae9466f7 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java @@ -109,7 +109,6 @@ protected void appendFormat(Formatter formatter, int width, int precision, boole @Override public Message getReferencedMessage() { - unsupported(); return null; } diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java index 3e2bdc61a3..d6aeb9074d 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java @@ -59,6 +59,7 @@ public class MessageActionImpl extends RestActionImpl implements Messag protected MessageEmbed embed = null; protected String nonce = null; protected boolean tts = false, override = false; + protected boolean mentionRepliedUser = true; protected EnumSet allowedMentions; protected Set mentionableUsers = new HashSet<>(); protected Set mentionableRoles = new HashSet<>(); @@ -194,6 +195,14 @@ public MessageActionImpl referenceById(long messageId) return this; } + @Nonnull + @Override + public MessageAction mentionRepliedUser(boolean mention) + { + mentionRepliedUser = mention; + return this; + } + @Nonnull @Override @CheckReturnValue @@ -490,14 +499,14 @@ protected DataObject getJSON() obj.put("nonce", nonce); } if (messageReference != 0) + { obj.put("message_reference", DataObject.empty() - .put("message_id", messageReference) - .put("channel_id", channel.getId())); + .put("message_id", messageReference) + .put("channel_id", channel.getId())); + } obj.put("tts", tts); - if (allowedMentions != null || !mentionableUsers.isEmpty() || !mentionableRoles.isEmpty()) - { + if (messageReference != 0L || allowedMentions != null || !mentionableUsers.isEmpty() || !mentionableRoles.isEmpty()) obj.put("allowed_mentions", getAllowedMentionsObj()); - } return obj; } @@ -526,6 +535,8 @@ protected DataObject getAllowedMentionsObj() parsable.remove(Message.MentionType.ROLE.getParseKey()); allowedMentionsObj.put("roles", DataArray.fromCollection(mentionableRoles)); } + if (messageReference != 0L) + allowedMentionsObj.put("replied_user", mentionRepliedUser); return allowedMentionsObj.put("parse", parsable); } From 450476689e24a629694a3cd665260a912b01c3eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sun, 15 Nov 2020 19:21:59 +0100 Subject: [PATCH 09/14] Add docs to new message type --- src/main/java/net/dv8tion/jda/api/entities/MessageType.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/dv8tion/jda/api/entities/MessageType.java b/src/main/java/net/dv8tion/jda/api/entities/MessageType.java index 77faf2a49c..6030574ddf 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/MessageType.java +++ b/src/main/java/net/dv8tion/jda/api/entities/MessageType.java @@ -88,6 +88,9 @@ public enum MessageType */ CHANNEL_FOLLOW_ADD(12), + /** + * Reply to another message. This usually comes with a {@link Message#getReferencedMessage() referenced message}. + */ INLINE_REPLY(19), /** From 6ba39a579ba5ea79e69e494fb7bb1a0aba4a9cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Mon, 16 Nov 2020 20:13:01 +0100 Subject: [PATCH 10/14] Improve documentation --- .../java/net/dv8tion/jda/api/entities/Message.java | 12 ++++++------ .../jda/api/requests/restaction/MessageAction.java | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/entities/Message.java b/src/main/java/net/dv8tion/jda/api/entities/Message.java index 61cfa43f1b..6349f369a1 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Message.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Message.java @@ -985,7 +985,7 @@ default MessageAction reply(@Nonnull CharSequence content) *
This is identical to {@code message.getChannel().sendMessage(content).reference(message)}. * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. * - *

For further info, see {@link MessageChannel#sendMessage(CharSequence)} and {@link MessageAction#reference(Message)}. + *

For further info, see {@link MessageChannel#sendMessage(MessageEmbed)} and {@link MessageAction#reference(Message)}. * * @param content * The content of the reply message @@ -1004,7 +1004,7 @@ default MessageAction reply(@Nonnull MessageEmbed content) *
This is identical to {@code message.getChannel().sendMessage(content).reference(message)}. * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. * - *

For further info, see {@link MessageChannel#sendMessage(CharSequence)} and {@link MessageAction#reference(Message)}. + *

For further info, see {@link MessageChannel#sendMessage(Message)} and {@link MessageAction#reference(Message)}. * * @param content * The content of the reply message @@ -1023,7 +1023,7 @@ default MessageAction reply(@Nonnull Message content) *
This is identical to {@code message.getChannel().sendMessageFormat(content, args).reference(message)}. * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. * - *

For further info, see {@link MessageChannel#sendMessage(CharSequence)} and {@link MessageAction#reference(Message)}. + *

For further info, see {@link MessageChannel#sendMessageFormat(String, Object...)} and {@link MessageAction#reference(Message)}. * * @param format * The string that should be formatted, if this is null or empty the content of the Message would be empty and cause a builder exception. @@ -1065,7 +1065,7 @@ default MessageAction reply(@Nonnull File file, @Nonnull AttachmentOption... opt *
This is identical to {@code message.getChannel().sendFile(data, name, options).reference(message)}. * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. * - *

For further info, see {@link MessageChannel#sendFile(File, net.dv8tion.jda.api.utils.AttachmentOption...)} and {@link MessageAction#reference(Message)}. + *

For further info, see {@link MessageChannel#sendFile(File, String, net.dv8tion.jda.api.utils.AttachmentOption...)} and {@link MessageAction#reference(Message)}. * * @param data * The data to upload to the channel in the reply @@ -1088,7 +1088,7 @@ default MessageAction reply(@Nonnull File data, @Nonnull String name, @Nonnull A *
This is identical to {@code message.getChannel().sendFile(data, name, options).reference(message)}. * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. * - *

For further info, see {@link MessageChannel#sendFile(File, net.dv8tion.jda.api.utils.AttachmentOption...)} and {@link MessageAction#reference(Message)}. + *

For further info, see {@link MessageChannel#sendFile(InputStream, String, net.dv8tion.jda.api.utils.AttachmentOption...)} and {@link MessageAction#reference(Message)}. * * @param data * The data to upload to the channel in the reply @@ -1111,7 +1111,7 @@ default MessageAction reply(@Nonnull InputStream data, @Nonnull String name, @No *
This is identical to {@code message.getChannel().sendFile(data, name, options).reference(message)}. * You can use {@link MessageAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message. * - *

For further info, see {@link MessageChannel#sendFile(File, net.dv8tion.jda.api.utils.AttachmentOption...)} and {@link MessageAction#reference(Message)}. + *

For further info, see {@link MessageChannel#sendFile(byte[], String, net.dv8tion.jda.api.utils.AttachmentOption...)} and {@link MessageAction#reference(Message)}. * * @param data * The data to upload to the channel in the reply diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java index 85e35b3490..a03e9dd1e3 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java @@ -186,6 +186,7 @@ static EnumSet getDefaultMentions() /** * Make the message a reply to the referenced message. *
You can only reply to messages from the same channel! + *
This will mention the author of the target message. You can disable this through {@link #mentionRepliedUser(boolean)}. * *

This requires {@link net.dv8tion.jda.api.Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY} in the channel! * @@ -201,6 +202,7 @@ static EnumSet getDefaultMentions() /** * Make the message a reply to the referenced message. *
You can only reply to messages from the same channel! + *
This will mention the author of the target message. You can disable this through {@link #mentionRepliedUser(boolean)}. * *

This requires {@link net.dv8tion.jda.api.Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY} in the channel! * You cannot reply to system messages (such as {@link net.dv8tion.jda.api.entities.MessageType#CHANNEL_PINNED_ADD CHANNEL_PINNED_ADD} and similar). @@ -223,6 +225,7 @@ default MessageAction referenceById(@Nonnull String messageId) /** * Make the message a reply to the referenced message. *
You can only reply to messages from the same channel! + *
This will mention the author of the target message. You can disable this through {@link #mentionRepliedUser(boolean)}. * *

This requires {@link net.dv8tion.jda.api.Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY} in the channel! * From ef73ca2f669f7c8043a8cd7fa7dd0d1fa6209cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Mon, 16 Nov 2020 20:57:01 +0100 Subject: [PATCH 11/14] Improve stacktrace from exceptions thrown by RestAction#complete --- .../net/dv8tion/jda/internal/requests/RestActionImpl.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/requests/RestActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/RestActionImpl.java index 11e206df40..0b6a070cf1 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/RestActionImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/RestActionImpl.java @@ -223,12 +223,14 @@ public T complete(boolean shouldQueue) throws RateLimitedException if (e.getCause() != null) { Throwable cause = e.getCause(); + if (cause instanceof ErrorResponseException) + throw (ErrorResponseException) cause.fillInStackTrace(); // this method will update the stacktrace to the current thread stack + if (cause instanceof RateLimitedException) + throw (RateLimitedException) cause.fillInStackTrace(); if (cause instanceof RuntimeException) throw (RuntimeException) cause; - else if (cause instanceof Error) + if (cause instanceof Error) throw (Error) cause; - else if (cause instanceof RateLimitedException) - throw (RateLimitedException) cause; } throw e; } From bf43563ff7489c3e463856c017a757ad6c9a884f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Mon, 16 Nov 2020 22:29:04 +0100 Subject: [PATCH 12/14] Always parse mentions in createMessage regardless of Guild#isLoaded Not every mention is included in the content so this is necessary Inline replies use mentions which are not in the content, this would fail only when the guild is loaded (due to our handling) --- .../java/net/dv8tion/jda/internal/entities/EntityBuilder.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java index bdda981920..34d4819545 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -1168,10 +1168,6 @@ public Message createMessage(DataObject jsonObject, MessageChannel chan, boolean GuildImpl guild = (GuildImpl) message.getGuild(); - // Don't do more computations when members are loaded already - if (guild.isLoaded()) - return message; - // Load users/members from message object through mentions List mentionedUsersList = new ArrayList<>(); List mentionedMembersList = new ArrayList<>(); From e720ac10b66e161d1c8e53562deb8f42d691cfc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Tue, 17 Nov 2020 16:55:20 +0100 Subject: [PATCH 13/14] Improve handling of mentions for direct messages --- .../jda/internal/entities/EntityBuilder.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java index 34d4819545..35a8af4ab8 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -1163,10 +1163,7 @@ public Message createMessage(DataObject jsonObject, MessageChannel chan, boolean break; } - if (!message.isFromGuild()) - return message; - - GuildImpl guild = (GuildImpl) message.getGuild(); + GuildImpl guild = message.isFromGuild() ? (GuildImpl) message.getGuild() : null; // Load users/members from message object through mentions List mentionedUsersList = new ArrayList<>(); @@ -1176,14 +1173,17 @@ public Message createMessage(DataObject jsonObject, MessageChannel chan, boolean for (int i = 0; i < userMentions.length(); i++) { DataObject mentionJson = userMentions.getObject(i); - if (mentionJson.isNull("member")) + if (guild == null || mentionJson.isNull("member")) { // Can't load user without member context so fake them if possible User mentionedUser = createUser(mentionJson); mentionedUsersList.add(mentionedUser); - Member mentionedMember = guild.getMember(mentionedUser); - if (mentionedMember != null) - mentionedMembersList.add(mentionedMember); + if (guild != null) + { + Member mentionedMember = guild.getMember(mentionedUser); + if (mentionedMember != null) + mentionedMembersList.add(mentionedMember); + } continue; } @@ -1196,8 +1196,7 @@ public Message createMessage(DataObject jsonObject, MessageChannel chan, boolean mentionedUsersList.add(mentionedMember.getUser()); } - if (!mentionedUsersList.isEmpty()) - message.setMentions(mentionedUsersList, mentionedMembersList); + message.setMentions(mentionedUsersList, mentionedMembersList); return message; } From e7bdee312907a9c7f1b374e50cca354bd4c1ce15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Fri, 20 Nov 2020 05:49:05 +0100 Subject: [PATCH 14/14] Improve some default behaviors --- .../requests/restaction/MessageAction.java | 29 +++++++++++++++++++ .../restaction/MessageActionImpl.java | 15 ++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java index a03e9dd1e3..db38314a6c 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageAction.java @@ -125,6 +125,33 @@ static EnumSet getDefaultMentions() return MessageActionImpl.getDefaultMentions(); } + /** + * Sets the default value for {@link #mentionRepliedUser(boolean)} + * + *

Default: true + * + * @param mention + * True, if replies should mention by default + */ + static void setDefaultMentionRepliedUser(boolean mention) + { + MessageActionImpl.setDefaultMentionRepliedUser(mention); + } + + /** + * Returns the default mention behavior for replies. + *
If this is {@code true} then all replies will mention the author of the target message by default. + * You can specify this individually with {@link #mentionRepliedUser(boolean)} for each message. + * + *

Default: true + * + * @return True, if replies mention by default + */ + static boolean isDefaultMentionRepliedUser() + { + return MessageActionImpl.isDefaultMentionRepliedUser(); + } + @Nonnull @Override MessageAction setCheck(@Nullable BooleanSupplier checks); @@ -251,6 +278,8 @@ default MessageAction reference(@Nonnull Message message) * Whether to mention the used, when replying to a message. *
This only matters in combination with {@link #reference(Message)} and {@link #referenceById(long)}! * + *

This is true by default but can be configured using {@link #setDefaultMentionRepliedUser(boolean)}! + * * @param mention * True, to mention the author if the referenced message * diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java index d6aeb9074d..42e97f968c 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageActionImpl.java @@ -52,6 +52,7 @@ public class MessageActionImpl extends RestActionImpl implements Messag { private static final String CONTENT_TOO_BIG = String.format("A message may not exceed %d characters. Please limit your input!", Message.MAX_CONTENT_LENGTH); protected static EnumSet defaultMentions = EnumSet.allOf(Message.MentionType.class); + protected static boolean defaultMentionRepliedUser = true; protected final Map files = new HashMap<>(); protected final Set ownedResources = new HashSet<>(); protected final StringBuilder content; @@ -59,7 +60,7 @@ public class MessageActionImpl extends RestActionImpl implements Messag protected MessageEmbed embed = null; protected String nonce = null; protected boolean tts = false, override = false; - protected boolean mentionRepliedUser = true; + protected boolean mentionRepliedUser = defaultMentionRepliedUser; protected EnumSet allowedMentions; protected Set mentionableUsers = new HashSet<>(); protected Set mentionableRoles = new HashSet<>(); @@ -78,6 +79,16 @@ public static EnumSet getDefaultMentions() return defaultMentions.clone(); } + public static void setDefaultMentionRepliedUser(boolean mention) + { + defaultMentionRepliedUser = mention; + } + + public static boolean isDefaultMentionRepliedUser() + { + return defaultMentionRepliedUser; + } + public MessageActionImpl(JDA api, Route.CompiledRoute route, MessageChannel channel) { super(api, route); @@ -505,7 +516,7 @@ protected DataObject getJSON() .put("channel_id", channel.getId())); } obj.put("tts", tts); - if (messageReference != 0L || allowedMentions != null || !mentionableUsers.isEmpty() || !mentionableRoles.isEmpty()) + if ((messageReference != 0L && !mentionRepliedUser) || allowedMentions != null || !mentionableUsers.isEmpty() || !mentionableRoles.isEmpty()) obj.put("allowed_mentions", getAllowedMentionsObj()); return obj; }