/*
* 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.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.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.requests.Requester;
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 net.dv8tion.jda.api.entities.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.
*
* Received Message
* Messages received through events or history query.
* These messages hold information of existing messages and
* can be modified or deleted.
* 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.
*
*
* 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 (8 MiB)
*
* @see MessageRequest#setFiles(Collection)
*/
int MAX_FILE_SIZE = 8 << 20;
/**
* The maximum sendable file size for nitro (50 MiB)
*
* @see MessageRequest#setFiles(Collection)
*/
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
*
* Index
* Name
* Description
*
*
* 0
* N/A
* The entire link
*
*
* 1
* guild
* The ID of the target guild
*
*
* 2
* channel
* The ID of the target channel
*
*
* 3
* message
* The 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 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 net.dv8tion.jda.api.entities.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 net.dv8tion.jda.api.entities.MessageChannel MessageChannel}
* of the {@link net.dv8tion.jda.api.entities.ChannelType ChannelType} specified.
*
* Useful for restricting functionality to a certain type of channels.
*
* @param type
* The {@link net.dv8tion.jda.api.entities.ChannelType ChannelType} to check against.
*
* @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}.
*/
boolean isFromType(@Nonnull ChannelType type);
/**
* 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}.
*
* @return True, if {@link #getChannelType()}.{@link ChannelType#isGuild() isGuild()} is true.
*/
default boolean isFromGuild()
{
return getChannelType().isGuild();
}
/**
* Gets the {@link net.dv8tion.jda.api.entities.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();
/**
* Returns the {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel} that this message was sent in.
*
* @return The MessageChannel of this Message
*/
@Nonnull
MessageChannelUnion getChannel();
/**
* 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.IllegalStateException
* If this was not sent in a {@link net.dv8tion.jda.api.entities.Guild}.
*
* @return The MessageChannel of this Message
*/
@Nonnull
GuildMessageChannelUnion getGuildChannel();
/**
* The {@link net.dv8tion.jda.api.entities.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.Category Category} for this message
*/
@Nullable
Category getCategory();
/**
* Returns the {@link net.dv8tion.jda.api.entities.Guild Guild} that this message was sent in.
* This is just a shortcut to {@link #getGuildChannel()}{@link net.dv8tion.jda.api.entities.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 net.dv8tion.jda.api.entities.GuildChannel}.
*
* @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 getButtons()
{
return getComponents().stream()
.map(LayoutComponent::getButtons)
.flatMap(List::stream)
.collect(Collectors.toList());
}
/**
* Gets the {@link Button} with the specified ID.
*
* Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#MESSAGE_CONTENT GatewayIntent.MESSAGE_CONTENT}
*
* @param id
* The id of the button
*
* @throws IllegalArgumentException
* If the id is null
*
* @return The {@link Button} or null of no button with that ID is present on this message
*/
@Nullable
default Button getButtonById(@Nonnull String id)
{
Checks.notNull(id, "Button ID");
return getButtons().stream()
.filter(it -> id.equals(it.getId()))
.findFirst().orElse(null);
}
/**
* All {@link Button Buttons} with the specified label attached to this message.
*
*
Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#MESSAGE_CONTENT GatewayIntent.MESSAGE_CONTENT}
*
* @param label
* The button label
* @param ignoreCase
* Whether to use {@link String#equalsIgnoreCase(String)} instead of {@link String#equals(Object)}
*
* @throws IllegalArgumentException
* If the provided label is null
*
* @return Immutable {@link List} of {@link Button Buttons} with the specified label
*/
@Nonnull
default List getButtonsByLabel(@Nonnull String label, boolean ignoreCase)
{
Checks.notNull(label, "Label");
Predicate filter;
if (ignoreCase)
filter = b -> label.equalsIgnoreCase(b.getLabel());
else
filter = b -> label.equals(b.getLabel());
return getButtons().stream()
.filter(filter)
.collect(Collectors.toList());
}
/**
* All {@link MessageReaction MessageReactions} that are on this Message.
*
* @return Immutable list of all MessageReactions on this message.
*
* @see MessageReaction
*/
@Nonnull
List getReactions();
/**
* All {@link StickerItem StickerItems} that are in this Message.
* The returned StickerItems may only contain necessary information such as the sticker id, format type, name, and icon url.
*
* @return Immutable list of all StickerItems in this message.
*/
@Nonnull
List getStickers();
/**
* Defines whether or not this Message triggers TTS (Text-To-Speech).
*
* @return If this message is TTS.
*/
boolean isTTS();
/**
* A {@link net.dv8tion.jda.api.entities.MessageActivity MessageActivity} that contains its type and party id.
*
* @return The activity, or {@code null} if no activity was added to the message.
*/
@Nullable
MessageActivity getActivity();
/**
* Edits this message and updates the content.
* Any other fields of the message will remain unchanged,
* you can use {@link net.dv8tion.jda.api.utils.messages.MessageEditRequest#setReplace(boolean) replace(true)} to remove everything else (embeds/attachments/components).
*
* The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The request was attempted after the account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed, or after {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
* was revoked in the {@link net.dv8tion.jda.api.entities.GuildMessageChannel GuildMessageChannel}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* The provided {@code messageId} is unknown in this MessageChannel, either due to the id being invalid, or
* the message it referred to has already been deleted.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* The request was attempted after the channel was deleted.
*
*
* @param newContent
* The new content of the message, or empty string to remove content (assumes other fields exist like embeds)
*
* @throws UnsupportedOperationException
* If this is a system message
* @throws IllegalStateException
* If the message is not authored by this bot
* @throws IllegalArgumentException
* If null is provided or the new content is longer than {@value #MAX_CONTENT_LENGTH} characters
*
* @return {@link MessageEditAction}
*
* @see MessageChannel#editMessageById(long, CharSequence)
*/
@Nonnull
@CheckReturnValue
MessageEditAction editMessage(@Nonnull CharSequence newContent);
/**
* Edits this message using the provided {@link MessageEditData}.
* You can use {@link net.dv8tion.jda.api.utils.messages.MessageEditBuilder MessageEditBuilder} to create a {@link MessageEditData} instance.
*
* The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The request was attempted after the account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed, or after {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
* was revoked in the {@link net.dv8tion.jda.api.entities.GuildMessageChannel GuildMessageChannel}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* The provided {@code messageId} is unknown in this MessageChannel, either due to the id being invalid, or
* the message it referred to has already been deleted.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* The request was attempted after the channel was deleted.
*
*
* @param data
* The {@link MessageEditData} used to update the message
*
* @throws UnsupportedOperationException
* If this is a system message
* @throws IllegalStateException
* If the message is not authored by this bot
* @throws IllegalArgumentException
* If null is provided
*
* @return {@link MessageEditAction}
*
* @see net.dv8tion.jda.api.utils.messages.MessageEditBuilder MessageEditBuilder
* @see MessageChannel#editMessageById(long, MessageEditData)
*/
@Nonnull
@CheckReturnValue
MessageEditAction editMessage(@Nonnull MessageEditData data);
/**
* Edits this message using the provided {@link MessageEmbed MessageEmbeds}.
* You can use {@link net.dv8tion.jda.api.EmbedBuilder EmbedBuilder} to create a {@link MessageEmbed} instance.
*
* The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The request was attempted after the account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed, or after {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
* was revoked in the {@link net.dv8tion.jda.api.entities.GuildMessageChannel GuildMessageChannel}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* The provided {@code messageId} is unknown in this MessageChannel, either due to the id being invalid, or
* the message it referred to has already been deleted.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* The request was attempted after the channel was deleted.
*
*
* @param embeds
* The new {@link MessageEmbed MessageEmbeds} of the message, empty list to remove embeds
*
* @throws UnsupportedOperationException
* If this is a system message
* @throws IllegalStateException
* If the message is not authored by this bot
* @throws IllegalArgumentException
*
* If {@code null} is provided
* If more than {@value Message#MAX_EMBED_COUNT} embeds are provided
*
*
* @return {@link MessageEditAction}
*
* @see net.dv8tion.jda.api.EmbedBuilder EmbedBuilder
* @see MessageChannel#editMessageEmbedsById(long, Collection)
*/
@Nonnull
@CheckReturnValue
MessageEditAction editMessageEmbeds(@Nonnull Collection extends MessageEmbed> embeds);
/**
* Edits this message using the provided {@link MessageEmbed MessageEmbeds}.
* You can use {@link net.dv8tion.jda.api.EmbedBuilder EmbedBuilder} to create a {@link MessageEmbed} instance.
*
* The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The request was attempted after the account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed, or after {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
* was revoked in the {@link net.dv8tion.jda.api.entities.GuildMessageChannel GuildMessageChannel}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* The provided {@code messageId} is unknown in this MessageChannel, either due to the id being invalid, or
* the message it referred to has already been deleted.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* The request was attempted after the channel was deleted.
*
*
* @param embeds
* The new {@link MessageEmbed MessageEmbeds} of the message, or an empty list to remove all embeds
*
* @throws UnsupportedOperationException
* If this is a system message
* @throws IllegalStateException
* If the message is not authored by this bot
* @throws IllegalArgumentException
*
* If {@code null} is provided
* If more than {@value Message#MAX_EMBED_COUNT} embeds are provided
*
*
* @return {@link MessageEditAction}
*
* @see net.dv8tion.jda.api.EmbedBuilder EmbedBuilder
* @see MessageChannel#editMessageEmbedsById(long, Collection)
*/
@Nonnull
@CheckReturnValue
default MessageEditAction editMessageEmbeds(@Nonnull MessageEmbed... embeds)
{
Checks.noneNull(embeds, "MessageEmbeds");
return editMessageEmbeds(Arrays.asList(embeds));
}
/**
* Edits this message using the provided {@link LayoutComponent LayoutComponents}.
*
* The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The request was attempted after the account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed, or after {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
* was revoked in the {@link net.dv8tion.jda.api.entities.GuildMessageChannel GuildMessageChannel}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* The provided {@code messageId} is unknown in this MessageChannel, either due to the id being invalid, or
* the message it referred to has already been deleted.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* The request was attempted after the channel was deleted.
*
*
* @param components
* The new {@link LayoutComponent LayoutComponents} of the message, or an empty list to remove all components
*
* @throws UnsupportedOperationException
* If this is a system message
* @throws IllegalStateException
* If the message is not authored by this bot
* @throws IllegalArgumentException
*
* If {@code null} is provided
* If any of the components is not {@link LayoutComponent#isMessageCompatible() message compatible}
* If more than {@value Message#MAX_COMPONENT_COUNT} components are provided
*
*
* @return {@link MessageEditAction}
*
* @see MessageChannel#editMessageComponentsById(long, Collection)
*/
@Nonnull
@CheckReturnValue
MessageEditAction editMessageComponents(@Nonnull Collection extends LayoutComponent> components);
/**
* Edits this message using the provided {@link LayoutComponent LayoutComponents}.
*
* The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The request was attempted after the account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed, or after {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
* was revoked in the {@link net.dv8tion.jda.api.entities.GuildMessageChannel GuildMessageChannel}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* The provided {@code messageId} is unknown in this MessageChannel, either due to the id being invalid, or
* the message it referred to has already been deleted.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* The request was attempted after the channel was deleted.
*
*
* @param components
* The new {@link LayoutComponent LayoutComponents} of the message, empty list to remove all components
*
* @throws UnsupportedOperationException
* If this is a system message
* @throws IllegalStateException
* If the message is not authored by this bot
* @throws IllegalArgumentException
*
* If {@code null} is provided
* If any of the components is not {@link LayoutComponent#isMessageCompatible() message compatible}
* If more than {@value Message#MAX_COMPONENT_COUNT} components are provided
*
*
* @return {@link MessageEditAction}
*
* @see MessageChannel#editMessageComponentsById(long, Collection)
*/
@Nonnull
@CheckReturnValue
default MessageEditAction editMessageComponents(@Nonnull LayoutComponent... components)
{
Checks.noneNull(components, "Components");
return editMessageComponents(Arrays.asList(components));
}
/**
* Edits this message using the provided format arguments.
*
* The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The request was attempted after the account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed, or after {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
* was revoked in the {@link net.dv8tion.jda.api.entities.GuildMessageChannel GuildMessageChannel}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* The provided {@code messageId} is unknown in this MessageChannel, either due to the id being invalid, or
* the message it referred to has already been deleted.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* The request was attempted after the channel was deleted.
*
*
* @param format
* Format String used to generate new Content
* @param args
* The arguments which should be used to format the given format String
*
* @throws IllegalArgumentException
* If provided {@code format} is {@code null} or blank.
* @throws UnsupportedOperationException
* If this is a system message
* @throws IllegalStateException
* If the message is not authored by this bot
* @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
* If this is a {@link net.dv8tion.jda.api.entities.GuildMessageChannel GuildMessageChannel} and this account does not have
* {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
* @throws java.util.IllegalFormatException
* If a format string contains an illegal syntax,
* a format specifier that is incompatible with the given arguments,
* insufficient arguments given the format string, or other illegal conditions.
* For specification of all possible formatting errors,
* see the Details
* section of the formatter class specification.
*
* @return {@link MessageEditAction}
*
* @see MessageChannel#editMessageFormatById(long, String, Object...)
*/
@Nonnull
@CheckReturnValue
MessageEditAction editMessageFormat(@Nonnull String format, @Nonnull Object... args);
/**
* Edits this message using the provided files.
*
* The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#REQUEST_ENTITY_TOO_LARGE REQUEST_ENTITY_TOO_LARGE}
* If any of the provided files is bigger than {@link Guild#getMaxFileSize()}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The request was attempted after the account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed, or after {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
* was revoked in the {@link net.dv8tion.jda.api.entities.GuildMessageChannel GuildMessageChannel}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* The provided {@code messageId} is unknown in this MessageChannel, either due to the id being invalid, or
* the message it referred to has already been deleted. This might also be triggered for ephemeral messages.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* The request was attempted after the channel was deleted.
*
*
* Resource Handling Note: Once the request is handed off to the requester, for example when you call {@link RestAction#queue()},
* the requester will automatically clean up all opened files by itself. You are only responsible to close them yourself if it is never handed off properly.
* For instance, if an exception occurs after using {@link FileUpload#fromData(File)}, before calling {@link RestAction#queue()}.
* You can safely use a try-with-resources to handle this, since {@link FileUpload#close()} becomes ineffective once the request is handed off.
*
* @param attachments
* The new attachments of the message (Can be {@link FileUpload FileUploads} or {@link net.dv8tion.jda.api.utils.AttachmentUpdate AttachmentUpdates})
*
* @throws UnsupportedOperationException
* If this is a system message
* @throws IllegalStateException
* If the message is not authored by this bot
* @throws IllegalArgumentException
* If {@code null} is provided
*
* @return {@link MessageEditAction} that can be used to further update the message
*
* @see AttachedFile#fromAttachment(Message.Attachment)
* @see FileUpload#fromData(InputStream, String)
*/
@Nonnull
@CheckReturnValue
MessageEditAction editMessageAttachments(@Nonnull Collection extends AttachedFile> attachments);
/**
* Edits this message using the provided files.
*
*
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#REQUEST_ENTITY_TOO_LARGE REQUEST_ENTITY_TOO_LARGE}
* If any of the provided files is bigger than {@link Guild#getMaxFileSize()}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The request was attempted after the account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed, or after {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
* was revoked in the {@link net.dv8tion.jda.api.entities.GuildMessageChannel GuildMessageChannel}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* The provided {@code messageId} is unknown in this MessageChannel, either due to the id being invalid, or
* the message it referred to has already been deleted. This might also be triggered for ephemeral messages.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* The request was attempted after the channel was deleted.
*
*
* Resource Handling Note: Once the request is handed off to the requester, for example when you call {@link RestAction#queue()},
* the requester will automatically clean up all opened files by itself. You are only responsible to close them yourself if it is never handed off properly.
* For instance, if an exception occurs after using {@link FileUpload#fromData(File)}, before calling {@link RestAction#queue()}.
* You can safely use a try-with-resources to handle this, since {@link FileUpload#close()} becomes ineffective once the request is handed off.
*
* @param attachments
* The new attachments of the message (Can be {@link FileUpload FileUploads} or {@link net.dv8tion.jda.api.utils.AttachmentUpdate AttachmentUpdates})
*
* @throws UnsupportedOperationException
* If this is a system message
* @throws IllegalStateException
* If the message is not authored by this bot
* @throws IllegalArgumentException
* If {@code null} is provided
*
* @return {@link MessageEditAction} that can be used to further update the message
*
* @see AttachedFile#fromAttachment(Message.Attachment)
* @see FileUpload#fromData(InputStream, String)
*/
@Nonnull
@CheckReturnValue
default MessageEditAction editMessageAttachments(@Nonnull AttachedFile... attachments)
{
Checks.noneNull(attachments, "Attachments");
return editMessageAttachments(Arrays.asList(attachments));
}
/**
* Replies and references this message.
* This is identical to {@code message.getGuildChannel().sendStickers(stickers).reference(message)}.
* You can use {@link MessageCreateAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message.
* By default there won't be any error thrown if the referenced message does not exist.
* This behavior can be changed with {@link MessageCreateAction#failOnInvalidReply(boolean)}.
*
*
For further info, see {@link GuildMessageChannel#sendStickers(Collection)} and {@link MessageCreateAction#setMessageReference(Message)}.
*
* @param stickers
* The 1-3 stickers to send
*
* @throws MissingAccessException
* If the currently logged in account does not have {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} in this channel
* @throws InsufficientPermissionException
*
* If this is a {@link ThreadChannel} and the bot does not have {@link Permission#MESSAGE_SEND_IN_THREADS Permission.MESSAGE_SEND_IN_THREADS}
* If this is not a {@link ThreadChannel} and the bot does not have {@link Permission#MESSAGE_SEND Permission.MESSAGE_SEND}
*
* @throws IllegalArgumentException
*
* If any of the provided stickers is a {@link GuildSticker},
* which is either {@link GuildSticker#isAvailable() unavailable} or from a different guild.
* If the list is empty or has more than 3 stickers
* If null is provided
*
* @throws IllegalStateException
* If this message was not sent in a {@link Guild}
*
* @return {@link MessageCreateAction}
*
* @see Sticker#fromId(long)
*/
@Nonnull
@CheckReturnValue
default MessageCreateAction replyStickers(@Nonnull Collection extends StickerSnowflake> stickers)
{
return getGuildChannel().sendStickers(stickers).setMessageReference(this);
}
/**
* Replies and references this message.
* This is identical to {@code message.getGuildChannel().sendStickers(stickers).reference(message)}.
* You can use {@link MessageCreateAction#mentionRepliedUser(boolean) mentionRepliedUser(false)} to not mention the author of the message.
* By default there won't be any error thrown if the referenced message does not exist.
* This behavior can be changed with {@link MessageCreateAction#failOnInvalidReply(boolean)}.
*
* For further info, see {@link GuildMessageChannel#sendStickers(Collection)} and {@link MessageCreateAction#setMessageReference(Message)}.
*
* @param stickers
* The 1-3 stickers to send
*
* @throws MissingAccessException
* If the currently logged in account does not have {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} in this channel
* @throws InsufficientPermissionException
*
* If this is a {@link ThreadChannel} and the bot does not have {@link Permission#MESSAGE_SEND_IN_THREADS Permission.MESSAGE_SEND_IN_THREADS}
* If this is not a {@link ThreadChannel} and the bot does not have {@link Permission#MESSAGE_SEND Permission.MESSAGE_SEND}
*
* @throws IllegalArgumentException
*
* If any of the provided stickers is a {@link GuildSticker},
* which is either {@link GuildSticker#isAvailable() unavailable} or from a different guild.
* If the list is empty or has more than 3 stickers
* If null is provided
*
* @throws IllegalStateException
* If this message was not sent in a {@link Guild}
*
* @return {@link MessageCreateAction}
*
* @see Sticker#fromId(long)
*/
@Nonnull
@CheckReturnValue
default MessageCreateAction replyStickers(@Nonnull StickerSnowflake... stickers)
{
return getGuildChannel().sendStickers(stickers).setMessageReference(this);
}
/**
* Shortcut for {@code getChannel().sendMessage(content).setMessageReference(this)}-
*
* @param content
* The reply content
*
* @throws InsufficientPermissionException
* If {@link MessageChannel#sendMessage(CharSequence)} throws
* @throws IllegalArgumentException
* If {@link MessageChannel#sendMessage(CharSequence)} throws
*
* @return {@link MessageCreateAction}
*/
@Nonnull
@CheckReturnValue
default MessageCreateAction reply(@Nonnull CharSequence content)
{
return getChannel().sendMessage(content).setMessageReference(this);
}
/**
* Shortcut for {@code getChannel().sendMessage(data).setMessageReference(this)}-
*
* @param msg
* The {@link MessageCreateData} to send
*
* @throws InsufficientPermissionException
* If {@link MessageChannel#sendMessage(MessageCreateData)} throws
* @throws IllegalArgumentException
* If {@link MessageChannel#sendMessage(MessageCreateData)} throws
*
* @return {@link MessageCreateAction}
*/
@Nonnull
@CheckReturnValue
default MessageCreateAction reply(@Nonnull MessageCreateData msg)
{
return getChannel().sendMessage(msg).setMessageReference(this);
}
/**
* Shortcut for {@code getChannel().sendMessageEmbeds(embed, other).setMessageReference(this)}-
*
* @param embed
* The {@link MessageEmbed} to send
* @param other
* Any addition {@link MessageEmbed MessageEmbeds} to send
*
* @throws InsufficientPermissionException
* If {@link MessageChannel#sendMessageEmbeds(MessageEmbed, MessageEmbed...)} throws
* @throws IllegalArgumentException
* If {@link MessageChannel#sendMessageEmbeds(MessageEmbed, MessageEmbed...)} throws
*
* @return {@link MessageCreateAction}
*/
@Nonnull
@CheckReturnValue
default MessageCreateAction replyEmbeds(@Nonnull MessageEmbed embed, @Nonnull MessageEmbed... other)
{
Checks.notNull(embed, "MessageEmbeds");
Checks.noneNull(other, "MessageEmbeds");
List embeds = new ArrayList<>(1 + other.length);
embeds.add(embed);
Collections.addAll(embeds, other);
return replyEmbeds(embeds);
}
/**
* Shortcut for {@code getChannel().sendMessageEmbeds(embeds).setMessageReference(this)}-
*
* @param embeds
* The {@link MessageEmbed MessageEmbeds} to send
*
* @throws InsufficientPermissionException
* If {@link MessageChannel#sendMessageEmbeds(Collection)} throws
* @throws IllegalArgumentException
* If {@link MessageChannel#sendMessageEmbeds(Collection)} throws
*
* @return {@link MessageCreateAction}
*/
@Nonnull
@CheckReturnValue
default MessageCreateAction replyEmbeds(@Nonnull Collection extends MessageEmbed> embeds)
{
return getChannel().sendMessageEmbeds(embeds).setMessageReference(this);
}
/**
* Shortcut for {@code getChannel().sendMessageFormat(format, args).setMessageReference(this)}-
*
* @param format
* The format string
* @param args
* The arguments to use in the format string
*
* @throws InsufficientPermissionException
* If {@link MessageChannel#sendMessageFormat(String, Object...)} throws
* @throws IllegalArgumentException
* If {@link MessageChannel#sendMessageFormat(String, Object...)} throws
*
* @return {@link MessageCreateAction}
*/
@Nonnull
@CheckReturnValue
default MessageCreateAction replyFormat(@Nonnull String format, @Nonnull Object... args)
{
return getChannel().sendMessageFormat(format, args).setMessageReference(this);
}
/**
* Shortcut for {@code getChannel().sendFiles(files).setMessageReference(this)}-
*
* @param files
* The {@link FileUpload FileUploads} to send
*
* @throws InsufficientPermissionException
* If {@link MessageChannel#sendFiles(FileUpload...)} throws
* @throws IllegalArgumentException
* If {@link MessageChannel#sendFiles(FileUpload...)} throws
*
* @return {@link MessageCreateAction}
*/
@Nonnull
@CheckReturnValue
default MessageCreateAction replyFiles(@Nonnull FileUpload... files)
{
return getChannel().sendFiles(files).setMessageReference(this);
}
/**
* Shortcut for {@code getChannel().sendFiles(files).setMessageReference(this)}-
*
* @param files
* The {@link FileUpload FileUploads} to send
*
* @throws InsufficientPermissionException
* If {@link MessageChannel#sendFiles(Collection)} throws
* @throws IllegalArgumentException
* If {@link MessageChannel#sendFiles(Collection)} throws
*
* @return {@link MessageCreateAction}
*/
@Nonnull
@CheckReturnValue
default MessageCreateAction replyFiles(@Nonnull Collection extends FileUpload> files)
{
return getChannel().sendFiles(files).setMessageReference(this);
}
/**
* Deletes this Message from Discord.
* If this Message was not sent by the currently logged in account, then this will fail unless the Message is from
* a {@link GuildChannel} and the current account has
* {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE} in the channel.
*
* To delete many messages at once in a {@link net.dv8tion.jda.api.entities.MessageChannel MessageChannel}
* you should use {@link net.dv8tion.jda.api.entities.MessageChannel#purgeMessages(List) MessageChannel.purgeMessages(List)} instead.
*
*
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The delete was attempted after the account lost access to the {@link GuildChannel}
* due to {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} being revoked, or the
* account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
* The delete was attempted after the account lost {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE} in
* the {@link GuildChannel} when deleting another Member's message
* or lost {@link Permission#MESSAGE_MANAGE}.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* If the message has already been deleted. This might also be triggered for ephemeral messages.
*
*
* @throws MissingAccessException
* If the currently logged in account does not have {@link Member#hasAccess(GuildChannel) access} in this channel.
* @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
* If this Message was not sent by the currently logged in account, the Message was sent in a
* {@link GuildChannel GuildChannel}, and the currently logged in account
* does not have {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE} in
* the channel.
* @throws java.lang.IllegalStateException
*
* If this Message was not sent by the currently logged in account and it was not sent in a
* {@link GuildChannel GuildChannel}.
* If this Message is ephemeral
* If this message type cannot be deleted. (See {@link MessageType#canDelete()})
*
*
* @return {@link net.dv8tion.jda.api.requests.restaction.AuditableRestAction AuditableRestAction}
*
* @see net.dv8tion.jda.api.entities.TextChannel#deleteMessages(java.util.Collection) TextChannel.deleteMessages(Collection)
* @see net.dv8tion.jda.api.entities.MessageChannel#purgeMessages(java.util.List) MessageChannel.purgeMessages(List)
*/
@Nonnull
@CheckReturnValue
AuditableRestAction delete();
/**
* Returns the {@link net.dv8tion.jda.api.JDA JDA} instance related to this Message.
*
* @return the corresponding JDA instance
*/
@Nonnull
JDA getJDA();
/**
* Whether or not this Message has been pinned in its parent channel.
*
* @return True - if this message has been pinned.
*/
boolean isPinned();
/**
* Used to add the Message to the {@link #getChannel() MessageChannel's} pinned message list.
* This is a shortcut method to {@link MessageChannel#pinMessageById(String)}.
*
* The success or failure of this action will not affect the return of {@link #isPinned()}.
*
*
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The pin request was attempted after the account lost access to the {@link GuildChannel}
* due to {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} being revoked, or the
* account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
* The pin request was attempted after the account lost {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE} in
* the {@link GuildChannel}.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* If the message has already been deleted. This might also be triggered for ephemeral messages.
*
*
* @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
* If this Message is from a {@link GuildChannel} and:
*
* Missing {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}.
* The account needs access the the channel to pin a message in it.
* Missing {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE}.
* Required to actually pin the Message.
*
* @throws IllegalStateException
* If this Message is ephemeral
*
* @return {@link RestAction RestAction} - Type: {@link java.lang.Void}
*/
@Nonnull
@CheckReturnValue
RestAction pin();
/**
* Used to remove the Message from the {@link #getChannel() MessageChannel's} pinned message list.
* This is a shortcut method to {@link MessageChannel#unpinMessageById(String)}.
*
* The success or failure of this action will not affect the return of {@link #isPinned()}.
*
*
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The unpin request was attempted after the account lost access to the {@link GuildChannel}
* due to {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} being revoked, or the
* account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
* The unpin request was attempted after the account lost {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE} in
* the {@link GuildChannel}.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* If the message has already been deleted. This might also be triggered for ephemeral messages.
*
*
* @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
* If this Message is from a {@link GuildChannel} and:
*
* Missing {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}.
* The account needs access the the channel to pin a message in it.
* Missing {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE}.
* Required to actually pin the Message.
*
* @throws IllegalStateException
* If this Message is ephemeral
*
* @return {@link RestAction RestAction} - Type: {@link java.lang.Void}
*/
@Nonnull
@CheckReturnValue
RestAction unpin();
/**
* Adds a reaction to this Message using an {@link Emoji}.
*
* This message instance will not be updated by this operation.
*
*
Reactions are the small emoji below a message that have a counter beside them
* showing how many users have reacted with the same emoji.
*
*
Neither success nor failure of this request will affect this Message's {@link #getReactions()} return as Message is immutable.
*
*
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The reaction request was attempted after the account lost access to the {@link GuildChannel}
* due to {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} being revoked
* Also can happen if the account lost the {@link Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#REACTION_BLOCKED REACTION_BLOCKED}
* The user has blocked the currently logged in account and the reaction failed
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#TOO_MANY_REACTIONS TOO_MANY_REACTIONS}
* The message already has too many reactions to proceed
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
* The reaction request was attempted after the account lost {@link Permission#MESSAGE_ADD_REACTION Permission.MESSAGE_ADD_REACTION}
* or {@link Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY}
* in the {@link GuildChannel} when adding the reaction.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_EMOJI UNKNOWN_EMOJI}
* The provided emoji was deleted, doesn't exist, or is not available to the currently logged-in account in this channel.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* If the message has already been deleted. This might also be triggered for ephemeral messages.
*
*
* @param emoji
* The {@link Emoji} to add as a reaction to this Message.
*
* @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
* If the MessageChannel this message was sent in was a {@link GuildChannel}
* and the logged in account does not have
*
* {@link Permission#MESSAGE_ADD_REACTION Permission.MESSAGE_ADD_REACTION}
* {@link Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY}
*
* @throws java.lang.IllegalArgumentException
*
* If the provided {@link Emoji} is null.
* If the provided {@link Emoji} is a custom emoji and cannot be used in the current channel.
* See {@link RichCustomEmoji#canInteract(User, MessageChannel)} or {@link RichCustomEmoji#canInteract(Member)} for more information.
*
* @throws IllegalStateException
* If this message is ephemeral
*
* @return {@link RestAction}
*/
@Nonnull
@CheckReturnValue
RestAction addReaction(@Nonnull Emoji emoji);
/**
* Removes all reactions from this Message.
* This is useful for moderator commands that wish to remove all reactions at once from a specific message.
*
* Please note that you can't clear reactions if this message was sent in a {@link net.dv8tion.jda.api.entities.PrivateChannel PrivateChannel}!
*
*
Neither success nor failure of this request will affect this Message's {@link #getReactions()} return as Message is immutable.
*
*
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The clear-reactions request was attempted after the account lost access to the {@link GuildChannel}
* due to {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} being revoked, or the
* account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
* The clear-reactions request was attempted after the account lost {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE}
* in the {@link GuildChannel} when adding the reaction.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* If the message has already been deleted. This might also be triggered for ephemeral messages.
*
*
* @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
* If the MessageChannel this message was sent in was a {@link GuildChannel}
* and the currently logged in account does not have {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE}
* in the channel.
* @throws java.lang.IllegalStateException
*
* If this message was not sent in a {@link net.dv8tion.jda.api.entities.Guild Guild}.
* If this message is ephemeral
*
*
*
* @return {@link RestAction}
*/
@Nonnull
@CheckReturnValue
RestAction clearReactions();
/**
* Removes all reactions for the specified {@link Emoji}.
*
* Please note that you can't clear reactions if this message was sent in a {@link net.dv8tion.jda.api.entities.PrivateChannel PrivateChannel}!
*
*
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The currently logged in account lost access to the channel by either being removed from the guild
* or losing the {@link Permission#VIEW_CHANNEL VIEW_CHANNEL} permission
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_EMOJI UNKNOWN_EMOJI}
* The provided emoji was deleted, doesn't exist, or is not available to the currently logged-in account in this channel.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* If the message has already been deleted. This might also be triggered for ephemeral messages.
*
*
* @param emoji
* The {@link Emoji} to remove reactions for
*
* @throws InsufficientPermissionException
* If the currently logged in account does not have {@link Permission#MESSAGE_MANAGE} in the channel
* @throws IllegalArgumentException
* If provided with null
* @throws java.lang.IllegalStateException
*
* If this message was not sent in a {@link net.dv8tion.jda.api.entities.Guild Guild}.
* If this message is ephemeral
*
*
* @return {@link RestAction}
*/
@Nonnull
@CheckReturnValue
RestAction clearReactions(@Nonnull Emoji emoji);
/**
* Removes own reaction from this Message using an {@link Emoji},
* you can use {@link #removeReaction(Emoji, User)} to remove reactions from other users,
* or {@link #clearReactions(Emoji)} to remove all reactions for the specified emoji.
*
* This message instance will not be updated by this operation.
*
*
Reactions are the small emojis below a message that have a counter beside them
* showing how many users have reacted with the same emoji.
*
*
Neither success nor failure of this request will affect this Message's {@link #getReactions()} return as Message is immutable.
*
*
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The reaction request was attempted after the account lost access to the {@link GuildChannel}
* due to {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} being revoked
* Also can happen if the account lost the {@link Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_EMOJI UNKNOWN_EMOJI}
* The provided emoji was deleted, doesn't exist, or is not available to the currently logged-in account in this channel.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* If the message has already been deleted. This might also be triggered for ephemeral messages.
*
*
* @param emoji
* The {@link Emoji} reaction to remove as a reaction from this Message.
*
* @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
* If the MessageChannel this message was sent in was a {@link GuildChannel}
* and the logged in account does not have {@link Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY}
* @throws java.lang.IllegalArgumentException
*
* If the provided {@link Emoji} is null.
* If the provided {@link Emoji} is a custom emoji and cannot be used in the current channel.
* See {@link RichCustomEmoji#canInteract(User, MessageChannel)} or {@link RichCustomEmoji#canInteract(Member)} for more information.
*
* @throws IllegalStateException
* If this is an ephemeral message
*
* @return {@link RestAction}
*/
@Nonnull
@CheckReturnValue
RestAction removeReaction(@Nonnull Emoji emoji);
/**
* Removes a {@link User User's} reaction from this Message using an {@link Emoji}.
*
* Please note that you can't remove reactions of other users if this message was sent in a {@link net.dv8tion.jda.api.entities.PrivateChannel PrivateChannel}!
*
*
This message instance will not be updated by this operation.
*
*
Reactions are the small emojis below a message that have a counter beside them
* showing how many users have reacted with the same emoji.
*
*
Neither success nor failure of this request will affect this Message's {@link #getReactions()} return as Message is immutable.
*
*
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The reaction request was attempted after the account lost access to the {@link GuildChannel}
* due to {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} being revoked
* Also can happen if the account lost the {@link Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
* The reaction request was attempted after the account lost {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE}
* in the {@link GuildChannel} when removing the reaction.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_EMOJI UNKNOWN_EMOJI}
* The provided emoji was deleted, doesn't exist, or is not available to the currently logged-in account in this channel.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* If the message has already been deleted. This might also be triggered for ephemeral messages.
*
*
* @param emoji
* The {@link Emoji} reaction to remove as a reaction from this Message.
* @param user
* The {@link User} to remove the reaction for.
*
* @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
* If the MessageChannel this message was sent in was a {@link GuildChannel}
* and the logged in account does not have {@link Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY}.
* @throws java.lang.IllegalArgumentException
*
* If the provided {@code emoji} is null.
* If the provided {@code emoji} cannot be used in the current channel.
* See {@link RichCustomEmoji#canInteract(User, MessageChannel)} or {@link RichCustomEmoji#canInteract(Member)} for more information.
* If the provided user is null
*
* @throws java.lang.IllegalStateException
*
* If this message was not sent in a
* {@link net.dv8tion.jda.api.entities.Guild Guild}
* and the given user is not the {@link SelfUser}.
* If this message is ephemeral
*
*
*
* @return {@link RestAction}
*/
@Nonnull
@CheckReturnValue
RestAction removeReaction(@Nonnull Emoji emoji, @Nonnull User user);
/**
* This obtains the {@link User users} who reacted using the given {@link Emoji}.
*
* Messages maintain a list of reactions, alongside a list of users who added them.
*
*
Using this data, we can obtain a {@link ReactionPaginationAction}
* of the users who've reacted to this message.
*
*
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The retrieve request was attempted after the account lost access to the {@link GuildChannel}
* due to {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} being revoked
* Also can happen if the account lost the {@link Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_EMOJI UNKNOWN_EMOJI}
* The provided emoji was deleted, doesn't exist, or is not available to the currently logged-in account in this channel.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* If the message has already been deleted. This might also be triggered for ephemeral messages.
*
*
* @param emoji
* The {@link Emoji} to retrieve users for.
*
* @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
* If the MessageChannel this message was sent in was a {@link GuildChannel} and the
* logged in account does not have {@link Permission#MESSAGE_HISTORY Permission.MESSAGE_HISTORY} in the channel.
* @throws java.lang.IllegalArgumentException
* If the provided {@link Emoji} is null.
* @throws IllegalStateException
* If this Message is ephemeral
*
* @return The {@link ReactionPaginationAction} of the users who reacted with the provided emoji
*/
@Nonnull
@CheckReturnValue
ReactionPaginationAction retrieveReactionUsers(@Nonnull Emoji emoji);
/**
* This obtains the {@link MessageReaction} for the given {@link Emoji} on this message.
* The reaction instance also store which users reacted with the specified emoji.
*
* Messages store reactions by keeping a list of reaction names.
*
* @param emoji
* The unicode or custom emoji of the reaction emoji
*
* @throws java.lang.IllegalArgumentException
* If the provided emoji is null
*
* @return The {@link MessageReaction} or null if not present.
*/
@Nullable
@CheckReturnValue
MessageReaction getReaction(@Nonnull Emoji emoji);
/**
* Enables/Disables suppression of Embeds on this Message.
* Suppressing Embeds is equivalent to pressing the {@code X} in the top-right corner of an Embed inside the Discord client.
*
*
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The clear-reactions request was attempted after the account lost access to the {@link GuildChannel}
* due to {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} being revoked, or the
* account lost access to the {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
* The suppress-embeds request was attempted after the account lost {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE}
* in the {@link GuildChannel} when adding the reaction.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* If the message has already been deleted. This might also be triggered for ephemeral messages.
*
*
* @param suppressed
* Whether the embed should be suppressed
*
* @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
* If the MessageChannel this message was sent in was a {@link GuildChannel}
* and the currently logged in account does not have
* {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE} in the channel.
* @throws net.dv8tion.jda.api.exceptions.PermissionException
* If the MessageChannel this message was sent in was a {@link net.dv8tion.jda.api.entities.PrivateChannel PrivateChannel}
* and the message was not sent by the currently logged in account.
* @throws IllegalStateException
* If this Message is ephemeral
* @return {@link net.dv8tion.jda.api.requests.restaction.AuditableRestAction AuditableRestAction} - Type: {@link java.lang.Void}
*
* @see #isSuppressedEmbeds()
*/
@Nonnull
@CheckReturnValue
AuditableRestAction suppressEmbeds(boolean suppressed);
/**
* Attempts to crosspost this message.
*
* The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#ALREADY_CROSSPOSTED ALREADY_CROSSPOSTED}
* The target message has already been crossposted.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_ACCESS MISSING_ACCESS}
* The request was attempted after the account lost access to the
* {@link net.dv8tion.jda.api.entities.Guild Guild}
* typically due to being kicked or removed, or after {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL}
* was revoked in the {@link GuildChannel}
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
* The request was attempted after the account lost
* {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE} in the GuildMessageChannel.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* If the message has already been deleted. This might also be triggered for ephemeral messages.
*
* {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* The request was attempted after the channel was deleted.
*
*
* @throws IllegalStateException
*
* If the channel is not a {@link NewsChannel}.
* If the message is ephemeral.
*
* @throws MissingAccessException
* If the currently logged in account does not have {@link Member#hasAccess(GuildChannel) access} in this channel.
* @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
* If the currently logged in account does not have
* {@link Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} in this channel
* or if this message is from another user and we don't have {@link Permission#MESSAGE_MANAGE Permission.MESSAGE_MANAGE}.
*
* @return {@link RestAction} - Type: {@link Message}
*
* @since 4.2.1
*/
@Nonnull
@CheckReturnValue
RestAction crosspost();
/**
* Whether embeds are suppressed for this message.
* When Embeds are suppressed, they are not displayed on clients nor provided via API until un-suppressed.
* This is a shortcut method for checking if {@link #getFlags() getFlags()} contains
* {@link net.dv8tion.jda.api.entities.Message.MessageFlag#EMBEDS_SUPPRESSED MessageFlag#EMBEDS_SUPPRESSED}
*
* @return Whether or not Embeds are suppressed for this Message.
*
* @see #suppressEmbeds(boolean)
*/
boolean isSuppressedEmbeds();
/**
* Returns an EnumSet of all {@link Message.MessageFlag MessageFlags} present for this Message.
*
* @return Never-Null EnumSet of present {@link Message.MessageFlag MessageFlags}
*
* @see Message.MessageFlag
*/
@Nonnull
EnumSet getFlags();
/**
* Returns the raw message flags of this message
*
* @return The raw message flags
*
* @see #getFlags()
*/
long getFlagsRaw();
/**
* Whether this message is ephemeral.
* The message being ephemeral means it is only visible to the bot and the interacting user
* This is a shortcut method for checking if {@link #getFlags()} contains {@link MessageFlag#EPHEMERAL}
*
* @return Whether the message is ephemeral
*/
boolean isEphemeral();
/**
* Returns a possibly {@code null} {@link net.dv8tion.jda.api.entities.ThreadChannel ThreadChannel} that was started from this message.
* This can be {@code null} due to no ThreadChannel being started from it or the ThreadChannel later being deleted.
*
* @return The {@link net.dv8tion.jda.api.entities.ThreadChannel ThreadChannel} that was started from this message.
*/
@Nullable
ThreadChannel getStartedThread();
/**
* This specifies the {@link net.dv8tion.jda.api.entities.MessageType MessageType} of this Message.
*
* Messages can represent more than just simple text sent by Users, they can also be special messages that
* inform about events that occur. Messages can either be {@link net.dv8tion.jda.api.entities.MessageType#DEFAULT default messages}
* or special messages like {@link net.dv8tion.jda.api.entities.MessageType#GUILD_MEMBER_JOIN welcome messages}.
*
* @return The {@link net.dv8tion.jda.api.entities.MessageType MessageType} of this message.
*/
@Nonnull
MessageType getType();
/**
* This is sent on the message object when the message is a response to an {@link net.dv8tion.jda.api.interactions.Interaction Interaction} without an existing message.
*
*
This means responses to Message Components do not include this property, instead including a message reference object as components always exist on preexisting messages.
*
* @return The {@link net.dv8tion.jda.api.entities.Message.Interaction Interaction} of this message.
*/
@Nullable
Interaction getInteraction();
//TODO-v5: Docs
@CheckReturnValue
ThreadChannelAction createThreadChannel(String name);
/**
* Mention constants, useful for use with {@link java.util.regex.Pattern Patterns}
*/
enum MentionType
{
/**
* Represents a mention for a {@link User User}/{@link net.dv8tion.jda.api.entities.Member Member}
* The first and only group matches the id of the mention.
*/
USER("<@!?(\\d+)>", "users"),
/**
* Represents a mention for a {@link net.dv8tion.jda.api.entities.Role Role}
* The first and only group matches the id of the mention.
*/
ROLE("<@&(\\d+)>", "roles"),
/**
* Represents a mention for a {@link GuildChannel}
* The first and only group matches the id of the mention.
*/
CHANNEL("<#(\\d+)>", null),
/**
* Represents a mention for a {@link CustomEmoji}
* The first group matches the name of the emoji and the second the id of the mention.
*/
EMOJI("", null),
/**
* Represents a mention for all active users, literal {@code @here}
*/
HERE("@here", "everyone"),
/**
* Represents a mention for all users in a server, literal {@code @everyone}.
*/
EVERYONE("@everyone", "everyone");
private final Pattern pattern;
private final String parseKey;
MentionType(String regex, String parseKey)
{
this.pattern = Pattern.compile(regex);
this.parseKey = parseKey;
}
@Nonnull
public Pattern getPattern()
{
return pattern;
}
/**
* The Key returned by this method is used to determine the group or parsable mention group they are part of.
* It is used internally in methods like {@link MessageRequest#setAllowedMentions(Collection)}.
*
* Returns {@code null}, when they don't belong to any mention group.
*
* @return Nullable group key for mention parsing
*/
@Nullable
public String getParseKey()
{
return parseKey;
}
}
/**
* Enum representing the flags on a Message.
*
* Note: The Values defined in this Enum are not considered final and only represent the current State of known Flags.
*/
enum MessageFlag
{
/**
* The Message has been published to subscribed Channels (via Channel Following)
*/
CROSSPOSTED(0),
/**
* The Message originated from a Message in another Channel (via Channel Following)
*/
IS_CROSSPOST(1),
/**
* Embeds are suppressed on the Message.
* @see net.dv8tion.jda.api.entities.Message#isSuppressedEmbeds() Message#isSuppressedEmbeds()
*/
EMBEDS_SUPPRESSED(2),
/**
* Indicates, that the source message of this crosspost was deleted.
* This should only be possible in combination with {@link #IS_CROSSPOST}
*/
SOURCE_MESSAGE_DELETED(3),
/**
* Indicates, that this Message came from the urgent message system
*/
URGENT(4),
/**
* Indicates, that this Message is ephemeral, the Message is only visible to the bot and the interacting user
* @see Message#isEphemeral
*/
EPHEMERAL(6),
/**
* Indicates, that this Message is an interaction response and the bot is "thinking"
*/
LOADING(7);
private final int value;
MessageFlag(int offset)
{
this.value = 1 << offset;
}
/**
* Returns the value of the MessageFlag as represented in the bitfield. It is always a power of 2 (single bit)
*
* @return Non-Zero bit value of the field
*/
public int getValue()
{
return value;
}
/**
* Given a bitfield, this function extracts all Enum values according to their bit values and returns
* an EnumSet containing all matching MessageFlags
* @param bitfield
* Non-Negative integer representing a bitfield of MessageFlags
* @return Never-Null EnumSet of MessageFlags being found in the bitfield
*/
@Nonnull
public static EnumSet fromBitField(int bitfield)
{
Set set = Arrays.stream(MessageFlag.values())
.filter(e -> (e.value & bitfield) > 0)
.collect(Collectors.toSet());
return set.isEmpty() ? EnumSet.noneOf(MessageFlag.class) : EnumSet.copyOf(set);
}
/**
* Converts a Collection of MessageFlags back to the integer representing the bitfield.
* This is the reverse operation of {@link #fromBitField(int)}.
* @param coll
* A Non-Null Collection of MessageFlags
* @throws IllegalArgumentException
* If the provided Collection is {@code null}
* @return Integer value of the bitfield representing the given MessageFlags
*/
public static int toBitField(@Nonnull Collection coll)
{
Checks.notNull(coll, "Collection");
int flags = 0;
for (MessageFlag messageFlag : coll)
{
flags |= messageFlag.value;
}
return flags;
}
}
/**
* Represents a {@link net.dv8tion.jda.api.entities.Message Message} file attachment.
*/
class Attachment implements ISnowflake, AttachedFile
{
private static final Set IMAGE_EXTENSIONS = new HashSet<>(Arrays.asList("jpg",
"jpeg", "png", "gif", "webp", "tiff", "svg", "apng"));
private static final Set VIDEO_EXTENSIONS = new HashSet<>(Arrays.asList("webm",
"flv", "vob", "avi", "mov", "wmv", "amv", "mp4", "mpg", "mpeg", "gifv"));
private final long id;
private final String url;
private final String proxyUrl;
private final String fileName;
private final String contentType;
private final String description;
private final int size;
private final int height;
private final int width;
private final boolean ephemeral;
private final JDAImpl jda;
public Attachment(long id, String url, String proxyUrl, String fileName, String contentType, String description, int size, int height, int width, boolean ephemeral, JDAImpl jda)
{
this.id = id;
this.url = url;
this.proxyUrl = proxyUrl;
this.fileName = fileName;
this.contentType = contentType;
this.description = description;
this.size = size;
this.height = height;
this.width = width;
this.ephemeral = ephemeral;
this.jda = jda;
}
/**
* The corresponding JDA instance for this Attachment
*
* @return The corresponding JDA instance for this Attachment
*/
@Nonnull
public JDA getJDA()
{
return jda;
}
@Override
public long getIdLong()
{
return id;
}
/**
* The url of the Attachment, most likely on the Discord servers.
*
* @return Non-null String containing the Attachment URL.
*/
@Nonnull
public String getUrl()
{
return url;
}
/**
* Url to the resource proxied by the Discord CDN.
*
* @return Non-null String containing the proxied Attachment url.
*/
@Nonnull
public String getProxyUrl()
{
return proxyUrl;
}
/**
* Returns an {@link AttachmentProxy} for this attachment.
*
* @return Non-null {@link AttachmentProxy} of this attachment
*
* @see #getProxyUrl()
*/
@Nonnull
public AttachmentProxy getProxy()
{
return new AttachmentProxy(width > 0 && height > 0 ? proxyUrl : url);
}
/**
* The file name of the Attachment when it was first uploaded.
*
* @return Non-null String containing the Attachment file name.
*/
@Nonnull
public String getFileName()
{
return fileName;
}
/**
* The file extension of the Attachment when it was first uploaded.
* Null is returned if no characters follow the last occurrence of the '{@code .}' character
* (or if the character is not present in {@link #getFileName()}).
*
* @return Non-null String containing the Attachment file extension, or null if it can't be determined.
*/
@Nullable
public String getFileExtension()
{
int index = fileName.lastIndexOf('.') + 1;
return index == 0 || index == fileName.length() ? null : fileName.substring(index);
}
/**
* The Content-Type of this file.
* This is the Media type of the file that would be used in an HTTP request or similar.
*
* @return The content-type, or null if this isn't provided
*/
@Nullable
public String getContentType()
{
return contentType;
}
/**
* The description (alt text) of this attachment.
* This description is shown when hovering over the attachment in the client.
*
* @return The description, or null if this isn't provided
*/
@Nullable
public String getDescription()
{
return description;
}
/**
* Enqueues a request to retrieve the contents of this Attachment.
* The receiver is expected to close the retrieved {@link java.io.InputStream}.
*
* Example
*
{@code
* public void printContents(Message.Attachment attachment)
* {
* attachment.retrieveInputStream().thenAccept(in -> {
* StringBuilder builder = new StringBuilder();
* byte[] buf = byte[1024];
* int count = 0;
* while ((count = in.read(buf)) > 0)
* {
* builder.append(new String(buf, 0, count));
* }
* in.close();
* System.out.println(builder);
* }).exceptionally(t -> { // handle failure
* t.printStackTrace();
* return null;
* });
* }
* }
*
* @return {@link java.util.concurrent.CompletableFuture} - Type: {@link java.io.InputStream}
*
* @deprecated Replaced by {@link #getProxy}, see {@link AttachmentProxy#download()}
*/
@Nonnull
@Deprecated
@ForRemoval
@ReplaceWith("getProxy().download()")
public CompletableFuture retrieveInputStream() // it is expected that the response is closed by the callback!
{
CompletableFuture future = new CompletableFuture<>();
Request req = getRequest();
OkHttpClient httpClient = getJDA().getHttpClient();
httpClient.newCall(req).enqueue(FunctionalCallback
.onFailure((call, e) -> future.completeExceptionally(new UncheckedIOException(e)))
.onSuccess((call, response) -> {
if (response.isSuccessful())
{
InputStream body = IOUtil.getBody(response);
if (!future.complete(body))
IOUtil.silentClose(response);
}
else
{
future.completeExceptionally(new HttpException(response.code() + ": " + response.message()));
IOUtil.silentClose(response);
}
}).build());
return future;
}
/**
* Downloads the attachment into the current working directory using the file name provided by {@link #getFileName()}.
* This will download the file using the {@link net.dv8tion.jda.api.JDA#getCallbackPool() callback pool}.
* Alternatively you can use {@link #retrieveInputStream()} and use a continuation with a different executor.
*
* Example
*
{@code
* public void saveLocally(Message.Attachment attachment)
* {
* attachment.downloadToFile()
* .thenAccept(file -> System.out.println("Saved attachment to " + file.getName()))
* .exceptionally(t ->
* { // handle failure
* t.printStackTrace();
* return null;
* });
* }
* }
*
* @return {@link java.util.concurrent.CompletableFuture} - Type: {@link java.io.File}
*
* @deprecated Replaced by {@link #getProxy}, see {@link AttachmentProxy#downloadToFile(File)}
*/
@Nonnull
@Deprecated
@ForRemoval
@ReplaceWith("getProxy().downloadToFile()")
public CompletableFuture downloadToFile() // using relative path
{
return downloadToFile(getFileName());
}
/**
* Downloads the attachment to a file at the specified path (relative or absolute).
* This will download the file using the {@link net.dv8tion.jda.api.JDA#getCallbackPool() callback pool}.
* Alternatively you can use {@link #retrieveInputStream()} and use a continuation with a different executor.
*
* Example
*
{@code
* public void saveLocally(Message.Attachment attachment)
* {
* attachment.downloadToFile("/tmp/" + attachment.getFileName())
* .thenAccept(file -> System.out.println("Saved attachment to " + file.getName()))
* .exceptionally(t ->
* { // handle failure
* t.printStackTrace();
* return null;
* });
* }
* }
*
* @param path
* The path to save the file to
*
* @throws java.lang.IllegalArgumentException
* If the provided path is null
*
* @return {@link java.util.concurrent.CompletableFuture} - Type: {@link java.io.File}
*
* @deprecated Replaced by {@link #getProxy}, see {@link AttachmentProxy#downloadToFile(File)}
*/
@Nonnull
@Deprecated
@ForRemoval
@ReplaceWith("getProxy().downloadToFile(new File(String))")
public CompletableFuture downloadToFile(String path)
{
Checks.notNull(path, "Path");
return downloadToFile(new File(path));
}
/**
* Downloads the attachment to a file at the specified path (relative or absolute).
* This will download the file using the {@link net.dv8tion.jda.api.JDA#getCallbackPool() callback pool}.
* Alternatively you can use {@link #retrieveInputStream()} and use a continuation with a different executor.
*
* Example
*
{@code
* public void saveLocally(Message.Attachment attachment)
* {
* attachment.downloadToFile(new File("/tmp/" + attachment.getFileName()))
* .thenAccept(file -> System.out.println("Saved attachment to " + file.getName()))
* .exceptionally(t ->
* { // handle failure
* t.printStackTrace();
* return null;
* });
* }
* }
*
* @param file
* The file to write to
*
* @throws java.lang.IllegalArgumentException
* If the provided file is null or cannot be written to
*
* @return {@link java.util.concurrent.CompletableFuture} - Type: {@link java.io.File}
*
* @deprecated Replaced by {@link #getProxy}, see {@link AttachmentProxy#downloadToFile(File)}
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
@Nonnull
@ForRemoval
@ReplaceWith("getProxy().downloadToFile(File)")
public CompletableFuture downloadToFile(File file)
{
Checks.notNull(file, "File");
try
{
if (!file.exists())
file.createNewFile();
else
Checks.check(file.canWrite(), "Cannot write to file %s", file.getName());
}
catch (IOException e)
{
throw new IllegalArgumentException("Cannot create file", e);
}
return retrieveInputStream().thenApplyAsync((stream) -> {
try (FileOutputStream out = new FileOutputStream(file))
{
byte[] buf = new byte[1024];
int count;
while ((count = stream.read(buf)) > 0)
{
out.write(buf, 0, count);
}
return file;
}
catch (IOException e)
{
throw new UncheckedIOException(e);
}
finally
{
IOUtil.silentClose(stream);
}
}, getJDA().getCallbackPool());
}
/**
* Retrieves the image of this attachment and provides an {@link net.dv8tion.jda.api.entities.Icon} equivalent.
* Useful with {@link net.dv8tion.jda.api.managers.AccountManager#setAvatar(Icon)}.
* This will download the file using the {@link net.dv8tion.jda.api.JDA#getCallbackPool() callback pool}.
* Alternatively you can use {@link #retrieveInputStream()} and use a continuation with a different executor.
*
* Example
*
{@code
* public void changeAvatar(Message.Attachment attachment)
* {
* attachment.retrieveAsIcon().thenCompose(icon -> {
* SelfUser self = attachment.getJDA().getSelfUser();
* AccountManager manager = self.getManager();
* return manager.setAvatar(icon).submit();
* }).exceptionally(t -> {
* t.printStackTrace();
* return null;
* });
* }
* }
*
* @throws java.lang.IllegalStateException
* If this is not an image ({@link #isImage()})
*
* @return {@link java.util.concurrent.CompletableFuture} - Type: {@link net.dv8tion.jda.api.entities.Icon}
*
* @deprecated Replaced by {@link #getProxy}, see {@link AttachmentProxy#downloadAsIcon()}
*/
@Nonnull
@ReplaceWith("getProxy().downloadAsIcon()")
public CompletableFuture retrieveAsIcon()
{
if (!isImage())
throw new IllegalStateException("Cannot create an Icon out of this attachment. This is not an image.");
return retrieveInputStream().thenApplyAsync((stream) ->
{
try
{
return Icon.from(stream);
}
catch (IOException e)
{
throw new UncheckedIOException(e);
}
finally
{
IOUtil.silentClose(stream);
}
}, getJDA().getCallbackPool());
}
protected Request getRequest()
{
return new Request.Builder()
.url(getUrl())
.addHeader("user-agent", Requester.USER_AGENT)
.addHeader("accept-encoding", "gzip, deflate")
.build();
}
/**
* The size of the attachment in bytes.
* Example: if {@code getSize()} returns 1024, then the attachment is 1024 bytes, or 1KiB, in size.
*
* @return Positive int containing the size of the Attachment.
*/
public int getSize()
{
return size;
}
/**
* The height of the Attachment if this Attachment is an image/video.
* If this Attachment is neither an image, nor a video, this returns -1.
*
* @return int containing image/video Attachment height, or -1 if attachment is neither image nor video.
*/
public int getHeight()
{
return height;
}
/**
* The width of the Attachment if this Attachment is an image/video.
* If this Attachment is neither an image, nor a video, this returns -1.
*
* @return int containing image/video Attachment width, or -1 if attachment is neither image nor video.
*/
public int getWidth()
{
return width;
}
/**
* Whether or not this attachment is from an ephemeral Message.
* If this Attachment is ephemeral, it will automatically be removed after 2 weeks. The attachment is guaranteed to be available as long as the message itself exists.
*
* @return True if this attachment is from an ephemeral message
*/
public boolean isEphemeral()
{
return ephemeral;
}
/**
* Whether or not this attachment is an Image,
* based on {@link #getWidth()}, {@link #getHeight()}, and {@link #getFileExtension()}.
*
* @return True if this attachment is an image
*/
public boolean isImage()
{
if (width < 0) return false; //if width is -1, so is height
String extension = getFileExtension();
return extension != null && IMAGE_EXTENSIONS.contains(extension.toLowerCase());
}
/**
* Whether or not this attachment is a video,
* based on {@link #getWidth()}, {@link #getHeight()}, and {@link #getFileExtension()}.
*
* @return True if this attachment is a video
*/
public boolean isVideo()
{
if (width < 0) return false; //if width is -1, so is height
String extension = getFileExtension();
return extension != null && VIDEO_EXTENSIONS.contains(extension.toLowerCase());
}
/**
* Whether or not this attachment is marked as spoiler,
* based on {@link #getFileName()}.
*
* @return True if this attachment is marked as spoiler
*
* @since 4.2.1
*/
public boolean isSpoiler()
{
return getFileName().startsWith("SPOILER_");
}
@Override
public void close() {}
@Override
public void forceClose() {}
@Override
public void addPart(@Nonnull MultipartBody.Builder builder, int index) {}
@Nonnull
@Override
public DataObject toAttachmentData(int index)
{
return DataObject.empty().put("id", id);
}
}
/**
* Represents an {@link net.dv8tion.jda.api.interactions.Interaction Interaction} provided with a {@link net.dv8tion.jda.api.entities.Message Message}.
*/
class Interaction implements ISnowflake
{
private final long id;
private final int type;
private final String name;
private final User user;
private final Member member;
public Interaction(long id, int type, String name, User user, Member member)
{
this.id = id;
this.type = type;
this.name = name;
this.user = user;
this.member = member;
}
@Override
public long getIdLong()
{
return id;
}
/**
* The raw interaction type.
* It is recommended to use {@link #getType()} instead.
*
* @return The raw interaction type
*/
public int getTypeRaw()
{
return type;
}
/**
* The {@link net.dv8tion.jda.api.interactions.InteractionType} for this interaction.
*
* @return The {@link net.dv8tion.jda.api.interactions.InteractionType} or {@link net.dv8tion.jda.api.interactions.InteractionType#UNKNOWN}
*/
@Nonnull
public InteractionType getType()
{
return InteractionType.fromKey(getTypeRaw());
}
/**
* The command name.
*
* @return The command name
*/
@Nonnull
public String getName()
{
return name;
}
/**
* The {@link User} who caused this interaction.
*
* @return The {@link User}
*/
@Nonnull
public User getUser()
{
return user;
}
/**
* The {@link Member} who caused this interaction.
* This is null if the interaction is not from a guild.
*
* @return The {@link Member}
*/
@Nullable
public Member getMember()
{
return member;
}
}
}