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

net.dv8tion.jda.internal.entities.ReceivedMessage Maven / Gradle / Ivy

Go to download

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

There is a newer version: 5.1.0
Show newest version
/*
 * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.dv8tion.jda.internal.entities;

import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.channel.Channel;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.attribute.ICategorizableChannel;
import net.dv8tion.jda.api.entities.channel.concrete.Category;
import net.dv8tion.jda.api.entities.channel.concrete.NewsChannel;
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.entities.channel.unions.GuildMessageChannelUnion;
import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion;
import net.dv8tion.jda.api.entities.emoji.CustomEmoji;
import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji;
import net.dv8tion.jda.api.entities.messages.MessagePoll;
import net.dv8tion.jda.api.entities.sticker.StickerItem;
import net.dv8tion.jda.api.exceptions.InsufficientPermissionException;
import net.dv8tion.jda.api.exceptions.PermissionException;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.requests.ErrorResponse;
import net.dv8tion.jda.api.requests.GatewayIntent;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.requests.Route;
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
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.MarkdownSanitizer;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.api.utils.messages.MessageEditData;
import net.dv8tion.jda.internal.JDAImpl;
import net.dv8tion.jda.internal.interactions.InteractionHookImpl;
import net.dv8tion.jda.internal.requests.CompletedRestAction;
import net.dv8tion.jda.internal.requests.ErrorMapper;
import net.dv8tion.jda.internal.requests.RestActionImpl;
import net.dv8tion.jda.internal.requests.restaction.AuditableRestActionImpl;
import net.dv8tion.jda.internal.requests.restaction.MessageEditActionImpl;
import net.dv8tion.jda.internal.requests.restaction.pagination.ReactionPaginationActionImpl;
import net.dv8tion.jda.internal.utils.Checks;
import net.dv8tion.jda.internal.utils.EncodingUtil;
import net.dv8tion.jda.internal.utils.EntityString;
import net.dv8tion.jda.internal.utils.Helpers;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ReceivedMessage implements Message
{
    public static boolean didContentIntentWarning = false;
    private final Object mutex = new Object();

    protected final JDAImpl api;
    protected final long id;
    protected final long channelId;
    protected final long guildId;
    protected final long applicationId;
    protected final int flags;
    protected final int position;
    protected final boolean fromWebhook;
    protected final boolean pinned;
    protected final boolean isTTS;
    protected final MessageType type;
    protected final MessageChannel channel;
    protected final Guild guild;
    protected final MessageReference messageReference;
    protected final User author;
    protected final Member member;
    protected final String content;
    protected final String nonce;
    protected final MessageActivity activity;
    protected final MessagePoll poll;
    protected final OffsetDateTime editedTime;
    protected final Mentions mentions;
    protected final Message.Interaction interaction;
    protected final ThreadChannel startedThread;
    protected final List reactions;
    protected final List attachments;
    protected final List embeds;
    protected final List stickers;
    protected final List components;

    protected WebhookClient webhook;

    // LAZY EVALUATED
    protected String altContent = null;
    protected String strippedContent = null;

    protected List invites = null;

    public ReceivedMessage(
            long id, long channelId, long guildId, JDA jda, Guild guild, MessageChannel channel, MessageType type, MessageReference messageReference,
            boolean fromWebhook, long applicationId, boolean  tts, boolean pinned,
            String content, String nonce, User author, Member member, MessageActivity activity, MessagePoll poll, OffsetDateTime editTime,
            Mentions mentions, List reactions, List attachments, List embeds,
            List stickers, List components,
            int flags, Message.Interaction interaction, ThreadChannel startedThread, int position)
    {
        this.id = id;
        this.channelId = channelId;
        this.channel = channel;
        this.guildId = guildId;
        this.guild = guild;
        this.messageReference = messageReference;
        this.type = type;
        this.api = (JDAImpl) jda;
        this.fromWebhook = fromWebhook;
        this.applicationId = applicationId;
        this.pinned = pinned;
        this.content = content;
        this.nonce = nonce;
        this.isTTS = tts;
        this.author = author;
        this.member = member;
        this.activity = activity;
        this.editedTime = editTime;
        this.mentions = mentions;
        this.reactions = Collections.unmodifiableList(reactions);
        this.attachments = Collections.unmodifiableList(attachments);
        this.embeds = Collections.unmodifiableList(embeds);
        this.stickers = Collections.unmodifiableList(stickers);
        this.components = Collections.unmodifiableList(components);
        this.flags = flags;
        this.interaction = interaction;
        this.startedThread = startedThread;
        this.position = position;
        this.poll = poll;
    }

    private void checkSystem(String comment)
    {
        if (type.isSystem())
            throw new IllegalStateException("Cannot " + comment + " a system message!");
    }

    private void checkUser()
    {
        if (!getJDA().getSelfUser().equals(getAuthor()))
            throw new IllegalStateException("Attempted to update message that was not sent by this account. You cannot modify other User's messages!");
    }


    private void checkIntent()
    {
        // Checks whether access to content is limited and the message content intent is not enabled
        if (!didContentIntentWarning && !api.isIntent(GatewayIntent.MESSAGE_CONTENT))
        {
            SelfUser selfUser = api.getSelfUser();
            if (!Objects.equals(selfUser, author) && !mentions.getUsers().contains(selfUser) && isFromGuild())
            {
                didContentIntentWarning = true;
                JDAImpl.LOG.warn(
                    "Attempting to access message content without GatewayIntent.MESSAGE_CONTENT.\n" +
                    "Discord now requires to explicitly enable access to this using the MESSAGE_CONTENT intent.\n" +
                    "Useful resources to learn more:\n" +
                    "\t- https://support-dev.discord.com/hc/en-us/articles/4404772028055-Message-Content-Privileged-Intent-FAQ\n" +
                    "\t- https://jda.wiki/using-jda/gateway-intents-and-member-cache-policy/\n" +
                    "\t- https://jda.wiki/using-jda/troubleshooting/#cannot-get-message-content-attempting-to-access-message-content-without-gatewayintent\n" +
                    "Or suppress this warning if this is intentional with Message.suppressContentIntentWarning()"
                );
            }
        }
    }

    public ReceivedMessage withHook(WebhookClient hook)
    {
        this.webhook = hook;
        return this;
    }

    @Nonnull
    @Override
    public JDA getJDA()
    {
        return api;
    }

    @Nullable
    @Override
    public MessageReference getMessageReference()
    {
        return messageReference;
    }

    @Override
    public boolean isPinned()
    {
        return pinned;
    }

    @Nonnull
    @Override
    public RestAction pin()
    {
        checkSystem("pin");
        if (isEphemeral())
            throw new IllegalStateException("Cannot pin ephemeral messages.");

        if (hasChannel())
            return getChannel().pinMessageById(getIdLong());

        Route.CompiledRoute route = Route.Messages.ADD_PINNED_MESSAGE.compile(getChannelId(), getId());
        return new RestActionImpl<>(getJDA(), route);
    }

    @Nonnull
    @Override
    public RestAction unpin()
    {
        checkSystem("unpin");
        if (isEphemeral())
            throw new IllegalStateException("Cannot unpin ephemeral messages.");

        if (hasChannel())
            return getChannel().unpinMessageById(getIdLong());

        Route.CompiledRoute route = Route.Messages.REMOVE_PINNED_MESSAGE.compile(getChannelId(), getId());
        return new RestActionImpl<>(getJDA(), route);
    }

    @Nonnull
    @Override
    public RestAction addReaction(@Nonnull Emoji emoji)
    {
        if (isEphemeral())
            throw new IllegalStateException("Cannot add reactions to ephemeral messages.");
        
        Checks.notNull(emoji, "Emoji");

        if (hasChannel())
        {
            boolean missingReaction = reactions.stream()
                    .map(MessageReaction::getEmoji)
                    .noneMatch(r -> r.getAsReactionCode().equals(emoji.getAsReactionCode()));

            if (missingReaction && emoji instanceof RichCustomEmoji)
            {
                Checks.check(((RichCustomEmoji) emoji).canInteract(getJDA().getSelfUser(), getChannel()),
                        "Cannot react with the provided emoji because it is not available in the current getChannel().");
            }

            return getChannel().addReactionById(getId(), emoji);
        }

        String encoded = EncodingUtil.encodeReaction(emoji.getAsReactionCode());
        Route.CompiledRoute route = Route.Messages.ADD_REACTION.compile(getChannelId(), getId(), encoded, "@me");
        return new RestActionImpl<>(getJDA(), route);
    }

    @Nonnull
    @Override
    public RestAction clearReactions()
    {
        if (isEphemeral())
            throw new IllegalStateException("Cannot clear reactions from ephemeral messages.");
        if (!isFromGuild())
            throw new IllegalStateException("Cannot clear reactions from a message in a Group or PrivateChannel.");

        if (channel instanceof GuildMessageChannel)
            return ((GuildMessageChannel) channel).clearReactionsById(getId());

        Route.CompiledRoute route = Route.Messages.REMOVE_ALL_REACTIONS.compile(getChannelId(), getId());
        return new RestActionImpl<>(getJDA(), route);
    }

    @Nonnull
    @Override
    public RestAction clearReactions(@Nonnull Emoji emoji)
    {
        if (isEphemeral())
            throw new IllegalStateException("Cannot clear reactions from ephemeral messages.");
        if (!isFromGuild())
            throw new IllegalStateException("Cannot clear reactions from a message in a Group or PrivateChannel.");

        if (channel instanceof GuildMessageChannel)
            return ((GuildMessageChannel) channel).clearReactionsById(getId(), emoji);

        String encoded = EncodingUtil.encodeReaction(emoji.getAsReactionCode());
        Route.CompiledRoute route = Route.Messages.CLEAR_EMOJI_REACTIONS.compile(getChannelId(), getId(), encoded);
        return new RestActionImpl<>(getJDA(), route);
    }

    @Nonnull
    @Override
    public RestAction removeReaction(@Nonnull Emoji emoji)
    {
        if (isEphemeral())
            throw new IllegalStateException("Cannot remove reactions from ephemeral messages.");

        if (hasChannel())
            return getChannel().removeReactionById(getId(), emoji);

        String encoded = EncodingUtil.encodeReaction(emoji.getAsReactionCode());
        Route.CompiledRoute route = Route.Messages.REMOVE_REACTION.compile(getChannelId(), getId(), encoded, "@me");
        return new RestActionImpl<>(getJDA(), route);
    }

    @Nonnull
    @Override
    public RestAction removeReaction(@Nonnull Emoji emoji, @Nonnull User user)
    {
        Checks.notNull(user, "User");
        if (isEphemeral())
            throw new IllegalStateException("Cannot remove reactions from ephemeral messages.");

        // check if the passed user is the SelfUser, then the ChannelType doesn't matter and
        // we can safely remove that
        if (user.equals(getJDA().getSelfUser()))
            return removeReaction(emoji);

        if (!isFromGuild())
            throw new IllegalStateException("Cannot remove reactions of others from a message in a Group or PrivateChannel.");

        if (channel instanceof GuildMessageChannel)
            return ((GuildMessageChannel) channel).removeReactionById(getIdLong(), emoji, user);

        String encoded = EncodingUtil.encodeReaction(emoji.getAsReactionCode());
        Route.CompiledRoute route = Route.Messages.REMOVE_REACTION.compile(getChannelId(), getId(), encoded, user.getId());
        return new RestActionImpl<>(getJDA(), route);
    }

    @Nonnull
    @Override
    public ReactionPaginationAction retrieveReactionUsers(@Nonnull Emoji emoji)
    {
        if (isEphemeral())
            throw new IllegalStateException("Cannot retrieve reactions on ephemeral messages.");

        if (hasChannel())
            return getChannel().retrieveReactionUsersById(id, emoji);

        return new ReactionPaginationActionImpl(this, emoji.getAsReactionCode());
    }

    @Nullable
    @Override
    public MessageReaction getReaction(@Nonnull Emoji emoji)
    {
        Checks.notNull(emoji, "Emoji");
        String code = emoji.getAsReactionCode();
        return this.reactions.stream()
                .filter(r -> code.equals(r.getEmoji().getAsReactionCode()))
                .findFirst().orElse(null);
    }

    @Nonnull
    @Override
    public MessageType getType()
    {
        return type;
    }

    @Nullable
    @Override
    public Interaction getInteraction()
    {
        return interaction;
    }

    @Override
    public long getIdLong()
    {
        return id;
    }

    @Nonnull
    @Override
    public String getJumpUrl()
    {
        return Helpers.format(Message.JUMP_URL, isFromGuild() ? getGuildId() : "@me", getChannelId(), getId());
    }

    @Override
    public boolean isEdited()
    {
        return editedTime != null;
    }

    @Override
    public OffsetDateTime getTimeEdited()
    {
        return editedTime;
    }

    @Nonnull
    @Override
    public User getAuthor()
    {
        return author;
    }

    @Override
    public Member getMember()
    {
        return member;
    }

    @Override
    public int getApproximatePosition()
    {
        if (!getChannelType().isThread())
            throw new IllegalStateException("This message was not sent in a thread.");

        return position;
    }

    @Nonnull
    @Override
    public String getContentStripped()
    {
        if (strippedContent != null)
            return strippedContent;
        synchronized (mutex)
        {
            if (strippedContent != null)
                return strippedContent;
            return strippedContent = MarkdownSanitizer.sanitize(getContentDisplay());
        }
    }

    @Nonnull
    @Override
    public String getContentDisplay()
    {
        if (altContent != null)
            return altContent;

        synchronized (mutex)
        {
            if (altContent != null)
                return altContent;
            String tmp = getContentRaw();
            for (User user : mentions.getUsers())
            {
                String name;
                if (hasGuild() && getGuild().isMember(user))
                    name = getGuild().getMember(user).getEffectiveName();
                else
                    name = user.getName();
                tmp = tmp.replaceAll("<@!?" + Pattern.quote(user.getId()) + '>', '@' + Matcher.quoteReplacement(name));
            }
            for (CustomEmoji emoji : mentions.getCustomEmojis())
            {
                tmp = tmp.replace(emoji.getAsMention(), ":" + emoji.getName() + ":");
            }
            for (GuildChannel mentionedChannel : mentions.getChannels())
            {
                tmp = tmp.replace(mentionedChannel.getAsMention(), '#' + mentionedChannel.getName());
            }
            for (Role mentionedRole : mentions.getRoles())
            {
                tmp = tmp.replace(mentionedRole.getAsMention(), '@' + mentionedRole.getName());
            }
            return altContent = tmp;
        }
    }

    @Nonnull
    @Override
    public String getContentRaw()
    {
        checkIntent();
        return content;
    }

    @Nonnull
    @Override
    public List getInvites()
    {
        if (invites != null)
            return invites;
        synchronized (mutex)
        {
            if (invites != null)
                return invites;
            invites = new ArrayList<>();
            Matcher m = INVITE_PATTERN.matcher(getContentRaw());
            while (m.find())
                invites.add(m.group(1));
            return invites = Collections.unmodifiableList(invites);
        }
    }

    @Override
    public String getNonce()
    {
        return nonce;
    }

    @Override
    public boolean isFromType(@Nonnull ChannelType type)
    {
        return getChannelType() == type;
    }

    @Override
    public boolean isFromGuild()
    {
        return guildId != 0L;
    }

    @Nonnull
    @Override
    public ChannelType getChannelType()
    {
        return channel == null ? ChannelType.UNKNOWN : getChannel().getType();
    }

    @Nonnull
    @Override
    public MessageChannelUnion getChannel()
    {
        if (channel != null)
            return (MessageChannelUnion) channel;
        throw new IllegalStateException("Channel is unavailable in this context. Use getChannelIdLong() instead!");
    }

    @Nonnull
    @Override
    public GuildMessageChannelUnion getGuildChannel()
    {
        if (channel == null || channel instanceof GuildMessageChannelUnion)
            return (GuildMessageChannelUnion) getChannel();
        throw new IllegalStateException("This message was not sent in a guild.");
    }

    @Override
    public Category getCategory()
    {
        Channel channel = this.channel;
        if (channel instanceof ThreadChannel)
            channel = ((ThreadChannel) channel).getParentChannel();

        return channel instanceof ICategorizableChannel
            ? ((ICategorizableChannel) channel).getParentCategory()
            : null;
    }

    @Override
    public boolean hasGuild()
    {
        return guild != null;
    }

    @Override
    public long getGuildIdLong()
    {
        return guildId;
    }

    @Nonnull
    @Override
    public Guild getGuild()
    {
        if (guild == null)
        {
            ChannelType channelType = getChannelType();
            if (channelType == ChannelType.UNKNOWN || channelType.isGuild())
                throw new IllegalStateException("This message instance does not provide a guild instance! Use getGuildId() instead.");
            else
                throw new IllegalStateException("This message was not sent in a guild");
        }
        return guild;
    }

    @Nonnull
    @Override
    public List getAttachments()
    {
        checkIntent();
        return attachments;
    }

    @Nonnull
    @Override
    public List getEmbeds()
    {
        checkIntent();
        return embeds;
    }

    @Nonnull
    @Override
    public List getComponents()
    {
        checkIntent();
        return components;
    }

    @Override
    public MessagePoll getPoll()
    {
        checkIntent();
        return poll;
    }

    @Nonnull
    @Override
    public AuditableRestAction endPoll()
    {
        checkUser();
        if (poll == null)
            throw new IllegalStateException("This message does not contain a poll");
        return new AuditableRestActionImpl<>(getJDA(), Route.Messages.END_POLL.compile(getChannelId(), getId()), (response, request) -> {
            JDAImpl jda = (JDAImpl) getJDA();
            EntityBuilder entityBuilder = jda.getEntityBuilder();
            if (hasChannel())
                return entityBuilder.createMessageWithChannel(response.getObject(), channel, false);
            return entityBuilder.createMessageFromWebhook(response.getObject(), hasGuild() ? getGuild() : null);
        });
    }

    @Nonnull
    @Override
    public Mentions getMentions()
    {
        return mentions;
    }

    @Nonnull
    @Override
    public List getReactions()
    {
        return reactions;
    }

    @Nonnull
    @Override
    public List getStickers()
    {
        return this.stickers;
    }

    @Override
    public boolean isWebhookMessage()
    {
        return fromWebhook;
    }

    @Override
    public long getApplicationIdLong()
    {
        return applicationId;
    }

    @Override
    public boolean hasChannel()
    {
        return channel != null;
    }

    @Override
    public long getChannelIdLong()
    {
        return channelId;
    }

    @Override
    public boolean isTTS()
    {
        return isTTS;
    }

    @Nullable
    @Override
    public MessageActivity getActivity()
    {
        return activity;
    }

    @Nonnull
    @Override
    public MessageEditAction editMessage(@Nonnull CharSequence newContent)
    {
        MessageEditActionImpl action = editRequest();
        action.setContent(newContent.toString());

        if (isWebhookRequest())
            return action.withHook(webhook);

        checkSystem("edit");
        checkUser();

        return action.setContent(newContent.toString());
    }

    @Nonnull
    @Override
    public MessageEditAction editMessageEmbeds(@Nonnull Collection embeds)
    {
        MessageEditActionImpl action = editRequest();
        action.setEmbeds(embeds);

        if (isWebhookRequest())
            return action.withHook(webhook);

        checkSystem("edit");
        checkUser();

        return action;
    }

    @Nonnull
    @Override
    public MessageEditAction editMessageComponents(@Nonnull Collection components)
    {
        MessageEditActionImpl action = editRequest();
        action.setComponents(components);

        if (isWebhookRequest())
            return action.withHook(webhook);

        checkSystem("edit");
        checkUser();

        return action;
    }

    @Nonnull
    @Override
    public MessageEditAction editMessageFormat(@Nonnull String format, @Nonnull Object... args)
    {
        MessageEditActionImpl action = editRequest();
        action.setContent(String.format(format, args));

        if (isWebhookRequest())
            return action.withHook(webhook);

        checkSystem("edit");
        checkUser();

        return action;
    }

    @Nonnull
    @Override
    public MessageEditAction editMessageAttachments(@Nonnull Collection attachments)
    {
        MessageEditActionImpl action = editRequest();
        action.setAttachments(attachments);

        if (isWebhookRequest())
            return action.withHook(webhook);

        checkSystem("edit");
        checkUser();

        return action;
    }

    @Nonnull
    @Override
    public MessageEditAction editMessage(@Nonnull MessageEditData newContent)
    {
        MessageEditActionImpl action = editRequest();
        action.applyData(newContent);

        if (isWebhookRequest())
            return action.withHook(webhook);

        checkSystem("edit");
        checkUser();

        return action;
    }

    @Nonnull
    @Override
    public AuditableRestAction delete()
    {
        if (!type.canDelete())
            throw new IllegalStateException("Cannot delete messages of type " + type);

        if (isWebhookRequest())
        {
            Route.CompiledRoute route = Route.Webhooks.EXECUTE_WEBHOOK_DELETE.compile(webhook.getId(), webhook.getToken(), getId());
            final AuditableRestActionImpl action = new AuditableRestActionImpl<>(getJDA(), route);
            action.setErrorMapper(getUnknownWebhookErrorMapper());
            return action;
        }

        SelfUser self = getJDA().getSelfUser();
        boolean isSelfAuthored = self.equals(getAuthor());

        if (!isSelfAuthored && !isFromGuild())
            throw new IllegalStateException("Cannot delete another User's messages in a PrivateChannel.");

        if (isEphemeral())
            throw new IllegalStateException("Cannot delete ephemeral messages.");

        if (channel instanceof GuildMessageChannel && !isSelfAuthored)
        {
            GuildMessageChannel gChan = (GuildMessageChannel) channel;
            Member sMember = getGuild().getSelfMember();
            Checks.checkAccess(sMember, gChan);
            if (!sMember.hasPermission(gChan, Permission.MESSAGE_MANAGE))
                throw new InsufficientPermissionException(gChan, Permission.MESSAGE_MANAGE);
        }

        Route.CompiledRoute route = Route.Messages.DELETE_MESSAGE.compile(getChannelId(), getId());
        return new AuditableRestActionImpl<>(getJDA(), route);
    }

    @Nonnull
    @Override
    public AuditableRestAction suppressEmbeds(boolean suppressed)
    {
        SelfUser self = api.getSelfUser();

        Route.CompiledRoute route;
        if (isWebhookRequest())
        {
            route = Route.Webhooks.EXECUTE_WEBHOOK_EDIT.compile(webhook.getId(), webhook.getToken(), getId());
        }
        else
        {
            if (isEphemeral())
                throw new IllegalStateException("Cannot suppress embeds on ephemeral messages.");

            if (!self.equals(getAuthor()))
            {
                if (!isFromGuild())
                    throw new PermissionException("Cannot suppress embeds of others in a PrivateChannel.");

                if (hasChannel())
                {
                    GuildMessageChannel gChan = getGuildChannel();
                    if (!getGuild().getSelfMember().hasPermission(gChan, Permission.MESSAGE_MANAGE))
                        throw new InsufficientPermissionException(gChan, Permission.MESSAGE_MANAGE);
                }
            }

            route = Route.Messages.EDIT_MESSAGE.compile(getChannelId(), getId());
        }

        int newFlags = flags;
        int suppressionValue = MessageFlag.EMBEDS_SUPPRESSED.getValue();
        if (suppressed)
            newFlags |= suppressionValue;
        else
            newFlags &= ~suppressionValue;
        DataObject body = DataObject.empty().put("flags", newFlags);

        final AuditableRestActionImpl action = new AuditableRestActionImpl<>(api, route, body);
        action.setErrorMapper(getUnknownWebhookErrorMapper());
        return action;
    }

    @Nonnull
    @Override
    public RestAction crosspost()
    {
        if (isEphemeral())
            throw new IllegalStateException("Cannot crosspost ephemeral messages.");
        
        if (getFlags().contains(MessageFlag.CROSSPOSTED))
            return new CompletedRestAction<>(getJDA(), this);

        if (!hasChannel())
        {
            Route.CompiledRoute route = Route.Messages.CROSSPOST_MESSAGE.compile(getChannelId(), getId());
            return new RestActionImpl<>(api, route, (response, request) ->
                api.getEntityBuilder().createMessageFromWebhook(response.getObject(), guild));
        }

        MessageChannelUnion channel = getChannel();
        if (!(channel instanceof NewsChannel))
            throw new IllegalStateException("This message was not sent in a news channel");
        NewsChannel newsChannel = (NewsChannel) channel;
        Checks.checkAccess(getGuild().getSelfMember(), newsChannel);
        if (!getAuthor().equals(getJDA().getSelfUser()) && !getGuild().getSelfMember().hasPermission(newsChannel, Permission.MESSAGE_MANAGE))
            throw new InsufficientPermissionException(newsChannel, Permission.MESSAGE_MANAGE);
        return newsChannel.crosspostMessageById(getId());
    }

    @Override
    public boolean isSuppressedEmbeds()
    {
        return (this.flags & MessageFlag.EMBEDS_SUPPRESSED.getValue()) > 0;
    }

    @Nonnull
    @Override
    public EnumSet getFlags()
    {
        return MessageFlag.fromBitField(flags);
    }

    @Override
    public long getFlagsRaw()
    {
        return flags;
    }

    @Override
    public boolean isEphemeral()
    {
        return (this.flags & MessageFlag.EPHEMERAL.getValue()) != 0;
    }

    @Override
    public boolean isSuppressedNotifications()
    {
        return (this.flags & MessageFlag.NOTIFICATIONS_SUPPRESSED.getValue()) != 0;
    }

    @Nullable
    @Override
    public ThreadChannel getStartedThread()
    {
        return this.startedThread;
    }

    @Override
    public ThreadChannelAction createThreadChannel(String name)
    {
        return getGuildChannel().asThreadContainer().createThreadChannel(name, this.getIdLong());
    }

    @Override
    public boolean equals(Object o)
    {
        if (o == this)
            return true;
        if (!(o instanceof ReceivedMessage))
            return false;
        ReceivedMessage oMsg = (ReceivedMessage) o;
        return this.id == oMsg.id;
    }

    @Override
    public int hashCode()
    {
        return Long.hashCode(id);
    }

    @Override
    @SuppressWarnings("deprecation")
    public String toString()
    {
        return new EntityString(this)
                .addMetadata("author", author.getDiscriminator().equals("0000") ? author.getName() : author.getAsTag())
                .addMetadata("content", String.format("%.20s ...", this))
                .toString();
    }

    @Override
    public void formatTo(Formatter formatter, int flags, int width, int precision)
    {
        boolean upper = (flags & FormattableFlags.UPPERCASE) == FormattableFlags.UPPERCASE;
        boolean leftJustified = (flags & FormattableFlags.LEFT_JUSTIFY) == FormattableFlags.LEFT_JUSTIFY;
        boolean alt = (flags & FormattableFlags.ALTERNATE) == FormattableFlags.ALTERNATE;

        String out = alt ? getContentRaw() : getContentDisplay();

        if (upper)
            out = out.toUpperCase(formatter.locale());

        try
        {
            Appendable appendable = formatter.out();
            if (precision > -1 && out.length() > precision)
            {
                appendable.append(Helpers.truncate(out, precision - 3)).append("...");
                return;
            }

            if (leftJustified)
                appendable.append(Helpers.rightPad(out, width));
            else
                appendable.append(Helpers.leftPad(out, width));
        }
        catch (IOException e)
        {
            throw new UncheckedIOException(e);
        }
    }

    private boolean isWebhookRequest()
    {
        return webhook != null && (!(webhook instanceof InteractionHook) || !((InteractionHook) webhook).isExpired());
    }

    @Nonnull
    private MessageEditActionImpl editRequest()
    {
        final MessageEditActionImpl messageEditAction = hasChannel()
                ? new MessageEditActionImpl(getChannel(), getId())
                : new MessageEditActionImpl(getJDA(), hasGuild() ? getGuild() : null, getChannelId(), getId());

        messageEditAction.setErrorMapper(getUnknownWebhookErrorMapper());
        return messageEditAction;
    }

    private ErrorMapper getUnknownWebhookErrorMapper()
    {
        if (!isWebhookRequest())
            return null;

        return (response, request, exception) ->
        {
            if (webhook instanceof InteractionHookImpl
                    && !((InteractionHookImpl) webhook).isAck()
                    && exception.getErrorResponse() == ErrorResponse.UNKNOWN_WEBHOOK)
                return new IllegalStateException("Sending a webhook request requires the interaction to be acknowledged before expiration", exception);
            else
                return null;
        };
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy