From 29a5cad0622e3878eb8eff6f4b69f2059898e923 Mon Sep 17 00:00:00 2001 From: Austin Keener Date: Sat, 5 Feb 2022 02:42:31 -0500 Subject: [PATCH 01/29] Reworked mentions into Message#getMentions --- .../net/dv8tion/jda/api/entities/Message.java | 241 +---------- .../jda/api/entities/MessageMentions.java | 321 +++++++++++++++ .../internal/entities/AbstractMessage.java | 80 +--- .../jda/internal/entities/EntityBuilder.java | 15 +- .../entities/MessageMentionsImpl.java | 389 ++++++++++++++++++ .../internal/entities/ReceivedMessage.java | 349 +--------------- .../jda/internal/entities/SystemMessage.java | 9 +- .../internal/utils/AllowedMentionsImpl.java | 6 +- 8 files changed, 746 insertions(+), 664 deletions(-) create mode 100644 src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java create mode 100644 src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java 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 b23dc35b59..96ca4f166c 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Message.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Message.java @@ -245,246 +245,9 @@ default Message getReferencedMessage() : null; } - /** - * 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 - * counts direct mentions of the user and not mentions through roles or the everyone tag. - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * - * @return immutable list of mentioned users - */ - @Nonnull - List getMentionedUsers(); - - /** - * A {@link org.apache.commons.collections4.Bag Bag} of mentioned users. - *
This can be used to retrieve the amount of times a user was mentioned in this message. This only - * counts direct mentions of the user and not mentions through roles or the everyone tag. - * - *

Example

- *
{@code
-     * public void sendCount(Message msg)
-     * {
-     *     List mentions = msg.getMentionedUsers(); // distinct list, in order of appearance
-     *     Bag count = msg.getMentionedUsersBag();
-     *     StringBuilder content = new StringBuilder();
-     *     for (User user : mentions)
-     *     {
-     *         content.append(user.getAsTag())
-     *                .append(": ")
-     *                .append(count.getCount(user))
-     *                .append("\n");
-     *     }
-     *     msg.getChannel().sendMessage(content.toString()).queue();
-     * }
-     * }
- * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * - * @return {@link org.apache.commons.collections4.Bag Bag} of mentioned users - * - * @see #getMentionedUsers() - */ - @Nonnull - Bag getMentionedUsersBag(); - - /** - * A immutable list of all mentioned {@link net.dv8tion.jda.api.entities.TextChannel TextChannels}. - *
If none were mentioned, this list is empty. Elements are sorted in order of appearance. - * - *

