All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.dv8tion.jda.api.entities.Message Maven / Gradle / Ivy

Go to download

Java wrapper for the popular chat & VOIP service: Discord https://discord.com

There is a newer version: 5.1.0
Show newest version
/*
 * 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.annotations.ForRemoval;
import net.dv8tion.jda.annotations.ReplaceWith;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.channel.Channel;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer;
import net.dv8tion.jda.api.entities.channel.concrete.*;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.entities.channel.unions.GuildMessageChannelUnion;
import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion;
import net.dv8tion.jda.api.entities.emoji.CustomEmoji;
import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji;
import net.dv8tion.jda.api.entities.sticker.GuildSticker;
import net.dv8tion.jda.api.entities.sticker.Sticker;
import net.dv8tion.jda.api.entities.sticker.StickerItem;
import net.dv8tion.jda.api.entities.sticker.StickerSnowflake;
import net.dv8tion.jda.api.exceptions.HttpException;
import net.dv8tion.jda.api.exceptions.InsufficientPermissionException;
import net.dv8tion.jda.api.exceptions.MissingAccessException;
import net.dv8tion.jda.api.interactions.InteractionType;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.requests.RestConfig;
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
import net.dv8tion.jda.api.requests.restaction.MessageEditAction;
import net.dv8tion.jda.api.requests.restaction.ThreadChannelAction;
import net.dv8tion.jda.api.requests.restaction.pagination.ReactionPaginationAction;
import net.dv8tion.jda.api.utils.AttachedFile;
import net.dv8tion.jda.api.utils.AttachmentProxy;
import net.dv8tion.jda.api.utils.FileUpload;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageEditData;
import net.dv8tion.jda.api.utils.messages.MessageRequest;
import net.dv8tion.jda.internal.JDAImpl;
import net.dv8tion.jda.internal.entities.ReceivedMessage;
import net.dv8tion.jda.internal.requests.FunctionalCallback;
import net.dv8tion.jda.internal.utils.Checks;
import net.dv8tion.jda.internal.utils.IOUtil;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;

import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.*;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Represents a Text message received from Discord.
 * 
This represents messages received from {@link MessageChannel MessageChannels}. * *

This type is not updated. JDA does not keep track of changes to messages, it is advised to do this via events such * as {@link net.dv8tion.jda.api.events.message.MessageUpdateEvent MessageUpdateEvent} and similar. * *

Message Differences
* There are 2 implementations of this interface in JDA. *

    *
  1. Received Message *
    Messages received through events or history query. * These messages hold information of existing messages and * can be modified or deleted.
  2. *
  3. System Message *
    Specification of Received Messages that are generated by Discord * 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.
  4. *
* *

When a feature is not available it will throw an {@link java.lang.UnsupportedOperationException UnsupportedOperationException} * as per interface specifications. *
Specific operations may have specified information available in the {@code throws} javadoc. * *

Formattable
* This interface extends {@link java.util.Formattable Formattable} and can be used with a {@link java.util.Formatter Formatter} * such as used by {@link String#format(String, Object...) String.format(String, Object...)} * or {@link java.io.PrintStream#printf(String, Object...) PrintStream.printf(String, Object...)}. * *

This will use {@link #getContentDisplay()} rather than {@link Object#toString()}! *
Supported Features: *

    *
  • Alternative *
    - Using {@link #getContentRaw()} * (Example: {@code %#s} - uses {@link #getContentDisplay()})
  • * *
  • Width/Left-Justification *
    - Ensures the size of a format * (Example: {@code %20s} - uses at minimum 20 chars; * {@code %-10s} - uses left-justified padding)
  • * *
  • Precision *
    - Cuts the content to the specified size * (replacing last 3 chars with {@code ...}; Example: {@code %.20s})
  • *
* *

More information on formatting syntax can be found in the {@link java.util.Formatter format syntax documentation}! * * @see MessageChannel#getIterableHistory() * @see MessageChannel#getHistory() * @see MessageChannel#getHistoryAfter(String, int) * @see MessageChannel#getHistoryBefore(String, int) * @see MessageChannel#getHistoryAround(String, int) * @see MessageChannel#getHistoryFromBeginning(int) * @see MessageChannel#retrieveMessageById(String) * * @see MessageChannel#deleteMessageById(String) * @see MessageChannel#editMessageById(String, CharSequence) */ public interface Message extends ISnowflake, Formattable { /** Template for {@link #getJumpUrl()}.*/ String JUMP_URL = "https://discord.com/channels/%s/%s/%s"; /** * The maximum sendable file size (25 MiB) * * @see MessageRequest#setFiles(Collection) */ int MAX_FILE_SIZE = 25 << 20; /** * The maximum sendable file size for nitro (50 MiB) * * @see MessageRequest#setFiles(Collection) * * @deprecated Self-bots are not supported anymore and the value is outdated. */ @Deprecated int MAX_FILE_SIZE_NITRO = 50 << 20; /** * The maximum amount of files sendable within a single message ({@value}) * * @see MessageRequest#setFiles(Collection) */ int MAX_FILE_AMOUNT = 10; /** * The maximum amount of characters sendable in one message. ({@value}) *
This only applies to the raw content and not embeds! * * @see MessageRequest#setContent(String) */ int MAX_CONTENT_LENGTH = 2000; /** * The maximum amount of reactions that can be added to one message ({@value}) * * @see Message#addReaction(Emoji) */ int MAX_REACTIONS = 20; /** * The maximum amount of Embeds that can be added to one message ({@value}) * * @see MessageChannel#sendMessageEmbeds(Collection) * @see MessageRequest#setEmbeds(Collection) */ int MAX_EMBED_COUNT = 10; /** * The maximum amount of {@link Sticker Stickers} that can be added to a message ({@value}) * * @see GuildMessageChannel#sendStickers(StickerSnowflake...) * @see MessageCreateAction#setStickers(StickerSnowflake...) */ int MAX_STICKER_COUNT = 3; /** * The maximum amount of {@link LayoutComponent LayoutComponents} that can be added to a message ({@value}) */ int MAX_COMPONENT_COUNT = 5; /** * The maximum character length for a {@link #getNonce() nonce} ({@value}) */ int MAX_NONCE_LENGTH = 25; /** * Pattern used to find instant invites in strings. * *

The only named group is at index 1 with the name {@code "code"}. * * @see #getInvites() */ Pattern INVITE_PATTERN = Pattern.compile( "(?:https?://)?" + // Scheme "(?:\\w+\\.)?" + // Subdomain "discord(?:(?:app)?\\.com" + // Discord domain "/invite|\\.gg)/(?[a-z0-9-]+)" + // Path "(?:\\?\\S*)?(?:#\\S*)?", // Useless query or URN appendix Pattern.CASE_INSENSITIVE); /** * Pattern used to find {@link #getJumpUrl() Jump URLs} in strings. * *

Groups
*

* * * * * * * * * * * * * * * * * * * * * * * * * * *
Javadoc is stupid, this is not a required tag
IndexNameDescription
0N/AThe entire link
1guildThe ID of the target guild
2channelThe ID of the target channel
3messageThe ID of the target message
* You can use the names with {@link java.util.regex.Matcher#group(String) Matcher.group(String)} * and the index with {@link java.util.regex.Matcher#group(int) Matcher.group(int)}. * * @see #getJumpUrl() */ Pattern JUMP_URL_PATTERN = Pattern.compile( "(?:https?://)?" + // Scheme "(?:\\w+\\.)?" + // Subdomain "discord(?:app)?\\.com" + // Discord domain "/channels/(?\\d+)/(?\\d+)/(?\\d+)" + // Path "(?:\\?\\S*)?(?:#\\S*)?", // Useless query or URN appendix Pattern.CASE_INSENSITIVE); /** * Suppresses the warning for missing the {@link net.dv8tion.jda.api.requests.GatewayIntent#MESSAGE_CONTENT MESSAGE_CONTENT} intent and using one of the dependent getters. */ static void suppressContentIntentWarning() { ReceivedMessage.didContentIntentWarning = true; } /** * Returns the {@link MessageReference} for this Message. This will be null if this Message has no reference. * *

You can access all the information about a reference through this object. * Additionally, you can retrieve the referenced Message if discord did not load it in time. This can be done with {@link MessageReference#resolve()}. * * @return The message reference, or null. */ @Nullable MessageReference getMessageReference(); /** * Referenced message. * *

This will have different meaning depending on the {@link #getType() type} of message. * Usually, this is a {@link MessageType#INLINE_REPLY INLINE_REPLY} reference. * This can be null even if the type is {@link MessageType#INLINE_REPLY INLINE_REPLY}, when the message it references doesn't exist or discord wasn't able to resolve it in time. * *

This differs from a {@link MessageReference}, which contains the raw IDs attached to the reference, and allows you to retrieve the referenced message * * @return The referenced message, or null * * @see #getMessageReference() */ @Nullable default Message getReferencedMessage() { return getMessageReference() != null ? getMessageReference().getMessage() : null; } /** * The {@link Mentions} used in this message. * *

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

Example
* {@code * System.out.println("Message mentioned these users: " + message.getMentions().getUsers()); * System.out.println("Message used these custom emojis: " + message.getMentions().getCustomEmojis()); * } * * @return {@link Mentions} for this message. */ @Nonnull Mentions getMentions(); /** * Returns whether or not this Message has been edited before. * * @return True if this message has been edited. */ boolean isEdited(); /** * Provides the {@link java.time.OffsetDateTime OffsetDateTime} defining when this Message was last * edited. If this Message has not been edited ({@link #isEdited()} is {@code false}), then this method * will return {@code null}. * * @return Time of the most recent edit, or {@code null} if the Message has never been edited. */ @Nullable OffsetDateTime getTimeEdited(); /** * The author of this Message * * @return Message author */ @Nonnull User getAuthor(); /** * Returns the author of this Message as a {@link net.dv8tion.jda.api.entities.Member member}. *
This is only valid if the Message was actually sent in a GuildMessageChannel. This will return {@code null} * if the message was not sent in a GuildMessageChannel, or if the message was sent by a Webhook. *
You can check the type of channel this message was sent from using {@link #isFromType(ChannelType)} or {@link #getChannelType()}. * *

Discord does not provide a member object for messages returned by {@link RestAction RestActions} of any kind. * This will return null if the message was retrieved through {@link MessageChannel#retrieveMessageById(long)} or similar means, * unless the member is already cached. * * @return Message author, or {@code null} if the message was not sent in a GuildMessageChannel, or if the message was sent by a Webhook. * * @see #isWebhookMessage() */ @Nullable Member getMember(); /** * Returns the approximate position of this message in a {@link ThreadChannel}. *
This can be used to estimate the relative position of a message in a thread, by comparing against {@link ThreadChannel#getTotalMessageCount()}. * *

Notes: *

    *
  • The position might contain gaps or duplicates.
  • *
  • The position is not set on messages sent earlier than July 19th, 2022, and will return -1.
  • *
* * @throws IllegalStateException * If this message was not sent in a {@link ThreadChannel}. * * @return The approximate position of this message, or {@code -1} if this message is too old. * * @see Discord docs: position property on the message object */ int getApproximatePosition(); /** * Returns the jump-to URL for the received message. Clicking this URL in the Discord client will cause the client to * jump to the specified message. * * @return A String representing the jump-to URL for the message */ @Nonnull String getJumpUrl(); /** * The textual content of this message in the format that would be shown to the Discord client. All * {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} entities will be resolved to the format * shown by the Discord client instead of the {@literal } format. * *

This includes resolving: *
{@link User Users} / {@link net.dv8tion.jda.api.entities.Member Members} * to their @Username/@Nickname format, *
{@link GuildChannel GuildChannels} to their #ChannelName format, *
{@link net.dv8tion.jda.api.entities.Role Roles} to their @RoleName format *
{@link CustomEmoji Custom Emojis} (not unicode emojis!) to their {@code :name:} format. * *

If you want the actual Content (mentions as {@literal <@id>}), use {@link #getContentRaw()} instead * *

Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#MESSAGE_CONTENT GatewayIntent.MESSAGE_CONTENT} * * @return The textual content of the message with mentions resolved to be visually like the Discord client. */ @Nonnull String getContentDisplay(); /** * The raw textual content of this message. Does not resolve {@link net.dv8tion.jda.api.entities.IMentionable IMentionable} * entities like {@link #getContentDisplay()} does. This means that this is the completely raw textual content of the message * received from Discord and can contain mentions specified by * Discord's Message Formatting. * *

Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#MESSAGE_CONTENT GatewayIntent.MESSAGE_CONTENT} * * @return The raw textual content of the message, containing unresolved Discord message formatting. */ @Nonnull String getContentRaw(); /** * Gets the textual content of this message using {@link #getContentDisplay()} and then strips it of markdown characters * like {@literal *, **, __, ~~, ||} that provide text formatting. Any characters that match these but are not being used * for formatting are escaped to prevent possible formatting. * *

Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#MESSAGE_CONTENT GatewayIntent.MESSAGE_CONTENT} * * @return The textual content from {@link #getContentDisplay()} with all text formatting characters removed or escaped. */ @Nonnull String getContentStripped(); /** * Creates an immutable List of {@link net.dv8tion.jda.api.entities.Invite Invite} codes * that are included in this Message. *
This will use the {@link java.util.regex.Pattern Pattern} provided * under {@link #INVITE_PATTERN} to construct a {@link java.util.regex.Matcher Matcher} that will * parse the {@link #getContentRaw()} output and include all codes it finds in a list. * *

You can use the codes to retrieve/validate invites via * {@link net.dv8tion.jda.api.entities.Invite#resolve(JDA, String) Invite.resolve(JDA, String)} * * @return Immutable list of invite codes */ @Nonnull List getInvites(); /** * 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 MessageCreateAction#setNonce(String) MessageCreateAction.setNonce(String)}! * * @return The validation nonce * * @see MessageCreateAction#setNonce(String) * @see Cryptographic Nonce - Wikipedia */ @Nullable String getNonce(); /** * Used to determine if this Message was received from a {@link MessageChannel} * of the {@link net.dv8tion.jda.api.entities.channel.ChannelType ChannelType} specified. * *

Useful for restricting functionality to a certain type of channels. * * @param type * The {@link ChannelType ChannelType} to check against. * * @return True if the {@link net.dv8tion.jda.api.entities.channel.ChannelType ChannelType} which this message was received * from is the same as the one specified by {@code type}. */ boolean isFromType(@Nonnull ChannelType type); /** * Whether this message was sent in a {@link Guild Guild}. *
If this is {@code false} then {@link #getGuild()} will throw an {@link java.lang.IllegalStateException}. * * @return True, if {@link #getChannelType()}.{@link ChannelType#isGuild() isGuild()} is true. */ boolean isFromGuild(); /** * Gets the {@link net.dv8tion.jda.api.entities.channel.ChannelType ChannelType} that this message was received from. * * @return The ChannelType which this message was received from. */ @Nonnull ChannelType getChannelType(); /** * Indicates if this Message was sent by a {@link net.dv8tion.jda.api.entities.Webhook Webhook} instead of a * {@link User User}. *
Useful if you want to ignore non-users. * * @return True if this message was sent by a {@link net.dv8tion.jda.api.entities.Webhook Webhook}. */ boolean isWebhookMessage(); /** * If this message is from an application-owned {@link net.dv8tion.jda.api.entities.Webhook Webhook} or * is a response to an {@link net.dv8tion.jda.api.interactions.Interaction Interaction}, this will return * the application's id. * * @return The application's id or {@code null} if this message was not sent by an application */ @Nullable default String getApplicationId() { return getApplicationIdLong() == 0 ? null : Long.toUnsignedString(getApplicationIdLong()); } /** * If this message is from an application-owned {@link net.dv8tion.jda.api.entities.Webhook Webhook} or * is a response to an {@link net.dv8tion.jda.api.interactions.Interaction Interaction}, this will return * the application's id. * * @return The application's id or 0 if this message was not sent by an application */ long getApplicationIdLong(); /** * Whether this message instance has an available {@link #getChannel()}. * *

This can be {@code false} for messages sent via webhooks, or in the context of interactions. * * @return True, if {@link #getChannel()} is available */ boolean hasChannel(); /** * The ID for the channel this message was sent in. *
This is useful when {@link #getChannel()} is unavailable, for instance on webhook messages. * * @return The channel id */ long getChannelIdLong(); /** * The ID for the channel this message was sent in. *
This is useful when {@link #getChannel()} is unavailable, for instance on webhook messages. * * @return The channel id */ @Nonnull default String getChannelId() { return Long.toUnsignedString(getChannelIdLong()); } /** * Returns the {@link MessageChannel} that this message was sent in. * * @throws IllegalStateException * If the channel is not available (see {@link #hasChannel()}) * * @return The MessageChannel of this Message * * @see #hasChannel() * @see #getChannelIdLong() */ @Nonnull MessageChannelUnion getChannel(); /** * Returns the {@link GuildMessageChannel} that this message was sent in * if it was sent in a Guild. * * @throws java.lang.IllegalStateException * If this was not sent in a {@link Guild} or the channel is not available (see {@link #hasChannel()}). * * @return The MessageChannel of this Message * * @see #hasChannel() * @see #getChannelIdLong() */ @Nonnull GuildMessageChannelUnion getGuildChannel(); /** * The {@link Category Category} this * message was sent in. This will always be {@code null} for DMs. *
Equivalent to {@code getGuildChannel().getParentCategory()} if this was sent in a {@link GuildMessageChannel}. * * @return {@link net.dv8tion.jda.api.entities.channel.concrete.Category Category} for this message */ @Nullable Category getCategory(); /** * Whether this message instance provides a guild instance via {@link #getGuild()}. *
This is different from {@link #isFromGuild()}, which checks whether the message was sent in a guild. * This method describes whether {@link #getGuild()} is usable. * *

This can be {@code false} for messages sent via webhooks, or in the context of interactions. * * @return True, if {@link #getGuild()} is provided */ boolean hasGuild(); /** * The ID for the guild this message was sent in. *
This is useful when {@link #getGuild()} is not provided, for instance on webhook messages. * * @return The guild id, or 0 if this message was not sent in a guild */ long getGuildIdLong(); /** * The ID for the guild this message was sent in. *
This is useful when {@link #getGuild()} is not provided, for instance on webhook messages. * * @return The guild id, or null if this message was not sent in a guild */ @Nullable default String getGuildId() { return isFromGuild() ? Long.toUnsignedString(getGuildIdLong()) : null; } /** * Returns the {@link Guild Guild} that this message was sent in. *
This is just a shortcut to {@link #getGuildChannel()}{@link GuildChannel#getGuild() .getGuild()}. *
This is only valid if the Message was actually sent in a GuildMessageChannel. *
You can check the type of channel this message was sent from using {@link #isFromType(ChannelType)} or {@link #getChannelType()}. * * @throws java.lang.IllegalStateException * If this was not sent in a {@link GuildChannel} or the guild instance is not provided * * @return The Guild this message was sent in * * @see #isFromGuild() * @see #isFromType(ChannelType) * @see #getChannelType() */ @Nonnull Guild getGuild(); /** * 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. * *

Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#MESSAGE_CONTENT GatewayIntent.MESSAGE_CONTENT} * * @return Immutable list of {@link net.dv8tion.jda.api.entities.Message.Attachment Attachments}. */ @Nonnull List getAttachments(); /** * An immutable list of {@link net.dv8tion.jda.api.entities.MessageEmbed MessageEmbeds} that are part of this Message. * *

Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#MESSAGE_CONTENT GatewayIntent.MESSAGE_CONTENT} * * @return Immutable list of all given MessageEmbeds. */ @Nonnull List getEmbeds(); /** * Layouts of interactive components, usually {@link ActionRow ActionRows}. *
You can use {@link MessageRequest#setComponents(LayoutComponent...)} to update these. * *

Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#MESSAGE_CONTENT GatewayIntent.MESSAGE_CONTENT} * * @return Immutable {@link List} of {@link LayoutComponent} * * @see #getActionRows() * @see #getButtons() * @see #getButtonById(String) */ @Nonnull List getComponents(); /** * Rows of interactive components such as {@link Button Buttons}. *
You can use {@link MessageRequest#setComponents(LayoutComponent...)} to update these. * *

Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#MESSAGE_CONTENT GatewayIntent.MESSAGE_CONTENT} * * @return Immutable {@link List} of {@link ActionRow} * * @see #getButtons() * @see #getButtonById(String) */ @Nonnull default List getActionRows() { return getComponents() .stream() .filter(ActionRow.class::isInstance) .map(ActionRow.class::cast) .collect(Collectors.toList()); } /** * All {@link Button Buttons} attached to this message. * *

Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#MESSAGE_CONTENT GatewayIntent.MESSAGE_CONTENT} * * @return Immutable {@link List} of {@link Button Buttons} */ @Nonnull default List





© 2015 - 2024 Weber Informatics LLC | Privacy Policy