From 2d97b27b49cd2e653a41fd39d8c2f22af894be53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sun, 15 Oct 2023 12:41:15 +0200 Subject: [PATCH] Handle messages from inaccessible threads and add webhook support (#2390) --- .../api/entities/IncomingWebhookClient.java | 27 + .../net/dv8tion/jda/api/entities/Message.java | 215 +++++--- .../jda/api/entities/MessageReaction.java | 72 ++- .../net/dv8tion/jda/api/entities/Webhook.java | 2 +- .../jda/api/entities/WebhookClient.java | 220 +++++--- .../jda/api/entities/channel/ChannelType.java | 5 +- .../GenericInteractionCreateEvent.java | 6 + .../CommandAutoCompleteInteractionEvent.java | 2 +- .../command/SlashCommandInteractionEvent.java | 2 +- ...enericComponentInteractionCreateEvent.java | 2 +- .../jda/api/interactions/Interaction.java | 24 +- .../jda/api/interactions/InteractionHook.java | 40 +- .../CommandAutoCompleteInteraction.java | 8 +- .../commands/SlashCommandInteraction.java | 5 + .../net/dv8tion/jda/api/requests/Route.java | 3 +- .../AbstractWebhookMessageAction.java | 101 ++++ .../restaction/ThreadCreateMetadata.java | 118 ++++ .../WebhookMessageCreateAction.java | 128 +++-- .../WebhookMessageDeleteAction.java | 25 + .../restaction/WebhookMessageEditAction.java | 3 +- .../WebhookMessageRetrieveAction.java | 27 + .../internal/entities/AbstractMessage.java | 519 ------------------ .../entities/AbstractWebhookClient.java | 30 +- .../jda/internal/entities/EntityBuilder.java | 73 ++- .../internal/entities/ReceivedMessage.java | 389 ++++++++++--- .../jda/internal/entities/SystemMessage.java | 113 ---- .../jda/internal/entities/WebhookImpl.java | 86 ++- .../handle/InteractionCreateHandler.java | 22 +- .../internal/handle/MessageCreateHandler.java | 2 + .../MessageReactionClearEmojiHandler.java | 2 +- .../handle/MessageReactionHandler.java | 2 +- .../internal/handle/MessageUpdateHandler.java | 2 + .../interactions/InteractionHookImpl.java | 118 ++-- .../interactions/InteractionImpl.java | 17 +- .../CommandInteractionPayloadImpl.java | 8 +- .../MessageContextInteractionImpl.java | 2 +- .../component/ComponentInteractionImpl.java | 22 +- .../requests/IncomingWebhookClientImpl.java | 105 ++++ .../AbstractWebhookMessageActionImpl.java | 69 +++ .../restaction/MessageEditActionImpl.java | 44 +- .../WebhookMessageCreateActionImpl.java | 106 +++- .../WebhookMessageDeleteActionImpl.java | 38 ++ .../WebhookMessageEditActionImpl.java | 23 +- .../WebhookMessageRetrieveActionImpl.java | 43 ++ .../ReactionPaginationActionImpl.java | 4 +- .../dv8tion/jda/ChannelConsistencyTest.java | 15 +- 46 files changed, 1773 insertions(+), 1116 deletions(-) create mode 100644 src/main/java/net/dv8tion/jda/api/entities/IncomingWebhookClient.java create mode 100644 src/main/java/net/dv8tion/jda/api/requests/restaction/AbstractWebhookMessageAction.java create mode 100644 src/main/java/net/dv8tion/jda/api/requests/restaction/ThreadCreateMetadata.java create mode 100644 src/main/java/net/dv8tion/jda/api/requests/restaction/WebhookMessageDeleteAction.java create mode 100644 src/main/java/net/dv8tion/jda/api/requests/restaction/WebhookMessageRetrieveAction.java delete mode 100644 src/main/java/net/dv8tion/jda/internal/entities/AbstractMessage.java delete mode 100644 src/main/java/net/dv8tion/jda/internal/entities/SystemMessage.java create mode 100644 src/main/java/net/dv8tion/jda/internal/requests/IncomingWebhookClientImpl.java create mode 100644 src/main/java/net/dv8tion/jda/internal/requests/restaction/AbstractWebhookMessageActionImpl.java create mode 100644 src/main/java/net/dv8tion/jda/internal/requests/restaction/WebhookMessageDeleteActionImpl.java create mode 100644 src/main/java/net/dv8tion/jda/internal/requests/restaction/WebhookMessageRetrieveActionImpl.java diff --git a/src/main/java/net/dv8tion/jda/api/entities/IncomingWebhookClient.java b/src/main/java/net/dv8tion/jda/api/entities/IncomingWebhookClient.java new file mode 100644 index 0000000000..796a555752 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/IncomingWebhookClient.java @@ -0,0 +1,27 @@ +/* + * 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; + +/** + * Specialization of {@link WebhookClient} for incoming webhooks. + *
An incoming webhook, is a webhook that sends messages with a customizable name and avatar. + * + *

These webhooks are primarily used to integrate external services. + */ +public interface IncomingWebhookClient extends WebhookClient +{ +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/Message.java b/src/main/java/net/dv8tion/jda/api/entities/Message.java index 334c071747..b0062f494f 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Message.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Message.java @@ -23,6 +23,7 @@ import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer; import net.dv8tion.jda.api.entities.channel.concrete.*; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.unions.GuildMessageChannelUnion; @@ -77,7 +78,7 @@ /** * Represents a Text message received from Discord. - *
This represents messages received from {@link net.dv8tion.jda.api.entities.channel.middleman.MessageChannel MessageChannels}. + *
This represents messages received from {@link MessageChannel MessageChannels}. * *

This type is not updated. JDA does not keep track of changes to messages, it is advised to do this via events such * as {@link net.dv8tion.jda.api.events.message.MessageUpdateEvent MessageUpdateEvent} and similar. @@ -307,7 +308,7 @@ default Message getReferencedMessage() /** * The {@link Mentions} used in this message. * - *

This includes {@link Member Members}, {@link net.dv8tion.jda.api.entities.channel.middleman.GuildChannel GuildChannels}, {@link Role Roles}, and {@link CustomEmoji CustomEmojis}. + *

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
@@ -353,7 +354,7 @@ default Message getReferencedMessage() *
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 net.dv8tion.jda.api.entities.channel.middleman.MessageChannel#retrieveMessageById(long)} or similar means, + * 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. @@ -399,7 +400,7 @@ default Message getReferencedMessage() *

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.channel.middleman.GuildChannel GuildChannels} to their #ChannelName format, + *
{@link GuildChannel GuildChannels} to their #ChannelName format, *
{@link net.dv8tion.jda.api.entities.Role Roles} to their @RoleName format *
{@link CustomEmoji Custom Emojis} (not unicode emojis!) to their {@code :name:} format. * @@ -480,15 +481,12 @@ default Message getReferencedMessage() boolean isFromType(@Nonnull ChannelType type); /** - * Whether this message was sent in a {@link net.dv8tion.jda.api.entities.Guild Guild}. + * Whether this message was sent in a {@link Guild Guild}. *
If this is {@code false} then {@link #getGuild()} will throw an {@link java.lang.IllegalStateException}. * * @return True, if {@link #getChannelType()}.{@link ChannelType#isGuild() isGuild()} is true. */ - default boolean isFromGuild() - { - return getChannelType().isGuild(); - } + boolean isFromGuild(); /** * Gets the {@link net.dv8tion.jda.api.entities.channel.ChannelType ChannelType} that this message was received from. @@ -530,21 +528,59 @@ default String getApplicationId() long getApplicationIdLong(); /** - * Returns the {@link net.dv8tion.jda.api.entities.channel.middleman.MessageChannel MessageChannel} that this message was sent in. + * Whether this message instance has an available {@link #getChannel()}. + * + *

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

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