This may include TextChannels from other {@link net.dv8tion.jda.api.entities.Guild Guilds} - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * - * @return immutable list of mentioned TextChannels - */ - @Nonnull - List getMentionedChannels(); - - /** - * A {@link org.apache.commons.collections4.Bag Bag} of mentioned channels. - *
This can be used to retrieve the amount of times a channel was mentioned in this message. - * - *

Example

- *
{@code
-     * public void sendCount(Message msg)
-     * {
-     *     List mentions = msg.getMentionedTextChannels(); // distinct list, in order of appearance
-     *     Bag count = msg.getMentionedTextChannelsBag();
-     *     StringBuilder content = new StringBuilder();
-     *     for (TextChannel channel : mentions)
-     *     {
-     *         content.append("#")
-     *                .append(channel.getName())
-     *                .append(": ")
-     *                .append(count.getCount(channel))
-     *                .append("\n");
-     *     }
-     *     msg.getChannel().sendMessage(content.toString()).queue();
-     * }
-     * }
- * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * - * @return {@link org.apache.commons.collections4.Bag Bag} of mentioned channels - * - * @see #getMentionedChannels() - */ - @Nonnull - Bag getMentionedChannelsBag(); - - /** - * A immutable list of all mentioned {@link net.dv8tion.jda.api.entities.Role Roles}. - *
If none were mentioned, this list is empty. Elements are sorted in order of appearance. This only - * counts direct mentions of the role and not mentions through the everyone tag. - * - *

This may include Roles from other {@link net.dv8tion.jda.api.entities.Guild Guilds} - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * - * @return immutable list of mentioned Roles - */ + //TODO | Docs @Nonnull - List getMentionedRoles(); - - /** - * A {@link org.apache.commons.collections4.Bag Bag} of mentioned roles. - *
This can be used to retrieve the amount of times a role was mentioned in this message. This only - * counts direct mentions of the role and not mentions through the everyone tag. - * If a role is not {@link net.dv8tion.jda.api.entities.Role#isMentionable() mentionable} it will not be included. - * - *

Example

- *
{@code
-     * public void sendCount(Message msg)
-     * {
-     *     List mentions = msg.getMentionedRoles(); // distinct list, in order of appearance
-     *     Bag count = msg.getMentionedRolesBag();
-     *     StringBuilder content = new StringBuilder();
-     *     for (Role role : mentions)
-     *     {
-     *         content.append(role.getName())
-     *                .append(": ")
-     *                .append(count.getCount(role))
-     *                .append("\n");
-     *     }
-     *     msg.getChannel().sendMessage(content.toString()).queue();
-     * }
-     * }
- * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * - * @return {@link org.apache.commons.collections4.Bag Bag} of mentioned roles - * - * @see #getMentionedRoles() - */ - @Nonnull - Bag getMentionedRolesBag(); - - /** - * Creates an immutable list of {@link net.dv8tion.jda.api.entities.Member Members} - * representing the users of {@link #getMentionedUsers()} in the specified - * {@link net.dv8tion.jda.api.entities.Guild Guild}. - *
This is only a convenience method and will skip all users that are not in the specified - * Guild. - * - * @param guild - * Non-null {@link net.dv8tion.jda.api.entities.Guild Guild} - * that will be used to retrieve Members. - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * @throws java.lang.IllegalArgumentException - * If the specified Guild is {@code null} - * - * @return Immutable list of mentioned Members - * - * @since 3.4.0 - */ - @Nonnull - List getMentionedMembers(@Nonnull Guild guild); - - /** - * Creates an immutable list of {@link net.dv8tion.jda.api.entities.Member Members} - * representing the users of {@link #getMentionedUsers()} in the - * {@link net.dv8tion.jda.api.entities.Guild Guild} this Message was sent in. - *
This is only a convenience method and will skip all users that are not in the specified Guild. - *
It will provide the {@link #getGuild()} output Guild to {@link #getMentionedMembers(Guild)}. - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * @throws java.lang.IllegalStateException - * If this message was not sent in a {@link net.dv8tion.jda.api.entities.TextChannel TextChannel} - * - * @return Immutable list of mentioned Members - * - * @since 3.4.0 - */ - @Nonnull - List getMentionedMembers(); - - /** - * Combines all instances of {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} - * filtered by the specified {@link net.dv8tion.jda.api.entities.Message.MentionType MentionType} values. - *
This does not include {@link #getMentionedMembers()} to avoid duplicates. - * - *

If no MentionType values are given this will fallback to all types. - * - * @param types - * Amount of {@link net.dv8tion.jda.api.entities.Message.MentionType MentionTypes} - * to include in the list of mentions - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * @throws java.lang.IllegalArgumentException - * If provided with {@code null} - * - * @return Immutable list of filtered {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} instances - * - * @since 3.4.0 - */ - @Nonnull - List getMentions(@Nonnull MentionType... types); - - /** - * Checks if given {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} - * was mentioned in this message in any way (@User, @everyone, @here, @Role). - *
If no filtering {@link net.dv8tion.jda.api.entities.Message.MentionType MentionTypes} are - * specified this will fallback to all mention types. - * - *

{@link Message.MentionType#HERE MentionType.HERE} and {@link Message.MentionType#EVERYONE MentionType.EVERYONE} - * will only be checked, if the given {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} is of type - * {@link net.dv8tion.jda.api.entities.User User} or {@link net.dv8tion.jda.api.entities.Member Member}. - *
Online status of Users/Members is NOT considered when checking {@link Message.MentionType#HERE MentionType.HERE}. - * - * @param mentionable - * The mentionable entity to check on. - * @param types - * The types to include when checking whether this type was mentioned. - * This will be used with {@link #getMentions(Message.MentionType...) getMentions(MentionType...)} - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * - * @return True, if the given mentionable was mentioned in this message - */ - boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull MentionType... types); - - /** - * Indicates if this Message mentions everyone using @everyone or @here. - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * - * @return True, if message is mentioning everyone - */ - boolean mentionsEveryone(); + MessageMentions getMentions(); /** * Returns whether or not this Message has been edited before. diff --git a/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java b/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java new file mode 100644 index 0000000000..c5d2a4edd3 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java @@ -0,0 +1,321 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities; + +import net.dv8tion.jda.api.JDA; +import org.apache.commons.collections4.Bag; + +import javax.annotation.Nonnull; +import java.util.List; + +//TODO-v5 | Docs +public interface MessageMentions +{ + //TODO-v5 | Docs + JDA getJDA(); + + //TODO-v5 | Docs + Message getMessage(); + + /** + * Indicates if this Message mentions everyone using @everyone or @here. + * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * + * @return True, if message is mentioning everyone + */ + boolean mentionsEveryone(); + + /** + * 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 + * counts direct mentions of the user and not mentions through roles or the everyone tag. + * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * + * @return immutable list of mentioned users + */ + List getUsers(); + + /** + * A {@link org.apache.commons.collections4.Bag Bag} of mentioned users. + *
This can be used to retrieve the amount of times a user was mentioned in this message. This only + * counts direct mentions of the user and not mentions through roles or the everyone tag. + * + *

Example

+ *
{@code
+     * void sendCount(Message msg)
+     * {
+     *     List mentions = msg.getMentionedUsers(); // distinct list, in order of appearance
+     *     Bag count = msg.getMentionedUsersBag();
+     *     StringBuilder content = new StringBuilder();
+     *     for (User user : mentions)
+     *     {
+     *         content.append(user.getAsTag())
+     *                .append(": ")
+     *                .append(count.getCount(user))
+     *                .append("\n");
+     *     }
+     *     msg.getChannel().sendMessage(content.toString()).queue();
+     * }
+     * }
+ * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * + * @return {@link org.apache.commons.collections4.Bag Bag} of mentioned users + * + * @see #getUsers() + */ + @Nonnull + Bag getUsersBag(); + + /** + * A immutable list of all mentioned {@link net.dv8tion.jda.api.entities.TextChannel TextChannels}. + *
If none were mentioned, this list is empty. Elements are sorted in order of appearance. + * + *

This may include TextChannels from other {@link net.dv8tion.jda.api.entities.Guild Guilds} + * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * + * @return immutable list of mentioned TextChannels + */ + @Nonnull + List getChannels(); + + /** + * A {@link org.apache.commons.collections4.Bag Bag} of mentioned channels. + *
This can be used to retrieve the amount of times a channel was mentioned in this message. + * + *

Example

+ *
{@code
+     * void sendCount(Message msg)
+     * {
+     *     List mentions = msg.getMentionedTextChannels(); // distinct list, in order of appearance
+     *     Bag count = msg.getMentionedTextChannelsBag();
+     *     StringBuilder content = new StringBuilder();
+     *     for (TextChannel channel : mentions)
+     *     {
+     *         content.append("#")
+     *                .append(channel.getName())
+     *                .append(": ")
+     *                .append(count.getCount(channel))
+     *                .append("\n");
+     *     }
+     *     msg.getChannel().sendMessage(content.toString()).queue();
+     * }
+     * }
+ * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * + * @return {@link org.apache.commons.collections4.Bag Bag} of mentioned channels + * + * @see #getChannels() + */ + @Nonnull + Bag getChannelsBag(); + + /** + * A immutable list of all mentioned {@link net.dv8tion.jda.api.entities.Role Roles}. + *
If none were mentioned, this list is empty. Elements are sorted in order of appearance. This only + * counts direct mentions of the role and not mentions through the everyone tag. + * + *

This may include Roles from other {@link net.dv8tion.jda.api.entities.Guild Guilds} + * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * + * @return immutable list of mentioned Roles + */ + @Nonnull + List getRoles(); + + /** + * A {@link org.apache.commons.collections4.Bag Bag} of mentioned roles. + *
This can be used to retrieve the amount of times a role was mentioned in this message. This only + * counts direct mentions of the role and not mentions through the everyone tag. + * If a role is not {@link net.dv8tion.jda.api.entities.Role#isMentionable() mentionable} it will not be included. + * + *

Example

+ *
{@code
+     * void sendCount(Message msg)
+     * {
+     *     List mentions = msg.getMentionedRoles(); // distinct list, in order of appearance
+     *     Bag count = msg.getMentionedRolesBag();
+     *     StringBuilder content = new StringBuilder();
+     *     for (Role role : mentions)
+     *     {
+     *         content.append(role.getName())
+     *                .append(": ")
+     *                .append(count.getCount(role))
+     *                .append("\n");
+     *     }
+     *     msg.getChannel().sendMessage(content.toString()).queue();
+     * }
+     * }
+ * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * + * @return {@link org.apache.commons.collections4.Bag Bag} of mentioned roles + * + * @see #getRoles() + */ + @Nonnull + Bag getRolesBag(); + + /** + * All {@link net.dv8tion.jda.api.entities.Emote Emotes} used in this Message. + *
This only includes Custom Emotes, not unicode Emojis. JDA classifies Emotes as the Custom Emojis uploaded + * to a Guild and retrievable with {@link net.dv8tion.jda.api.entities.Guild#getEmotes()}. These are not the same + * as the unicode emojis that Discord also supports. Elements are sorted in order of appearance. + * + *

Unicode emojis are not included as {@link net.dv8tion.jda.api.entities.Emote Emote}! + * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * + * @return An immutable list of the Emotes used in this message (example match {@literal <:jda:230988580904763393>}) + */ + @Nonnull + List getEmotes(); + + /** + * A {@link org.apache.commons.collections4.Bag Bag} of emotes used in this message. + *
This can be used to retrieve the amount of times an emote was used in this message. + * + *

Example

+ *
{@code
+     * void sendCount(Message msg)
+     * {
+     *     List emotes = msg.getEmotes(); // distinct list, in order of appearance
+     *     Bag count = msg.getEmotesBag();
+     *     StringBuilder content = new StringBuilder();
+     *     for (Emote emote : emotes)
+     *     {
+     *         content.append(emote.getName())
+     *                .append(": ")
+     *                .append(count.getCount(role))
+     *                .append("\n");
+     *     }
+     *     msg.getChannel().sendMessage(content.toString()).queue();
+     * }
+     * }
+ * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * + * @return {@link org.apache.commons.collections4.Bag Bag} of used emotes + * + * @see #getEmotes() + */ + @Nonnull + Bag getEmotesBag(); + + /** + * Creates an immutable list of {@link net.dv8tion.jda.api.entities.Member Members} + * representing the users of {@link #getUsers()} in the specified + * {@link net.dv8tion.jda.api.entities.Guild Guild}. + *
This is only a convenience method and will skip all users that are not in the specified + * Guild. + * + * @param guild + * Non-null {@link net.dv8tion.jda.api.entities.Guild Guild} + * that will be used to retrieve Members. + * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * @throws java.lang.IllegalArgumentException + * If the specified Guild is {@code null} + * + * @return Immutable list of mentioned Members + * + * @since 3.4.0 + */ + @Nonnull + List getMembers(@Nonnull Guild guild); + + /** + * Creates an immutable list of {@link net.dv8tion.jda.api.entities.Member Members} + * representing the users of {@link #getUsers()} in the + * {@link net.dv8tion.jda.api.entities.Guild Guild} this Message was sent in. + *
This is only a convenience method and will skip all users that are not in the specified Guild. + *
It will provide the {@link Message()#getGuild() Message's Guild} to {@link #getMembers(Guild)}. + * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * @throws java.lang.IllegalStateException + * If this message was not sent in a {@link net.dv8tion.jda.api.entities.TextChannel TextChannel} + * + * @return Immutable list of mentioned Members + * + * @since 3.4.0 + */ + @Nonnull + List getMembers(); + + /** + * Combines all instances of {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} + * filtered by the specified {@link net.dv8tion.jda.api.entities.Message.MentionType MentionType} values. + *
This does not include {@link #getMembers()} to avoid duplicates. + * + *

If no MentionType values are given this will fallback to all types. + * + * @param types + * Amount of {@link net.dv8tion.jda.api.entities.Message.MentionType MentionTypes} + * to include in the list of mentions + * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * @throws java.lang.IllegalArgumentException + * If provided with {@code null} + * + * @return Immutable list of filtered {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} instances + * + * @since 3.4.0 + */ + @Nonnull + List getMentions(@Nonnull Message.MentionType... types); + + /** + * Checks if given {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} + * was mentioned in this message in any way (@User, @everyone, @here, @Role). + *
If no filtering {@link net.dv8tion.jda.api.entities.Message.MentionType MentionTypes} are + * specified this will fallback to all mention types. + * + *

{@link Message.MentionType#HERE MentionType.HERE} and {@link Message.MentionType#EVERYONE MentionType.EVERYONE} + * will only be checked, if the given {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} is of type + * {@link net.dv8tion.jda.api.entities.User User} or {@link net.dv8tion.jda.api.entities.Member Member}. + *
Online status of Users/Members is NOT considered when checking {@link Message.MentionType#HERE MentionType.HERE}. + * + * @param mentionable + * The mentionable entity to check on. + * @param types + * The types to include when checking whether this type was mentioned. + * This will be used with {@link #getMentions(Message.MentionType...) getMentions(MentionType...)} + * + * @throws java.lang.UnsupportedOperationException + * If this is a system message + * + * @return True, if the given mentionable was mentioned in this message + */ + boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull Message.MentionType... types); +} 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 a511c65022..c2dd4ad7b4 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java @@ -116,90 +116,12 @@ public MessageReference getMessageReference() @Nonnull @Override - public Bag getMentionedUsersBag() + public MessageMentions getMentions() { unsupported(); return null; } - @Nonnull - @Override - public Bag getMentionedChannelsBag() - { - unsupported(); - return null; - } - - @Nonnull - @Override - public Bag getMentionedRolesBag() - { - unsupported(); - return null; - } - - @Nonnull - @Override - public List getMentionedUsers() - { - unsupported(); - return null; - } - - @Nonnull - @Override - public List getMentionedChannels() - { - unsupported(); - return null; - } - - @Nonnull - @Override - public List getMentionedRoles() - { - unsupported(); - return null; - } - - @Nonnull - @Override - public List getMentionedMembers(@Nonnull Guild guild) - { - unsupported(); - return null; - } - - @Nonnull - @Override - public List getMentionedMembers() - { - unsupported(); - return null; - } - - @Nonnull - @Override - public List getMentions(@Nonnull MentionType... types) - { - unsupported(); - return null; - } - - @Override - public boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull MentionType... types) - { - unsupported(); - return false; - } - - @Override - public boolean mentionsEveryone() - { - unsupported(); - return false; - } - @Override public boolean isEdited() { 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 8a904354d0..c5ad95e5b7 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -1449,6 +1449,8 @@ else if (channel == null) mentionedRoles.add(arr.getLong(i)); }); + MessageMentions mentions = new MessageMentionsImpl(mentionsEveryone, mentionedUsers, mentionedRoles); + MessageType type = MessageType.fromId(jsonObject.getInt("type")); ReceivedMessage message; Message referencedMessage = null; @@ -1513,15 +1515,14 @@ else if (MISSING_CHANNEL.equals(ex.getMessage())) throw new IllegalArgumentException(UNKNOWN_MESSAGE_TYPE); if (!type.isSystem()) { - message = new ReceivedMessage(id, channel, type, messageReference, fromWebhook, - mentionsEveryone, mentionedUsers, mentionedRoles, tts, pinned, - content, nonce, user, member, activity, editTime, reactions, attachments, embeds, stickers, components, flags, messageInteraction); + message = new ReceivedMessage(id, channel, type, messageReference, fromWebhook, tts, pinned, + content, nonce, user, member, activity, editTime, mentions, reactions, attachments, embeds, stickers, components, flags, messageInteraction); } else { - message = new SystemMessage(id, channel, type, messageReference, fromWebhook, - mentionsEveryone, mentionedUsers, mentionedRoles, tts, pinned, - content, nonce, user, member, activity, editTime, reactions, attachments, embeds, stickers, flags); + message = new SystemMessage(id, channel, type, messageReference, fromWebhook, tts, pinned, + content, nonce, user, member, activity, editTime, mentions, reactions, attachments, embeds, stickers, flags); + return message; // We don't need to parse mentions for system messages, they are always empty anyway } @@ -1558,7 +1559,7 @@ else if (MISSING_CHANNEL.equals(ex.getMessage())) mentionedUsersList.add(mentionedMember.getUser()); } - message.setMentions(mentionedUsersList, mentionedMembersList); + ((MessageMentionsImpl) message.getMentions()).setUserMemberMentions(mentionedUsersList, mentionedMembersList); return message; } diff --git a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java new file mode 100644 index 0000000000..42f9c264eb --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java @@ -0,0 +1,389 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.entities; + +import gnu.trove.set.TLongSet; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.utils.MiscUtil; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.EmoteImpl; +import net.dv8tion.jda.internal.utils.Checks; +import org.apache.commons.collections4.Bag; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.bag.HashBag; + +import javax.annotation.Nonnull; +import java.util.*; +import java.util.function.Function; +import java.util.regex.Matcher; + +//TODO-v5 | Docs +public class MessageMentionsImpl implements MessageMentions +{ + private Message message; + + private final boolean mentionsEveryone; + private final TLongSet mentionedUsers; + private final TLongSet mentionedRoles; + + private List userMentions = null; + private List memberMentions = null; + private List emoteMentions = null; + private List roleMentions = null; + private List channelMentions = null; + + public MessageMentionsImpl(boolean mentionsEveryone, TLongSet mentionedUsers, TLongSet mentionedRoles) + { + this.mentionsEveryone = mentionsEveryone; + this.mentionedUsers = mentionedUsers; + this.mentionedRoles = mentionedRoles; + } + + //TODO-v5 | Docs + public JDA getJDA() + { + return message.getJDA(); + } + + //TODO-v5 | Docs + public Message getMessage() + { + return message; + } + + @Override + public boolean mentionsEveryone() + { + return mentionsEveryone; + } + + @Override + public synchronized List getUsers() + { + if (userMentions == null) + userMentions = Collections.unmodifiableList(processMentions(Message.MentionType.USER, new ArrayList<>(), true, this::matchUser)); + return userMentions; + } + + @Override + @Nonnull + public Bag getUsersBag() + { + return processMentions(Message.MentionType.USER, new HashBag<>(), false, this::matchUser); + } + + @Override + @Nonnull + public synchronized List getChannels() + { + //TODO-MessageMentions: This needs to be updated as you can match.. quie a few more chanels than just TextChannels + if (channelMentions == null) + channelMentions = Collections.unmodifiableList(processMentions(Message.MentionType.CHANNEL, new ArrayList<>(), true, this::matchTextChannel)); + return channelMentions; + } + + @Override + @Nonnull + public Bag getChannelsBag() + { + return processMentions(Message.MentionType.CHANNEL, new HashBag<>(), false, this::matchTextChannel); + } + + @Override + @Nonnull + public synchronized List getRoles() + { + if (roleMentions == null) + roleMentions = Collections.unmodifiableList(processMentions(Message.MentionType.ROLE, new ArrayList<>(), true, this::matchRole)); + return roleMentions; + } + + @Override + @Nonnull + public Bag getRolesBag() + { + return processMentions(Message.MentionType.ROLE, new HashBag<>(), false, this::matchRole); + } + + @Override + @Nonnull + public List getMembers(@Nonnull Guild guild) + { + Checks.notNull(guild, "Guild"); + if (message.isFromGuild() && guild.equals(message.getGuild()) && memberMentions != null) + return memberMentions; + List mentionedUsers = getUsers(); + List members = new ArrayList<>(); + for (User user : mentionedUsers) + { + Member member = guild.getMember(user); + if (member != null) + members.add(member); + } + + return Collections.unmodifiableList(members); + } + + @Override + @Nonnull + public List getMembers() + { + if (message.isFromGuild()) + return getMembers(message.getGuild()); + else + throw new IllegalStateException("You must specify a Guild for Messages which are not sent from a TextChannel!"); + } + + @Override + @Nonnull + public synchronized List getEmotes() + { + if (this.emoteMentions == null) + emoteMentions = Collections.unmodifiableList(processMentions(Message.MentionType.EMOTE, new ArrayList<>(), true, this::matchEmote)); + return emoteMentions; + } + + @Override + @Nonnull + public Bag getEmotesBag() + { + return processMentions(Message.MentionType.EMOTE, new HashBag<>(), false, this::matchEmote); + } + + @Override + @Nonnull + public List getMentions(@Nonnull Message.MentionType... types) + { + if (types == null || types.length == 0) + return getMentions(Message.MentionType.values()); + List mentions = new ArrayList<>(); + // boolean duplicate checks + // not using Set because channel and role might have the same ID + boolean channel = false; + boolean role = false; + boolean user = false; + boolean emote = false; + for (Message.MentionType type : types) + { + switch (type) + { + case EVERYONE: + case HERE: + default: continue; + case CHANNEL: + if (!channel) + mentions.addAll(getChannels()); + channel = true; + break; + case USER: + if (!user) + mentions.addAll(getUsers()); + user = true; + break; + case ROLE: + if (!role) + mentions.addAll(getRoles()); + role = true; + break; + case EMOTE: + if (!emote) + mentions.addAll(getEmotes()); + emote = true; + } + } + return Collections.unmodifiableList(mentions); + } + + @Override + public boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull Message.MentionType... types) + { + Checks.notNull(types, "Mention Types"); + if (types.length == 0) + return isMentioned(mentionable, Message.MentionType.values()); + final boolean isUserEntity = mentionable instanceof User || mentionable instanceof Member; + for (Message.MentionType type : types) + { + switch (type) + { + case HERE: + { + if (isMass("@here") && isUserEntity) + return true; + break; + } + case EVERYONE: + { + if (isMass("@everyone") && isUserEntity) + return true; + break; + } + case USER: + { + if (isUserMentioned(mentionable)) + return true; + break; + } + case ROLE: + { + if (isRoleMentioned(mentionable)) + return true; + break; + } + case CHANNEL: + { + if (mentionable instanceof TextChannel) + { + if (getChannels().contains(mentionable)) + return true; + } + break; + } + case EMOTE: + { + if (mentionable instanceof Emote) + { + if (getEmotes().contains(mentionable)) + return true; + } + break; + } +// default: continue; + } + } + return false; + } + + // ======== Setters ============ + + public void setMessage(Message message) + { + this.message = message; + } + + public void setUserMemberMentions(List users, List members) + { + String content = message.getContentRaw(); + users.sort(Comparator.comparing((user) -> + Math.max(content.indexOf("<@" + user.getId() + ">"), + content.indexOf("<@!" + user.getId() + ">") + ))); + members.sort(Comparator.comparing((user) -> + Math.max(content.indexOf("<@" + user.getId() + ">"), + content.indexOf("<@!" + user.getId() + ">") + ))); + + this.userMentions = Collections.unmodifiableList(users); + this.memberMentions = Collections.unmodifiableList(members); + } + + // ============= Internal Helpers ================= + + private > C processMentions(Message.MentionType type, C collection, boolean distinct, Function map) + { + Matcher matcher = type.getPattern().matcher(message.getContentRaw()); + while (matcher.find()) + { + try + { + T elem = map.apply(matcher); + if (elem == null || (distinct && collection.contains(elem))) + continue; + collection.add(elem); + } + catch (NumberFormatException ignored) {} + } + return collection; + } + + private User matchUser(Matcher matcher) + { + long userId = MiscUtil.parseSnowflake(matcher.group(1)); + if (!mentionedUsers.contains(userId)) + return null; + User user = getJDA().getUserById(userId); + if (user == null && userMentions != null) + user = userMentions.stream().filter(it -> it.getIdLong() == userId).findFirst().orElse(null); + return user; + } + + private TextChannel matchTextChannel(Matcher matcher) + { + long channelId = MiscUtil.parseSnowflake(matcher.group(1)); + return getJDA().getTextChannelById(channelId); + } + + private Role matchRole(Matcher matcher) + { + long roleId = MiscUtil.parseSnowflake(matcher.group(1)); + if (!mentionedRoles.contains(roleId)) + return null; + if (message.getChannelType().isGuild()) + return message.getGuild().getRoleById(roleId); + else + return getJDA().getRoleById(roleId); + } + + private Emote matchEmote(Matcher m) + { + long emoteId = MiscUtil.parseSnowflake(m.group(2)); + String name = m.group(1); + boolean animated = m.group(0).startsWith(" reactions; protected final List attachments; protected final List embeds; protected final List stickers; protected final List components; - protected final TLongSet mentionedUsers; - protected final TLongSet mentionedRoles; protected final int flags; protected final Message.Interaction interaction; @@ -83,18 +77,13 @@ public class ReceivedMessage extends AbstractMessage protected String altContent = null; protected String strippedContent = null; - protected List userMentions = null; - protected List memberMentions = null; - protected List emoteMentions = null; - protected List roleMentions = null; - protected List channelMentions = null; protected List invites = null; public ReceivedMessage( long id, MessageChannel channel, MessageType type, MessageReference messageReference, - 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, List stickers, List components, int flags, Message.Interaction interaction) + boolean fromWebhook, boolean tts, boolean pinned, String content, String nonce, User author, + Member member, MessageActivity activity, OffsetDateTime editTime, MessageMentions mentions, List reactions, + List attachments, List embeds, List stickers, List components, int flags, Message.Interaction interaction) { super(content, nonce, tts); this.id = id; @@ -103,21 +92,21 @@ public ReceivedMessage( this.type = type; this.api = (channel != null) ? (JDAImpl) channel.getJDA() : null; this.fromWebhook = fromWebhook; - this.mentionsEveryone = mentionsEveryone; this.pinned = pinned; this.author = author; this.member = member; this.activity = activity; this.editedTime = editTime; + this.mentions = mentions; this.reactions = Collections.unmodifiableList(reactions); this.attachments = Collections.unmodifiableList(attachments); this.embeds = Collections.unmodifiableList(embeds); this.stickers = Collections.unmodifiableList(stickers); this.components = Collections.unmodifiableList(components); - this.mentionedUsers = mentionedUsers; - this.mentionedRoles = mentionedRoles; this.flags = flags; this.interaction = interaction; + + ((MessageMentionsImpl) mentions).setMessage(this); } public ReceivedMessage withHook(InteractionHook hook) @@ -357,259 +346,6 @@ public String getJumpUrl() return String.format("https://discord.com/channels/%s/%s/%s", isFromGuild() ? getGuild().getId() : "@me", getChannel().getId(), getId()); } - private User matchUser(Matcher matcher) - { - long userId = MiscUtil.parseSnowflake(matcher.group(1)); - if (!mentionedUsers.contains(userId)) - return null; - User user = getJDA().getUserById(userId); - if (user == null && userMentions != null) - user = userMentions.stream().filter(it -> it.getIdLong() == userId).findFirst().orElse(null); - return user; - } - - @Nonnull - @Override - public synchronized List getMentionedUsers() - { - if (userMentions == null) - userMentions = Collections.unmodifiableList(processMentions(MentionType.USER, new ArrayList<>(), true, this::matchUser)); - return userMentions; - } - - @Nonnull - @Override - public Bag getMentionedUsersBag() - { - return processMentions(MentionType.USER, new HashBag<>(), false, this::matchUser); - } - - private TextChannel matchTextChannel(Matcher matcher) - { - long channelId = MiscUtil.parseSnowflake(matcher.group(1)); - return getJDA().getTextChannelById(channelId); - } - - @Nonnull - @Override - public synchronized List getMentionedChannels() - { - //TODO-v5: This needs to be updated as you can match.. quie a few more chanels than just TextChannels - if (channelMentions == null) - channelMentions = Collections.unmodifiableList(processMentions(MentionType.CHANNEL, new ArrayList<>(), true, this::matchTextChannel)); - return channelMentions; - } - - @Nonnull - @Override - public Bag getMentionedChannelsBag() - { - return processMentions(MentionType.CHANNEL, new HashBag<>(), false, this::matchTextChannel); - } - - private Role matchRole(Matcher matcher) - { - long roleId = MiscUtil.parseSnowflake(matcher.group(1)); - if (!mentionedRoles.contains(roleId)) - return null; - if (getChannelType().isGuild()) - return getGuild().getRoleById(roleId); - else - return getJDA().getRoleById(roleId); - } - - @Nonnull - @Override - public synchronized List getMentionedRoles() - { - if (roleMentions == null) - roleMentions = Collections.unmodifiableList(processMentions(MentionType.ROLE, new ArrayList<>(), true, this::matchRole)); - return roleMentions; - } - - @Nonnull - @Override - public Bag getMentionedRolesBag() - { - return processMentions(MentionType.ROLE, new HashBag<>(), false, this::matchRole); - } - - @Nonnull - @Override - public List getMentionedMembers(@Nonnull Guild guild) - { - Checks.notNull(guild, "Guild"); - if (isFromGuild() && guild.equals(getGuild()) && memberMentions != null) - return memberMentions; - List mentionedUsers = getMentionedUsers(); - List members = new ArrayList<>(); - for (User user : mentionedUsers) - { - Member member = guild.getMember(user); - if (member != null) - members.add(member); - } - - return Collections.unmodifiableList(members); - } - - @Nonnull - @Override - public List getMentionedMembers() - { - if (isFromGuild()) - return getMentionedMembers(getGuild()); - else - throw new IllegalStateException("You must specify a Guild for Messages which are not sent from a TextChannel!"); - } - - @Nonnull - @Override - public List getMentions(@Nonnull MentionType... types) - { - if (types == null || types.length == 0) - return getMentions(MentionType.values()); - List mentions = new ArrayList<>(); - // boolean duplicate checks - // not using Set because channel and role might have the same ID - boolean channel = false; - boolean role = false; - boolean user = false; - boolean emote = false; - for (MentionType type : types) - { - switch (type) - { - case EVERYONE: - case HERE: - default: continue; - case CHANNEL: - if (!channel) - mentions.addAll(getMentionedChannels()); - channel = true; - break; - case USER: - if (!user) - mentions.addAll(getMentionedUsers()); - user = true; - break; - case ROLE: - if (!role) - mentions.addAll(getMentionedRoles()); - role = true; - break; - case EMOTE: - if (!emote) - mentions.addAll(getEmotes()); - emote = true; - } - } - return Collections.unmodifiableList(mentions); - } - - @Override - public boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull MentionType... types) - { - Checks.notNull(types, "Mention Types"); - if (types.length == 0) - return isMentioned(mentionable, MentionType.values()); - final boolean isUserEntity = mentionable instanceof User || mentionable instanceof Member; - for (MentionType type : types) - { - switch (type) - { - case HERE: - { - if (isMass("@here") && isUserEntity) - return true; - break; - } - case EVERYONE: - { - if (isMass("@everyone") && isUserEntity) - return true; - break; - } - case USER: - { - if (isUserMentioned(mentionable)) - return true; - break; - } - case ROLE: - { - if (isRoleMentioned(mentionable)) - return true; - break; - } - case CHANNEL: - { - if (mentionable instanceof TextChannel) - { - if (getMentionedChannels().contains(mentionable)) - return true; - } - break; - } - case EMOTE: - { - if (mentionable instanceof Emote) - { - if (getEmotes().contains(mentionable)) - return true; - } - break; - } -// default: continue; - } - } - return false; - } - - private boolean isUserMentioned(IMentionable mentionable) - { - if (mentionable instanceof User) - { - return getMentionedUsers().contains(mentionable); - } - else if (mentionable instanceof Member) - { - final Member member = (Member) mentionable; - return getMentionedUsers().contains(member.getUser()); - } - return false; - } - - private boolean isRoleMentioned(IMentionable mentionable) - { - if (mentionable instanceof Role) - { - return getMentionedRoles().contains(mentionable); - } - else if (mentionable instanceof Member) - { - final Member member = (Member) mentionable; - return CollectionUtils.containsAny(getMentionedRoles(), member.getRoles()); - } - else if (isFromGuild() && mentionable instanceof User) - { - final Member member = getGuild().getMember((User) mentionable); - return member != null && CollectionUtils.containsAny(getMentionedRoles(), member.getRoles()); - } - return false; - } - - private boolean isMass(String s) - { - return mentionsEveryone && content.contains(s); - } - - @Override - public boolean mentionsEveryone() - { - return mentionsEveryone; - } - @Override public boolean isEdited() { @@ -655,12 +391,13 @@ public String getContentDisplay() { if (altContent != null) return altContent; + synchronized (mutex) { if (altContent != null) return altContent; String tmp = content; - for (User user : getMentionedUsers()) + for (User user : mentions.getUsers()) { String name; if (isFromGuild() && getGuild().isMember(user)) @@ -673,11 +410,11 @@ public String getContentDisplay() { tmp = tmp.replace(emote.getAsMention(), ":" + emote.getName() + ":"); } - for (TextChannel mentionedChannel : getMentionedChannels()) + for (TextChannel mentionedChannel : mentions.getChannels()) { tmp = tmp.replace(mentionedChannel.getAsMention(), '#' + mentionedChannel.getName()); } - for (Role mentionedRole : getMentionedRoles()) + for (Role mentionedRole : mentions.getRoles()) { tmp = tmp.replace(mentionedRole.getAsMention(), '@' + mentionedRole.getName()); } @@ -801,31 +538,25 @@ public List getActionRows() return components; } - private Emote matchEmote(Matcher m) + @Nonnull + @Override + public MessageMentions getMentions() { - long emoteId = MiscUtil.parseSnowflake(m.group(2)); - String name = m.group(1); - boolean animated = m.group(0).startsWith(" getEmotes() + public List getEmotes() { - if (this.emoteMentions == null) - emoteMentions = Collections.unmodifiableList(processMentions(MentionType.EMOTE, new ArrayList<>(), true, this::matchEmote)); - return emoteMentions; + return mentions.getEmotes(); } @Nonnull @Override public Bag getEmotesBag() { - return processMentions(MentionType.EMOTE, new HashBag<>(), false, this::matchEmote); + return mentions.getEmotesBag(); } @Nonnull @@ -1055,48 +786,4 @@ public void formatTo(Formatter formatter, int flags, int width, int precision) appendFormat(formatter, width, precision, leftJustified, out); } - - public void setMentions(List users, List members) - { - users.sort(Comparator.comparing((user) -> - Math.max(content.indexOf("<@" + user.getId() + ">"), - content.indexOf("<@!" + user.getId() + ">") - ))); - members.sort(Comparator.comparing((user) -> - Math.max(content.indexOf("<@" + user.getId() + ">"), - content.indexOf("<@!" + user.getId() + ">") - ))); - - this.userMentions = Collections.unmodifiableList(users); - this.memberMentions = Collections.unmodifiableList(members); - } - - private > C processMentions(MentionType type, C collection, boolean distinct, Function map) - { - Matcher matcher = type.getPattern().matcher(getContentRaw()); - while (matcher.find()) - { - try - { - T elem = map.apply(matcher); - if (elem == null || (distinct && collection.contains(elem))) - continue; - collection.add(elem); - } - catch (NumberFormatException ignored) {} - } - return collection; - } - - private static class FormatToken - { - public final String format; - public final int start; - - public FormatToken(String format, int start) - { - this.format = format; - this.start = start; - } - } } 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 3ed44f1343..65b9fbad6f 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/SystemMessage.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/SystemMessage.java @@ -32,13 +32,12 @@ public class SystemMessage extends ReceivedMessage { public SystemMessage( long id, MessageChannel channel, MessageType type, MessageReference messageReference, - boolean fromWebhook, boolean mentionsEveryone, TLongSet mentionedUsers, TLongSet mentionedRoles, - boolean tts, boolean pinned, - String content, String nonce, User author, Member member, MessageActivity activity, OffsetDateTime editTime, + boolean fromWebhook, boolean tts, boolean pinned, + String content, String nonce, User author, Member member, MessageActivity activity, OffsetDateTime editTime, MessageMentions mentions, List reactions, List attachments, List embeds, List stickers, int flags) { - super(id, channel, type, messageReference, fromWebhook, mentionsEveryone, mentionedUsers, mentionedRoles, - tts, pinned, content, nonce, author, member, activity, editTime, reactions, attachments, embeds, stickers, Collections.emptyList(), flags, null); + super(id, channel, type, messageReference, fromWebhook, tts, pinned, content, nonce, author, member, + activity, editTime, mentions, reactions, attachments, embeds, stickers, Collections.emptyList(), flags, null); } @Nonnull diff --git a/src/main/java/net/dv8tion/jda/internal/utils/AllowedMentionsImpl.java b/src/main/java/net/dv8tion/jda/internal/utils/AllowedMentionsImpl.java index 6b299a457b..9d19516c8e 100644 --- a/src/main/java/net/dv8tion/jda/internal/utils/AllowedMentionsImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/utils/AllowedMentionsImpl.java @@ -108,7 +108,7 @@ public AllowedMentionsImpl applyMessage(Message message) else { // Only ping everyone if the message also did - if (message.mentionsEveryone()) + if (message.getMentions().mentionsEveryone()) { String content = message.getContentRaw(); EnumSet parse = EnumSet.noneOf(Message.MentionType.class); @@ -123,8 +123,8 @@ public AllowedMentionsImpl applyMessage(Message message) this.parse = EnumSet.noneOf(Message.MentionType.class); } - this.mention(message.getMentionedUsers()) - .mention(message.getMentionedRoles()); + this.mention(message.getMentions().getUsers()) + .mention(message.getMentions().getRoles()); } return this; } From e8e9d3bb59150667cc96de3d59d95e51c34c7886 Mon Sep 17 00:00:00 2001 From: Austin Keener Date: Sat, 5 Feb 2022 02:58:02 -0500 Subject: [PATCH 02/29] Fixed examples --- src/examples/java/MessageListenerExample.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/examples/java/MessageListenerExample.java b/src/examples/java/MessageListenerExample.java index 808cd1dd1e..5a5785bfd0 100644 --- a/src/examples/java/MessageListenerExample.java +++ b/src/examples/java/MessageListenerExample.java @@ -170,7 +170,7 @@ else if (msg.startsWith("!kick")) //Note, I used "startsWith, not equals. if (message.isFromType(ChannelType.TEXT)) { //If no users are provided, we can't kick anyone! - if (message.getMentionedUsers().isEmpty()) + if (message.getMentions().getUsers().isEmpty()) { channel.sendMessage("You must mention 1 or more Users to be kicked!").queue(); } @@ -188,7 +188,7 @@ else if (msg.startsWith("!kick")) //Note, I used "startsWith, not equals. } //Loop over all mentioned users, kicking them one at a time. Mwauahahah! - List mentionedUsers = message.getMentionedUsers(); + List mentionedUsers = message.getMentions().getUsers(); for (User user : mentionedUsers) { Member member = guild.getMember(user); //We get the member object for each mentioned user to kick them! From 50e02146c158ff1ee8b7a5dc73f8149b5d902790 Mon Sep 17 00:00:00 2001 From: Austin Keener Date: Sat, 5 Feb 2022 03:03:43 -0500 Subject: [PATCH 03/29] Removed unneeded TODOs --- .../net/dv8tion/jda/internal/entities/MessageMentionsImpl.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java index 42f9c264eb..5beb1ec46f 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java @@ -32,7 +32,6 @@ import java.util.function.Function; import java.util.regex.Matcher; -//TODO-v5 | Docs public class MessageMentionsImpl implements MessageMentions { private Message message; @@ -54,13 +53,11 @@ public MessageMentionsImpl(boolean mentionsEveryone, TLongSet mentionedUsers, TL this.mentionedRoles = mentionedRoles; } - //TODO-v5 | Docs public JDA getJDA() { return message.getJDA(); } - //TODO-v5 | Docs public Message getMessage() { return message; From e93130530bfe64499072334766f7baf2090a8d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sun, 17 Apr 2022 15:20:04 +0200 Subject: [PATCH 04/29] Finish mentions rework implementation --- .../jda/api/entities/MessageMentions.java | 107 ++------- .../jda/internal/entities/EntityBuilder.java | 58 +---- .../entities/MessageMentionsImpl.java | 207 ++++++++++-------- 3 files changed, 149 insertions(+), 223 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java b/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java index c5d2a4edd3..651fe7bc28 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java +++ b/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java @@ -26,16 +26,11 @@ public interface MessageMentions { //TODO-v5 | Docs + @Nonnull JDA getJDA(); - //TODO-v5 | Docs - Message getMessage(); - /** - * Indicates if this Message mentions everyone using @everyone or @here. - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * Indicates if this Message mentions everyone, using @everyone or @here. * * @return True, if message is mentioning everyone */ @@ -46,11 +41,9 @@ public interface MessageMentions *
If no user was mentioned, this list is empty. Elements are sorted in order of appearance. This only * counts direct mentions of the user and not mentions through roles or the everyone tag. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * * @return immutable list of mentioned users */ + @Nonnull List getUsers(); /** @@ -62,8 +55,8 @@ public interface MessageMentions *

{@code
      * void sendCount(Message msg)
      * {
-     *     List mentions = msg.getMentionedUsers(); // distinct list, in order of appearance
-     *     Bag count = msg.getMentionedUsersBag();
+     *     List mentions = msg.getMentions().getUsers(); // distinct list, in order of appearance
+     *     Bag count = msg.getMentions().getUsersBag();
      *     StringBuilder content = new StringBuilder();
      *     for (User user : mentions)
      *     {
@@ -76,9 +69,6 @@ public interface MessageMentions
      * }
      * }
* - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * * @return {@link org.apache.commons.collections4.Bag Bag} of mentioned users * * @see #getUsers() @@ -87,18 +77,15 @@ public interface MessageMentions Bag getUsersBag(); /** - * A immutable list of all mentioned {@link net.dv8tion.jda.api.entities.TextChannel TextChannels}. + * A immutable list of all mentioned {@link net.dv8tion.jda.api.entities.GuildChannel GuildChannels}. *
If none were mentioned, this list is empty. Elements are sorted in order of appearance. * - *

This may include TextChannels from other {@link net.dv8tion.jda.api.entities.Guild Guilds} - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + *

This may include GuildChannels from other {@link net.dv8tion.jda.api.entities.Guild Guilds} * * @return immutable list of mentioned TextChannels */ @Nonnull - List getChannels(); + List getChannels(); /** * A {@link org.apache.commons.collections4.Bag Bag} of mentioned channels. @@ -108,10 +95,10 @@ public interface MessageMentions *

{@code
      * void sendCount(Message msg)
      * {
-     *     List mentions = msg.getMentionedTextChannels(); // distinct list, in order of appearance
-     *     Bag count = msg.getMentionedTextChannelsBag();
+     *     List mentions = msg.getMentions().getChannels(); // distinct list, in order of appearance
+     *     Bag count = msg.getMentions().getChannelsBag();
      *     StringBuilder content = new StringBuilder();
-     *     for (TextChannel channel : mentions)
+     *     for (GuildChannel channel : mentions)
      *     {
      *         content.append("#")
      *                .append(channel.getName())
@@ -123,15 +110,12 @@ public interface MessageMentions
      * }
      * }
* - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * * @return {@link org.apache.commons.collections4.Bag Bag} of mentioned channels * * @see #getChannels() */ @Nonnull - Bag getChannelsBag(); + Bag getChannelsBag(); /** * A immutable list of all mentioned {@link net.dv8tion.jda.api.entities.Role Roles}. @@ -140,9 +124,6 @@ public interface MessageMentions * *

This may include Roles from other {@link net.dv8tion.jda.api.entities.Guild Guilds} * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * * @return immutable list of mentioned Roles */ @Nonnull @@ -152,14 +133,13 @@ public interface MessageMentions * A {@link org.apache.commons.collections4.Bag Bag} of mentioned roles. *
This can be used to retrieve the amount of times a role was mentioned in this message. This only * counts direct mentions of the role and not mentions through the everyone tag. - * If a role is not {@link net.dv8tion.jda.api.entities.Role#isMentionable() mentionable} it will not be included. * *

Example

*
{@code
      * void sendCount(Message msg)
      * {
-     *     List mentions = msg.getMentionedRoles(); // distinct list, in order of appearance
-     *     Bag count = msg.getMentionedRolesBag();
+     *     List mentions = msg.getMentions().getRoles(); // distinct list, in order of appearance
+     *     Bag count = msg.getMentions().getRolesBag();
      *     StringBuilder content = new StringBuilder();
      *     for (Role role : mentions)
      *     {
@@ -172,9 +152,6 @@ public interface MessageMentions
      * }
      * }
* - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * * @return {@link org.apache.commons.collections4.Bag Bag} of mentioned roles * * @see #getRoles() @@ -190,9 +167,6 @@ public interface MessageMentions * *

Unicode emojis are not included as {@link net.dv8tion.jda.api.entities.Emote Emote}! * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * * @return An immutable list of the Emotes used in this message (example match {@literal <:jda:230988580904763393>}) */ @Nonnull @@ -206,8 +180,8 @@ public interface MessageMentions *

{@code
      * void sendCount(Message msg)
      * {
-     *     List emotes = msg.getEmotes(); // distinct list, in order of appearance
-     *     Bag count = msg.getEmotesBag();
+     *     List emotes = msg.getMentions().getEmotes(); // distinct list, in order of appearance
+     *     Bag count = msg.getMentions().getEmotesBag();
      *     StringBuilder content = new StringBuilder();
      *     for (Emote emote : emotes)
      *     {
@@ -220,9 +194,6 @@ public interface MessageMentions
      * }
      * }
* - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * * @return {@link org.apache.commons.collections4.Bag Bag} of used emotes * * @see #getEmotes() @@ -230,52 +201,23 @@ public interface MessageMentions @Nonnull Bag getEmotesBag(); - /** - * Creates an immutable list of {@link net.dv8tion.jda.api.entities.Member Members} - * representing the users of {@link #getUsers()} in the specified - * {@link net.dv8tion.jda.api.entities.Guild Guild}. - *
This is only a convenience method and will skip all users that are not in the specified - * Guild. - * - * @param guild - * Non-null {@link net.dv8tion.jda.api.entities.Guild Guild} - * that will be used to retrieve Members. - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * @throws java.lang.IllegalArgumentException - * If the specified Guild is {@code null} - * - * @return Immutable list of mentioned Members - * - * @since 3.4.0 - */ - @Nonnull - List getMembers(@Nonnull Guild guild); - /** * Creates an immutable list of {@link net.dv8tion.jda.api.entities.Member Members} * representing the users of {@link #getUsers()} in the * {@link net.dv8tion.jda.api.entities.Guild Guild} this Message was sent in. - *
This is only a convenience method and will skip all users that are not in the specified Guild. - *
It will provide the {@link Message()#getGuild() Message's Guild} to {@link #getMembers(Guild)}. - * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * @throws java.lang.IllegalStateException - * If this message was not sent in a {@link net.dv8tion.jda.api.entities.TextChannel TextChannel} * - * @return Immutable list of mentioned Members - * - * @since 3.4.0 + * @return Immutable list of mentioned Members, or an empty list if this message was not sent in a guild */ @Nonnull List getMembers(); + @Nonnull + Bag getMembersBag(); + /** * Combines all instances of {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} * filtered by the specified {@link net.dv8tion.jda.api.entities.Message.MentionType MentionType} values. - *
This does not include {@link #getMembers()} to avoid duplicates. + *
This does not include {@link #getUsers()} to avoid duplicates. * *

If no MentionType values are given this will fallback to all types. * @@ -283,14 +225,10 @@ public interface MessageMentions * Amount of {@link net.dv8tion.jda.api.entities.Message.MentionType MentionTypes} * to include in the list of mentions * - * @throws java.lang.UnsupportedOperationException - * If this is a system message * @throws java.lang.IllegalArgumentException * If provided with {@code null} * * @return Immutable list of filtered {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} instances - * - * @since 3.4.0 */ @Nonnull List getMentions(@Nonnull Message.MentionType... types); @@ -312,9 +250,6 @@ public interface MessageMentions * The types to include when checking whether this type was mentioned. * This will be used with {@link #getMentions(Message.MentionType...) getMentions(MentionType...)} * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * * @return True, if the given mentionable was mentioned in this message */ boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull Message.MentionType... types); 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 4cce953497..8c0a2b1766 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -1389,6 +1389,10 @@ else if (channel.getUser() == null && !isAuthorSelfUser) private ReceivedMessage createMessage0(DataObject jsonObject, @Nonnull MessageChannel channel, boolean modifyCache) { + MessageType type = MessageType.fromId(jsonObject.getInt("type")); + if (type == MessageType.UNKNOWN) + throw new IllegalArgumentException(UNKNOWN_MESSAGE_TYPE); + final long id = jsonObject.getLong("id"); final DataObject author = jsonObject.getObject("author"); final long authorId = author.getLong("id"); @@ -1472,10 +1476,6 @@ private ReceivedMessage createMessage0(DataObject jsonObject, @Nonnull MessageCh mentionedRoles.add(arr.getLong(i)); }); - MessageMentions mentions = new MessageMentionsImpl(mentionsEveryone, mentionedUsers, mentionedRoles); - - MessageType type = MessageType.fromId(jsonObject.getInt("type")); - ReceivedMessage message; Message referencedMessage = null; if (!jsonObject.isNull("referenced_message")) { @@ -1534,56 +1534,22 @@ else if (MISSING_CHANNEL.equals(ex.getMessage())) messageInteraction = createMessageInteraction(guild, jsonObject.getObject("interaction")); } - if (type == MessageType.UNKNOWN) - throw new IllegalArgumentException(UNKNOWN_MESSAGE_TYPE); + GuildImpl guild = channel instanceof GuildChannel ? ((AbstractGuildChannelImpl) channel).getGuild() : null; + MessageMentions mentions = new MessageMentionsImpl( + api, guild, content, messageReference != null, + mentionsEveryone, jsonObject.getArray("mentions"), jsonObject.getArray("mention_roles") + ); + if (!type.isSystem()) { - message = new ReceivedMessage(id, channel, type, messageReference, fromWebhook, tts, pinned, + return new ReceivedMessage(id, channel, type, messageReference, fromWebhook, tts, pinned, content, nonce, user, member, activity, editTime, mentions, reactions, attachments, embeds, stickers, components, flags, messageInteraction); } else { - message = new SystemMessage(id, channel, type, messageReference, fromWebhook, tts, pinned, + return new SystemMessage(id, channel, type, messageReference, fromWebhook, tts, pinned, content, nonce, user, member, activity, editTime, mentions, reactions, attachments, embeds, stickers, flags); - - return message; // We don't need to parse mentions for system messages, they are always empty anyway } - - GuildImpl guild = message.isFromGuild() ? (GuildImpl) message.getGuild() : null; - - // Load users/members from message object through mentions - List mentionedUsersList = new ArrayList<>(); - List mentionedMembersList = new ArrayList<>(); - DataArray userMentions = jsonObject.getArray("mentions"); - - for (int i = 0; i < userMentions.length(); i++) - { - DataObject mentionJson = userMentions.getObject(i); - 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); - if (guild != null) - { - Member mentionedMember = guild.getMember(mentionedUser); - if (mentionedMember != null) - mentionedMembersList.add(mentionedMember); - } - continue; - } - - // Load member/user from mention (gateway messages only) - DataObject memberJson = mentionJson.getObject("member"); - mentionJson.remove("member"); - memberJson.put("user", mentionJson); - Member mentionedMember = createMember(guild, memberJson); - mentionedMembersList.add(mentionedMember); - mentionedUsersList.add(mentionedMember.getUser()); - } - - ((MessageMentionsImpl) message.getMentions()).setUserMemberMentions(mentionedUsersList, mentionedMembersList); - return message; } private static MessageActivity createMessageActivity(DataObject jsonObject) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java index 5beb1ec46f..420851d67d 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java @@ -16,51 +16,67 @@ package net.dv8tion.jda.internal.entities; +import gnu.trove.map.TLongObjectMap; +import gnu.trove.map.hash.TLongObjectHashMap; import gnu.trove.set.TLongSet; +import gnu.trove.set.hash.TLongHashSet; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.utils.MiscUtil; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.internal.JDAImpl; -import net.dv8tion.jda.internal.entities.EmoteImpl; import net.dv8tion.jda.internal.utils.Checks; import org.apache.commons.collections4.Bag; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.bag.HashBag; import javax.annotation.Nonnull; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.function.Function; import java.util.regex.Matcher; +import java.util.stream.Collectors; public class MessageMentionsImpl implements MessageMentions { - private Message message; - + private final JDAImpl jda; + private final GuildImpl guild; + private final String content; + private final boolean hasMessageReference; private final boolean mentionsEveryone; - private final TLongSet mentionedUsers; - private final TLongSet mentionedRoles; + private final TLongObjectMap mentionedMembers; + private final TLongObjectMap mentionedRoles; - private List userMentions = null; private List memberMentions = null; private List emoteMentions = null; private List roleMentions = null; - private List channelMentions = null; + private List channelMentions = null; - public MessageMentionsImpl(boolean mentionsEveryone, TLongSet mentionedUsers, TLongSet mentionedRoles) + public MessageMentionsImpl(JDAImpl jda, GuildImpl guild, String content, boolean hasReference, + boolean mentionsEveryone, DataArray userMentions, DataArray roleMentions) { + this.jda = jda; + this.guild = guild; + this.content = content; + this.hasMessageReference = hasReference; this.mentionsEveryone = mentionsEveryone; - this.mentionedUsers = mentionedUsers; - this.mentionedRoles = mentionedRoles; - } + this.mentionedMembers = new TLongObjectHashMap<>(userMentions.length()); + this.mentionedRoles = new TLongObjectHashMap<>(roleMentions.length()); - public JDA getJDA() - { - return message.getJDA(); + userMentions.stream(DataArray::getObject) + .forEach(obj -> mentionedMembers.put(obj.getObject("user").getUnsignedLong("id"), obj)); + roleMentions.stream(DataArray::getObject) + .forEach(obj -> mentionedRoles.put(obj.getUnsignedLong("id"), obj)); } - public Message getMessage() + @Nonnull + @Override + public JDA getJDA() { - return message; + return jda; } @Override @@ -69,40 +85,39 @@ public boolean mentionsEveryone() return mentionsEveryone; } + @Nonnull @Override - public synchronized List getUsers() + public List getUsers() { - if (userMentions == null) - userMentions = Collections.unmodifiableList(processMentions(Message.MentionType.USER, new ArrayList<>(), true, this::matchUser)); - return userMentions; + List members = getMembers(); + return members.stream().map(Member::getUser).collect(Collectors.toList()); } - @Override @Nonnull + @Override public Bag getUsersBag() { return processMentions(Message.MentionType.USER, new HashBag<>(), false, this::matchUser); } - @Override @Nonnull - public synchronized List getChannels() + @Override + public synchronized List getChannels() { - //TODO-MessageMentions: This needs to be updated as you can match.. quie a few more chanels than just TextChannels if (channelMentions == null) - channelMentions = Collections.unmodifiableList(processMentions(Message.MentionType.CHANNEL, new ArrayList<>(), true, this::matchTextChannel)); + channelMentions = Collections.unmodifiableList(processMentions(Message.MentionType.CHANNEL, new ArrayList<>(), true, this::matchChannel)); return channelMentions; } - @Override @Nonnull - public Bag getChannelsBag() + @Override + public Bag getChannelsBag() { - return processMentions(Message.MentionType.CHANNEL, new HashBag<>(), false, this::matchTextChannel); + return processMentions(Message.MentionType.CHANNEL, new HashBag<>(), false, this::matchChannel); } - @Override @Nonnull + @Override public synchronized List getRoles() { if (roleMentions == null) @@ -110,60 +125,71 @@ public synchronized List getRoles() return roleMentions; } - @Override @Nonnull + @Override public Bag getRolesBag() { return processMentions(Message.MentionType.ROLE, new HashBag<>(), false, this::matchRole); } - @Override @Nonnull - public List getMembers(@Nonnull Guild guild) + @Override + public synchronized List getMembers() { - Checks.notNull(guild, "Guild"); - if (message.isFromGuild() && guild.equals(message.getGuild()) && memberMentions != null) + if (guild == null) + return Collections.emptyList(); + if (memberMentions != null) return memberMentions; - List mentionedUsers = getUsers(); - List members = new ArrayList<>(); - for (User user : mentionedUsers) - { - Member member = guild.getMember(user); - if (member != null) - members.add(member); - } - return Collections.unmodifiableList(members); + // Parse members from mentions array in order of appearance + EntityBuilder entityBuilder = jda.getEntityBuilder(); + TLongSet unseen = new TLongHashSet(mentionedMembers.keySet()); + ArrayList members = processMentions(Message.MentionType.USER, new ArrayList<>(), true, (matcher) -> { + long id = Long.parseUnsignedLong(matcher.group(1)); + DataObject member = mentionedMembers.get(id); + unseen.remove(id); + return member == null ? null : entityBuilder.createMember(guild, member); + }); + + // Add reply mention at first index + if (hasMessageReference && !unseen.isEmpty()) + members.add(0, entityBuilder.createMember(guild, mentionedMembers.get(unseen.iterator().next()))); + + // Update member cache + members.stream() + .map(MemberImpl.class::cast) + .forEach(entityBuilder::updateMemberCache); + + return memberMentions = Collections.unmodifiableList(members); } - @Override @Nonnull - public List getMembers() + @Override + public Bag getMembersBag() { - if (message.isFromGuild()) - return getMembers(message.getGuild()); - else - throw new IllegalStateException("You must specify a Guild for Messages which are not sent from a TextChannel!"); + if (guild == null) + return new HashBag<>(); + return processMentions(Message.MentionType.USER, new HashBag<>(), false, this::matchMember); } - @Override @Nonnull + @Override public synchronized List getEmotes() { - if (this.emoteMentions == null) + if (emoteMentions == null) emoteMentions = Collections.unmodifiableList(processMentions(Message.MentionType.EMOTE, new ArrayList<>(), true, this::matchEmote)); return emoteMentions; } - @Override @Nonnull + @Override public Bag getEmotesBag() { return processMentions(Message.MentionType.EMOTE, new HashBag<>(), false, this::matchEmote); } - @Override @Nonnull + @Override public List getMentions(@Nonnull Message.MentionType... types) { if (types == null || types.length == 0) @@ -189,7 +215,7 @@ public List getMentions(@Nonnull Message.MentionType... types) break; case USER: if (!user) - mentions.addAll(getUsers()); + mentions.addAll(getMembers()); user = true; break; case ROLE: @@ -265,34 +291,11 @@ public boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull Message.M return false; } - // ======== Setters ============ - - public void setMessage(Message message) - { - this.message = message; - } - - public void setUserMemberMentions(List users, List members) - { - String content = message.getContentRaw(); - users.sort(Comparator.comparing((user) -> - Math.max(content.indexOf("<@" + user.getId() + ">"), - content.indexOf("<@!" + user.getId() + ">") - ))); - members.sort(Comparator.comparing((user) -> - Math.max(content.indexOf("<@" + user.getId() + ">"), - content.indexOf("<@!" + user.getId() + ">") - ))); - - this.userMentions = Collections.unmodifiableList(users); - this.memberMentions = Collections.unmodifiableList(members); - } - // ============= Internal Helpers ================= private > C processMentions(Message.MentionType type, C collection, boolean distinct, Function map) { - Matcher matcher = type.getPattern().matcher(message.getContentRaw()); + Matcher matcher = type.getPattern().matcher(content); while (matcher.find()) { try @@ -310,27 +313,49 @@ private > C processMentions(Message.MentionType type, private User matchUser(Matcher matcher) { long userId = MiscUtil.parseSnowflake(matcher.group(1)); - if (!mentionedUsers.contains(userId)) + if (!mentionedMembers.containsKey(userId)) return null; User user = getJDA().getUserById(userId); - if (user == null && userMentions != null) - user = userMentions.stream().filter(it -> it.getIdLong() == userId).findFirst().orElse(null); + if (user == null) + { + user = getMembers().stream() + .filter(it -> it.getIdLong() == userId) + .map(Member::getUser) + .findFirst() + .orElse(null); + } return user; } - private TextChannel matchTextChannel(Matcher matcher) + private Member matchMember(Matcher matcher) + { + long userId = MiscUtil.parseSnowflake(matcher.group(1)); + if (!mentionedMembers.containsKey(userId)) + return null; + Member member = guild.getMemberById(userId); + if (member == null) + { + member = getMembers().stream() + .filter(it -> it.getIdLong() == userId) + .findFirst() + .orElse(null); + } + return member; + } + + private GuildChannel matchChannel(Matcher matcher) { long channelId = MiscUtil.parseSnowflake(matcher.group(1)); - return getJDA().getTextChannelById(channelId); + return getJDA().getGuildChannelById(channelId); } private Role matchRole(Matcher matcher) { long roleId = MiscUtil.parseSnowflake(matcher.group(1)); - if (!mentionedRoles.contains(roleId)) + if (!mentionedRoles.containsKey(roleId)) return null; - if (message.getChannelType().isGuild()) - return message.getGuild().getRoleById(roleId); + if (guild != null) + return guild.getRoleById(roleId); else return getJDA().getRoleById(roleId); } @@ -342,7 +367,7 @@ private Emote matchEmote(Matcher m) boolean animated = m.group(0).startsWith(" Date: Sun, 17 Apr 2022 15:46:00 +0200 Subject: [PATCH 05/29] Add interaction mentions --- .../interactions/commands/OptionMapping.java | 159 ++------- .../entities/InteractionMentions.java | 104 ++++++ .../entities/MessageMentionsImpl.java | 300 ++-------------- .../entities/mentions/AbstractMentions.java | 319 ++++++++++++++++++ 4 files changed, 465 insertions(+), 417 deletions(-) create mode 100644 src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java create mode 100644 src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionMapping.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionMapping.java index 7f8549d0e8..9688819de2 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionMapping.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionMapping.java @@ -17,16 +17,19 @@ package net.dv8tion.jda.api.interactions.commands; import gnu.trove.map.TLongObjectMap; +import gnu.trove.map.hash.TLongObjectHashMap; +import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.interactions.Interaction; import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.entities.InteractionMentions; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.*; -import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.Objects; /** * Name/Value pair for a {@link CommandInteraction} option. @@ -43,159 +46,35 @@ public class OptionMapping private final OptionType type; private final String name; private final TLongObjectMap resolved; + private final MessageMentions mentions; - public OptionMapping(DataObject data, TLongObjectMap resolved) + public OptionMapping(DataObject data, TLongObjectMap resolved, JDA jda, Guild guild) { this.data = data; this.type = OptionType.fromKey(data.getInt("type", -1)); this.name = data.getString("name"); this.resolved = resolved; - } - - private > C parseMentions(C coll, Pattern pattern, boolean duplicates, Function resolver) - { - Matcher matcher = pattern.matcher(getAsString()); - while (matcher.find()) - { - try - { - T obj = resolver.apply(matcher); - if (obj != null && (duplicates || !coll.contains(obj))) - coll.add(obj); - } - catch (NumberFormatException ignored) {} - } - - return coll; + if (type == OptionType.STRING) + mentions = new InteractionMentions(getAsString(), resolved, (JDAImpl) jda, (GuildImpl) guild); + else + mentions = new InteractionMentions("", new TLongObjectHashMap<>(0), (JDAImpl) jda, (GuildImpl) guild); } /** - * Resolved {@link Member} mentions for a {@link OptionType#STRING STRING} option. - *
If this option is not of type {@link OptionType#STRING STRING}, this always returns an empty list. + * Resolved mentions for a {@link OptionType#STRING STRING} option. + *
If this option is not of type {@link OptionType#STRING STRING}, this always returns empty lists. * Mentions are sorted by occurrence. * - *

This only contains members of the guild. - * If the user mentions users from other guilds, they will only be provided by {@link #getMentionedUsers()}. + *

Mentioned {@link Member members} and {@link Role roles} are always of the same guild. + * If the interaction {@link Interaction#getUser() user}, mentions users from other guilds, they will only be provided by {@link MessageMentions#getUsers()}. * *

This is not supported for {@link CommandAutoCompleteInteraction}. * - * @return {@link List} of {@link Member} the resolved guild user mentions in a string option + * @return {@link MessageMentions} for this option */ @Nonnull - public List getMentionedMembers() + public MessageMentions getMentions() { - if (type != OptionType.STRING) - return Collections.emptyList(); - - return parseMentions(new ArrayList<>(), Message.MentionType.USER.getPattern(), false, (matcher) -> { - long id = Long.parseUnsignedLong(matcher.group(1)); - Object obj = resolved.get(id); - return obj instanceof Member ? (Member) obj : null; - }); - } - - /** - * Resolved {@link User} mentions for a {@link OptionType#STRING STRING} option. - *
If this option is not of type {@link OptionType#STRING STRING}, this always returns an empty list. - * Mentions are sorted by occurrence. - * - *

This may also contain users which are not members in the guild! - * - *

This is not supported for {@link CommandAutoCompleteInteraction}. - * - * @return {@link List} of {@link User} the resolved guild user mentions in a string option - */ - @Nonnull - public List getMentionedUsers() - { - if (type != OptionType.STRING) - return Collections.emptyList(); - - return parseMentions(new ArrayList<>(), Message.MentionType.USER.getPattern(), false, (matcher) -> { - long id = Long.parseUnsignedLong(matcher.group(1)); - Object obj = resolved.get(id); - if (obj instanceof User) - return (User) obj; - if (obj instanceof Member) - return ((Member) obj).getUser(); - return null; - }); - } - - /** - * Resolved {@link Role} mentions for a {@link OptionType#STRING STRING} option. - *
If this option is not of type {@link OptionType#STRING STRING}, this always returns an empty list. - * Mentions are sorted by occurrence. - * - *

This is not supported for {@link CommandAutoCompleteInteraction}. - * - * @return {@link List} of {@link Role} the resolved guild role mentions in a string option - */ - @Nonnull - public List getMentionedRoles() - { - if (type != OptionType.STRING) - return Collections.emptyList(); - - return parseMentions(new ArrayList<>(), Message.MentionType.ROLE.getPattern(), false, (matcher) -> { - long id = Long.parseUnsignedLong(matcher.group(1)); - Object obj = resolved.get(id); - return obj instanceof Role ? (Role) obj : null; - }); - } - - /** - * Resolved {@link GuildChannel} mentions for a {@link OptionType#STRING STRING} option. - *
If this option is not of type {@link OptionType#STRING STRING}, this always returns an empty list. - * Mentions are sorted by occurrence. - * - *

This is not supported for {@link CommandAutoCompleteInteraction}. - * - * @return {@link List} of {@link GuildChannel} the resolved guild channel mentions in a string option - */ - @Nonnull - public List getMentionedChannels() - { - if (type != OptionType.STRING) - return Collections.emptyList(); - - return parseMentions(new ArrayList<>(), Message.MentionType.CHANNEL.getPattern(), false, (matcher) -> { - long id = Long.parseUnsignedLong(matcher.group(1)); - Object obj = resolved.get(id); - return obj instanceof GuildChannel ? (GuildChannel) obj : null; - }); - } - - /** - * All resolved {@link IMentionable mentions} for a {@link OptionType#STRING STRING} option. - *
If this option is not of type {@link OptionType#STRING STRING}, this always returns an empty list. - * Mentions are sorted by occurrence. - * - *

This is not supported for {@link CommandAutoCompleteInteraction}. - * - * This merges {@link #getMentionedUsers()}, {@link #getMentionedMembers()}, {@link #getMentionedRoles()}, and {@link #getMentionedChannels()}. - * - * @return {@link List} of {@link IMentionable} the resolved mentions in a string option - */ - @Nonnull - public List getMentions() - { - if (type != OptionType.STRING) - return Collections.emptyList(); - - List users = getMentionedUsers(); - List members = getMentionedMembers(); - List roles = getMentionedRoles(); - List channels = getMentionedChannels(); - users.removeIf(user -> members.stream().anyMatch(m -> m.getIdLong() == user.getIdLong())); - - List mentions = new ArrayList<>(users.size() + members.size() + roles.size() + channels.size()); - mentions.addAll(users); - mentions.addAll(members); - mentions.addAll(roles); - mentions.addAll(channels); - mentions.sort(Comparator.comparingInt(mention -> getAsString().indexOf(mention.getId()))); - return mentions; } diff --git a/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java new file mode 100644 index 0000000000..33a8364cd4 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java @@ -0,0 +1,104 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.entities; + +import gnu.trove.map.TLongObjectMap; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.utils.MiscUtil; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.mentions.AbstractMentions; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.regex.Matcher; + +public class InteractionMentions extends AbstractMentions +{ + protected final TLongObjectMap resolved; + + public InteractionMentions(String content, TLongObjectMap resolved, JDAImpl jda, GuildImpl guild) + { + super(content, jda, guild, false); + this.resolved = resolved; + } + + protected User matchUser(Matcher matcher) + { + long userId = MiscUtil.parseSnowflake(matcher.group(1)); + Object it = resolved.get(userId); + return it instanceof User + ? (User) it + : it instanceof Member + ? ((Member) it).getUser() + : null; + } + + protected Member matchMember(Matcher matcher) + { + long userId = MiscUtil.parseSnowflake(matcher.group(1)); + Object it = resolved.get(userId); + return it instanceof Member ? (Member) it : null; + } + + protected GuildChannel matchChannel(Matcher matcher) + { + long channelId = MiscUtil.parseSnowflake(matcher.group(1)); + Object it = resolved.get(channelId); + return it instanceof GuildChannel ? (GuildChannel) it : null; + } + + protected Role matchRole(Matcher matcher) + { + long roleId = MiscUtil.parseSnowflake(matcher.group(1)); + Object it = resolved.get(roleId); + return it instanceof Role ? (Role) it : null; + } + + protected Emote matchEmote(Matcher m) + { + long emoteId = MiscUtil.parseSnowflake(m.group(2)); + String name = m.group(1); + boolean animated = m.group(0).startsWith(" mentionedMembers; + private final TLongObjectMap memberMentions; private final TLongObjectMap mentionedRoles; - private List memberMentions = null; - private List emoteMentions = null; - private List roleMentions = null; - private List channelMentions = null; - - public MessageMentionsImpl(JDAImpl jda, GuildImpl guild, String content, boolean hasReference, + public MessageMentionsImpl(JDAImpl jda, GuildImpl guild, String content, boolean mentionsEveryone, DataArray userMentions, DataArray roleMentions) { - this.jda = jda; - this.guild = guild; - this.content = content; - this.hasMessageReference = hasReference; - this.mentionsEveryone = mentionsEveryone; - this.mentionedMembers = new TLongObjectHashMap<>(userMentions.length()); + super(content, jda, guild, mentionsEveryone); + this.memberMentions = new TLongObjectHashMap<>(userMentions.length()); this.mentionedRoles = new TLongObjectHashMap<>(roleMentions.length()); userMentions.stream(DataArray::getObject) - .forEach(obj -> mentionedMembers.put(obj.getObject("user").getUnsignedLong("id"), obj)); + .forEach(obj -> memberMentions.put(obj.getObject("user").getUnsignedLong("id"), obj)); roleMentions.stream(DataArray::getObject) .forEach(obj -> mentionedRoles.put(obj.getUnsignedLong("id"), obj)); } - @Nonnull - @Override - public JDA getJDA() - { - return jda; - } - - @Override - public boolean mentionsEveryone() - { - return mentionsEveryone; - } - - @Nonnull - @Override - public List getUsers() - { - List members = getMembers(); - return members.stream().map(Member::getUser).collect(Collectors.toList()); - } - - @Nonnull - @Override - public Bag getUsersBag() - { - return processMentions(Message.MentionType.USER, new HashBag<>(), false, this::matchUser); - } - - @Nonnull - @Override - public synchronized List getChannels() - { - if (channelMentions == null) - channelMentions = Collections.unmodifiableList(processMentions(Message.MentionType.CHANNEL, new ArrayList<>(), true, this::matchChannel)); - return channelMentions; - } - - @Nonnull - @Override - public Bag getChannelsBag() - { - return processMentions(Message.MentionType.CHANNEL, new HashBag<>(), false, this::matchChannel); - } - - @Nonnull - @Override - public synchronized List getRoles() - { - if (roleMentions == null) - roleMentions = Collections.unmodifiableList(processMentions(Message.MentionType.ROLE, new ArrayList<>(), true, this::matchRole)); - return roleMentions; - } - - @Nonnull - @Override - public Bag getRolesBag() - { - return processMentions(Message.MentionType.ROLE, new HashBag<>(), false, this::matchRole); - } - @Nonnull @Override public synchronized List getMembers() { if (guild == null) return Collections.emptyList(); - if (memberMentions != null) - return memberMentions; + if (mentionedMembers != null) + return mentionedMembers; // Parse members from mentions array in order of appearance EntityBuilder entityBuilder = jda.getEntityBuilder(); - TLongSet unseen = new TLongHashSet(mentionedMembers.keySet()); + TLongSet unseen = new TLongHashSet(memberMentions.keySet()); ArrayList members = processMentions(Message.MentionType.USER, new ArrayList<>(), true, (matcher) -> { long id = Long.parseUnsignedLong(matcher.group(1)); - DataObject member = mentionedMembers.get(id); + DataObject member = memberMentions.get(id); unseen.remove(id); return member == null ? null : entityBuilder.createMember(guild, member); }); - // Add reply mention at first index - if (hasMessageReference && !unseen.isEmpty()) - members.add(0, entityBuilder.createMember(guild, mentionedMembers.get(unseen.iterator().next()))); + // Add reply mentions at beginning + for (TLongIterator iter = unseen.iterator(); iter.hasNext();) + members.add(0, entityBuilder.createMember(guild, memberMentions.get(iter.next()))); // Update member cache members.stream() .map(MemberImpl.class::cast) .forEach(entityBuilder::updateMemberCache); - return memberMentions = Collections.unmodifiableList(members); + return mentionedMembers = Collections.unmodifiableList(members); } @Nonnull @@ -172,148 +94,10 @@ public Bag getMembersBag() return processMentions(Message.MentionType.USER, new HashBag<>(), false, this::matchMember); } - @Nonnull - @Override - public synchronized List getEmotes() - { - if (emoteMentions == null) - emoteMentions = Collections.unmodifiableList(processMentions(Message.MentionType.EMOTE, new ArrayList<>(), true, this::matchEmote)); - return emoteMentions; - } - - @Nonnull - @Override - public Bag getEmotesBag() - { - return processMentions(Message.MentionType.EMOTE, new HashBag<>(), false, this::matchEmote); - } - - @Nonnull - @Override - public List getMentions(@Nonnull Message.MentionType... types) - { - if (types == null || types.length == 0) - return getMentions(Message.MentionType.values()); - List mentions = new ArrayList<>(); - // boolean duplicate checks - // not using Set because channel and role might have the same ID - boolean channel = false; - boolean role = false; - boolean user = false; - boolean emote = false; - for (Message.MentionType type : types) - { - switch (type) - { - case EVERYONE: - case HERE: - default: continue; - case CHANNEL: - if (!channel) - mentions.addAll(getChannels()); - channel = true; - break; - case USER: - if (!user) - mentions.addAll(getMembers()); - user = true; - break; - case ROLE: - if (!role) - mentions.addAll(getRoles()); - role = true; - break; - case EMOTE: - if (!emote) - mentions.addAll(getEmotes()); - emote = true; - } - } - return Collections.unmodifiableList(mentions); - } - - @Override - public boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull Message.MentionType... types) - { - Checks.notNull(types, "Mention Types"); - if (types.length == 0) - return isMentioned(mentionable, Message.MentionType.values()); - final boolean isUserEntity = mentionable instanceof User || mentionable instanceof Member; - for (Message.MentionType type : types) - { - switch (type) - { - case HERE: - { - if (isMass("@here") && isUserEntity) - return true; - break; - } - case EVERYONE: - { - if (isMass("@everyone") && isUserEntity) - return true; - break; - } - case USER: - { - if (isUserMentioned(mentionable)) - return true; - break; - } - case ROLE: - { - if (isRoleMentioned(mentionable)) - return true; - break; - } - case CHANNEL: - { - if (mentionable instanceof TextChannel) - { - if (getChannels().contains(mentionable)) - return true; - } - break; - } - case EMOTE: - { - if (mentionable instanceof Emote) - { - if (getEmotes().contains(mentionable)) - return true; - } - break; - } -// default: continue; - } - } - return false; - } - - // ============= Internal Helpers ================= - - private > C processMentions(Message.MentionType type, C collection, boolean distinct, Function map) - { - Matcher matcher = type.getPattern().matcher(content); - while (matcher.find()) - { - try - { - T elem = map.apply(matcher); - if (elem == null || (distinct && collection.contains(elem))) - continue; - collection.add(elem); - } - catch (NumberFormatException ignored) {} - } - return collection; - } - - private User matchUser(Matcher matcher) + protected User matchUser(Matcher matcher) { long userId = MiscUtil.parseSnowflake(matcher.group(1)); - if (!mentionedMembers.containsKey(userId)) + if (!memberMentions.containsKey(userId)) return null; User user = getJDA().getUserById(userId); if (user == null) @@ -327,10 +111,10 @@ private User matchUser(Matcher matcher) return user; } - private Member matchMember(Matcher matcher) + protected Member matchMember(Matcher matcher) { long userId = MiscUtil.parseSnowflake(matcher.group(1)); - if (!mentionedMembers.containsKey(userId)) + if (!memberMentions.containsKey(userId)) return null; Member member = guild.getMemberById(userId); if (member == null) @@ -343,13 +127,13 @@ private Member matchMember(Matcher matcher) return member; } - private GuildChannel matchChannel(Matcher matcher) + protected GuildChannel matchChannel(Matcher matcher) { long channelId = MiscUtil.parseSnowflake(matcher.group(1)); return getJDA().getGuildChannelById(channelId); } - private Role matchRole(Matcher matcher) + protected Role matchRole(Matcher matcher) { long roleId = MiscUtil.parseSnowflake(matcher.group(1)); if (!mentionedRoles.containsKey(roleId)) @@ -360,7 +144,7 @@ private Role matchRole(Matcher matcher) return getJDA().getRoleById(roleId); } - private Emote matchEmote(Matcher m) + protected Emote matchEmote(Matcher m) { long emoteId = MiscUtil.parseSnowflake(m.group(2)); String name = m.group(1); @@ -370,42 +154,4 @@ private Emote matchEmote(Matcher m) emote = new EmoteImpl(emoteId, jda).setName(name).setAnimated(animated); return emote; } - - private boolean isUserMentioned(IMentionable mentionable) - { - if (mentionable instanceof User) - { - return getUsers().contains(mentionable); - } - else if (mentionable instanceof Member) - { - final Member member = (Member) mentionable; - return getUsers().contains(member.getUser()); - } - return false; - } - - private boolean isRoleMentioned(IMentionable mentionable) - { - if (mentionable instanceof Role) - { - return getRoles().contains(mentionable); - } - else if (mentionable instanceof Member) - { - final Member member = (Member) mentionable; - return CollectionUtils.containsAny(getRoles(), member.getRoles()); - } - else if (guild != null && mentionable instanceof User) - { - final Member member = guild.getMember((User) mentionable); - return member != null && CollectionUtils.containsAny(getRoles(), member.getRoles()); - } - return false; - } - - private boolean isMass(String s) - { - return mentionsEveryone && content.contains(s); - } } diff --git a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java new file mode 100644 index 0000000000..d40c030360 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java @@ -0,0 +1,319 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.entities.mentions; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.utils.Checks; +import org.apache.commons.collections4.Bag; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.bag.HashBag; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.regex.Matcher; + +public abstract class AbstractMentions implements MessageMentions +{ + protected final String content; + protected final JDAImpl jda; + protected final GuildImpl guild; + protected final boolean mentionsEveryone; + + protected List mentionedUsers; + protected List mentionedMembers; + protected List mentionedRoles; + protected List mentionedChannels; + protected List mentionedEmotes; + + public AbstractMentions(String content, JDAImpl jda, GuildImpl guild, boolean mentionsEveryone) + { + this.content = content; + this.jda = jda; + this.guild = guild; + this.mentionsEveryone = mentionsEveryone; + } + + @Nonnull + @Override + public JDA getJDA() + { + return jda; + } + + @Override + public boolean mentionsEveryone() + { + return mentionsEveryone; + } + + @Nonnull + @Override + public synchronized List getUsers() + { + if (mentionedUsers != null) + return mentionedUsers; + return mentionedUsers = Collections.unmodifiableList(processMentions(Message.MentionType.USER, new ArrayList<>(), true, this::matchUser)); + } + + @Nonnull + @Override + public Bag getUsersBag() + { + return processMentions(Message.MentionType.USER, new HashBag<>(), true, this::matchUser); + } + + @Nonnull + @Override + public synchronized List getChannels() + { + if (mentionedChannels != null) + return mentionedChannels; + return mentionedChannels = Collections.unmodifiableList(processMentions(Message.MentionType.CHANNEL, new ArrayList<>(), true, this::matchChannel)); + } + + @Nonnull + @Override + public Bag getChannelsBag() + { + return processMentions(Message.MentionType.CHANNEL, new HashBag<>(), true, this::matchChannel); + } + + @Nonnull + @Override + public List getRoles() + { + if (mentionedRoles != null) + return mentionedRoles; + return mentionedRoles = Collections.unmodifiableList(processMentions(Message.MentionType.ROLE, new ArrayList<>(), true, this::matchRole)); + } + + @Nonnull + @Override + public Bag getRolesBag() + { + return processMentions(Message.MentionType.ROLE, new HashBag<>(), true, this::matchRole); + } + + @Nonnull + @Override + public List getEmotes() + { + if (mentionedEmotes != null) + return mentionedEmotes; + return mentionedEmotes = Collections.unmodifiableList(processMentions(Message.MentionType.EMOTE, new ArrayList<>(), true, this::matchEmote)); + } + + @Nonnull + @Override + public Bag getEmotesBag() + { + return processMentions(Message.MentionType.EMOTE, new HashBag<>(), true, this::matchEmote); + } + + @Nonnull + @Override + public List getMembers() + { + if (mentionedMembers != null) + return mentionedMembers; + return mentionedMembers = Collections.unmodifiableList(processMentions(Message.MentionType.USER, new ArrayList<>(), true, this::matchMember)); + } + + @Nonnull + @Override + public Bag getMembersBag() + { + return processMentions(Message.MentionType.USER, new HashBag<>(), true, this::matchMember); + } + + @Nonnull + @Override + public List getMentions(@Nonnull Message.MentionType... types) + { + if (types == null || types.length == 0) + return getMentions(Message.MentionType.values()); + List mentions = new ArrayList<>(); + // boolean duplicate checks + // not using Set because channel and role might have the same ID + boolean channel = false; + boolean role = false; + boolean user = false; + boolean emote = false; + for (Message.MentionType type : types) + { + switch (type) + { + case EVERYONE: + case HERE: + default: continue; + case CHANNEL: + if (!channel) + mentions.addAll(getChannels()); + channel = true; + break; + case USER: + if (!user) + mentions.addAll(getMembers()); + user = true; + break; + case ROLE: + if (!role) + mentions.addAll(getRoles()); + role = true; + break; + case EMOTE: + if (!emote) + mentions.addAll(getEmotes()); + emote = true; + } + } + return Collections.unmodifiableList(mentions); + } + + @Override + public boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull Message.MentionType... types) + { + Checks.notNull(types, "Mention Types"); + if (types.length == 0) + return isMentioned(mentionable, Message.MentionType.values()); + final boolean isUserEntity = mentionable instanceof User || mentionable instanceof Member; + for (Message.MentionType type : types) + { + switch (type) + { + case HERE: + { + if (isMass("@here") && isUserEntity) + return true; + break; + } + case EVERYONE: + { + if (isMass("@everyone") && isUserEntity) + return true; + break; + } + case USER: + { + if (isUserMentioned(mentionable)) + return true; + break; + } + case ROLE: + { + if (isRoleMentioned(mentionable)) + return true; + break; + } + case CHANNEL: + { + if (mentionable instanceof TextChannel) + { + if (getChannels().contains(mentionable)) + return true; + } + break; + } + case EMOTE: + { + if (mentionable instanceof Emote) + { + if (getEmotes().contains(mentionable)) + return true; + } + break; + } +// default: continue; + } + } + return false; + } + + // Internal parsing methods + + protected > C processMentions(Message.MentionType type, C collection, boolean distinct, Function map) + { + Matcher matcher = type.getPattern().matcher(content); + while (matcher.find()) + { + try + { + T elem = map.apply(matcher); + if (elem == null || (distinct && collection.contains(elem))) + continue; + collection.add(elem); + } + catch (NumberFormatException ignored) {} + } + return collection; + } + + protected abstract User matchUser(Matcher matcher); + + protected abstract Member matchMember(Matcher matcher); + + protected abstract GuildChannel matchChannel(Matcher matcher); + + protected abstract Role matchRole(Matcher matcher); + + protected abstract Emote matchEmote(Matcher m); + + protected boolean isUserMentioned(IMentionable mentionable) + { + if (mentionable instanceof User) + { + return getUsers().contains(mentionable); + } + else if (mentionable instanceof Member) + { + final Member member = (Member) mentionable; + return getUsers().contains(member.getUser()); + } + return false; + } + + protected boolean isRoleMentioned(IMentionable mentionable) + { + if (mentionable instanceof Role) + { + return getRoles().contains(mentionable); + } + else if (mentionable instanceof Member) + { + final Member member = (Member) mentionable; + return CollectionUtils.containsAny(getRoles(), member.getRoles()); + } + else if (guild != null && mentionable instanceof User) + { + final Member member = guild.getMember((User) mentionable); + return member != null && CollectionUtils.containsAny(getRoles(), member.getRoles()); + } + return false; + } + + protected boolean isMass(String s) + { + return mentionsEveryone && content.contains(s); + } +} From 1c5b1369952f281c513cb3d2dd49a9c11f5d9200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sun, 17 Apr 2022 15:54:14 +0200 Subject: [PATCH 06/29] Fix errors --- .../java/net/dv8tion/jda/internal/entities/EntityBuilder.java | 4 ++-- .../net/dv8tion/jda/internal/entities/ReceivedMessage.java | 4 +--- .../interactions/command/CommandInteractionPayloadImpl.java | 2 +- 3 files changed, 4 insertions(+), 6 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 8c0a2b1766..eeed4e1e8b 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -1536,8 +1536,8 @@ else if (MISSING_CHANNEL.equals(ex.getMessage())) GuildImpl guild = channel instanceof GuildChannel ? ((AbstractGuildChannelImpl) channel).getGuild() : null; MessageMentions mentions = new MessageMentionsImpl( - api, guild, content, messageReference != null, - mentionsEveryone, jsonObject.getArray("mentions"), jsonObject.getArray("mention_roles") + api, guild, content, mentionsEveryone, + jsonObject.getArray("mentions"), jsonObject.getArray("mention_roles") ); if (!type.isSystem()) 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 d45baf6c23..559370e800 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java @@ -105,8 +105,6 @@ public ReceivedMessage( this.components = Collections.unmodifiableList(components); this.flags = flags; this.interaction = interaction; - - ((MessageMentionsImpl) mentions).setMessage(this); } public ReceivedMessage withHook(InteractionHook hook) @@ -410,7 +408,7 @@ public String getContentDisplay() { tmp = tmp.replace(emote.getAsMention(), ":" + emote.getName() + ":"); } - for (TextChannel mentionedChannel : mentions.getChannels()) + for (GuildChannel mentionedChannel : mentions.getChannels()) { tmp = tmp.replace(mentionedChannel.getAsMention(), '#' + mentionedChannel.getName()); } diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/command/CommandInteractionPayloadImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/command/CommandInteractionPayloadImpl.java index 4c094f0e57..578be8cad7 100644 --- a/src/main/java/net/dv8tion/jda/internal/interactions/command/CommandInteractionPayloadImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/interactions/command/CommandInteractionPayloadImpl.java @@ -83,7 +83,7 @@ public CommandInteractionPayloadImpl(JDAImpl jda, DataObject data) private void parseOptions(DataArray options) { options.stream(DataArray::getObject) - .map(json -> new OptionMapping(json, resolved)) + .map(json -> new OptionMapping(json, resolved, getJDA(), getGuild())) .forEach(this.options::add); } From 181fc16dfed808a6fd6d63f0789c72f42a2b5c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sun, 17 Apr 2022 16:07:54 +0200 Subject: [PATCH 07/29] Sort mentions in getMentions(...) --- .../net/dv8tion/jda/api/entities/MessageMentions.java | 3 +-- .../jda/internal/entities/mentions/AbstractMentions.java | 9 +++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java b/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java index 651fe7bc28..f8d14635d0 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java +++ b/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java @@ -222,8 +222,7 @@ public interface MessageMentions *

If no MentionType values are given this will fallback to all types. * * @param types - * Amount of {@link net.dv8tion.jda.api.entities.Message.MentionType MentionTypes} - * to include in the list of mentions + * {@link net.dv8tion.jda.api.entities.Message.MentionType MentionTypes} to include * * @throws java.lang.IllegalArgumentException * If provided with {@code null} diff --git a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java index d40c030360..7b0a25b351 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java @@ -26,10 +26,7 @@ import org.apache.commons.collections4.bag.HashBag; import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.function.Function; import java.util.regex.Matcher; @@ -149,6 +146,7 @@ public Bag getMembersBag() @Nonnull @Override + @SuppressWarnings("ConstantConditions") public List getMentions(@Nonnull Message.MentionType... types) { if (types == null || types.length == 0) @@ -188,6 +186,9 @@ public List getMentions(@Nonnull Message.MentionType... types) emote = true; } } + + // Sort mentions by occurrence + mentions.sort(Comparator.comparingInt(it -> content.indexOf(it.getId()))); return Collections.unmodifiableList(mentions); } From 83e38bc20bb2cf7f47e1262e9298fb4ebeb68465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sun, 17 Apr 2022 16:13:41 +0200 Subject: [PATCH 08/29] Add back synchronization and guild check --- .../entities/mentions/AbstractMentions.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java index 7b0a25b351..42fa4909e1 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java @@ -98,8 +98,10 @@ public Bag getChannelsBag() @Nonnull @Override - public List getRoles() + public synchronized List getRoles() { + if (guild == null) + return Collections.emptyList(); if (mentionedRoles != null) return mentionedRoles; return mentionedRoles = Collections.unmodifiableList(processMentions(Message.MentionType.ROLE, new ArrayList<>(), true, this::matchRole)); @@ -109,12 +111,14 @@ public List getRoles() @Override public Bag getRolesBag() { + if (guild == null) + return new HashBag<>(); return processMentions(Message.MentionType.ROLE, new HashBag<>(), true, this::matchRole); } @Nonnull @Override - public List getEmotes() + public synchronized List getEmotes() { if (mentionedEmotes != null) return mentionedEmotes; @@ -130,8 +134,10 @@ public Bag getEmotesBag() @Nonnull @Override - public List getMembers() + public synchronized List getMembers() { + if (guild == null) + return Collections.emptyList(); if (mentionedMembers != null) return mentionedMembers; return mentionedMembers = Collections.unmodifiableList(processMentions(Message.MentionType.USER, new ArrayList<>(), true, this::matchMember)); @@ -141,6 +147,8 @@ public List getMembers() @Override public Bag getMembersBag() { + if (guild == null) + return new HashBag<>(); return processMentions(Message.MentionType.USER, new HashBag<>(), true, this::matchMember); } From ea60fb77a7639471c71333904f9561b917449ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Wed, 20 Apr 2022 11:11:37 +0200 Subject: [PATCH 09/29] Cleanup EntityBuilder#createMessage0 --- .../jda/internal/entities/EntityBuilder.java | 39 +++++++------------ 1 file changed, 13 insertions(+), 26 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 eeed4e1e8b..628be25d62 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -18,8 +18,6 @@ import gnu.trove.map.TLongObjectMap; import gnu.trove.map.hash.TLongObjectHashMap; -import gnu.trove.set.TLongSet; -import gnu.trove.set.hash.TLongHashSet; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.audit.ActionType; @@ -1397,14 +1395,16 @@ private ReceivedMessage createMessage0(DataObject jsonObject, @Nonnull MessageCh final DataObject author = jsonObject.getObject("author"); final long authorId = author.getLong("id"); MemberImpl member = null; + GuildImpl guild = null; + if (channel instanceof GuildChannel) + guild = (GuildImpl) ((GuildChannel) channel).getGuild(); + // Member details for author if (channel.getType().isGuild() && !jsonObject.isNull("member")) { DataObject memberJson = jsonObject.getObject("member"); memberJson.put("user", author); - GuildChannel guildChannel = (GuildChannel) channel; - Guild guild = guildChannel.getGuild(); - member = createMember((GuildImpl) guild, memberJson); + member = createMember(guild, memberJson); if (modifyCache) { // Update member cache with new information if needed @@ -1421,21 +1421,22 @@ private ReceivedMessage createMessage0(DataObject jsonObject, @Nonnull MessageCh final String nonce = jsonObject.isNull("nonce") ? null : jsonObject.get("nonce").toString(); final int flags = jsonObject.getInt("flags", 0); + // Message accessories MessageChannel tmpChannel = channel; // because java final List attachments = map(jsonObject, "attachments", this::createMessageAttachment); final List embeds = map(jsonObject, "embeds", this::createMessageEmbed); final List reactions = map(jsonObject, "reactions", (obj) -> createMessageReaction(tmpChannel, id, obj)); final List stickers = map(jsonObject, "sticker_items", this::createSticker); + // Message activity (for game invites/spotify) MessageActivity activity = null; - if (!jsonObject.isNull("activity")) activity = createMessageActivity(jsonObject); + // Message Author User user; - if (channel.getType().isGuild()) + if (guild != null) { - Guild guild = ((GuildChannel) channel).getGuild(); if (member == null) member = (MemberImpl) guild.getMemberById(authorId); user = member != null ? member.getUser() : null; @@ -1467,15 +1468,7 @@ private ReceivedMessage createMessage0(DataObject jsonObject, @Nonnull MessageCh if (modifyCache && !fromWebhook) // update the user information on message receive updateUser((UserImpl) user, author); - TLongSet mentionedRoles = new TLongHashSet(); - TLongSet mentionedUsers = new TLongHashSet(map(jsonObject, "mentions", (o) -> o.getLong("id"))); - Optional roleMentionArr = jsonObject.optArray("mention_roles"); - roleMentionArr.ifPresent((arr) -> - { - for (int i = 0; i < arr.length(); i++) - mentionedRoles.add(arr.getLong(i)); - }); - + // Message Reference (Reply or Pin) Message referencedMessage = null; if (!jsonObject.isNull("referenced_message")) { @@ -1498,7 +1491,6 @@ else if (MISSING_CHANNEL.equals(ex.getMessage())) } MessageReference messageReference = null; - if (!jsonObject.isNull("message_reference")) // always contains the channel + message id for a referenced message { // used for when referenced_message is not provided DataObject messageReferenceJson = jsonObject.getObject("message_reference"); @@ -1512,6 +1504,7 @@ else if (MISSING_CHANNEL.equals(ex.getMessage())) ); } + // Message Components List components = Collections.emptyList(); Optional componentsArrayOpt = jsonObject.optArray("components"); if (componentsArrayOpt.isPresent()) @@ -1523,18 +1516,12 @@ else if (MISSING_CHANNEL.equals(ex.getMessage())) .collect(Collectors.toList()); } + // Application command and component replies Message.Interaction messageInteraction = null; if (!jsonObject.isNull("interaction")) - { - GuildImpl guild = null; - if (channel instanceof GuildChannel) - { - guild = (GuildImpl) ((GuildChannel) (channel)).getGuild(); - } messageInteraction = createMessageInteraction(guild, jsonObject.getObject("interaction")); - } - GuildImpl guild = channel instanceof GuildChannel ? ((AbstractGuildChannelImpl) channel).getGuild() : null; + // Lazy Mention parsing and caching (includes reply mentions) MessageMentions mentions = new MessageMentionsImpl( api, guild, content, mentionsEveryone, jsonObject.getArray("mentions"), jsonObject.getArray("mention_roles") From b09ce8fb44b9ca6d4190a039f2149adce35b9fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Wed, 20 Apr 2022 11:32:21 +0200 Subject: [PATCH 10/29] Handle user mentions in interactions --- .../internal/entities/mentions/AbstractMentions.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java index 42fa4909e1..d704074331 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java @@ -16,6 +16,8 @@ package net.dv8tion.jda.internal.entities.mentions; +import gnu.trove.map.TLongObjectMap; +import gnu.trove.map.hash.TLongObjectHashMap; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.internal.JDAImpl; @@ -180,7 +182,14 @@ public List getMentions(@Nonnull Message.MentionType... types) break; case USER: if (!user) - mentions.addAll(getMembers()); + { + TLongObjectMap set = new TLongObjectHashMap<>(); + for (User u : getUsers()) + set.put(u.getIdLong(), u); + for (Member m : getMembers()) + set.put(m.getIdLong(), m); + mentions.addAll(set.valueCollection()); + } user = true; break; case ROLE: From 88f72135b769d54c094edfb2af6bba55bc41d1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Wed, 20 Apr 2022 11:34:12 +0200 Subject: [PATCH 11/29] Fix parsing bug --- .../jda/internal/entities/MessageMentionsImpl.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java index 01a801784c..6d08a2c1c3 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java @@ -49,7 +49,12 @@ public MessageMentionsImpl(JDAImpl jda, GuildImpl guild, String content, this.mentionedRoles = new TLongObjectHashMap<>(roleMentions.length()); userMentions.stream(DataArray::getObject) - .forEach(obj -> memberMentions.put(obj.getObject("user").getUnsignedLong("id"), obj)); + .forEach(obj -> { + DataObject member = obj.getObject("member"); + obj.remove("user"); + member.put("user", obj); + memberMentions.put(obj.getUnsignedLong("id"), member); + }); roleMentions.stream(DataArray::getObject) .forEach(obj -> mentionedRoles.put(obj.getUnsignedLong("id"), obj)); } @@ -69,7 +74,6 @@ public synchronized List getMembers() ArrayList members = processMentions(Message.MentionType.USER, new ArrayList<>(), true, (matcher) -> { long id = Long.parseUnsignedLong(matcher.group(1)); DataObject member = memberMentions.get(id); - unseen.remove(id); return member == null ? null : entityBuilder.createMember(guild, member); }); From 8cfbad4550a9fd3dca26562ccd6495aaf2c96fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Wed, 20 Apr 2022 11:54:20 +0200 Subject: [PATCH 12/29] Improve mention parsing for members and update cache eagerly --- .../entities/MessageMentionsImpl.java | 62 ++++++++----------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java index 6d08a2c1c3..4b6274f47c 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java @@ -38,25 +38,35 @@ public class MessageMentionsImpl extends AbstractMentions { - private final TLongObjectMap memberMentions; + private final TLongObjectMap mentionedUsers; private final TLongObjectMap mentionedRoles; public MessageMentionsImpl(JDAImpl jda, GuildImpl guild, String content, boolean mentionsEveryone, DataArray userMentions, DataArray roleMentions) { super(content, jda, guild, mentionsEveryone); - this.memberMentions = new TLongObjectHashMap<>(userMentions.length()); + this.mentionedUsers = new TLongObjectHashMap<>(userMentions.length()); this.mentionedRoles = new TLongObjectHashMap<>(roleMentions.length()); userMentions.stream(DataArray::getObject) .forEach(obj -> { + if (obj.isNull("member")) + { + this.mentionedUsers.put(obj.getUnsignedLong("id"), obj.put("is_member", false)); + return; + } + DataObject member = obj.getObject("member"); obj.remove("user"); - member.put("user", obj); - memberMentions.put(obj.getUnsignedLong("id"), member); + member.put("user", obj).put("is_member", true); + this.mentionedUsers.put(obj.getUnsignedLong("id"), member); }); + roleMentions.stream(DataArray::getObject) .forEach(obj -> mentionedRoles.put(obj.getUnsignedLong("id"), obj)); + + // Eager parsing member mentions for caching purposes + getMembers(); } @Nonnull @@ -70,16 +80,12 @@ public synchronized List getMembers() // Parse members from mentions array in order of appearance EntityBuilder entityBuilder = jda.getEntityBuilder(); - TLongSet unseen = new TLongHashSet(memberMentions.keySet()); - ArrayList members = processMentions(Message.MentionType.USER, new ArrayList<>(), true, (matcher) -> { - long id = Long.parseUnsignedLong(matcher.group(1)); - DataObject member = memberMentions.get(id); - return member == null ? null : entityBuilder.createMember(guild, member); - }); + TLongSet unseen = new TLongHashSet(mentionedUsers.keySet()); + ArrayList members = processMentions(Message.MentionType.USER, new ArrayList<>(), true, this::matchMember); // Add reply mentions at beginning for (TLongIterator iter = unseen.iterator(); iter.hasNext();) - members.add(0, entityBuilder.createMember(guild, memberMentions.get(iter.next()))); + members.add(0, entityBuilder.createMember(guild, mentionedUsers.get(iter.next()))); // Update member cache members.stream() @@ -101,34 +107,20 @@ public Bag getMembersBag() protected User matchUser(Matcher matcher) { long userId = MiscUtil.parseSnowflake(matcher.group(1)); - if (!memberMentions.containsKey(userId)) - return null; - User user = getJDA().getUserById(userId); - if (user == null) - { - user = getMembers().stream() - .filter(it -> it.getIdLong() == userId) - .map(Member::getUser) - .findFirst() - .orElse(null); - } - return user; + DataObject mention = mentionedUsers.get(userId); + if (!mention.getBoolean("is_member")) + return jda.getEntityBuilder().createUser(mention); + Member member = matchMember(matcher); + return member == null ? null : member.getUser(); } protected Member matchMember(Matcher matcher) { - long userId = MiscUtil.parseSnowflake(matcher.group(1)); - if (!memberMentions.containsKey(userId)) - return null; - Member member = guild.getMemberById(userId); - if (member == null) - { - member = getMembers().stream() - .filter(it -> it.getIdLong() == userId) - .findFirst() - .orElse(null); - } - return member; + long id = Long.parseUnsignedLong(matcher.group(1)); + DataObject member = mentionedUsers.get(id); + return member != null && member.getBoolean("is_member") + ? jda.getEntityBuilder().createMember(guild, member) + : null; } protected GuildChannel matchChannel(Matcher matcher) From 8b7a4e1011f5ed40b6ea3a1ba495da3df04d3b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Wed, 20 Apr 2022 15:17:52 +0200 Subject: [PATCH 13/29] Add missing null check --- .../net/dv8tion/jda/internal/entities/MessageMentionsImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java index 4b6274f47c..e5fa9b0fec 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java @@ -108,6 +108,8 @@ protected User matchUser(Matcher matcher) { long userId = MiscUtil.parseSnowflake(matcher.group(1)); DataObject mention = mentionedUsers.get(userId); + if (mention == null) + return null; if (!mention.getBoolean("is_member")) return jda.getEntityBuilder().createUser(mention); Member member = matchMember(matcher); From 3597015102194b94d8c51b4d97084d4dcd0d4c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Wed, 20 Apr 2022 15:49:12 +0200 Subject: [PATCH 14/29] Parse user mentions on messages too --- .../entities/MessageMentionsImpl.java | 62 +++++++++++++------ 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java index e5fa9b0fec..64c91d2b1b 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java @@ -27,8 +27,6 @@ import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.internal.JDAImpl; import net.dv8tion.jda.internal.entities.mentions.AbstractMentions; -import org.apache.commons.collections4.Bag; -import org.apache.commons.collections4.bag.HashBag; import javax.annotation.Nonnull; import java.util.ArrayList; @@ -38,32 +36,32 @@ public class MessageMentionsImpl extends AbstractMentions { - private final TLongObjectMap mentionedUsers; - private final TLongObjectMap mentionedRoles; + private final TLongObjectMap userMentionMap; + private final TLongObjectMap roleMentionMap; public MessageMentionsImpl(JDAImpl jda, GuildImpl guild, String content, boolean mentionsEveryone, DataArray userMentions, DataArray roleMentions) { super(content, jda, guild, mentionsEveryone); - this.mentionedUsers = new TLongObjectHashMap<>(userMentions.length()); - this.mentionedRoles = new TLongObjectHashMap<>(roleMentions.length()); + this.userMentionMap = new TLongObjectHashMap<>(userMentions.length()); + this.roleMentionMap = new TLongObjectHashMap<>(roleMentions.length()); userMentions.stream(DataArray::getObject) .forEach(obj -> { if (obj.isNull("member")) { - this.mentionedUsers.put(obj.getUnsignedLong("id"), obj.put("is_member", false)); + this.userMentionMap.put(obj.getUnsignedLong("id"), obj.put("is_member", false)); return; } DataObject member = obj.getObject("member"); obj.remove("user"); member.put("user", obj).put("is_member", true); - this.mentionedUsers.put(obj.getUnsignedLong("id"), member); + this.userMentionMap.put(obj.getUnsignedLong("id"), member); }); roleMentions.stream(DataArray::getObject) - .forEach(obj -> mentionedRoles.put(obj.getUnsignedLong("id"), obj)); + .forEach(obj -> roleMentionMap.put(obj.getUnsignedLong("id"), obj)); // Eager parsing member mentions for caching purposes getMembers(); @@ -80,12 +78,19 @@ public synchronized List getMembers() // Parse members from mentions array in order of appearance EntityBuilder entityBuilder = jda.getEntityBuilder(); - TLongSet unseen = new TLongHashSet(mentionedUsers.keySet()); - ArrayList members = processMentions(Message.MentionType.USER, new ArrayList<>(), true, this::matchMember); + TLongSet unseen = new TLongHashSet(userMentionMap.keySet()); + ArrayList members = processMentions(Message.MentionType.USER, new ArrayList<>(), true, (matcher) -> { + unseen.remove(Long.parseUnsignedLong(matcher.group(1))); + return matchMember(matcher); + }); // Add reply mentions at beginning for (TLongIterator iter = unseen.iterator(); iter.hasNext();) - members.add(0, entityBuilder.createMember(guild, mentionedUsers.get(iter.next()))); + { + DataObject mention = userMentionMap.get(iter.next()); + if (mention.getBoolean("is_member")) + members.add(0, entityBuilder.createMember(guild, mention)); + } // Update member cache members.stream() @@ -97,17 +102,36 @@ public synchronized List getMembers() @Nonnull @Override - public Bag getMembersBag() + public synchronized List getUsers() { - if (guild == null) - return new HashBag<>(); - return processMentions(Message.MentionType.USER, new HashBag<>(), false, this::matchMember); + if (mentionedUsers != null) + return mentionedUsers; + + // Parse members from mentions array in order of appearance + EntityBuilder entityBuilder = jda.getEntityBuilder(); + TLongSet unseen = new TLongHashSet(userMentionMap.keySet()); + List users = processMentions(Message.MentionType.USER, new ArrayList<>(), true, (matcher) -> { + unseen.remove(Long.parseUnsignedLong(matcher.group(1))); + return matchUser(matcher); + }); + + // Add reply mentions at beginning + for (TLongIterator iter = unseen.iterator(); iter.hasNext();) + { + DataObject mention = userMentionMap.get(iter.next()); + if (mention.getBoolean("is_member")) + users.add(0, entityBuilder.createUser(mention.getObject("user"))); + else + users.add(0, entityBuilder.createUser(mention)); + } + + return mentionedUsers = Collections.unmodifiableList(users); } protected User matchUser(Matcher matcher) { long userId = MiscUtil.parseSnowflake(matcher.group(1)); - DataObject mention = mentionedUsers.get(userId); + DataObject mention = userMentionMap.get(userId); if (mention == null) return null; if (!mention.getBoolean("is_member")) @@ -119,7 +143,7 @@ protected User matchUser(Matcher matcher) protected Member matchMember(Matcher matcher) { long id = Long.parseUnsignedLong(matcher.group(1)); - DataObject member = mentionedUsers.get(id); + DataObject member = userMentionMap.get(id); return member != null && member.getBoolean("is_member") ? jda.getEntityBuilder().createMember(guild, member) : null; @@ -134,7 +158,7 @@ protected GuildChannel matchChannel(Matcher matcher) protected Role matchRole(Matcher matcher) { long roleId = MiscUtil.parseSnowflake(matcher.group(1)); - if (!mentionedRoles.containsKey(roleId)) + if (!roleMentionMap.containsKey(roleId)) return null; if (guild != null) return guild.getRoleById(roleId); From 387bd7325c8d84efa36a964fcb532957f06f459b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Wed, 20 Apr 2022 17:09:53 +0200 Subject: [PATCH 15/29] Role mentions are only an id --- .../jda/internal/entities/MessageMentionsImpl.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java index 64c91d2b1b..e84aa363fd 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java @@ -33,18 +33,19 @@ import java.util.Collections; import java.util.List; import java.util.regex.Matcher; +import java.util.stream.Collectors; public class MessageMentionsImpl extends AbstractMentions { private final TLongObjectMap userMentionMap; - private final TLongObjectMap roleMentionMap; + private final TLongSet roleMentionMap; public MessageMentionsImpl(JDAImpl jda, GuildImpl guild, String content, boolean mentionsEveryone, DataArray userMentions, DataArray roleMentions) { super(content, jda, guild, mentionsEveryone); this.userMentionMap = new TLongObjectHashMap<>(userMentions.length()); - this.roleMentionMap = new TLongObjectHashMap<>(roleMentions.length()); + this.roleMentionMap = new TLongHashSet(roleMentions.stream(DataArray::getUnsignedLong).collect(Collectors.toList())); userMentions.stream(DataArray::getObject) .forEach(obj -> { @@ -60,8 +61,7 @@ public MessageMentionsImpl(JDAImpl jda, GuildImpl guild, String content, this.userMentionMap.put(obj.getUnsignedLong("id"), member); }); - roleMentions.stream(DataArray::getObject) - .forEach(obj -> roleMentionMap.put(obj.getUnsignedLong("id"), obj)); + // Eager parsing member mentions for caching purposes getMembers(); @@ -158,7 +158,7 @@ protected GuildChannel matchChannel(Matcher matcher) protected Role matchRole(Matcher matcher) { long roleId = MiscUtil.parseSnowflake(matcher.group(1)); - if (!roleMentionMap.containsKey(roleId)) + if (!roleMentionMap.contains(roleId)) return null; if (guild != null) return guild.getRoleById(roleId); From c7392ad94299097a32b50b668654c61011155bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Wed, 20 Apr 2022 21:24:45 +0200 Subject: [PATCH 16/29] Move matchEmote --- .../jda/internal/entities/InteractionMentions.java | 11 ----------- .../jda/internal/entities/MessageMentionsImpl.java | 11 ----------- .../entities/mentions/AbstractMentions.java | 13 ++++++++++++- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java index 33a8364cd4..7d4594c9a8 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java @@ -67,17 +67,6 @@ protected Role matchRole(Matcher matcher) return it instanceof Role ? (Role) it : null; } - protected Emote matchEmote(Matcher m) - { - long emoteId = MiscUtil.parseSnowflake(m.group(2)); - String name = m.group(1); - boolean animated = m.group(0).startsWith("> C processMentions(Message.MentionType ty protected abstract Role matchRole(Matcher matcher); - protected abstract Emote matchEmote(Matcher m); + protected Emote matchEmote(Matcher m) + { + long emoteId = MiscUtil.parseSnowflake(m.group(2)); + String name = m.group(1); + boolean animated = m.group(0).startsWith(" Date: Thu, 21 Apr 2022 15:37:35 +0200 Subject: [PATCH 17/29] Make use of UserSnowflake and fix bags --- .../entities/mentions/AbstractMentions.java | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java index 3b564e0dfe..3d287de7a6 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java @@ -81,7 +81,7 @@ public synchronized List getUsers() @Override public Bag getUsersBag() { - return processMentions(Message.MentionType.USER, new HashBag<>(), true, this::matchUser); + return processMentions(Message.MentionType.USER, new HashBag<>(), false, this::matchUser); } @Nonnull @@ -97,7 +97,7 @@ public synchronized List getChannels() @Override public Bag getChannelsBag() { - return processMentions(Message.MentionType.CHANNEL, new HashBag<>(), true, this::matchChannel); + return processMentions(Message.MentionType.CHANNEL, new HashBag<>(), false, this::matchChannel); } @Nonnull @@ -117,7 +117,7 @@ public Bag getRolesBag() { if (guild == null) return new HashBag<>(); - return processMentions(Message.MentionType.ROLE, new HashBag<>(), true, this::matchRole); + return processMentions(Message.MentionType.ROLE, new HashBag<>(), false, this::matchRole); } @Nonnull @@ -133,7 +133,7 @@ public synchronized List getEmotes() @Override public Bag getEmotesBag() { - return processMentions(Message.MentionType.EMOTE, new HashBag<>(), true, this::matchEmote); + return processMentions(Message.MentionType.EMOTE, new HashBag<>(), false, this::matchEmote); } @Nonnull @@ -153,7 +153,7 @@ public Bag getMembersBag() { if (guild == null) return new HashBag<>(); - return processMentions(Message.MentionType.USER, new HashBag<>(), true, this::matchMember); + return processMentions(Message.MentionType.USER, new HashBag<>(), false, this::matchMember); } @Nonnull @@ -217,20 +217,19 @@ public boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull Message.M Checks.notNull(types, "Mention Types"); if (types.length == 0) return isMentioned(mentionable, Message.MentionType.values()); - final boolean isUserEntity = mentionable instanceof User || mentionable instanceof Member; for (Message.MentionType type : types) { switch (type) { case HERE: { - if (isMass("@here") && isUserEntity) + if (isMass("@here") && mentionable instanceof UserSnowflake) return true; break; } case EVERYONE: { - if (isMass("@everyone") && isUserEntity) + if (isMass("@everyone") && mentionable instanceof UserSnowflake) return true; break; } @@ -310,16 +309,7 @@ protected Emote matchEmote(Matcher m) protected boolean isUserMentioned(IMentionable mentionable) { - if (mentionable instanceof User) - { - return getUsers().contains(mentionable); - } - else if (mentionable instanceof Member) - { - final Member member = (Member) mentionable; - return getUsers().contains(member.getUser()); - } - return false; + return mentionable instanceof UserSnowflake && getUsers().stream().anyMatch(user -> user.getIdLong() == mentionable.getIdLong()); } protected boolean isRoleMentioned(IMentionable mentionable) From 00e6c95ce830d1ffb2d0a3131920004f1dc56616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Thu, 21 Apr 2022 15:45:20 +0200 Subject: [PATCH 18/29] Reduce code duplication --- .../entities/InteractionMentions.java | 20 ------------------- .../entities/MessageMentionsImpl.java | 6 ++++++ .../entities/mentions/AbstractMentions.java | 5 +---- 3 files changed, 7 insertions(+), 24 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java index 7d4594c9a8..445d3cff7a 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java @@ -21,7 +21,6 @@ import net.dv8tion.jda.api.utils.MiscUtil; import net.dv8tion.jda.internal.JDAImpl; import net.dv8tion.jda.internal.entities.mentions.AbstractMentions; -import org.apache.commons.collections4.CollectionUtils; import java.util.regex.Matcher; @@ -71,23 +70,4 @@ protected boolean isUserMentioned(IMentionable mentionable) { return resolved.containsKey(mentionable.getIdLong()); } - - protected boolean isRoleMentioned(IMentionable mentionable) - { - if (mentionable instanceof Role) - { - return getRoles().contains(mentionable); - } - else if (mentionable instanceof Member) - { - final Member member = (Member) mentionable; - return CollectionUtils.containsAny(getRoles(), member.getRoles()); - } - else if (guild != null && mentionable instanceof User) - { - final Member member = guild.getMember((User) mentionable); - return member != null && CollectionUtils.containsAny(getRoles(), member.getRoles()); - } - return false; - } } diff --git a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java index 7e74862dbb..6e389fb938 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java @@ -165,4 +165,10 @@ protected Role matchRole(Matcher matcher) else return getJDA().getRoleById(roleId); } + + @Override + protected boolean isUserMentioned(IMentionable mentionable) + { + return userMentionMap.containsKey(mentionable.getIdLong()); + } } diff --git a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java index 3d287de7a6..e4ddc1d3ac 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java @@ -307,10 +307,7 @@ protected Emote matchEmote(Matcher m) return emote; } - protected boolean isUserMentioned(IMentionable mentionable) - { - return mentionable instanceof UserSnowflake && getUsers().stream().anyMatch(user -> user.getIdLong() == mentionable.getIdLong()); - } + protected abstract boolean isUserMentioned(IMentionable mentionable); protected boolean isRoleMentioned(IMentionable mentionable) { From fd74ff37501e2b0567c034f73b9cd628c196529b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Thu, 21 Apr 2022 17:58:28 +0200 Subject: [PATCH 19/29] Improve getMentions and isMentioned code --- .../entities/mentions/AbstractMentions.java | 72 ++++++------------- 1 file changed, 20 insertions(+), 52 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java index e4ddc1d3ac..5e6a001476 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java @@ -164,45 +164,31 @@ public List getMentions(@Nonnull Message.MentionType... types) if (types == null || types.length == 0) return getMentions(Message.MentionType.values()); List mentions = new ArrayList<>(); - // boolean duplicate checks - // not using Set because channel and role might have the same ID - boolean channel = false; - boolean role = false; - boolean user = false; - boolean emote = false; - for (Message.MentionType type : types) + // Conversion to set to prevent duplication of types + for (Message.MentionType type : EnumSet.of(types[0], types)) { switch (type) { - case EVERYONE: - case HERE: - default: continue; case CHANNEL: - if (!channel) - mentions.addAll(getChannels()); - channel = true; + mentions.addAll(getChannels()); break; case USER: - if (!user) - { - TLongObjectMap set = new TLongObjectHashMap<>(); - for (User u : getUsers()) - set.put(u.getIdLong(), u); - for (Member m : getMembers()) - set.put(m.getIdLong(), m); - mentions.addAll(set.valueCollection()); - } - user = true; + TLongObjectMap set = new TLongObjectHashMap<>(); + for (User u : getUsers()) + set.put(u.getIdLong(), u); + for (Member m : getMembers()) + set.put(m.getIdLong(), m); + mentions.addAll(set.valueCollection()); break; case ROLE: - if (!role) - mentions.addAll(getRoles()); - role = true; + mentions.addAll(getRoles()); break; case EMOTE: - if (!emote) - mentions.addAll(getEmotes()); - emote = true; + mentions.addAll(getEmotes()); + break; +// case EVERYONE: +// case HERE: +// default: continue; } } @@ -222,48 +208,30 @@ public boolean isMentioned(@Nonnull IMentionable mentionable, @Nonnull Message.M switch (type) { case HERE: - { if (isMass("@here") && mentionable instanceof UserSnowflake) return true; break; - } case EVERYONE: - { if (isMass("@everyone") && mentionable instanceof UserSnowflake) return true; break; - } case USER: - { if (isUserMentioned(mentionable)) return true; break; - } case ROLE: - { if (isRoleMentioned(mentionable)) return true; break; - } case CHANNEL: - { - if (mentionable instanceof TextChannel) - { - if (getChannels().contains(mentionable)) - return true; - } + if (mentionable instanceof GuildChannel && getChannels().contains(mentionable)) + return true; break; - } case EMOTE: - { - if (mentionable instanceof Emote) - { - if (getEmotes().contains(mentionable)) - return true; - } + if (mentionable instanceof Emote && getEmotes().contains(mentionable)) + return true; break; - } -// default: continue; +// default: continue; } } return false; From aa1647e9db50ee39408a2e30831d110ffb53245a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Thu, 21 Apr 2022 18:07:00 +0200 Subject: [PATCH 20/29] Add missing overrides --- .../dv8tion/jda/internal/entities/InteractionMentions.java | 5 +++++ .../dv8tion/jda/internal/entities/MessageMentionsImpl.java | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java index 445d3cff7a..f923e12920 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/InteractionMentions.java @@ -34,6 +34,7 @@ public InteractionMentions(String content, TLongObjectMap resolved, JDAI this.resolved = resolved; } + @Override protected User matchUser(Matcher matcher) { long userId = MiscUtil.parseSnowflake(matcher.group(1)); @@ -45,6 +46,7 @@ protected User matchUser(Matcher matcher) : null; } + @Override protected Member matchMember(Matcher matcher) { long userId = MiscUtil.parseSnowflake(matcher.group(1)); @@ -52,6 +54,7 @@ protected Member matchMember(Matcher matcher) return it instanceof Member ? (Member) it : null; } + @Override protected GuildChannel matchChannel(Matcher matcher) { long channelId = MiscUtil.parseSnowflake(matcher.group(1)); @@ -59,6 +62,7 @@ protected GuildChannel matchChannel(Matcher matcher) return it instanceof GuildChannel ? (GuildChannel) it : null; } + @Override protected Role matchRole(Matcher matcher) { long roleId = MiscUtil.parseSnowflake(matcher.group(1)); @@ -66,6 +70,7 @@ protected Role matchRole(Matcher matcher) return it instanceof Role ? (Role) it : null; } + @Override protected boolean isUserMentioned(IMentionable mentionable) { return resolved.containsKey(mentionable.getIdLong()); diff --git a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java index 6e389fb938..ac6f4dc398 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/MessageMentionsImpl.java @@ -128,6 +128,7 @@ public synchronized List getUsers() return mentionedUsers = Collections.unmodifiableList(users); } + @Override protected User matchUser(Matcher matcher) { long userId = MiscUtil.parseSnowflake(matcher.group(1)); @@ -140,6 +141,7 @@ protected User matchUser(Matcher matcher) return member == null ? null : member.getUser(); } + @Override protected Member matchMember(Matcher matcher) { long id = Long.parseUnsignedLong(matcher.group(1)); @@ -149,12 +151,14 @@ protected Member matchMember(Matcher matcher) : null; } + @Override protected GuildChannel matchChannel(Matcher matcher) { long channelId = MiscUtil.parseSnowflake(matcher.group(1)); return getJDA().getGuildChannelById(channelId); } + @Override protected Role matchRole(Matcher matcher) { long roleId = MiscUtil.parseSnowflake(matcher.group(1)); From d7bdd6c5f6ddddd5fa53a5853fde17f13bd3f163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Thu, 21 Apr 2022 18:09:39 +0200 Subject: [PATCH 21/29] Rename interface --- .../{MessageMentions.java => Mentions.java} | 2 +- .../java/net/dv8tion/jda/api/entities/Message.java | 2 +- .../api/interactions/commands/OptionMapping.java | 8 ++++---- .../jda/internal/entities/AbstractMessage.java | 2 +- .../jda/internal/entities/EntityBuilder.java | 2 +- .../jda/internal/entities/ReceivedMessage.java | 13 ++++++------- .../jda/internal/entities/SystemMessage.java | 3 +-- .../entities/mentions/AbstractMentions.java | 2 +- 8 files changed, 16 insertions(+), 18 deletions(-) rename src/main/java/net/dv8tion/jda/api/entities/{MessageMentions.java => Mentions.java} (99%) diff --git a/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java b/src/main/java/net/dv8tion/jda/api/entities/Mentions.java similarity index 99% rename from src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java rename to src/main/java/net/dv8tion/jda/api/entities/Mentions.java index f8d14635d0..a8153ef9dd 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/MessageMentions.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Mentions.java @@ -23,7 +23,7 @@ import java.util.List; //TODO-v5 | Docs -public interface MessageMentions +public interface Mentions { //TODO-v5 | Docs @Nonnull 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 d76503ee35..8df09ac3e7 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Message.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Message.java @@ -260,7 +260,7 @@ default Message getReferencedMessage() //TODO | Docs @Nonnull - MessageMentions getMentions(); + Mentions getMentions(); /** * Returns whether or not this Message has been edited before. diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionMapping.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionMapping.java index 9688819de2..5a4185f2ba 100644 --- a/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionMapping.java +++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/OptionMapping.java @@ -46,7 +46,7 @@ public class OptionMapping private final OptionType type; private final String name; private final TLongObjectMap resolved; - private final MessageMentions mentions; + private final Mentions mentions; public OptionMapping(DataObject data, TLongObjectMap resolved, JDA jda, Guild guild) { @@ -66,14 +66,14 @@ public OptionMapping(DataObject data, TLongObjectMap resolved, JDA jda, * Mentions are sorted by occurrence. * *

Mentioned {@link Member members} and {@link Role roles} are always of the same guild. - * If the interaction {@link Interaction#getUser() user}, mentions users from other guilds, they will only be provided by {@link MessageMentions#getUsers()}. + * If the interaction {@link Interaction#getUser() user}, mentions users from other guilds, they will only be provided by {@link net.dv8tion.jda.api.entities.Mentions#getUsers()}. * *

This is not supported for {@link CommandAutoCompleteInteraction}. * - * @return {@link MessageMentions} for this option + * @return {@link net.dv8tion.jda.api.entities.Mentions} for this option */ @Nonnull - public MessageMentions getMentions() + public Mentions getMentions() { return mentions; } 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 a9f3e21a10..b27d15975a 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java @@ -116,7 +116,7 @@ public MessageReference getMessageReference() @Nonnull @Override - public MessageMentions getMentions() + public Mentions getMentions() { unsupported(); return null; 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 72ea3e5761..3a326dcff5 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -1520,7 +1520,7 @@ else if (MISSING_CHANNEL.equals(ex.getMessage())) messageInteraction = createMessageInteraction(guild, jsonObject.getObject("interaction")); // Lazy Mention parsing and caching (includes reply mentions) - MessageMentions mentions = new MessageMentionsImpl( + Mentions mentions = new MessageMentionsImpl( api, guild, content, mentionsEveryone, jsonObject.getArray("mentions"), jsonObject.getArray("mention_roles") ); 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 6a49ba9cf0..96fb3e830f 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java @@ -30,7 +30,6 @@ import net.dv8tion.jda.api.requests.restaction.MessageAction; import net.dv8tion.jda.api.requests.restaction.pagination.ReactionPaginationAction; import net.dv8tion.jda.api.utils.MarkdownSanitizer; -import net.dv8tion.jda.api.utils.MiscUtil; import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.internal.JDAImpl; import net.dv8tion.jda.internal.requests.CompletedRestAction; @@ -62,7 +61,7 @@ public class ReceivedMessage extends AbstractMessage protected final Member member; protected final MessageActivity activity; protected final OffsetDateTime editedTime; - protected final MessageMentions mentions; + protected final Mentions mentions; protected final List reactions; protected final List attachments; protected final List embeds; @@ -80,10 +79,10 @@ public class ReceivedMessage extends AbstractMessage protected List invites = null; public ReceivedMessage( - long id, MessageChannel channel, MessageType type, MessageReference messageReference, - boolean fromWebhook, boolean tts, boolean pinned, String content, String nonce, User author, - Member member, MessageActivity activity, OffsetDateTime editTime, MessageMentions mentions, List reactions, - List attachments, List embeds, List stickers, List components, int flags, Message.Interaction interaction) + long id, MessageChannel channel, MessageType type, MessageReference messageReference, + boolean fromWebhook, boolean tts, boolean pinned, String content, String nonce, User author, + Member member, MessageActivity activity, OffsetDateTime editTime, Mentions mentions, List reactions, + List attachments, List embeds, List stickers, List components, int flags, Message.Interaction interaction) { super(content, nonce, tts); this.id = id; @@ -539,7 +538,7 @@ public List getActionRows() @Nonnull @Override - public MessageMentions getMentions() + public Mentions getMentions() { return mentions; } 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 b1c848fd06..e43f02f8ff 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/SystemMessage.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/SystemMessage.java @@ -16,7 +16,6 @@ package net.dv8tion.jda.internal.entities; -import gnu.trove.set.TLongSet; import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.interactions.components.LayoutComponent; import net.dv8tion.jda.api.requests.RestAction; @@ -33,7 +32,7 @@ public class SystemMessage extends ReceivedMessage public SystemMessage( long id, MessageChannel channel, MessageType type, MessageReference messageReference, boolean fromWebhook, boolean tts, boolean pinned, - String content, String nonce, User author, Member member, MessageActivity activity, OffsetDateTime editTime, MessageMentions mentions, + String content, String nonce, User author, Member member, MessageActivity activity, OffsetDateTime editTime, Mentions mentions, List reactions, List attachments, List embeds, List stickers, int flags) { super(id, channel, type, messageReference, fromWebhook, tts, pinned, content, nonce, author, member, diff --git a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java index 5e6a001476..5a04b792ea 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java @@ -34,7 +34,7 @@ import java.util.function.Function; import java.util.regex.Matcher; -public abstract class AbstractMentions implements MessageMentions +public abstract class AbstractMentions implements Mentions { protected final String content; protected final JDAImpl jda; From 4702a18f46bd0c0ecddcf9c0e5be3d5e24f04031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Thu, 21 Apr 2022 18:17:03 +0200 Subject: [PATCH 22/29] Improve isRoleMentioned --- .../entities/mentions/AbstractMentions.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java index 5a04b792ea..b5a21e1b03 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/mentions/AbstractMentions.java @@ -280,20 +280,13 @@ protected Emote matchEmote(Matcher m) protected boolean isRoleMentioned(IMentionable mentionable) { if (mentionable instanceof Role) - { return getRoles().contains(mentionable); - } - else if (mentionable instanceof Member) - { - final Member member = (Member) mentionable; - return CollectionUtils.containsAny(getRoles(), member.getRoles()); - } + Member member = null; + if (mentionable instanceof Member) + member = (Member) mentionable; else if (guild != null && mentionable instanceof User) - { - final Member member = guild.getMember((User) mentionable); - return member != null && CollectionUtils.containsAny(getRoles(), member.getRoles()); - } - return false; + member = guild.getMember((User) mentionable); + return member != null && CollectionUtils.containsAny(getRoles(), member.getRoles()); } protected boolean isMass(String s) From 2f5e2e43d29dd3a0f4a7ae90d8bb09312e1d6e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Fri, 22 Apr 2022 13:23:53 +0200 Subject: [PATCH 23/29] Add missing documentation and fix a bunch of docs --- .../dv8tion/jda/api/entities/Mentions.java | 102 +++- .../net/dv8tion/jda/api/entities/Message.java | 530 ++++++++++-------- 2 files changed, 360 insertions(+), 272 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/entities/Mentions.java b/src/main/java/net/dv8tion/jda/api/entities/Mentions.java index a8153ef9dd..44b1b18d6d 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Mentions.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Mentions.java @@ -22,34 +22,46 @@ import javax.annotation.Nonnull; import java.util.List; -//TODO-v5 | Docs +/** + * Interface to access the mentions of various entities. + */ public interface Mentions { - //TODO-v5 | Docs + /** + * The corresponding JDA instance + * + * @return The jda instance + */ @Nonnull JDA getJDA(); /** - * Indicates if this Message mentions everyone, using @everyone or @here. + * Indicates if everyone is mentioned, by either using {@code @everyone} or {@code @here}. * - * @return True, if message is mentioning everyone + *

This is different from checking if {@code @everyone} is in the string, since mentions require additional flags to trigger. + * + * @return True, if everyone is mentioned */ boolean mentionsEveryone(); /** * 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 - * counts direct mentions of the user and not mentions through roles or the everyone tag. + * counts direct mentions of the user and not mentions through roles or everyone mentions. + * + *

This might also contain users which are not present in {@link #getMembers()}. * - * @return immutable list of mentioned users + * @return Immutable list of mentioned users */ @Nonnull List getUsers(); /** - * A {@link org.apache.commons.collections4.Bag Bag} of mentioned users. - *
This can be used to retrieve the amount of times a user was mentioned in this message. This only - * counts direct mentions of the user and not mentions through roles or the everyone tag. + * A {@link org.apache.commons.collections4.Bag Bag} of mentioned {@link net.dv8tion.jda.api.entities.User Users}. + *
This can be used to retrieve the amount of times a user was mentioned. This only + * counts direct mentions of the user and not mentions through roles or everyone mentions. + * + *

This might also contain users which are not present in {@link #getMembers()}. * *

Example

*
{@code
@@ -77,19 +89,21 @@ public interface Mentions
     Bag getUsersBag();
 
     /**
-     * A immutable list of all mentioned {@link net.dv8tion.jda.api.entities.GuildChannel GuildChannels}.
+     * An immutable list of all mentioned {@link net.dv8tion.jda.api.entities.GuildChannel GuildChannels}.
      * 
If none were mentioned, this list is empty. Elements are sorted in order of appearance. * *

This may include GuildChannels from other {@link net.dv8tion.jda.api.entities.Guild Guilds} * - * @return immutable list of mentioned TextChannels + * @return Immutable list of mentioned GuildChannels */ @Nonnull List getChannels(); /** * A {@link org.apache.commons.collections4.Bag Bag} of mentioned channels. - *
This can be used to retrieve the amount of times a channel was mentioned in this message. + *
This can be used to retrieve the amount of times a channel was mentioned. + * + *

This may include GuildChannels from other {@link net.dv8tion.jda.api.entities.Guild Guilds} * *

Example

*
{@code
@@ -118,9 +132,9 @@ public interface Mentions
     Bag getChannelsBag();
 
     /**
-     * A immutable list of all mentioned {@link net.dv8tion.jda.api.entities.Role Roles}.
+     * An immutable list of all mentioned {@link net.dv8tion.jda.api.entities.Role Roles}.
      * 
If none were mentioned, this list is empty. Elements are sorted in order of appearance. This only - * counts direct mentions of the role and not mentions through the everyone tag. + * counts direct mentions of the role and not mentions through everyone mentions. * *

This may include Roles from other {@link net.dv8tion.jda.api.entities.Guild Guilds} * @@ -131,8 +145,10 @@ public interface Mentions /** * A {@link org.apache.commons.collections4.Bag Bag} of mentioned roles. - *
This can be used to retrieve the amount of times a role was mentioned in this message. This only - * counts direct mentions of the role and not mentions through the everyone tag. + *
This can be used to retrieve the amount of times a role was mentioned. This only + * counts direct mentions of the role and not mentions through everyone mentions. + * + *

This may include Roles from other {@link net.dv8tion.jda.api.entities.Guild Guilds} * *

Example

*
{@code
@@ -160,21 +176,21 @@ public interface Mentions
     Bag getRolesBag();
 
     /**
-     * All {@link net.dv8tion.jda.api.entities.Emote Emotes} used in this Message.
+     * All {@link net.dv8tion.jda.api.entities.Emote Emotes} used.
      * 
This only includes Custom Emotes, not unicode Emojis. JDA classifies Emotes as the Custom Emojis uploaded * to a Guild and retrievable with {@link net.dv8tion.jda.api.entities.Guild#getEmotes()}. These are not the same * as the unicode emojis that Discord also supports. Elements are sorted in order of appearance. * *

Unicode emojis are not included as {@link net.dv8tion.jda.api.entities.Emote Emote}! * - * @return An immutable list of the Emotes used in this message (example match {@literal <:jda:230988580904763393>}) + * @return An immutable list of the Emotes used (example match {@literal <:jda:230988580904763393>}) */ @Nonnull List getEmotes(); /** - * A {@link org.apache.commons.collections4.Bag Bag} of emotes used in this message. - *
This can be used to retrieve the amount of times an emote was used in this message. + * A {@link org.apache.commons.collections4.Bag Bag} of emotes used. + *
This can be used to retrieve the amount of times an emote was used. * *

Example

*
{@code
@@ -202,24 +218,54 @@ public interface Mentions
     Bag getEmotesBag();
 
     /**
-     * Creates an immutable list of {@link net.dv8tion.jda.api.entities.Member Members}
-     * representing the users of {@link #getUsers()} in the
-     * {@link net.dv8tion.jda.api.entities.Guild Guild} this Message was sent in.
+     * An immutable list of all mentioned {@link net.dv8tion.jda.api.entities.Member Members}.
+     * 
If none were mentioned, this list is empty. Elements are sorted in order of appearance. This only + * counts direct mentions of the role and not mentions through everyone mentions. * - * @return Immutable list of mentioned Members, or an empty list if this message was not sent in a guild + *

This is always empty in {@link PrivateChannel PrivateChannels}. + * + * @return Immutable list of mentioned Members, or an empty list */ @Nonnull List getMembers(); + /** + * A {@link org.apache.commons.collections4.Bag Bag} of mentioned {@link net.dv8tion.jda.api.entities.Member Members}. + *
This can be used to retrieve the amount of times a user was mentioned. This only + * counts direct mentions of the member and not mentions through roles or everyone mentions. + * + *

Example

+ *
{@code
+     * void sendCount(Message msg)
+     * {
+     *     List mentions = msg.getMentions().getMembers(); // distinct list, in order of appearance
+     *     Bag count = msg.getMentions().getMembersBag();
+     *     StringBuilder content = new StringBuilder();
+     *     for (Member user : mentions)
+     *     {
+     *         content.append(member.getUser().getAsTag())
+     *                .append(": ")
+     *                .append(count.getCount(member))
+     *                .append("\n");
+     *     }
+     *     msg.getChannel().sendMessage(content.toString()).queue();
+     * }
+     * }
+ * + * @return {@link org.apache.commons.collections4.Bag Bag} of mentioned members + * + * @see #getMembers() + */ @Nonnull Bag getMembersBag(); /** * Combines all instances of {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} * filtered by the specified {@link net.dv8tion.jda.api.entities.Message.MentionType MentionType} values. - *
This does not include {@link #getUsers()} to avoid duplicates. + *
If a {@link Member} is available, it will be taken in favor of a {@link User}. + * This only provides either the Member or the User instance, rather than both. * - *

If no MentionType values are given this will fallback to all types. + *

If no MentionType values are given, all types are used. * * @param types * {@link net.dv8tion.jda.api.entities.Message.MentionType MentionTypes} to include @@ -234,9 +280,9 @@ public interface Mentions /** * Checks if given {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} - * was mentioned in this message in any way (@User, @everyone, @here, @Role). + * was mentioned in any way (@User, @everyone, @here, @Role). *
If no filtering {@link net.dv8tion.jda.api.entities.Message.MentionType MentionTypes} are - * specified this will fallback to all mention types. + * specified, all types are used. * *

{@link Message.MentionType#HERE MentionType.HERE} and {@link Message.MentionType#EVERYONE MentionType.EVERYONE} * will only be checked, if the given {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} is of type 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 8df09ac3e7..c208e1f127 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Message.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Message.java @@ -16,6 +16,7 @@ package net.dv8tion.jda.api.entities; import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.MessageBuilder; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.exceptions.HttpException; import net.dv8tion.jda.api.exceptions.InsufficientPermissionException; @@ -37,7 +38,6 @@ import net.dv8tion.jda.internal.utils.IOUtil; import okhttp3.OkHttpClient; import okhttp3.Request; -import org.apache.commons.collections4.Bag; import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; @@ -69,7 +69,7 @@ * on certain events. Commonly this is used in groups or to indicate a pin within a MessageChannel. * The different types can be found in the {@link net.dv8tion.jda.api.entities.MessageType MessageType} enum. *

  • Data Message - *
    This type is produced by {@link net.dv8tion.jda.api.MessageBuilder MessageBuilder} + *
    This type is produced by {@link MessageBuilder MessageBuilder} * and only holds sendable information such as content or nonce. These messages do not allow * any modifications via RestActions or information that is generated when sent such as the id to be used.
  • * @@ -102,7 +102,7 @@ * *

    More information on formatting syntax can be found in the {@link java.util.Formatter format syntax documentation}! * - * @see net.dv8tion.jda.api.MessageBuilder MessageBuilder + * @see MessageBuilder MessageBuilder * @see MessageChannel#sendMessage(Message) * * @see MessageChannel#getIterableHistory() @@ -258,15 +258,31 @@ default Message getReferencedMessage() : null; } - //TODO | Docs + /** + * The {@link Mentions} used in this message. + * + *

    This includes {@link Member Members}, {@link GuildChannel GuildChannels}, {@link Role Roles}, and {@link Emote Emotes}. + * Can also be used to check if a message mentions {@code @everyone} or {@link @here}. + * + *

    Example

    + * {@code + * System.out.println("Message mentioned these users: " + message.getMentions().getUsers()); + * System.out.println("Message used these emotes: " + message.getMentions().getEmotes()); + * } + * + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) + * + * @return {@link Mentions} for this message. + */ @Nonnull Mentions getMentions(); /** * Returns whether or not this Message has been edited before. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return True if this message has been edited. */ @@ -277,8 +293,8 @@ default Message getReferencedMessage() * edited. If this Message has not been edited ({@link #isEdited()} is {@code false}), then this method * will return {@code null}. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return Time of the most recent edit, or {@code null} if the Message has never been edited. */ @@ -288,8 +304,8 @@ default Message getReferencedMessage() /** * The author of this Message * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return Message author */ @@ -306,8 +322,8 @@ default Message getReferencedMessage() * This will return null if the message was retrieved through {@link MessageChannel#retrieveMessageById(long)} or similar means, * unless the member is already cached. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return Message author, or {@code null} if the message was not sent in a TextChannel, or if the message was sent by a Webhook. * @@ -321,7 +337,7 @@ default Message getReferencedMessage() * jump to the specified message. * * @throws java.lang.UnsupportedOperationException - * If this is a system message + * If this is a data message * * @return A String representing the jump-to URL for the message */ @@ -342,8 +358,8 @@ default Message getReferencedMessage() * *

    If you want the actual Content (mentions as {@literal <@id>}), use {@link #getContentRaw()} instead * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return The textual content of the message with mentions resolved to be visually like the Discord client. */ @@ -366,8 +382,8 @@ default Message getReferencedMessage() * like {@literal *, **, __, ~~, ||} that provide text formatting. Any characters that match these but are not being used * for formatting are escaped to prevent possible formatting. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return The textual content from {@link #getContentDisplay()} with all text formatting characters removed or escaped. */ @@ -384,8 +400,8 @@ default Message getReferencedMessage() *

    You can use the codes to retrieve/validate invites via * {@link net.dv8tion.jda.api.entities.Invite#resolve(JDA, String) Invite.resolve(JDA, String)} * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return Immutable list of invite codes */ @@ -395,13 +411,13 @@ default Message getReferencedMessage() /** * Validation nonce for this Message *
    This can be used to validate that a Message was properly sent to the Discord Service. - *
    To set a nonce before sending you may use {@link net.dv8tion.jda.api.MessageBuilder#setNonce(String) MessageBuilder.setNonce(String)}! + *
    To set a nonce before sending you may use {@link MessageBuilder#setNonce(String) MessageBuilder.setNonce(String)}! * * @return The validation nonce * * @since 3.4.0 * - * @see net.dv8tion.jda.api.MessageBuilder#setNonce(String) + * @see MessageBuilder#setNonce(String) * @see Cryptographic Nonce - Wikipedia */ @Nullable @@ -418,8 +434,8 @@ default Message getReferencedMessage() * @param type * The {@link net.dv8tion.jda.api.entities.ChannelType ChannelType} to check against. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return True if the {@link net.dv8tion.jda.api.entities.ChannelType ChannelType} which this message was received * from is the same as the one specified by {@code type}. @@ -430,6 +446,9 @@ default Message getReferencedMessage() * Whether this message was sent in a {@link net.dv8tion.jda.api.entities.Guild Guild}. *
    If this is {@code false} then {@link #getGuild()} will throw an {@link java.lang.IllegalStateException}. * + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) + * * @return True, if {@link #getChannelType()}.{@link ChannelType#isGuild() isGuild()} is true. */ default boolean isFromGuild() @@ -442,8 +461,8 @@ default boolean isFromGuild() *
    This will never be {@link net.dv8tion.jda.api.entities.ChannelType#VOICE} as Messages can't be sent to * {@link net.dv8tion.jda.api.entities.VoiceChannel VoiceChannels}. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return The ChannelType which this message was received from. */ @@ -455,8 +474,8 @@ default boolean isFromGuild() * {@link net.dv8tion.jda.api.entities.User User}. *
    Useful if you want to ignore non-users. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return True if this message was sent by a {@link net.dv8tion.jda.api.entities.Webhook Webhook}. */ @@ -465,8 +484,8 @@ default boolean isFromGuild() /** * Returns the {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel} that this message was sent in. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return The MessageChannel of this Message */ @@ -477,8 +496,8 @@ default boolean isFromGuild() * Returns the {@link net.dv8tion.jda.api.entities.GuildMessageChannel GuildMessageChannel} that this message was sent in * if it was sent in a Guild. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * @throws java.lang.IllegalStateException * If this was not sent in a {@link net.dv8tion.jda.api.entities.Guild}. * @@ -495,8 +514,8 @@ default boolean isFromGuild() *

    Use {@link #getChannel()} for an ambiguous {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel} * if you do not need functionality specific to {@link net.dv8tion.jda.api.entities.PrivateChannel PrivateChannel}. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * @throws java.lang.IllegalStateException * If this was not sent in a {@link net.dv8tion.jda.api.entities.PrivateChannel}. * @@ -517,8 +536,8 @@ default boolean isFromGuild() *

    Use {@link #getChannel()} for an ambiguous {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel} * if you do not need functionality specific to {@link net.dv8tion.jda.api.entities.TextChannel TextChannel}. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * @throws java.lang.IllegalStateException * If this was not sent in a {@link net.dv8tion.jda.api.entities.TextChannel}. * @@ -539,8 +558,8 @@ default boolean isFromGuild() *

    Use {@link #getChannel()} for an ambiguous {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel} * if you do not need functionality specific to {@link net.dv8tion.jda.api.entities.NewsChannel NewsChannel}. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * @throws java.lang.IllegalStateException * If this was not sent in a {@link net.dv8tion.jda.api.entities.NewsChannel}. * @@ -558,8 +577,8 @@ default boolean isFromGuild() * message was sent in. This will always be {@code null} for DMs. *
    Equivalent to {@code getTextChannel().getParentCategory()} if this was sent in a {@link net.dv8tion.jda.api.entities.TextChannel}. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return {@link net.dv8tion.jda.api.entities.Category Category} for this message */ @@ -572,8 +591,8 @@ default boolean isFromGuild() *
    This is only valid if the Message was actually sent in a TextChannel. *
    You can check the type of channel this message was sent from using {@link #isFromType(ChannelType)} or {@link #getChannelType()}. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * @throws java.lang.IllegalStateException * If this was not sent in a {@link net.dv8tion.jda.api.entities.TextChannel}. * @@ -590,8 +609,8 @@ default boolean isFromGuild() * An immutable list of {@link net.dv8tion.jda.api.entities.Message.Attachment Attachments} that are attached to this message. *
    Most likely this will only ever be 1 {@link net.dv8tion.jda.api.entities.Message.Attachment Attachment} at most. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message + * @throws UnsupportedOperationException + * If this is a Data Message (output of {@link MessageBuilder MessageBuilder}) * * @return Immutable list of {@link net.dv8tion.jda.api.entities.Message.Attachment Attachments}. */ @@ -602,9 +621,6 @@ default boolean isFromGuild() * An immutable list of {@link net.dv8tion.jda.api.entities.MessageEmbed MessageEmbeds} that are part of this * Message. * - * @throws java.lang.UnsupportedOperationException - * If this is a system message - * * @return Immutable list of all given MessageEmbeds. */ @Nonnull @@ -683,59 +699,11 @@ default List