retrieveApplicationInfo();
+ /**
+ * A {@link net.dv8tion.jda.api.requests.restaction.pagination.PaginationAction PaginationAction} implementation
+ * which allows you to {@link Iterable iterate} over {@link Entitlement}s that are applicable to the logged in application.
+ *
+ * @return {@link EntitlementPaginationAction EntitlementPaginationAction}
+ */
+ @Nonnull
+ @CheckReturnValue
+ EntitlementPaginationAction retrieveEntitlements();
+
/**
* Configures the required scopes applied to the {@link #getInviteUrl(Permission...)} and similar methods.
*
To use slash commands you must add {@code "applications.commands"} to these scopes. The scope {@code "bot"} is always applied.
diff --git a/src/main/java/net/dv8tion/jda/api/Permission.java b/src/main/java/net/dv8tion/jda/api/Permission.java
index 22e99f903d7..1b15040d788 100644
--- a/src/main/java/net/dv8tion/jda/api/Permission.java
+++ b/src/main/java/net/dv8tion/jda/api/Permission.java
@@ -15,8 +15,6 @@
*/
package net.dv8tion.jda.api;
-import net.dv8tion.jda.annotations.ForRemoval;
-import net.dv8tion.jda.annotations.ReplaceWith;
import net.dv8tion.jda.internal.utils.Checks;
import javax.annotation.Nonnull;
@@ -33,19 +31,17 @@ public enum Permission
// General Server / Channel Permissions
MANAGE_CHANNEL( 4, true, true, "Manage Channels"),
MANAGE_SERVER( 5, true, false, "Manage Server"),
- VIEW_AUDIT_LOGS( 7, true, false, "View Audit Logs"),
- VIEW_CHANNEL( 10, true, true, "View Channel(s)"),
+ VIEW_AUDIT_LOGS( 7, true, false, "View Audit Log"),
+ VIEW_CHANNEL( 10, true, true, "View Channels"),
VIEW_GUILD_INSIGHTS( 19, true, false, "View Server Insights"),
MANAGE_ROLES( 28, true, false, "Manage Roles"),
MANAGE_PERMISSIONS( 28, false, true, "Manage Permissions"),
MANAGE_WEBHOOKS( 29, true, true, "Manage Webhooks"),
- @Deprecated
- @ForRemoval(deadline = "5.0.0")
- @ReplaceWith("MANAGE_GUILD_EXPRESSIONS")
- MANAGE_EMOJIS_AND_STICKERS( 30, true, false, "Manage Emojis and Stickers"),
- MANAGE_GUILD_EXPRESSIONS( 30, true, false, "Manage Emojis, Stickers, and Soundboards"),
+ MANAGE_GUILD_EXPRESSIONS( 30, true, false, "Manage Expressions"),
MANAGE_EVENTS( 33, true, true, "Manage Events"),
VIEW_CREATOR_MONETIZATION_ANALYTICS(41, true, false, "View Creator Analytics"),
+ CREATE_GUILD_EXPRESSIONS( 43, true, false, "Create Expressions"),
+ CREATE_SCHEDULED_EVENTS( 44, true, false, "Create Events"),
// Membership Permissions
CREATE_INSTANT_INVITE(0, true, true, "Create Instant Invite"),
@@ -68,6 +64,7 @@ public enum Permission
USE_APPLICATION_COMMANDS( 31, true, true, "Use Application Commands"),
MESSAGE_EXT_STICKER( 37, true, true, "Use External Stickers"),
MESSAGE_ATTACH_VOICE_MESSAGE(46, true, true, "Send Voice Messages"),
+ MESSAGE_SEND_POLLS( 49, true, true, "Create Polls"),
// Thread Permissions
MANAGE_THREADS( 34, true, true, "Manage Threads"),
diff --git a/src/main/java/net/dv8tion/jda/api/entities/BulkBanResponse.java b/src/main/java/net/dv8tion/jda/api/entities/BulkBanResponse.java
new file mode 100644
index 00000000000..d165bace9bc
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/entities/BulkBanResponse.java
@@ -0,0 +1,62 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Response to {@link Guild#ban(java.util.Collection, Duration)}
+ *
+ * This response includes a list of successfully banned users and users which could not be banned.
+ * Discord might fail to ban a user due to permission issues or an internal server error.
+ */
+public class BulkBanResponse
+{
+ private final List bannedUsers;
+ private final List failedUsers;
+
+ public BulkBanResponse(@Nonnull List bannedUsers, @Nonnull List failedUsers)
+ {
+ this.bannedUsers = Collections.unmodifiableList(bannedUsers);
+ this.failedUsers = Collections.unmodifiableList(failedUsers);
+ }
+
+ /**
+ * List of successfully banned users.
+ *
+ * @return {@link List} of {@link UserSnowflake}
+ */
+ @Nonnull
+ public List getBannedUsers()
+ {
+ return bannedUsers;
+ }
+
+ /**
+ * List of users which could not be banned.
+ *
+ * @return {@link List} of {@link UserSnowflake}
+ */
+ @Nonnull
+ public List getFailedUsers()
+ {
+ return failedUsers;
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/entities/Entitlement.java b/src/main/java/net/dv8tion/jda/api/entities/Entitlement.java
new file mode 100644
index 00000000000..94fb94dfa35
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/entities/Entitlement.java
@@ -0,0 +1,188 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.time.OffsetDateTime;
+
+/**
+ * Represents a user or guild that has access to a premium offering in your application.
+ *
+ * @see Discord Docs about Entitlements
+ */
+public interface Entitlement extends ISnowflake
+{
+
+ /**
+ * The id of the SKU related to this {@link Entitlement Entitlement}
+ *
+ * @return The id of the SKU related to this {@link Entitlement Entitlement}
+ */
+ long getSkuIdLong();
+
+ /**
+ * The id of the SKU related to this {@link Entitlement Entitlement}
+ *
+ * @return The id of the SKU related to this {@link Entitlement Entitlement}
+ */
+ @Nonnull
+ default String getSkuId()
+ {
+ return Long.toUnsignedString(getSkuIdLong());
+ }
+
+ /**
+ * The id of the parent application of this {@link Entitlement Entitlement}
+ *
+ * @return The id of the parent application of this {@link Entitlement Entitlement}
+ */
+ long getApplicationIdLong();
+
+ /**
+ * The id of the parent application of this {@link Entitlement Entitlement}
+ *
+ * @return The id of the parent application of this {@link Entitlement Entitlement}
+ */
+ @Nonnull
+ default String getApplicationId()
+ {
+ return Long.toUnsignedString(getApplicationIdLong());
+ }
+
+ /**
+ * The id of the user that purchased the {@link Entitlement Entitlement}
+ *
+ * @return The id of the user that purchased the {@link Entitlement Entitlement}
+ */
+ long getUserIdLong();
+
+ /**
+ * The id of the user that purchased the {@link Entitlement Entitlement}
+ *
+ * @return The id of the user that purchased the {@link Entitlement Entitlement}
+ */
+ default String getUserId()
+ {
+ return Long.toUnsignedString(getUserIdLong());
+ }
+
+ /**
+ * The guild id that is granted access to the {@link Entitlement Entitlement}s SKU
+ *
+ * @return The id of the guild that purchased the {@link Entitlement Entitlement} or 0 if this is not a guild subscription
+ */
+ long getGuildIdLong();
+
+ /**
+ * The guild id that is granted access to the {@link Entitlement Entitlement}s SKU
+ *
+ * @return The id of the guild that purchased the {@link Entitlement Entitlement} or {@code null} if this is not a guild subscription
+ */
+ @Nullable
+ default String getGuildId()
+ {
+ if (getGuildIdLong() == 0)
+ return null;
+
+ return Long.toUnsignedString(getGuildIdLong());
+ }
+
+ /**
+ * The type of the Entitlement
+ *
The only possible type of Entitlement currently is {@link EntitlementType#APPLICATION_SUBSCRIPTION}
+ *
Discord doesn't currently support other types for entitlements.
+ *
+ * @return the {@link Entitlement Entitlement} type
+ */
+ @Nonnull
+ EntitlementType getType();
+
+ /**
+ * Whether the {@link Entitlement Entitlement} has been deleted or not.
+ *
+ * @return True if the {@link Entitlement Entitlement} was deleted, False otherwise
+ *
+ * @see net.dv8tion.jda.api.events.entitlement.EntitlementDeleteEvent
+ */
+ boolean isDeleted();
+
+ /**
+ * The start date at which the {@link Entitlement Entitlement} is valid.
+ *
+ * @return Start date at which the {@link Entitlement Entitlement} is valid. Not present when using test entitlements.
+ */
+ @Nullable
+ OffsetDateTime getTimeStarting();
+
+ /**
+ * Date at which the {@link Entitlement Entitlement} is no longer valid.
+ *
+ * @return Date at which the {@link Entitlement Entitlement} is no longer valid. Not present when using test entitlements.
+ */
+ @Nullable
+ OffsetDateTime getTimeEnding();
+
+ /**
+ * Represents the type of this Entitlement
+ */
+ enum EntitlementType
+ {
+ APPLICATION_SUBSCRIPTION(8),
+ /**
+ * Placeholder for unsupported types.
+ */
+ UNKNOWN(-1);
+
+ private final int key;
+
+ EntitlementType(int key)
+ {
+ this.key = key;
+ }
+
+ /**
+ * The Discord defined id key for this EntitlementType.
+ *
+ * @return the id key.
+ */
+ public int getKey()
+ {
+ return key;
+ }
+
+ /**
+ * Gets the EntitlementType related to the provided key.
+ *
If an unknown key is provided, this returns {@link #UNKNOWN}
+ *
+ * @param key
+ * The Discord key referencing a EntitlementType.
+ *
+ * @return The EntitlementType that has the key provided, or {@link #UNKNOWN} for unknown key.
+ */
+ @Nonnull
+ public static EntitlementType fromKey(int key)
+ {
+ for (EntitlementType type : values())
+ {
+ if (type.getKey() == key)
+ return type;
+ }
+ return UNKNOWN;
+ }
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/entities/Guild.java b/src/main/java/net/dv8tion/jda/api/entities/Guild.java
index c409d0ab6ee..2881b502950 100644
--- a/src/main/java/net/dv8tion/jda/api/entities/Guild.java
+++ b/src/main/java/net/dv8tion/jda/api/entities/Guild.java
@@ -3629,8 +3629,8 @@ default AuditableRestAction kick(@Nonnull UserSnowflake user, @Nullable St
* {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
*
The target Member cannot be banned due to a permission discrepancy
*
- * {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MEMBER UNKNOWN_MEMBER}
- *
The specified Member was removed from the Guild before finishing the task
+ * {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_USER UNKNOWN_USER}
+ *
The user does not exist
*
*
* @param user
@@ -3661,6 +3661,90 @@ default AuditableRestAction kick(@Nonnull UserSnowflake user, @Nullable St
@CheckReturnValue
AuditableRestAction ban(@Nonnull UserSnowflake user, int deletionTimeframe, @Nonnull TimeUnit unit);
+ /**
+ * Bans up to 200 of the provided users.
+ *
To set a ban reason, use {@link AuditableRestAction#reason(String)}.
+ *
+ * The {@link BulkBanResponse} includes a list of {@link BulkBanResponse#getFailedUsers() failed users},
+ * which is populated with users that could not be banned, for instance due to some internal server error or permission issues.
+ * This list of failed users also includes all users that were already banned.
+ *
+ *
Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} caused by
+ * the returned {@link RestAction RestAction} include the following:
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
+ *
The target Member cannot be banned due to a permission discrepancy
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#FAILED_TO_BAN_USERS FAILED_TO_BAN_USERS}
+ *
None of the users could be banned
+ *
+ *
+ * @param users
+ * The users to ban
+ * @param deletionTime
+ * Delete recent messages of the given timeframe (for instance the last hour with {@code Duration.ofHours(1)})
+ *
+ * @throws net.dv8tion.jda.api.exceptions.HierarchyException
+ * If any of the provided users is the guild owner or has a higher or equal role position
+ * @throws InsufficientPermissionException
+ * If the bot does not have {@link Permission#BAN_MEMBERS} or {@link Permission#MANAGE_SERVER}
+ * @throws IllegalArgumentException
+ *
+ * - If the users collection is null or contains null
+ * - If the deletionTime is negative
+ *
+ *
+ * @return {@link AuditableRestAction} - Type: {@link BulkBanResponse}
+ */
+ @Nonnull
+ @CheckReturnValue
+ AuditableRestAction ban(@Nonnull Collection users, @Nullable Duration deletionTime);
+
+ /**
+ * Bans up to 200 of the provided users.
+ *
To set a ban reason, use {@link AuditableRestAction#reason(String)}.
+ *
+ * The {@link BulkBanResponse} includes a list of {@link BulkBanResponse#getFailedUsers() failed users},
+ * which is populated with users that could not be banned, for instance due to some internal server error or permission issues.
+ * This list of failed users also includes all users that were already banned.
+ *
+ *
Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} caused by
+ * the returned {@link RestAction RestAction} include the following:
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
+ *
The target Member cannot be banned due to a permission discrepancy
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#FAILED_TO_BAN_USERS FAILED_TO_BAN_USERS}
+ *
None of the users could be banned
+ *
+ *
+ * @param users
+ * The users to ban
+ * @param deletionTimeframe
+ * The timeframe for the history of messages that will be deleted. (seconds precision)
+ * @param unit
+ * Timeframe unit as a {@link TimeUnit} (for example {@code ban(user, 7, TimeUnit.DAYS)}).
+ *
+ * @throws net.dv8tion.jda.api.exceptions.HierarchyException
+ * If any of the provided users is the guild owner or has a higher or equal role position
+ * @throws InsufficientPermissionException
+ * If the bot does not have {@link Permission#BAN_MEMBERS} or {@link Permission#MANAGE_SERVER}
+ * @throws IllegalArgumentException
+ *
+ * - If null is provided
+ * - If the deletionTimeframe is negative
+ *
+ *
+ * @return {@link AuditableRestAction} - Type: {@link BulkBanResponse}
+ */
+ @Nonnull
+ @CheckReturnValue
+ default AuditableRestAction ban(@Nonnull Collection users, int deletionTimeframe, @Nonnull TimeUnit unit)
+ {
+ Checks.notNull(unit, "TimeUnit");
+ return ban(users, Duration.ofSeconds(unit.toSeconds(deletionTimeframe)));
+ }
+
/**
* Unbans the specified {@link UserSnowflake} from this Guild.
*
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 d10b6cdd8d4..15c4d4c07ab 100644
--- a/src/main/java/net/dv8tion/jda/api/entities/Message.java
+++ b/src/main/java/net/dv8tion/jda/api/entities/Message.java
@@ -31,6 +31,7 @@
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.GuildSticker;
import net.dv8tion.jda.api.entities.sticker.Sticker;
import net.dv8tion.jda.api.entities.sticker.StickerItem;
@@ -48,6 +49,7 @@
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.PollVotersPaginationAction;
import net.dv8tion.jda.api.requests.restaction.pagination.ReactionPaginationAction;
import net.dv8tion.jda.api.utils.AttachedFile;
import net.dv8tion.jda.api.utils.AttachmentProxy;
@@ -55,10 +57,12 @@
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.MessagePollData;
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.restaction.pagination.PollVotersPaginationActionImpl;
import net.dv8tion.jda.internal.utils.Checks;
import net.dv8tion.jda.internal.utils.Helpers;
import net.dv8tion.jda.internal.utils.IOUtil;
@@ -681,6 +685,43 @@ default String getGuildId()
@Nonnull
List getComponents();
+ /**
+ * The {@link MessagePoll} attached to this message.
+ *
+ * @return Possibly-null poll instance for this message
+ *
+ * @see #endPoll()
+ */
+ @Nullable
+ MessagePoll getPoll();
+
+ /**
+ * End the poll attached to this message.
+ *
+ * @throws IllegalStateException
+ * If this poll was not sent by the currently logged in account or no poll was attached to this message
+ *
+ * @return {@link AuditableRestAction} - Type: {@link Message}
+ */
+ @Nonnull
+ @CheckReturnValue
+ AuditableRestAction endPoll();
+
+ /**
+ * Paginate the users who voted for a poll answer.
+ *
+ * @param answerId
+ * The id of the poll answer, usually the ordinal position of the answer (first is 1)
+ *
+ * @return {@link PollVotersPaginationAction}
+ */
+ @Nonnull
+ @CheckReturnValue
+ default PollVotersPaginationAction retrievePollVoters(long answerId)
+ {
+ return new PollVotersPaginationActionImpl(getJDA(), getChannelId(), getId(), answerId);
+ }
+
/**
* Rows of interactive components such as {@link Button Buttons}.
*
You can use {@link MessageRequest#setComponents(LayoutComponent...)} to update these.
@@ -1359,6 +1400,48 @@ default MessageCreateAction reply(@Nonnull MessageCreateData msg)
return getChannel().sendMessage(msg).setMessageReference(this);
}
+ /**
+ * Shortcut for {@code getChannel().sendMessagePoll(data).setMessageReference(this)}.
+ *
+ * Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include:
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
+ *
if this channel was deleted
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER}
+ *
If this is a {@link PrivateChannel} and the currently logged in account
+ * does not share any Guilds with the recipient User
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD}
+ *
If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER}
+ *
If this message was blocked by the harmful link filter
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_INVALID_CHANNEL_TYPE POLL_INVALID_CHANNEL_TYPE}
+ *
This channel does not allow polls
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_WITH_UNUSABLE_EMOJI POLL_WITH_UNUSABLE_EMOJI}
+ *
This poll uses an external emoji that the bot is not allowed to use
+ *
+ *
+ * @param poll
+ * The poll 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 replyPoll(@Nonnull MessagePollData poll)
+ {
+ return getChannel().sendMessagePoll(poll).setMessageReference(this);
+ }
+
/**
* Shortcut for {@code getChannel().sendMessageEmbeds(embed, other).setMessageReference(this)}.
*
diff --git a/src/main/java/net/dv8tion/jda/api/entities/MessageEmbed.java b/src/main/java/net/dv8tion/jda/api/entities/MessageEmbed.java
index d6771470591..13efabaf0be 100644
--- a/src/main/java/net/dv8tion/jda/api/entities/MessageEmbed.java
+++ b/src/main/java/net/dv8tion/jda/api/entities/MessageEmbed.java
@@ -18,6 +18,7 @@
import net.dv8tion.jda.annotations.ForRemoval;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.utils.AttachmentProxy;
+import net.dv8tion.jda.api.utils.FileProxy;
import net.dv8tion.jda.api.utils.ImageProxy;
import net.dv8tion.jda.api.utils.data.DataArray;
import net.dv8tion.jda.api.utils.data.DataObject;
@@ -655,12 +656,14 @@ public boolean equals(Object obj)
public static class VideoInfo
{
protected final String url;
+ protected final String proxyUrl;
protected final int width;
protected final int height;
- public VideoInfo(String url, int width, int height)
+ public VideoInfo(String url, String proxyUrl, int width, int height)
{
this.url = url;
+ this.proxyUrl = proxyUrl;
this.width = width;
this.height = height;
}
@@ -676,6 +679,32 @@ public String getUrl()
return url;
}
+ /**
+ * The url of the video, proxied by Discord
+ *
This url is used to access the video through Discord instead of directly to prevent ip scraping.
+ *
+ * @return Possibly-null String containing the proxied video url.
+ */
+ @Nullable
+ public String getProxyUrl()
+ {
+ return proxyUrl;
+ }
+
+ /**
+ * Returns a {@link FileProxy} for this embed video.
+ *
+ * @return Possibly-null {@link FileProxy} of this embed video
+ *
+ * @see #getProxyUrl()
+ */
+ @Nullable
+ public FileProxy getProxy()
+ {
+ final String proxyUrl = getProxyUrl();
+ return proxyUrl == null ? null : new FileProxy(proxyUrl);
+ }
+
/**
* The width of the video.
*
This usually isn't the actual video width, but instead the starting embed window size.
diff --git a/src/main/java/net/dv8tion/jda/api/entities/WebhookClient.java b/src/main/java/net/dv8tion/jda/api/entities/WebhookClient.java
index 2956d91d391..5a81a73cd93 100644
--- a/src/main/java/net/dv8tion/jda/api/entities/WebhookClient.java
+++ b/src/main/java/net/dv8tion/jda/api/entities/WebhookClient.java
@@ -32,6 +32,8 @@
import net.dv8tion.jda.api.utils.MiscUtil;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageEditData;
+import net.dv8tion.jda.api.utils.messages.MessagePollBuilder;
+import net.dv8tion.jda.api.utils.messages.MessagePollData;
import net.dv8tion.jda.internal.requests.IncomingWebhookClientImpl;
import net.dv8tion.jda.internal.utils.Checks;
@@ -128,6 +130,43 @@ public interface WebhookClient extends ISnowflake
@CheckReturnValue
WebhookMessageCreateAction sendMessage(@Nonnull MessageCreateData message);
+ /**
+ * Send a message poll to this webhook.
+ *
+ * If this is an {@link InteractionHook InteractionHook} this method will be delayed until the interaction is acknowledged.
+ *
+ *
Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include:
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_WEBHOOK UNKNOWN_WEBHOOK}
+ *
The webhook is no longer available, either it was deleted or in case of interactions it expired.
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD}
+ *
If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER}
+ *
If this message was blocked by the harmful link filter
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_INVALID_CHANNEL_TYPE POLL_INVALID_CHANNEL_TYPE}
+ *
This channel does not allow polls
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_WITH_UNUSABLE_EMOJI POLL_WITH_UNUSABLE_EMOJI}
+ *
This poll uses an external emoji that the bot is not allowed to use
+ *
+ *
+ * @param poll
+ * The {@link MessagePollData} to send
+ *
+ * @throws IllegalArgumentException
+ * If null is provided
+ *
+ * @return {@link net.dv8tion.jda.api.requests.restaction.WebhookMessageCreateAction}
+ *
+ * @see MessagePollBuilder
+ */
+ @Nonnull
+ @CheckReturnValue
+ WebhookMessageCreateAction sendMessagePoll(@Nonnull MessagePollData poll);
+
/**
* Send a message to this webhook.
*
diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/middleman/MessageChannel.java b/src/main/java/net/dv8tion/jda/api/entities/channel/middleman/MessageChannel.java
index c325f093903..4d4fc7314f1 100644
--- a/src/main/java/net/dv8tion/jda/api/entities/channel/middleman/MessageChannel.java
+++ b/src/main/java/net/dv8tion/jda/api/entities/channel/middleman/MessageChannel.java
@@ -35,6 +35,7 @@
import net.dv8tion.jda.api.requests.restaction.MessageEditAction;
import net.dv8tion.jda.api.requests.restaction.pagination.MessagePaginationAction;
import net.dv8tion.jda.api.requests.restaction.pagination.PaginationAction;
+import net.dv8tion.jda.api.requests.restaction.pagination.PollVotersPaginationAction;
import net.dv8tion.jda.api.requests.restaction.pagination.ReactionPaginationAction;
import net.dv8tion.jda.api.utils.AttachedFile;
import net.dv8tion.jda.api.utils.FileUpload;
@@ -42,6 +43,7 @@
import net.dv8tion.jda.api.utils.data.DataArray;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageEditData;
+import net.dv8tion.jda.api.utils.messages.MessagePollData;
import net.dv8tion.jda.internal.JDAImpl;
import net.dv8tion.jda.internal.entities.EntityBuilder;
import net.dv8tion.jda.internal.requests.RestActionImpl;
@@ -49,6 +51,7 @@
import net.dv8tion.jda.internal.requests.restaction.MessageCreateActionImpl;
import net.dv8tion.jda.internal.requests.restaction.MessageEditActionImpl;
import net.dv8tion.jda.internal.requests.restaction.pagination.MessagePaginationActionImpl;
+import net.dv8tion.jda.internal.requests.restaction.pagination.PollVotersPaginationActionImpl;
import net.dv8tion.jda.internal.requests.restaction.pagination.ReactionPaginationActionImpl;
import net.dv8tion.jda.internal.utils.Checks;
@@ -644,6 +647,52 @@ default MessageCreateAction sendMessageComponents(@Nonnull Collection extends
return new MessageCreateActionImpl(this).setComponents(components);
}
+ /**
+ * Send a message to this channel.
+ *
+ * Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include:
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
+ *
if this channel was deleted
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER}
+ *
If this is a {@link PrivateChannel} and the currently logged in account
+ * does not share any Guilds with the recipient User
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD}
+ *
If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER}
+ *
If this message was blocked by the harmful link filter
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_INVALID_CHANNEL_TYPE POLL_INVALID_CHANNEL_TYPE}
+ *
This channel does not allow polls
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_WITH_UNUSABLE_EMOJI POLL_WITH_UNUSABLE_EMOJI}
+ *
This poll uses an external emoji that the bot is not allowed to use
+ *
+ *
+ * @param poll
+ * The poll to send
+ *
+ * @throws UnsupportedOperationException
+ * If this is a {@link PrivateChannel} and the recipient is a bot
+ * @throws IllegalArgumentException
+ * If the poll is null
+ * @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException
+ * If this is a {@link GuildMessageChannel} and this account does not have
+ * {@link net.dv8tion.jda.api.Permission#VIEW_CHANNEL Permission.VIEW_CHANNEL} or {@link net.dv8tion.jda.api.Permission#MESSAGE_SEND Permission.MESSAGE_SEND}
+ *
+ * @return {@link MessageCreateAction}
+ */
+ @Nonnull
+ @CheckReturnValue
+ default MessageCreateAction sendMessagePoll(@Nonnull MessagePollData poll)
+ {
+ Checks.notNull(poll, "Poll");
+ return new MessageCreateActionImpl(this).setPoll(poll);
+ }
+
/**
* Send a message to this channel.
*
@@ -972,6 +1021,108 @@ default AuditableRestAction deleteMessageById(long messageId)
return deleteMessageById(Long.toUnsignedString(messageId));
}
+ /**
+ * End the poll attached to this message.
+ *
+ * A bot cannot expire the polls of other users.
+ *
+ *
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_AUTHOR_EDIT INVALID_AUTHOR_EDIT}
+ *
If the poll was sent by another user
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_EXPIRE_MISSING_POLL CANNOT_EXPIRE_MISSING_POLL}
+ *
The message did not have a poll attached
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
+ *
The message no longer exists
+ *
+ *
+ * @param messageId
+ * The ID for the poll message
+ *
+ * @throws IllegalArgumentException
+ * If the provided messageId is not a valid snowflake
+ *
+ * @return {@link AuditableRestAction} - Type: {@link Message}
+ */
+ @Nonnull
+ @CheckReturnValue
+ default AuditableRestAction endPollById(@Nonnull String messageId)
+ {
+ Checks.isSnowflake(messageId, "Message ID");
+ return new AuditableRestActionImpl<>(getJDA(), Route.Messages.END_POLL.compile(getId(), messageId), (response, request) -> {
+ JDAImpl jda = (JDAImpl) getJDA();
+ return jda.getEntityBuilder().createMessageWithChannel(response.getObject(), MessageChannel.this, false);
+ });
+ }
+
+ /**
+ * End the poll attached to this message.
+ *
+ * A bot cannot expire the polls of other users.
+ *
+ *
The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_AUTHOR_EDIT INVALID_AUTHOR_EDIT}
+ *
If the poll was sent by another user
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_EXPIRE_MISSING_POLL CANNOT_EXPIRE_MISSING_POLL}
+ *
The message did not have a poll attached
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
+ *
The message no longer exists
+ *
+ *
+ * @param messageId
+ * The ID for the poll message
+ *
+ * @return {@link AuditableRestAction} - Type: {@link Message}
+ */
+ @Nonnull
+ @CheckReturnValue
+ default AuditableRestAction endPollById(long messageId)
+ {
+ return endPollById(Long.toUnsignedString(messageId));
+ }
+
+ /**
+ * Paginate the users who voted for a poll answer.
+ *
+ * @param messageId
+ * The message id for the poll
+ * @param answerId
+ * The id of the poll answer, usually the ordinal position of the answer (first is 1)
+ *
+ * @throws IllegalArgumentException
+ * If the message id is not a valid snowflake
+ *
+ * @return {@link PollVotersPaginationAction}
+ */
+ @Nonnull
+ @CheckReturnValue
+ default PollVotersPaginationAction retrievePollVotersById(@Nonnull String messageId, long answerId)
+ {
+ return new PollVotersPaginationActionImpl(getJDA(), getId(), messageId, answerId);
+ }
+
+ /**
+ * Paginate the users who voted for a poll answer.
+ *
+ * @param messageId
+ * The message id for the poll
+ * @param answerId
+ * The id of the poll answer, usually the ordinal position of the answer (first is 1)
+ *
+ * @return {@link PollVotersPaginationAction}
+ */
+ @Nonnull
+ @CheckReturnValue
+ default PollVotersPaginationAction retrievePollVotersById(long messageId, long answerId)
+ {
+ return new PollVotersPaginationActionImpl(getJDA(), getId(), Long.toUnsignedString(messageId), answerId);
+ }
+
/**
* Creates a new {@link net.dv8tion.jda.api.entities.MessageHistory MessageHistory} object for each call of this method.
*
MessageHistory is NOT an internal message cache, but rather it queries the Discord servers for previously sent messages.
diff --git a/src/main/java/net/dv8tion/jda/api/entities/messages/MessagePoll.java b/src/main/java/net/dv8tion/jda/api/entities/messages/MessagePoll.java
new file mode 100644
index 00000000000..f7bcef204d0
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/entities/messages/MessagePoll.java
@@ -0,0 +1,273 @@
+/*
+ * 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.messages;
+
+import net.dv8tion.jda.api.entities.Message;
+import net.dv8tion.jda.api.entities.emoji.Emoji;
+import net.dv8tion.jda.api.entities.emoji.EmojiUnion;
+import net.dv8tion.jda.api.utils.messages.MessagePollBuilder;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.time.Duration;
+import java.time.OffsetDateTime;
+import java.util.List;
+
+/**
+ * Poll sent with messages.
+ *
+ * @see Message#getPoll()
+ * @see Message#endPoll()
+ */
+public interface MessagePoll
+{
+ /** Maximum length of a {@link MessagePollBuilder#setTitle(String) poll question title} ({@value}) */
+ int MAX_QUESTION_TEXT_LENGTH = 300;
+ /** Maximum length of a {@link MessagePollBuilder#addAnswer(String)} poll answer title} ({@value}) */
+ int MAX_ANSWER_TEXT_LENGTH = 55;
+ /** Maximum amount of {@link MessagePollBuilder#addAnswer(String) poll answers} ({@value}) */
+ int MAX_ANSWERS = 10;
+ /** Maximum {@link MessagePollBuilder#setDuration(Duration) duration} of poll ({@value}) */
+ long MAX_DURATION_HOURS = 7 * 24;
+
+ /**
+ * The layout of the poll.
+ *
+ * @return The poll layout, or {@link LayoutType#UNKNOWN} if unknown
+ */
+ @Nonnull
+ LayoutType getLayout();
+
+ /**
+ * The poll question, representing the title.
+ *
+ * @return {@link Question}
+ */
+ @Nonnull
+ Question getQuestion();
+
+ /**
+ * The poll answers.
+ *
+ * Each answer also has the current {@link Answer#getVotes() votes}.
+ * The votes might not be finalized and might be incorrect before the poll has expired,
+ * see {@link #isFinalizedVotes()}.
+ *
+ * @return Immutable {@link List} of {@link Answer}
+ */
+ @Nonnull
+ List getAnswers();
+
+ /**
+ * The time when this poll will automatically expire.
+ *
+ * The author of the poll can always expire the poll manually, using {@link Message#endPoll()}.
+ *
+ * @return {@link OffsetDateTime} representing the time when the poll expires automatically, or null if it never expires
+ */
+ @Nullable
+ OffsetDateTime getTimeExpiresAt();
+
+ /**
+ * Whether this poll allows multiple answers to be selected.
+ *
+ * @return True, if this poll allows multi selection
+ */
+ boolean isMultiAnswer();
+
+ /**
+ * Whether this poll is finalized and recounted.
+ *
+ *
The votes for answers might be inaccurate due to eventual consistency, until this is true.
+ * Finalization does not mean the votes cannot change anymore, use {@link #isExpired()} to check if a poll has ended.
+ *
+ * @return True, if the votes have been precisely counted
+ */
+ boolean isFinalizedVotes();
+
+ /**
+ * Whether this poll has passed its {@link #getTimeExpiresAt() expiration time}.
+ *
+ * @return True, if this poll is expired.
+ */
+ default boolean isExpired()
+ {
+ return getTimeExpiresAt().isBefore(OffsetDateTime.now());
+ }
+
+ /**
+ * The question for a poll.
+ */
+ class Question
+ {
+ private final String text;
+ private final EmojiUnion emoji;
+
+ public Question(String text, Emoji emoji)
+ {
+ this.text = text;
+ this.emoji = (EmojiUnion) emoji;
+ }
+
+ /**
+ * The poll question title.
+ *
+ *
Shown above all answers.
+ *
+ * @return The question title
+ */
+ @Nonnull
+ public String getText()
+ {
+ return text;
+ }
+
+ /**
+ * Possible emoji related to the poll question.
+ *
+ * @return Possibly-null emoji
+ */
+ @Nullable
+ public EmojiUnion getEmoji()
+ {
+ return emoji;
+ }
+ }
+
+ /**
+ * One of the answers for a poll.
+ *
+ *
Provides the current {@link #getVotes()} and whether you have voted for it.
+ */
+ class Answer
+ {
+ private final long id;
+ private final String text;
+ private final EmojiUnion emoji;
+ private final int votes;
+ private final boolean selfVoted;
+
+ public Answer(long id, String text, EmojiUnion emoji, int votes, boolean selfVoted)
+ {
+ this.id = id;
+ this.text = text;
+ this.emoji = emoji;
+ this.votes = votes;
+ this.selfVoted = selfVoted;
+ }
+
+ /**
+ * The id of this answer.
+ *
+ * @return The answer id.
+ */
+ public long getId()
+ {
+ return id;
+ }
+
+ /**
+ * The text content of the answer.
+ *
+ * @return The answer label.
+ */
+ @Nonnull
+ public String getText()
+ {
+ return text;
+ }
+
+ /**
+ * The emoji assigned to this answer.
+ *
+ * @return {@link EmojiUnion}
+ */
+ @Nullable
+ public EmojiUnion getEmoji()
+ {
+ return emoji;
+ }
+
+ /**
+ * The number of votes this answer has received so far.
+ *
+ *
This might not be {@link #isFinalizedVotes() finalized}.
+ *
+ * @return The current number of votes
+ */
+ public int getVotes()
+ {
+ return votes;
+ }
+
+ /**
+ * Whether the answer was voted for by the currently logged in account.
+ *
+ * @return True, if the bot has voted for this.
+ */
+ public boolean isSelfVoted()
+ {
+ return selfVoted;
+ }
+ }
+
+ /**
+ * The poll layout.
+ *
+ *
Currently always {@link #DEFAULT}.
+ */
+ enum LayoutType
+ {
+ DEFAULT(1),
+ UNKNOWN(-1);
+
+ private final int key;
+
+ LayoutType(int key)
+ {
+ this.key = key;
+ }
+
+ /**
+ * The raw API key used to identify this layout.
+ *
+ * @return The API key
+ */
+ public int getKey()
+ {
+ return key;
+ }
+
+ /**
+ * Resolves the provided raw API key to the layout enum constant.
+ *
+ * @param key
+ * The API key
+ *
+ * @return The layout type or {@link #UNKNOWN}
+ */
+ public static LayoutType fromKey(int key)
+ {
+ for (LayoutType type : values())
+ {
+ if (type.key == key)
+ return type;
+ }
+ return UNKNOWN;
+ }
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/events/entitlement/EntitlementCreateEvent.java b/src/main/java/net/dv8tion/jda/api/events/entitlement/EntitlementCreateEvent.java
new file mode 100644
index 00000000000..b52fe994dfa
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/events/entitlement/EntitlementCreateEvent.java
@@ -0,0 +1,35 @@
+/*
+ * 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.events.entitlement;
+
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.Entitlement;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Indicates that a user subscribed to a SKU.
+ *
+ * @see #getEntitlement()
+ */
+public class EntitlementCreateEvent extends GenericEntitlementEvent
+{
+ public EntitlementCreateEvent(@Nonnull JDA api, long responseNumber, @Nonnull Entitlement entitlement)
+ {
+ super(api, responseNumber, entitlement);
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/events/entitlement/EntitlementDeleteEvent.java b/src/main/java/net/dv8tion/jda/api/events/entitlement/EntitlementDeleteEvent.java
new file mode 100644
index 00000000000..3325ed636c0
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/events/entitlement/EntitlementDeleteEvent.java
@@ -0,0 +1,40 @@
+/*
+ * 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.events.entitlement;
+
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.Entitlement;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Indicates that an {@link Entitlement Entitlement} was deleted. {@link Entitlement Entitlement} deletions are infrequent and occur strictly when:
+ *
+ * - Discord issues a refund for a subscription; or
+ * - Discord removes an {@link Entitlement Entitlement} via internal tooling
+ *
+ *
+ * @see #getEntitlement()
+ * @see EntitlementUpdateEvent
+ */
+public class EntitlementDeleteEvent extends GenericEntitlementEvent
+{
+ public EntitlementDeleteEvent(@Nonnull JDA api, long responseNumber, @Nonnull Entitlement entitlement)
+ {
+ super(api, responseNumber, entitlement);
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/events/entitlement/EntitlementUpdateEvent.java b/src/main/java/net/dv8tion/jda/api/events/entitlement/EntitlementUpdateEvent.java
new file mode 100644
index 00000000000..8602dad9601
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/events/entitlement/EntitlementUpdateEvent.java
@@ -0,0 +1,41 @@
+/*
+ * 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.events.entitlement;
+
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.Entitlement;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Indicates an {@link Entitlement Entitlement} has renewed for the next billing period.
+ * The {@link Entitlement#getTimeEnding() timeEnding} will have an updated value with the new expiration date.
+ *
+ * Notice
+ * The {@link Entitlement#getTimeEnding() timeEnding} is updated for active subscriptions at the end of every billing period to
+ * indicate renewal. When an {@link Entitlement Entitlement} has not been renewed, Discord will indicate this by not emitting
+ * an {@link EntitlementUpdateEvent} with the new {@link Entitlement#getTimeEnding() timeEnding} date
+ *
+ * @see #getEntitlement()
+ */
+public class EntitlementUpdateEvent extends GenericEntitlementEvent
+{
+ public EntitlementUpdateEvent(@Nonnull JDA api, long responseNumber, @Nonnull Entitlement entitlement)
+ {
+ super(api, responseNumber, entitlement);
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/events/entitlement/GenericEntitlementEvent.java b/src/main/java/net/dv8tion/jda/api/events/entitlement/GenericEntitlementEvent.java
new file mode 100644
index 00000000000..fa8071aeeb5
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/events/entitlement/GenericEntitlementEvent.java
@@ -0,0 +1,52 @@
+/*
+ * 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.events.entitlement;
+
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.Entitlement;
+import net.dv8tion.jda.api.events.Event;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Indicates that an {@link Entitlement Entitlement} was either created, updated, or deleted
+ *
+ * @see EntitlementCreateEvent
+ * @see EntitlementUpdateEvent
+ * @see EntitlementDeleteEvent
+ */
+public abstract class GenericEntitlementEvent extends Event
+{
+ protected final Entitlement entitlement;
+
+ protected GenericEntitlementEvent(@Nonnull JDA api, long responseNumber, @Nonnull Entitlement entitlement)
+ {
+ super(api, responseNumber);
+ this.entitlement = entitlement;
+ }
+
+ /**
+ * The {@link Entitlement Entitlement}
+ *
+ * @return The {@link Entitlement Entitlement}
+ */
+ @Nonnull
+ public Entitlement getEntitlement()
+ {
+ return entitlement;
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/events/entitlement/package-info.java b/src/main/java/net/dv8tion/jda/api/events/entitlement/package-info.java
new file mode 100644
index 00000000000..a963879c610
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/events/entitlement/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Events that indicate that a {@link net.dv8tion.jda.api.entities.Entitlement Entitlement}
+ * is either created, updated, or deleted.
+ */
+package net.dv8tion.jda.api.events.entitlement;
diff --git a/src/main/java/net/dv8tion/jda/api/events/interaction/GenericInteractionCreateEvent.java b/src/main/java/net/dv8tion/jda/api/events/interaction/GenericInteractionCreateEvent.java
index 2f1eac6ced3..24d40d8f20b 100644
--- a/src/main/java/net/dv8tion/jda/api/events/interaction/GenericInteractionCreateEvent.java
+++ b/src/main/java/net/dv8tion/jda/api/events/interaction/GenericInteractionCreateEvent.java
@@ -17,6 +17,7 @@
package net.dv8tion.jda.api.events.interaction;
import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.Entitlement;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
@@ -27,6 +28,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.util.List;
/**
* Indicates that an {@link Interaction} was created.
@@ -121,6 +123,13 @@ public User getUser()
return interaction.getUser();
}
+ @Nonnull
+ @Override
+ public List getEntitlements()
+ {
+ return interaction.getEntitlements();
+ }
+
@Override
public long getIdLong()
{
diff --git a/src/main/java/net/dv8tion/jda/api/events/interaction/command/GenericCommandInteractionEvent.java b/src/main/java/net/dv8tion/jda/api/events/interaction/command/GenericCommandInteractionEvent.java
index 37f69103cbe..b81d5c5fb28 100644
--- a/src/main/java/net/dv8tion/jda/api/events/interaction/command/GenericCommandInteractionEvent.java
+++ b/src/main/java/net/dv8tion/jda/api/events/interaction/command/GenericCommandInteractionEvent.java
@@ -24,6 +24,7 @@
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.modals.Modal;
import net.dv8tion.jda.api.requests.restaction.interactions.ModalCallbackAction;
+import net.dv8tion.jda.api.requests.restaction.interactions.PremiumRequiredCallbackAction;
import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction;
import javax.annotation.Nonnull;
@@ -118,4 +119,11 @@ public ModalCallbackAction replyModal(@Nonnull Modal modal)
{
return getInteraction().replyModal(modal);
}
+
+ @Nonnull
+ @Override
+ public PremiumRequiredCallbackAction replyWithPremiumRequired()
+ {
+ return getInteraction().replyWithPremiumRequired();
+ }
}
diff --git a/src/main/java/net/dv8tion/jda/api/events/interaction/component/GenericComponentInteractionCreateEvent.java b/src/main/java/net/dv8tion/jda/api/events/interaction/component/GenericComponentInteractionCreateEvent.java
index 82ef4c9a60e..2a04315b098 100644
--- a/src/main/java/net/dv8tion/jda/api/events/interaction/component/GenericComponentInteractionCreateEvent.java
+++ b/src/main/java/net/dv8tion/jda/api/events/interaction/component/GenericComponentInteractionCreateEvent.java
@@ -27,6 +27,7 @@
import net.dv8tion.jda.api.interactions.modals.Modal;
import net.dv8tion.jda.api.requests.restaction.interactions.MessageEditCallbackAction;
import net.dv8tion.jda.api.requests.restaction.interactions.ModalCallbackAction;
+import net.dv8tion.jda.api.requests.restaction.interactions.PremiumRequiredCallbackAction;
import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction;
import javax.annotation.Nonnull;
@@ -124,4 +125,11 @@ public ModalCallbackAction replyModal(@Nonnull Modal modal)
{
return interaction.replyModal(modal);
}
+
+ @Nonnull
+ @Override
+ public PremiumRequiredCallbackAction replyWithPremiumRequired()
+ {
+ return interaction.replyWithPremiumRequired();
+ }
}
diff --git a/src/main/java/net/dv8tion/jda/api/events/message/poll/GenericMessagePollVoteEvent.java b/src/main/java/net/dv8tion/jda/api/events/message/poll/GenericMessagePollVoteEvent.java
new file mode 100644
index 00000000000..f8ada658eb1
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/events/message/poll/GenericMessagePollVoteEvent.java
@@ -0,0 +1,137 @@
+/*
+ * 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.events.message.poll;
+
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.Message;
+import net.dv8tion.jda.api.entities.User;
+import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
+import net.dv8tion.jda.api.events.message.GenericMessageEvent;
+import net.dv8tion.jda.api.requests.RestAction;
+
+import javax.annotation.CheckReturnValue;
+import javax.annotation.Nonnull;
+
+/**
+ * Indicates that a poll vote was added/removed.
+ *
Every MessagePollVoteEvent is derived from this event and can be casted.
+ *
+ * Can be used to detect both remove and add events.
+ *
+ *
Requirements
+ *
+ *
These events require at least one of the following intents (Will not fire at all if neither is enabled):
+ *
+ * - {@link net.dv8tion.jda.api.requests.GatewayIntent#GUILD_MESSAGE_POLLS GUILD_MESSAGE_POLLS} to work in guild text channels
+ * - {@link net.dv8tion.jda.api.requests.GatewayIntent#DIRECT_MESSAGE_POLLS DIRECT_MESSAGE_POLLS} to work in private channels
+ *
+ */
+public class GenericMessagePollVoteEvent extends GenericMessageEvent
+{
+ protected final long userId;
+ protected final long messageId;
+ protected final long answerId;
+
+ public GenericMessagePollVoteEvent(@Nonnull MessageChannel channel, long responseNumber, long messageId, long userId, long answerId)
+ {
+ super(channel.getJDA(), responseNumber, messageId, channel);
+ this.userId = userId;
+ this.messageId = messageId;
+ this.answerId = answerId;
+ }
+
+ /**
+ * The id of the voting user.
+ *
+ * @return The user id
+ */
+ @Nonnull
+ public String getUserId()
+ {
+ return Long.toUnsignedString(userId);
+ }
+
+ /**
+ * The id for the voting user.
+ *
+ * @return The user id
+ */
+ public long getUserIdLong()
+ {
+ return userId;
+ }
+
+ /**
+ * The id of the answer, usually the ordinal position.
+ *
The first answer options is usually 1.
+ *
+ * @return The answer id
+ */
+ public long getAnswerId()
+ {
+ return answerId;
+ }
+
+ /**
+ * Retrieves the voting {@link User}.
+ *
+ * @return {@link RestAction} - Type: {@link User}
+ */
+ @Nonnull
+ @CheckReturnValue
+ public RestAction retrieveUser()
+ {
+ return getJDA().retrieveUserById(getUserIdLong());
+ }
+
+ /**
+ * Retrieves the voting {@link Member}.
+ *
+ * Note that banning a member will also fire {@link MessagePollVoteRemoveEvent} and no member will be available
+ * in those cases. An {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MEMBER UNKNOWN_MEMBER} error response
+ * should be the failure result.
+ *
+ * @throws IllegalStateException
+ * If this event is not from a guild
+ *
+ * @return {@link RestAction} - Type: {@link Member}
+ */
+ @Nonnull
+ @CheckReturnValue
+ public RestAction retrieveMember()
+ {
+ if (!getChannel().getType().isGuild())
+ throw new IllegalStateException("Cannot retrieve member for a vote that happened outside of a guild");
+ return getGuild().retrieveMemberById(getUserIdLong());
+ }
+
+ /**
+ * Retrieves the message for this event.
+ *
Simple shortcut for {@code getChannel().retrieveMessageById(getMessageId())}.
+ *
+ * The {@link Message#getMember() Message.getMember()} method will always return null for the resulting message.
+ * To retrieve the member you can use {@code getGuild().retrieveMember(message.getAuthor())}.
+ *
+ * @return {@link RestAction} - Type: {@link Message}
+ */
+ @Nonnull
+ @CheckReturnValue
+ public RestAction retrieveMessage()
+ {
+ return getChannel().retrieveMessageById(getMessageId());
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/events/message/poll/MessagePollVoteAddEvent.java b/src/main/java/net/dv8tion/jda/api/events/message/poll/MessagePollVoteAddEvent.java
new file mode 100644
index 00000000000..24fa90eefb8
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/events/message/poll/MessagePollVoteAddEvent.java
@@ -0,0 +1,43 @@
+/*
+ * 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.events.message.poll;
+
+import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Indicates that a user voted for a poll answer.
+ *
If the poll allows selecting multiple answers, one event per vote is sent.
+ *
+ * Can be used to track when a user votes for a poll answer
+ *
+ *
Requirements
+ *
+ *
These events require at least one of the following intents (Will not fire at all if neither is enabled):
+ *
+ * - {@link net.dv8tion.jda.api.requests.GatewayIntent#GUILD_MESSAGE_POLLS GUILD_MESSAGE_POLLS} to work in guild text channels
+ * - {@link net.dv8tion.jda.api.requests.GatewayIntent#DIRECT_MESSAGE_POLLS DIRECT_MESSAGE_POLLS} to work in private channels
+ *
+ */
+public class MessagePollVoteAddEvent extends GenericMessagePollVoteEvent
+{
+ public MessagePollVoteAddEvent(@Nonnull MessageChannel channel, long responseNumber, long messageId, long userId, long answerId)
+ {
+ super(channel, responseNumber, messageId, userId, answerId);
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/events/message/poll/MessagePollVoteRemoveEvent.java b/src/main/java/net/dv8tion/jda/api/events/message/poll/MessagePollVoteRemoveEvent.java
new file mode 100644
index 00000000000..5c261f88fec
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/events/message/poll/MessagePollVoteRemoveEvent.java
@@ -0,0 +1,43 @@
+/*
+ * 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.events.message.poll;
+
+import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Indicates that a user removed a vote for a poll answer.
+ *
If the poll allows selecting multiple answers, one event per vote is sent.
+ *
+ * Can be used to track when a user removes a vote for a poll answer
+ *
+ *
Requirements
+ *
+ *
These events require at least one of the following intents (Will not fire at all if neither is enabled):
+ *
+ * - {@link net.dv8tion.jda.api.requests.GatewayIntent#GUILD_MESSAGE_POLLS GUILD_MESSAGE_POLLS} to work in guild text channels
+ * - {@link net.dv8tion.jda.api.requests.GatewayIntent#DIRECT_MESSAGE_POLLS DIRECT_MESSAGE_POLLS} to work in private channels
+ *
+ */
+public class MessagePollVoteRemoveEvent extends GenericMessagePollVoteEvent
+{
+ public MessagePollVoteRemoveEvent(@Nonnull MessageChannel channel, long responseNumber, long messageId, long userId, long answerId)
+ {
+ super(channel, responseNumber, messageId, userId, answerId);
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java
index 3df7247bae9..0ee9eb61605 100644
--- a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java
+++ b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java
@@ -36,6 +36,10 @@
import net.dv8tion.jda.api.events.emoji.update.EmojiUpdateNameEvent;
import net.dv8tion.jda.api.events.emoji.update.EmojiUpdateRolesEvent;
import net.dv8tion.jda.api.events.emoji.update.GenericEmojiUpdateEvent;
+import net.dv8tion.jda.api.events.entitlement.EntitlementCreateEvent;
+import net.dv8tion.jda.api.events.entitlement.EntitlementDeleteEvent;
+import net.dv8tion.jda.api.events.entitlement.EntitlementUpdateEvent;
+import net.dv8tion.jda.api.events.entitlement.GenericEntitlementEvent;
import net.dv8tion.jda.api.events.guild.*;
import net.dv8tion.jda.api.events.guild.invite.GenericGuildInviteEvent;
import net.dv8tion.jda.api.events.guild.invite.GuildInviteCreateEvent;
@@ -57,6 +61,9 @@
import net.dv8tion.jda.api.events.interaction.command.*;
import net.dv8tion.jda.api.events.interaction.component.*;
import net.dv8tion.jda.api.events.message.*;
+import net.dv8tion.jda.api.events.message.poll.GenericMessagePollVoteEvent;
+import net.dv8tion.jda.api.events.message.poll.MessagePollVoteAddEvent;
+import net.dv8tion.jda.api.events.message.poll.MessagePollVoteRemoveEvent;
import net.dv8tion.jda.api.events.message.react.*;
import net.dv8tion.jda.api.events.role.GenericRoleEvent;
import net.dv8tion.jda.api.events.role.RoleCreateEvent;
@@ -182,6 +189,8 @@ public void onMessageReactionAdd(@Nonnull MessageReactionAddEvent event) {}
public void onMessageReactionRemove(@Nonnull MessageReactionRemoveEvent event) {}
public void onMessageReactionRemoveAll(@Nonnull MessageReactionRemoveAllEvent event) {}
public void onMessageReactionRemoveEmoji(@Nonnull MessageReactionRemoveEmojiEvent event) {}
+ public void onMessagePollVoteAdd(@Nonnull MessagePollVoteAddEvent event) {}
+ public void onMessagePollVoteRemove(@Nonnull MessagePollVoteRemoveEvent event) {}
//PermissionOverride Events
public void onPermissionOverrideDelete(@Nonnull PermissionOverrideDeleteEvent event) {}
@@ -363,6 +372,11 @@ public void onGuildStickerUpdateTags(@Nonnull GuildStickerUpdateTagsEvent event)
public void onGuildStickerUpdateDescription(@Nonnull GuildStickerUpdateDescriptionEvent event) {}
public void onGuildStickerUpdateAvailable(@Nonnull GuildStickerUpdateAvailableEvent event) {}
+ // Entitlement events
+ public void onEntitlementCreate(@Nonnull EntitlementCreateEvent event) {}
+ public void onEntitlementUpdate(@Nonnull EntitlementUpdateEvent event) {}
+ public void onEntitlementDelete(@Nonnull EntitlementDeleteEvent event) {}
+
// Debug Events
public void onHttpRequest(@Nonnull HttpRequestEvent event) {}
@@ -380,6 +394,7 @@ public void onGenericContextInteraction(@Nonnull GenericContextInteractionEvent<
public void onGenericSelectMenuInteraction(@Nonnull GenericSelectMenuInteractionEvent event) {}
public void onGenericMessage(@Nonnull GenericMessageEvent event) {}
public void onGenericMessageReaction(@Nonnull GenericMessageReactionEvent event) {}
+ public void onGenericMessagePollVote(@Nonnull GenericMessagePollVoteEvent event) {}
public void onGenericUser(@Nonnull GenericUserEvent event) {}
public void onGenericUserPresence(@Nonnull GenericUserPresenceEvent event) {}
public void onGenericUserUpdate(@Nonnull GenericUserUpdateEvent event) {}
@@ -403,6 +418,7 @@ public void onGenericEmoji(@Nonnull GenericEmojiEvent event) {}
public void onGenericEmojiUpdate(@Nonnull GenericEmojiUpdateEvent event) {}
public void onGenericGuildSticker(@Nonnull GenericGuildStickerEvent event) {}
public void onGenericGuildStickerUpdate(@Nonnull GenericGuildStickerUpdateEvent event) {}
+ public void onGenericEntitlement(@Nonnull GenericEntitlementEvent event) {}
public void onGenericPermissionOverride(@Nonnull GenericPermissionOverrideEvent event) {}
public void onGenericScheduledEventUpdate(@Nonnull GenericScheduledEventUpdateEvent event) {}
public void onGenericScheduledEventGateway(@Nonnull GenericScheduledEventGatewayEvent event) {}
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/DiscordLocale.java b/src/main/java/net/dv8tion/jda/api/interactions/DiscordLocale.java
index e03e7afa1d8..1c3426a83e3 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/DiscordLocale.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/DiscordLocale.java
@@ -43,6 +43,7 @@ public enum DiscordLocale
GREEK ("el", "Greek", "Ελληνικά"),
HINDI ("hi", "Hindi", "हिन्दी"),
HUNGARIAN ("hu", "Hungarian", "Magyar"),
+ INDONESIAN ("id", "Indonesian", "Bahasa Indonesia"),
ITALIAN ("it", "Italian", "Italiano"),
JAPANESE ("ja", "Japanese", "日本語"),
KOREAN ("ko", "Korean", "한국어"),
@@ -53,6 +54,7 @@ public enum DiscordLocale
ROMANIAN_ROMANIA ("ro", "Romanian, Romania", "Română"),
RUSSIAN ("ru", "Russian", "Pусский"),
SPANISH ("es-ES", "Spanish", "Español"),
+ SPANISH_LATAM ("es-419", "Spanish, LATAM", "Español, LATAM"),
SWEDISH ("sv-SE", "Swedish", "Svenska"),
THAI ("th", "Thai", "ไทย"),
TURKISH ("tr", "Turkish", "Türkçe"),
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/Interaction.java b/src/main/java/net/dv8tion/jda/api/interactions/Interaction.java
index 19f773c4341..32fbb0d5452 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/Interaction.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/Interaction.java
@@ -17,10 +17,7 @@
package net.dv8tion.jda.api.interactions;
import net.dv8tion.jda.api.JDA;
-import net.dv8tion.jda.api.entities.Guild;
-import net.dv8tion.jda.api.entities.ISnowflake;
-import net.dv8tion.jda.api.entities.Member;
-import net.dv8tion.jda.api.entities.User;
+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.middleman.GuildChannel;
@@ -33,6 +30,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.util.List;
/**
* Abstract representation for any kind of Discord interaction.
@@ -50,6 +48,8 @@
*
Which supports choice suggestions for auto-complete interactions via {@link IAutoCompleteCallback#replyChoices(Command.Choice...)}
* {@link IModalCallback}
*
Which supports replying using a {@link Modal} via {@link IModalCallback#replyModal(Modal)}
+ * {@link IPremiumRequiredReplyCallback}
+ *
Which will reply stating that an {@link Entitlement Entitlement} is required
*
*
* Once the interaction is acknowledged, you can not reply with these methods again. If the interaction is a {@link IDeferrableCallback deferrable},
@@ -231,6 +231,15 @@ default DiscordLocale getGuildLocale()
return getGuild().getLocale();
}
+ /**
+ * Returns the list of {@link Entitlement entitlements} for the current guild and user.
+ *
If this interaction is not from a guild, it will only contain entitlements of the user.
+ *
+ * @return The {@link List List} of {@link Entitlement Entitlement}
+ */
+ @Nonnull
+ List getEntitlements();
+
/**
* Returns the {@link net.dv8tion.jda.api.JDA JDA} instance of this interaction
*
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/callbacks/IPremiumRequiredReplyCallback.java b/src/main/java/net/dv8tion/jda/api/interactions/callbacks/IPremiumRequiredReplyCallback.java
new file mode 100644
index 00000000000..a25b923c945
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/interactions/callbacks/IPremiumRequiredReplyCallback.java
@@ -0,0 +1,37 @@
+/*
+ * 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.interactions.callbacks;
+
+import net.dv8tion.jda.api.requests.restaction.interactions.PremiumRequiredCallbackAction;
+import net.dv8tion.jda.api.entities.Entitlement;
+
+import javax.annotation.CheckReturnValue;
+import javax.annotation.Nonnull;
+
+/**
+ * Replies with an in-built client message stating that an {@link Entitlement Entitlement} is required.
+ *
+ * Replying with {@link #replyWithPremiumRequired()} will automatically acknowledge this interaction.
+ *
+ *
Note:This interaction requires monetization to be enabled.
+ */
+public interface IPremiumRequiredReplyCallback extends IDeferrableCallback
+{
+ @Nonnull
+ @CheckReturnValue
+ PremiumRequiredCallbackAction replyWithPremiumRequired();
+}
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/callbacks/IReplyCallback.java b/src/main/java/net/dv8tion/jda/api/interactions/callbacks/IReplyCallback.java
index 2988de9137c..dbaca131ea2 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/callbacks/IReplyCallback.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/callbacks/IReplyCallback.java
@@ -26,6 +26,8 @@
import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction;
import net.dv8tion.jda.api.utils.FileUpload;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
+import net.dv8tion.jda.api.utils.messages.MessagePollBuilder;
+import net.dv8tion.jda.api.utils.messages.MessagePollData;
import net.dv8tion.jda.internal.requests.restaction.interactions.ReplyCallbackActionImpl;
import net.dv8tion.jda.internal.utils.Checks;
@@ -146,6 +148,53 @@ default ReplyCallbackAction reply(@Nonnull MessageCreateData message)
return action.applyData(message);
}
+ /**
+ * Reply to this interaction and acknowledge it.
+ *
This will send a reply message for this interaction.
+ * You can use {@link ReplyCallbackAction#setEphemeral(boolean) setEphemeral(true)} to only let the target user see the message.
+ * Replies are non-ephemeral by default.
+ *
+ *
You only have 3 seconds to acknowledge an interaction!
+ *
When the acknowledgement is sent after the interaction expired, you will receive {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION ErrorResponse.UNKNOWN_INTERACTION}.
+ *
If your handling can take longer than 3 seconds, due to various rate limits or other conditions, you should use {@link #deferReply()} instead.
+ *
+ *
Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include:
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_INTERACTION UNKNOWN_INTERACTION}
+ *
If the interaction has already been acknowledged or timed out
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD}
+ *
If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER}
+ *
If this message was blocked by the harmful link filter
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_INVALID_CHANNEL_TYPE POLL_INVALID_CHANNEL_TYPE}
+ *
This channel does not allow polls
+ *
+ * - {@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_WITH_UNUSABLE_EMOJI POLL_WITH_UNUSABLE_EMOJI}
+ *
This poll uses an external emoji that the bot is not allowed to use
+ *
+ *
+ * @param poll
+ * The {@link MessagePollData} to send
+ *
+ * @throws IllegalArgumentException
+ * If null is provided
+ *
+ * @return {@link ReplyCallbackAction}
+ *
+ * @see net.dv8tion.jda.api.utils.messages.MessageCreateBuilder MessageCreateBuilder
+ * @see MessagePollBuilder
+ */
+ @Nonnull
+ @CheckReturnValue
+ default ReplyCallbackAction replyPoll(@Nonnull MessagePollData poll)
+ {
+ Checks.notNull(poll, "Message Poll");
+ return deferReply().setPoll(poll);
+ }
+
/**
* Reply to this interaction and acknowledge it.
*
This will send a reply message for this interaction.
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/CommandInteraction.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/CommandInteraction.java
index 73125956428..d0a87b1ed5f 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/commands/CommandInteraction.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/CommandInteraction.java
@@ -17,6 +17,7 @@
package net.dv8tion.jda.api.interactions.commands;
import net.dv8tion.jda.api.interactions.callbacks.IModalCallback;
+import net.dv8tion.jda.api.interactions.callbacks.IPremiumRequiredReplyCallback;
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
import net.dv8tion.jda.api.interactions.commands.context.ContextInteraction;
@@ -28,6 +29,6 @@
* @see ContextInteraction
* @see SlashCommandInteraction
*/
-public interface CommandInteraction extends IReplyCallback, CommandInteractionPayload, IModalCallback
+public interface CommandInteraction extends IReplyCallback, CommandInteractionPayload, IModalCallback, IPremiumRequiredReplyCallback
{
}
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/CommandInteractionPayload.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/CommandInteractionPayload.java
index 36103c8c72d..35b31f8454f 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/commands/CommandInteractionPayload.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/CommandInteractionPayload.java
@@ -149,6 +149,20 @@ default String getCommandString()
for (OptionMapping o : getOptions())
{
builder.append(" ").append(o.getName()).append(": ");
+ // Discord doesn't send the resolved entities on autocomplete interactions
+ if (this instanceof CommandAutoCompleteInteraction)
+ {
+ switch (o.getType())
+ {
+ case CHANNEL:
+ case USER:
+ case ROLE:
+ case MENTIONABLE:
+ builder.append(o.getAsLong());
+ continue;
+ }
+ }
+
switch (o.getType())
{
case CHANNEL:
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandData.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandData.java
index 60cfc3a3d27..4fee17fb19d 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandData.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandData.java
@@ -67,14 +67,13 @@ public SubcommandData(@Nonnull String name, @Nonnull String description)
protected void checkName(@Nonnull String name)
{
Checks.inRange(name, 1, 32, "Name");
- Checks.matches(name, Checks.ALPHANUMERIC_WITH_DASH, "Name");
Checks.isLowercase(name, "Name");
+ Checks.matches(name, Checks.ALPHANUMERIC_WITH_DASH, "Name");
}
protected void checkDescription(@Nonnull String description)
{
- Checks.notEmpty(description, "Description");
- Checks.notLonger(description, 100, "Description");
+ Checks.inRange(description, 1, 100, "Description");
}
/**
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandGroupData.java b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandGroupData.java
index 36d519f9645..7635b92906a 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandGroupData.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/commands/build/SubcommandGroupData.java
@@ -58,12 +58,10 @@ public class SubcommandGroupData implements SerializableData
*/
public SubcommandGroupData(@Nonnull String name, @Nonnull String description)
{
- Checks.notEmpty(name, "Name");
- Checks.notEmpty(description, "Description");
- Checks.notLonger(name, 32, "Name");
- Checks.notLonger(description, 100, "Description");
- Checks.matches(name, Checks.ALPHANUMERIC_WITH_DASH, "Name");
+ Checks.inRange(name, 1, 32, "Name");
Checks.isLowercase(name, "Name");
+ Checks.matches(name, Checks.ALPHANUMERIC_WITH_DASH, "Name");
+ Checks.inRange(description, 1, 100, "Description");
this.name = name;
this.description = description;
}
diff --git a/src/main/java/net/dv8tion/jda/api/interactions/components/ComponentInteraction.java b/src/main/java/net/dv8tion/jda/api/interactions/components/ComponentInteraction.java
index f56895df120..2b2290c2135 100644
--- a/src/main/java/net/dv8tion/jda/api/interactions/components/ComponentInteraction.java
+++ b/src/main/java/net/dv8tion/jda/api/interactions/components/ComponentInteraction.java
@@ -21,6 +21,7 @@
import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion;
import net.dv8tion.jda.api.interactions.callbacks.IMessageEditCallback;
import net.dv8tion.jda.api.interactions.callbacks.IModalCallback;
+import net.dv8tion.jda.api.interactions.callbacks.IPremiumRequiredReplyCallback;
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
import javax.annotation.Nonnull;
@@ -31,7 +32,7 @@
* Instead of {@link #deferReply()} and {@link #reply(String)} you can use {@link #deferEdit()} and {@link #editMessage(String)} with these interactions!
* You can only acknowledge an interaction once!
*/
-public interface ComponentInteraction extends IReplyCallback, IMessageEditCallback, IModalCallback
+public interface ComponentInteraction extends IReplyCallback, IMessageEditCallback, IModalCallback, IPremiumRequiredReplyCallback
{
/**
* The custom component ID provided to the component when it was originally created.
diff --git a/src/main/java/net/dv8tion/jda/api/managers/AccountManager.java b/src/main/java/net/dv8tion/jda/api/managers/AccountManager.java
index 079f11ab6ce..6c51dd078ec 100644
--- a/src/main/java/net/dv8tion/jda/api/managers/AccountManager.java
+++ b/src/main/java/net/dv8tion/jda/api/managers/AccountManager.java
@@ -50,6 +50,8 @@ public interface AccountManager extends Manager
long NAME = 1;
/** Used to reset the avatar field */
long AVATAR = 1 << 1;
+ /** Used to reset the banner field */
+ long BANNER = 1 << 2;
/**
* The {@link net.dv8tion.jda.api.entities.SelfUser SelfUser} that will be
@@ -137,4 +139,17 @@ public interface AccountManager extends Manager
@Nonnull
@CheckReturnValue
AccountManager setAvatar(@Nullable Icon avatar);
+
+ /**
+ * Sets the banner for the currently logged in account
+ *
+ * @param banner
+ * An {@link net.dv8tion.jda.api.entities.Icon Icon} instance representing
+ * the new banner for the current account, {@code null} to reset the banner to the default banner.
+ *
+ * @return AccountManager for chaining convenience
+ */
+ @Nonnull
+ @CheckReturnValue
+ AccountManager setBanner(@Nullable Icon banner);
}
diff --git a/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java b/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java
index f59c3a5c65e..49de2c34488 100644
--- a/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java
+++ b/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java
@@ -164,6 +164,7 @@ public enum ErrorResponse
SERVER_NOT_ENOUGH_BOOSTS( 50101, "This server needs more boosts to perform this action"),
MIXED_PREMIUM_ROLES_FOR_EMOJI( 50144, "Cannot mix subscription and non subscription roles for an emoji"),
ILLEGAL_EMOJI_CONVERSION( 50145, "Cannot convert between premium emoji and normal emoji"),
+ USER_MUST_BE_VERIFIED( 50178, "The user account must first be verified"),
MFA_NOT_ENABLED( 60003, "MFA auth required but not enabled"),
NO_USER_WITH_TAG_EXISTS( 80004, "No users with DiscordTag exist"),
REACTION_BLOCKED( 90001, "Reaction Blocked"),
@@ -184,6 +185,13 @@ public enum ErrorResponse
MESSAGE_BLOCKED_BY_AUTOMOD( 200000, "Message was blocked by automatic moderation"),
TITLE_BLOCKED_BY_AUTOMOD( 200001, "Title was blocked by automatic moderation"),
MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER( 240000, "Message blocked by harmful links filter"),
+ FAILED_TO_BAN_USERS( 500000, "Failed to ban users"),
+ POLL_VOTING_BLOCKED( 520000, "Poll voting blocked"),
+ POLL_EXPIRED( 520001, "Poll expired"),
+ POLL_INVALID_CHANNEL_TYPE( 520002, "Invalid channel type for poll creation"),
+ CANNOT_UPDATE_POLL_MESSAGE( 520003, "Cannot edit a poll message"),
+ POLL_WITH_UNUSABLE_EMOJI( 520004, "Cannot use an emoji included with the poll"),
+ CANNOT_EXPIRE_MISSING_POLL( 520006, "Cannot expire a non-poll message"),
SERVER_ERROR( 0, "Discord encountered an internal server error! Not good!");
diff --git a/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java b/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java
index 35d31623780..5964f0477cb 100644
--- a/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java
+++ b/src/main/java/net/dv8tion/jda/api/requests/GatewayIntent.java
@@ -178,6 +178,16 @@ public enum GatewayIntent
*/
AUTO_MODERATION_EXECUTION(21),
+ /**
+ * Events for poll votes in {@link net.dv8tion.jda.api.entities.Guild Guilds}.
+ */
+ GUILD_MESSAGE_POLLS(24),
+
+ /**
+ * Events for poll votes in {@link net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel PrivateChannels}.
+ */
+ DIRECT_MESSAGE_POLLS(25),
+
;
/**
diff --git a/src/main/java/net/dv8tion/jda/api/requests/Request.java b/src/main/java/net/dv8tion/jda/api/requests/Request.java
index 468a2df017d..cdb41171117 100644
--- a/src/main/java/net/dv8tion/jda/api/requests/Request.java
+++ b/src/main/java/net/dv8tion/jda/api/requests/Request.java
@@ -138,15 +138,26 @@ public void onFailure(Response response)
{
if (response.code == 429)
{
- onFailure(new RateLimitedException(route, response.retryAfter));
+ onRateLimited(response);
}
else
{
- onFailure(ErrorResponseException.create(
- ErrorResponse.fromJSON(response.optObject().orElse(null)), response));
+ onFailure(createErrorResponseException(response));
}
}
+ public void onRateLimited(Response response)
+ {
+ onFailure(new RateLimitedException(route, response.retryAfter));
+ }
+
+ @Nonnull
+ public ErrorResponseException createErrorResponseException(@Nonnull Response response)
+ {
+ return ErrorResponseException.create(
+ ErrorResponse.fromJSON(response.optObject().orElse(null)), response);
+ }
+
public void onFailure(Throwable failException)
{
if (done)
diff --git a/src/main/java/net/dv8tion/jda/api/requests/Route.java b/src/main/java/net/dv8tion/jda/api/requests/Route.java
index f15462924ca..6c1adb49045 100644
--- a/src/main/java/net/dv8tion/jda/api/requests/Route.java
+++ b/src/main/java/net/dv8tion/jda/api/requests/Route.java
@@ -45,6 +45,7 @@ public static class Applications
public static final Route GET_BOT_APPLICATION = new Route(GET, "oauth2/applications/@me");
public static final Route GET_ROLE_CONNECTION_METADATA = new Route(GET, "applications/{application_id}/role-connections/metadata");
public static final Route UPDATE_ROLE_CONNECTION_METADATA = new Route(PUT, "applications/{application_id}/role-connections/metadata");
+ public static final Route GET_ENTITLEMENTS = new Route(GET, "applications/{application_id}/entitlements");
}
public static class Interactions
@@ -103,6 +104,7 @@ public static class Guilds
public static final Route GET_BAN = new Route(GET, "guilds/{guild_id}/bans/{user_id}");
public static final Route UNBAN = new Route(DELETE, "guilds/{guild_id}/bans/{user_id}");
public static final Route BAN = new Route(PUT, "guilds/{guild_id}/bans/{user_id}");
+ public static final Route BULK_BAN = new Route(POST, "guilds/{guild_id}/bulk-ban");
public static final Route KICK_MEMBER = new Route(DELETE, "guilds/{guild_id}/members/{user_id}");
public static final Route MODIFY_MEMBER = new Route(PATCH, "guilds/{guild_id}/members/{user_id}");
public static final Route ADD_MEMBER = new Route(PUT, "guilds/{guild_id}/members/{user_id}");
@@ -260,6 +262,9 @@ public static class Messages
public static final Route GET_MESSAGE = new Route(GET, "channels/{channel_id}/messages/{message_id}");
public static final Route DELETE_MESSAGES = new Route(POST, "channels/{channel_id}/messages/bulk-delete");
+
+ public static final Route END_POLL = new Route(POST, "channels/{channel_id}/polls/{message_id}/expire");
+ public static final Route GET_POLL_ANSWER_VOTERS = new Route(GET, "channels/{channel_id}/polls/{message_id}/answers/{answer_id}");
}
public static class Invites
diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageCreateAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageCreateAction.java
index b15e92444ed..b60895186df 100644
--- a/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageCreateAction.java
+++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/MessageCreateAction.java
@@ -21,6 +21,7 @@
import net.dv8tion.jda.api.entities.sticker.GuildSticker;
import net.dv8tion.jda.api.entities.sticker.Sticker;
import net.dv8tion.jda.api.entities.sticker.StickerSnowflake;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.requests.FluentRestAction;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageCreateRequest;
@@ -54,14 +55,14 @@ static void setDefaultFailOnInvalidReply(boolean fail)
}
/**
- * Unique string/number used to identify messages using {@link Message#getNonce()} in events.
- *
This can be useful to handle round-trip messages.
+ * Unique string/number used to identify messages using {@link Message#getNonce()} in {@link MessageReceivedEvent}.
*
- * Discord also uses the nonce to dedupe messages for users, but this is not currently supported for bots.
- * However, for future proofing, it is highly recommended to use a unique nonce for each message.
+ *
The nonce can be used for deduping messages and marking them for use with {@link MessageReceivedEvent}.
+ * JDA will automatically generate a unique nonce per message, it is not necessary to do this manually.
*
* @param nonce
- * The nonce string to use
+ * The nonce string to use, must be unique per message.
+ * A unique nonce will be generated automatically if this is null.
*
* @throws IllegalArgumentException
* If the provided nonce is longer than {@value Message#MAX_NONCE_LENGTH} characters
diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/interactions/InteractionCallbackAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/interactions/InteractionCallbackAction.java
index e15775a0043..90d3b045bca 100644
--- a/src/main/java/net/dv8tion/jda/api/requests/restaction/interactions/InteractionCallbackAction.java
+++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/interactions/InteractionCallbackAction.java
@@ -55,6 +55,8 @@ enum ResponseType
COMMAND_AUTOCOMPLETE_CHOICES(8),
/** Respond with a modal */
MODAL(9),
+ /** Respond with the "Premium required" default Discord message for premium App subscriptions **/
+ PREMIUM_REQUIRED(10),
;
private final int raw;
diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/interactions/PremiumRequiredCallbackAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/interactions/PremiumRequiredCallbackAction.java
new file mode 100644
index 00000000000..4d2031865a6
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/interactions/PremiumRequiredCallbackAction.java
@@ -0,0 +1,29 @@
+/*
+ * 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.requests.restaction.interactions;
+
+import net.dv8tion.jda.api.requests.FluentRestAction;
+
+/**
+ * An {@link InteractionCallbackAction} that can be used to send the "Premium required" interaction response.
+ *
+ * @see net.dv8tion.jda.api.interactions.callbacks.IPremiumRequiredReplyCallback
+ */
+public interface PremiumRequiredCallbackAction extends InteractionCallbackAction, FluentRestAction
+{
+
+}
diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/EntitlementPaginationAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/EntitlementPaginationAction.java
new file mode 100644
index 00000000000..e850d0f7f0f
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/EntitlementPaginationAction.java
@@ -0,0 +1,149 @@
+package net.dv8tion.jda.api.requests.restaction.pagination;
+
+import net.dv8tion.jda.api.entities.Entitlement;
+import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.User;
+import net.dv8tion.jda.api.entities.UserSnowflake;
+import net.dv8tion.jda.internal.utils.Checks;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Collection;
+
+/**
+ * {@link PaginationAction PaginationAction} that paginates the application entitlements endpoint.
+ * By default, JDA will include {@link Entitlement Entitlement}s which have ended, that is, {@link Entitlement Entitlement}s which
+ * have gone past their {@link Entitlement#getTimeEnding() timeEnding}. You may use {@link EntitlementPaginationAction#excludeEnded excludeEnded(true)}
+ * to only return {@link Entitlement}s which are still active
+ *
+ *
Limits
+ * Minimum - 1
+ * Maximum - 100
+ *
Default - 100
+ *
+ *
Example
+ *
{@code
+ * //Fetch all entitlements for a given SKU id
+ * public static void fetchEntitlements(JDA api, String skuId, Consumer> callback) {
+ * List entitlements = new ArrayList<>()
+ * EntitlementPaginationAction action = api.retrieveEntitlements().skuIds(skuId).excludeEnded(true)
+ * action.forEachAsync((entitlement) -> {
+ * entitlements.add(entitlement)
+ * return true; //continues to retrieve all entitlements until there are none left to retrieve
+ * }.thenRun(() -> callback.accept(entitlements));
+ * }
+ * }
+ */
+public interface EntitlementPaginationAction extends PaginationAction
+{
+ /**
+ * Filter {@link Entitlement Entitlement}s to retrieve by the given user ID
+ *
+ * @param user
+ * The {@link UserSnowflake UserSnowflake} used to filter or {@code null} to remove user filtering.
+ * This can be a member or user instance of {@link User#fromId(long)}
+ *
+ * @return The current {@link EntitlementPaginationAction EntitlementPaginationAction} for chaining convenience
+ */
+ @Nonnull
+ EntitlementPaginationAction user(@Nullable UserSnowflake user);
+
+ /**
+ * Filters {@link Entitlement Entitlement}s by their SKU id
+ *
+ * @param skuIds
+ * The SKU ids to filter by
+ *
+ * @return The current {@link EntitlementPaginationAction EntitlementPaginationAction} for chaining convenience
+ */
+ @Nonnull
+ EntitlementPaginationAction skuIds(long... skuIds);
+
+ /**
+ * Filters {@link Entitlement Entitlement}s by their SKU id
+ *
+ * @param skuIds
+ * The SKU ids to filter by
+ *
+ * @throws java.lang.IllegalArgumentException
+ * If any of the provided {@code skuIds} are {@code null}, empty or are not a valid snowflake
+ *
+ * @return The current {@link EntitlementPaginationAction EntitlementPaginationAction} for chaining convenience
+ */
+ @Nonnull
+ EntitlementPaginationAction skuIds(@Nonnull String... skuIds);
+
+ /**
+ * Filters {@link Entitlement Entitlement}s by their SKU id
+ *
+ * @param skuIds
+ * The SKU ids to filter by
+ *
+ * @throws java.lang.IllegalArgumentException
+ * If any of the provided {@code skuIds} are {@code null}, empty or invalid snowflakes
+ *
+ * @return The current {@link EntitlementPaginationAction EntitlementPaginationAction} for chaining convenience
+ */
+ @Nonnull
+ EntitlementPaginationAction skuIds(@Nonnull Collection skuIds);
+
+ /**
+ * Filters {@link Entitlement Entitlement}s by a guild id
+ *
+ * @param guildId
+ * The guild id to filter by
+ *
+ * @return The current {@link EntitlementPaginationAction EntitlementPaginationAction} for chaining convenience
+ */
+ @Nonnull
+ EntitlementPaginationAction guild(long guildId);
+
+ /**
+ * Filters {@link Entitlement Entitlement}s by a guild id
+ *
+ * @param guildId
+ * The guild id to filter by
+ *
+ * @throws java.lang.IllegalArgumentException
+ * If the provided {@code guildId} is {@code null}, empty or is not a valid snowflake
+ *
+ * @return The current {@link EntitlementPaginationAction EntitlementPaginationAction} for chaining convenience
+ */
+ @Nonnull
+ default EntitlementPaginationAction guild(@Nonnull String guildId)
+ {
+ Checks.notNull(guildId, "guildId");
+ Checks.isSnowflake(guildId, "guildId");
+ return guild(Long.parseUnsignedLong(guildId));
+ }
+
+ /**
+ * Filters {@link Entitlement Entitlement}s by a {@link Guild Guild}
+ *
+ * @param guild
+ * The {@link Guild Guild} to filter by
+ *
+ * @throws java.lang.IllegalArgumentException
+ * If the provided {@code guild} is {@code null}
+ *
+ * @return The current {@link EntitlementPaginationAction EntitlementPaginationAction} for chaining convenience
+ */
+ @Nonnull
+ default EntitlementPaginationAction guild(@Nonnull Guild guild)
+ {
+ Checks.notNull(guild, "guild");
+ return guild(guild.getIdLong());
+ }
+
+ /**
+ * Whether to exclude subscriptions which have gone past their end date.
+ * Test entitlements which are created through the API do not have an end date.
+ *
+ * @param excludeEnded
+ * Whether to exclude ended subscriptions from returned {@link Entitlement Entitlement}s
+ *
+ * @return The current {@link EntitlementPaginationAction EntitlementPaginationAction} for chaining convenience
+ */
+ @Nonnull
+ EntitlementPaginationAction excludeEnded(boolean excludeEnded);
+}
diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/PollVotersPaginationAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/PollVotersPaginationAction.java
new file mode 100644
index 00000000000..79d0aa28abd
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/pagination/PollVotersPaginationAction.java
@@ -0,0 +1,31 @@
+/*
+ * 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.requests.restaction.pagination;
+
+import net.dv8tion.jda.api.entities.User;
+
+/**
+ * {@link PaginationAction PaginationAction} that paginates the votes for a poll answer.
+ *
+ *
Limits
+ * Minimum - 1
+ * Maximum - 1000
+ *
Default - 1000
+ */
+public interface PollVotersPaginationAction extends PaginationAction
+{
+}
diff --git a/src/main/java/net/dv8tion/jda/api/sharding/ShardManager.java b/src/main/java/net/dv8tion/jda/api/sharding/ShardManager.java
index d14eb62dd67..0f1bce911af 100644
--- a/src/main/java/net/dv8tion/jda/api/sharding/ShardManager.java
+++ b/src/main/java/net/dv8tion/jda/api/sharding/ShardManager.java
@@ -60,7 +60,7 @@
* @since 3.4
* @author Aljoscha Grebe
*/
-public interface ShardManager extends IGuildChannelContainer
+public interface ShardManager extends IGuildChannelContainer
{
/**
* Adds all provided listeners to the event-listeners that will be used to handle events.
@@ -969,7 +969,7 @@ default List getUsers()
/**
* Restarts all shards, shutting old ones down first.
- *
+ *
* As all shards need to connect to discord again this will take equally long as the startup of a new ShardManager
* (using the 5000ms + backoff as delay between starting new JDA instances).
*
diff --git a/src/main/java/net/dv8tion/jda/api/utils/data/DataArray.java b/src/main/java/net/dv8tion/jda/api/utils/data/DataArray.java
index 6c2bb1a0adc..bd8ab473e52 100644
--- a/src/main/java/net/dv8tion/jda/api/utils/data/DataArray.java
+++ b/src/main/java/net/dv8tion/jda/api/utils/data/DataArray.java
@@ -17,9 +17,9 @@
package net.dv8tion.jda.api.utils.data;
import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.CollectionType;
import net.dv8tion.jda.api.exceptions.ParsingException;
@@ -218,7 +218,7 @@ public static DataArray fromETF(@Nonnull byte[] data)
*/
public boolean isNull(int index)
{
- return data.get(index) == null;
+ return index >= length() || data.get(index) == null;
}
/**
@@ -782,12 +782,12 @@ public String toString()
@Nonnull
public String toPrettyString()
{
- DefaultPrettyPrinter.Indenter indent = new DefaultIndenter(" ", DefaultIndenter.SYS_LF);
- DefaultPrettyPrinter printer = new DefaultPrettyPrinter();
- printer.withObjectIndenter(indent).withArrayIndenter(indent);
try
{
- return mapper.writer(printer).writeValueAsString(data);
+ return mapper.writer(new DefaultPrettyPrinter())
+ .with(SerializationFeature.INDENT_OUTPUT)
+ .with(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
+ .writeValueAsString(data);
}
catch (JsonProcessingException e)
{
@@ -820,7 +820,9 @@ private T get(@Nonnull Class type, int index)
@Nullable
private T get(@Nonnull Class type, int index, @Nullable Function stringMapper, @Nullable Function numberMapper)
{
- Object value = data.get(index);
+ if (index < 0)
+ throw new IndexOutOfBoundsException("Index out of range: " + index);
+ Object value = index < data.size() ? data.get(index) : null;
if (value == null)
return null;
if (type.isInstance(value))
@@ -857,4 +859,21 @@ public DataArray toDataArray()
{
return this;
}
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ return true;
+ if (!(o instanceof DataArray))
+ return false;
+ DataArray objects = (DataArray) o;
+ return Objects.equals(data, objects.data);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(data);
+ }
}
diff --git a/src/main/java/net/dv8tion/jda/api/utils/data/DataObject.java b/src/main/java/net/dv8tion/jda/api/utils/data/DataObject.java
index fed87b68d9b..a935ee64b40 100644
--- a/src/main/java/net/dv8tion/jda/api/utils/data/DataObject.java
+++ b/src/main/java/net/dv8tion/jda/api/utils/data/DataObject.java
@@ -16,10 +16,14 @@
package net.dv8tion.jda.api.utils.data;
+import com.fasterxml.jackson.core.FormatFeature;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.json.JsonWriteFeature;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
+import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.MapType;
import net.dv8tion.jda.api.exceptions.ParsingException;
@@ -850,12 +854,12 @@ public String toString()
@Nonnull
public String toPrettyString()
{
- DefaultPrettyPrinter.Indenter indent = new DefaultIndenter(" ", DefaultIndenter.SYS_LF);
- DefaultPrettyPrinter printer = new DefaultPrettyPrinter();
- printer.withObjectIndenter(indent).withArrayIndenter(indent);
try
{
- return mapper.writer(printer).writeValueAsString(data);
+ return mapper.writer(new DefaultPrettyPrinter())
+ .with(SerializationFeature.INDENT_OUTPUT)
+ .with(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
+ .writeValueAsString(data);
}
catch (JsonProcessingException e)
{
diff --git a/src/main/java/net/dv8tion/jda/api/utils/data/etf/ExTermDecoder.java b/src/main/java/net/dv8tion/jda/api/utils/data/etf/ExTermDecoder.java
index de0a7469ff8..4500b8cdb26 100644
--- a/src/main/java/net/dv8tion/jda/api/utils/data/etf/ExTermDecoder.java
+++ b/src/main/java/net/dv8tion/jda/api/utils/data/etf/ExTermDecoder.java
@@ -278,7 +278,8 @@ private static Map unpackMap0(ByteBuffer buffer)
int arity = buffer.getInt();
while (arity-- > 0)
{
- String key = (String) unpack0(buffer);
+ Object rawKey = unpack0(buffer);
+ String key = String.valueOf(rawKey);
Object value = unpack0(buffer);
map.put(key, value);
}
diff --git a/src/main/java/net/dv8tion/jda/api/utils/data/etf/ExTermEncoder.java b/src/main/java/net/dv8tion/jda/api/utils/data/etf/ExTermEncoder.java
index cfb550804be..ceb8d10645b 100644
--- a/src/main/java/net/dv8tion/jda/api/utils/data/etf/ExTermEncoder.java
+++ b/src/main/java/net/dv8tion/jda/api/utils/data/etf/ExTermEncoder.java
@@ -88,16 +88,15 @@ private static ByteBuffer pack(ByteBuffer buffer, Object value)
if (value instanceof Byte)
return packSmallInt(buffer, (byte) value);
if (value instanceof Integer || value instanceof Short)
- return packInt(buffer, (int) value);
+ return packInt(buffer, ((Number) value).intValue());
if (value instanceof Long)
return packLong(buffer, (long) value);
if (value instanceof Float || value instanceof Double)
- return packFloat(buffer, (double) value);
+ return packFloat(buffer, ((Number) value).doubleValue());
if (value instanceof Boolean)
return packAtom(buffer, String.valueOf(value));
if (value == null)
return packAtom(buffer, "nil");
- // imagine we had templates :O
if (value instanceof long[])
return packArray(buffer, (long[]) value);
if (value instanceof int[])
diff --git a/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateBuilder.java b/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateBuilder.java
index 44690c659be..afa82c5858a 100644
--- a/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateBuilder.java
+++ b/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateBuilder.java
@@ -59,6 +59,7 @@
public class MessageCreateBuilder extends AbstractMessageBuilder implements MessageCreateRequest
{
private final List files = new ArrayList<>(10);
+ private MessagePollData poll;
private boolean tts;
public MessageCreateBuilder() {}
@@ -191,6 +192,21 @@ public List getAttachments()
return Collections.unmodifiableList(files);
}
+ @Nullable
+ @Override
+ public MessagePollData getPoll()
+ {
+ return poll;
+ }
+
+ @Nonnull
+ @Override
+ public MessageCreateBuilder setPoll(@Nullable MessagePollData poll)
+ {
+ this.poll = poll;
+ return this;
+ }
+
@Nonnull
@Override
public MessageCreateBuilder addFiles(@Nonnull Collection extends FileUpload> files)
@@ -222,7 +238,7 @@ public MessageCreateBuilder setSuppressedNotifications(boolean suppressed)
@Override
public boolean isEmpty()
{
- return Helpers.isBlank(content) && embeds.isEmpty() && files.isEmpty() && components.isEmpty();
+ return Helpers.isBlank(content) && embeds.isEmpty() && files.isEmpty() && components.isEmpty() && poll == null;
}
@Override
@@ -243,8 +259,8 @@ public MessageCreateData build()
List components = new ArrayList<>(this.components);
AllowedMentionsData mentions = this.mentions.copy();
- if (content.isEmpty() && embeds.isEmpty() && files.isEmpty() && components.isEmpty())
- throw new IllegalStateException("Cannot build an empty message. You need at least one of content, embeds, components, or files");
+ if (content.isEmpty() && embeds.isEmpty() && files.isEmpty() && components.isEmpty() && poll == null)
+ throw new IllegalStateException("Cannot build an empty message. You need at least one of content, embeds, components, poll, or files");
int length = Helpers.codePointLength(content);
if (length > Message.MAX_CONTENT_LENGTH)
@@ -255,7 +271,7 @@ public MessageCreateData build()
if (components.size() > Message.MAX_COMPONENT_COUNT)
throw new IllegalStateException("Cannot build message with over " + Message.MAX_COMPONENT_COUNT + " component layouts, provided " + components.size());
- return new MessageCreateData(content, embeds, files, components, mentions, tts, messageFlags);
+ return new MessageCreateData(content, embeds, files, components, mentions, poll, tts, messageFlags);
}
@Nonnull
diff --git a/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateData.java b/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateData.java
index 77e5690ef08..43110c8c278 100644
--- a/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateData.java
+++ b/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateData.java
@@ -27,6 +27,7 @@
import net.dv8tion.jda.internal.utils.IOUtil;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import java.util.*;
/**
@@ -44,19 +45,21 @@ public class MessageCreateData implements MessageData, AutoCloseable, Serializab
private final List files;
private final List components;
private final AllowedMentionsData mentions;
+ private final MessagePollData poll;
private final boolean tts;
private final int flags;
protected MessageCreateData(
String content,
List embeds, List files, List components,
- AllowedMentionsData mentions, boolean tts, int flags)
+ AllowedMentionsData mentions, MessagePollData poll, boolean tts, int flags)
{
this.content = content;
this.embeds = Collections.unmodifiableList(embeds);
this.files = Collections.unmodifiableList(files);
this.components = Collections.unmodifiableList(components);
this.mentions = mentions;
+ this.poll = poll;
this.tts = tts;
this.flags = flags;
}
@@ -237,6 +240,17 @@ public List extends FileUpload> getAttachments()
return getFiles();
}
+ /**
+ * The poll to send with the message
+ *
+ * @return The poll, or null if no poll is sent
+ */
+ @Nullable
+ public MessagePollData getPoll()
+ {
+ return poll;
+ }
+
@Override
public boolean isSuppressEmbeds()
{
@@ -316,6 +330,7 @@ public DataObject toData()
{
DataObject json = DataObject.empty();
json.put("content", content);
+ json.put("poll", poll);
json.put("embeds", DataArray.fromCollection(embeds));
json.put("components", DataArray.fromCollection(components));
json.put("tts", tts);
diff --git a/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateRequest.java b/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateRequest.java
index 5f5d54f7d7b..19c8ee9044e 100644
--- a/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateRequest.java
+++ b/src/main/java/net/dv8tion/jda/api/utils/messages/MessageCreateRequest.java
@@ -27,6 +27,7 @@
import net.dv8tion.jda.internal.utils.Checks;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
@@ -297,6 +298,27 @@ default R addFiles(@Nonnull FileUpload... files)
@Override
List getAttachments();
+ /**
+ * The poll attached to this message
+ *
+ * @return The attached poll, or null if no poll is present
+ */
+ @Nullable
+ MessagePollData getPoll();
+
+ /**
+ * Add a poll to this message.
+ *
+ * @param poll
+ * The poll to send
+ *
+ * @return The same instance for chaining
+ *
+ * @see MessagePollBuilder
+ */
+ @Nonnull
+ R setPoll(@Nullable MessagePollData poll);
+
/**
* Whether the message should use Text-to-Speech (TTS).
*
diff --git a/src/main/java/net/dv8tion/jda/api/utils/messages/MessagePollBuilder.java b/src/main/java/net/dv8tion/jda/api/utils/messages/MessagePollBuilder.java
new file mode 100644
index 00000000000..14b4d3641c4
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/utils/messages/MessagePollBuilder.java
@@ -0,0 +1,228 @@
+/*
+ * 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.utils.messages;
+
+import net.dv8tion.jda.api.entities.emoji.Emoji;
+import net.dv8tion.jda.api.entities.emoji.EmojiUnion;
+import net.dv8tion.jda.api.entities.messages.MessagePoll;
+import net.dv8tion.jda.internal.utils.Checks;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Builder for {@link MessagePollData}
+ *
+ * @see MessageCreateBuilder#setPoll(MessagePollData)
+ */
+public class MessagePollBuilder
+{
+ private final List answers = new ArrayList<>(MessagePoll.MAX_ANSWERS);
+ private MessagePoll.LayoutType layout = MessagePoll.LayoutType.DEFAULT;
+ private String title;
+ private Duration duration = Duration.ofHours(24);
+ private boolean isMultiAnswer;
+
+ /**
+ * Create a new builder instance
+ *
+ * @param title
+ * The poll title (up to {@link MessagePoll#MAX_QUESTION_TEXT_LENGTH} characters)
+ *
+ * @throws IllegalArgumentException
+ * If the title is blank or longer than {@link MessagePoll#MAX_QUESTION_TEXT_LENGTH} characters
+ */
+ public MessagePollBuilder(@Nonnull String title)
+ {
+ this.setTitle(title);
+ }
+
+ /**
+ * They poll layout.
+ *
+ * @param layout
+ * The layout
+ *
+ * @throws IllegalArgumentException
+ * If null or {@link net.dv8tion.jda.api.entities.messages.MessagePoll.LayoutType#UNKNOWN UNKNOWN} is provided
+ *
+ * @return The updated builder
+ */
+ @Nonnull
+ public MessagePollBuilder setLayout(@Nonnull MessagePoll.LayoutType layout)
+ {
+ Checks.notNull(layout, "Layout");
+ Checks.check(layout != MessagePoll.LayoutType.UNKNOWN, "Layout cannot be UNKNOWN");
+
+ this.layout = layout;
+ return this;
+ }
+
+ /**
+ * Change the title for this poll.
+ *
+ * @param title
+ * The poll title (up to {@link MessagePoll#MAX_QUESTION_TEXT_LENGTH} characters)
+ *
+ * @throws IllegalArgumentException
+ * If the title is blank or longer than {@link MessagePoll#MAX_QUESTION_TEXT_LENGTH} characters
+ *
+ * @return The updated builder
+ */
+ @Nonnull
+ public MessagePollBuilder setTitle(@Nonnull String title)
+ {
+ Checks.notBlank(title, "Title");
+ title = title.trim();
+ Checks.notLonger(title, MessagePoll.MAX_QUESTION_TEXT_LENGTH, "Title");
+
+ this.title = title;
+ return this;
+ }
+
+ /**
+ * Change the duration for this poll.
+ *
Default: {@code 1} day
+ *
+ * The poll will automatically expire after this duration.
+ *
+ * @param duration
+ * The duration of this poll (in hours resolution)
+ *
+ * @throws IllegalArgumentException
+ * If the duration is null, less than 1 hour, or longer than {@value MessagePoll#MAX_DURATION_HOURS} hours (7 days)
+ *
+ * @return The updated builder
+ */
+ @Nonnull
+ public MessagePollBuilder setDuration(@Nonnull Duration duration)
+ {
+ Checks.notNull(duration, "Duration");
+ Checks.positive(duration.toHours(), "Duration");
+ Checks.notLonger(duration, Duration.ofHours(MessagePoll.MAX_DURATION_HOURS), TimeUnit.HOURS, "Duration");
+
+ this.duration = duration;
+ return this;
+ }
+
+ /**
+ * Change the duration for this poll.
+ *
Default: {@code 1} day
+ *
+ *
The poll will automatically expire after this duration.
+ *
+ * @param duration
+ * The duration of this poll (in hours resolution)
+ * @param unit
+ * The time unit for the duration
+ *
+ * @throws IllegalArgumentException
+ * If the time unit is null or the duration is not between 1 and {@value MessagePoll#MAX_DURATION_HOURS} hours (7 days) long
+ *
+ * @return The updated builder
+ */
+ @Nonnull
+ public MessagePollBuilder setDuration(long duration, @Nonnull TimeUnit unit)
+ {
+ Checks.notNull(unit, "TimeUnit");
+ return setDuration(Duration.ofHours(unit.toHours(duration)));
+ }
+
+ /**
+ * Whether this poll allows selecting multiple answers.
+ *
Default: {@code false}
+ *
+ * @param multiAnswer
+ * True, if this poll should allow multiple answers
+ *
+ * @return The updated builder
+ */
+ @Nonnull
+ public MessagePollBuilder setMultiAnswer(boolean multiAnswer)
+ {
+ isMultiAnswer = multiAnswer;
+ return this;
+ }
+
+ /**
+ * Add an answer to this poll.
+ *
+ * @param title
+ * The answer title
+ *
+ * @throws IllegalArgumentException
+ * If the title is null, blank, or longer than {@value MessagePoll#MAX_ANSWER_TEXT_LENGTH} characters
+ *
+ * @return The updated builder
+ */
+ @Nonnull
+ public MessagePollBuilder addAnswer(@Nonnull String title)
+ {
+ return addAnswer(title, null);
+ }
+
+ /**
+ * Add an answer to this poll.
+ *
+ * @param title
+ * The answer title
+ * @param emoji
+ * Optional emoji to show next to the answer text
+ *
+ * @throws IllegalArgumentException
+ * If the title is null, blank, or longer than {@value MessagePoll#MAX_ANSWER_TEXT_LENGTH} characters
+ *
+ * @return The updated builder
+ */
+ @Nonnull
+ public MessagePollBuilder addAnswer(@Nonnull String title, @Nullable Emoji emoji)
+ {
+ Checks.notBlank(title, "Answer title");
+ title = title.trim();
+ Checks.notLonger(title, MessagePoll.MAX_ANSWER_TEXT_LENGTH, "Answer title");
+ Checks.check(this.answers.size() < MessagePoll.MAX_ANSWERS, "Poll cannot have more than %d answers", MessagePoll.MAX_ANSWERS);
+
+ this.answers.add(new MessagePoll.Answer(this.answers.size() + 1, title, (EmojiUnion) emoji, 0, false));
+ return this;
+ }
+
+ /**
+ * Build the poll data.
+ *
+ * @throws IllegalStateException
+ * If no answers have been added to the builder
+ *
+ * @return {@link MessagePollData}
+ */
+ @Nonnull
+ public MessagePollData build()
+ {
+ if (answers.isEmpty())
+ throw new IllegalStateException("Cannot build a poll without answers");
+ return new MessagePollData(
+ layout,
+ new MessagePoll.Question(title, null),
+ new ArrayList<>(answers),
+ duration,
+ isMultiAnswer
+ );
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/api/utils/messages/MessagePollData.java b/src/main/java/net/dv8tion/jda/api/utils/messages/MessagePollData.java
new file mode 100644
index 00000000000..4d71a28c2f4
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/api/utils/messages/MessagePollData.java
@@ -0,0 +1,106 @@
+/*
+ * 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.utils.messages;
+
+import net.dv8tion.jda.api.entities.messages.MessagePoll;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.api.utils.data.SerializableData;
+import net.dv8tion.jda.internal.utils.Helpers;
+import org.jetbrains.annotations.NotNull;
+
+import javax.annotation.Nonnull;
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A poll that can be attached to a {@link MessageCreateRequest}.
+ *
+ *
Example
+ *
{@code
+ * channel.sendMessage("Hello guys! Check my poll:")
+ * .setPoll(
+ * MessagePollData.builder("Which programming language is better?")
+ * .addAnswer("Java", Emoji.fromFormatted("<:java:1006323566314274856>"))
+ * .addAnswer("Kotlin", Emoji.fromFormatted("<:kotlin:295940257797636096>"))
+ * .build())
+ * .queue()
+ * }
+ *
+ * @see #builder(String)
+ * @see MessageCreateBuilder#setPoll(MessagePollData)
+ */
+public class MessagePollData implements SerializableData
+{
+ private final MessagePoll.LayoutType layout;
+ private final MessagePoll.Question question;
+ private final List answers;
+ private final Duration duration;
+ private final boolean isMultiAnswer;
+
+ public MessagePollData(MessagePoll.LayoutType layout, MessagePoll.Question question, List answers, Duration duration, boolean isMultiAnswer)
+ {
+ this.layout = layout;
+ this.question = question;
+ this.answers = answers;
+ this.duration = duration;
+ this.isMultiAnswer = isMultiAnswer;
+ }
+
+ /**
+ * Creates a new {@link MessagePollBuilder}.
+ *
+ * A poll must have at least one answer.
+ *
+ * @param title
+ * The poll title (up to {@value MessagePoll#MAX_QUESTION_TEXT_LENGTH} characters)
+ *
+ * @throws IllegalArgumentException
+ * If the title is blank or longer than {@value MessagePoll#MAX_QUESTION_TEXT_LENGTH} characters
+ *
+ * @return {@link MessagePollBuilder}
+ */
+ @Nonnull
+ public static MessagePollBuilder builder(@Nonnull String title)
+ {
+ return new MessagePollBuilder(title);
+ }
+
+ @NotNull
+ @Override
+ public DataObject toData()
+ {
+ DataObject data = DataObject.empty();
+
+ data.put("duration", TimeUnit.SECONDS.toHours(duration.getSeconds()));
+ data.put("allow_multiselect", isMultiAnswer);
+ data.put("layout_type", layout.getKey());
+
+ data.put("question", DataObject.empty()
+ .put("text", question.getText()));
+
+ data.put("answers", answers.stream()
+ .map(answer -> DataObject.empty()
+ .put("answer_id", answer.getId())
+ .put("poll_media", DataObject.empty()
+ .put("text", answer.getText())
+ .put("emoji", answer.getEmoji())))
+ .collect(Helpers.toDataArray()));
+
+ return data;
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java
index f55f9801110..01f84c3a505 100644
--- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java
@@ -52,6 +52,7 @@
import net.dv8tion.jda.api.requests.restaction.CommandCreateAction;
import net.dv8tion.jda.api.requests.restaction.CommandEditAction;
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
+import net.dv8tion.jda.api.requests.restaction.pagination.EntitlementPaginationAction;
import net.dv8tion.jda.api.sharding.ShardManager;
import net.dv8tion.jda.api.utils.*;
import net.dv8tion.jda.api.utils.cache.CacheFlag;
@@ -75,6 +76,7 @@
import net.dv8tion.jda.internal.requests.restaction.CommandEditActionImpl;
import net.dv8tion.jda.internal.requests.restaction.CommandListUpdateActionImpl;
import net.dv8tion.jda.internal.requests.restaction.GuildActionImpl;
+import net.dv8tion.jda.internal.requests.restaction.pagination.EntitlementPaginationActionImpl;
import net.dv8tion.jda.internal.utils.Helpers;
import net.dv8tion.jda.internal.utils.*;
import net.dv8tion.jda.internal.utils.cache.AbstractCacheView;
@@ -1164,6 +1166,13 @@ public RestAction retrieveApplicationInfo()
});
}
+ @Nonnull
+ @Override
+ public EntitlementPaginationAction retrieveEntitlements()
+ {
+ return new EntitlementPaginationActionImpl(this);
+ }
+
@Nonnull
@Override
public JDA setRequiredScopes(@Nonnull Collection scopes)
diff --git a/src/main/java/net/dv8tion/jda/internal/entities/AbstractWebhookClient.java b/src/main/java/net/dv8tion/jda/internal/entities/AbstractWebhookClient.java
index 87d0c8d18bf..386e43c3373 100644
--- a/src/main/java/net/dv8tion/jda/internal/entities/AbstractWebhookClient.java
+++ b/src/main/java/net/dv8tion/jda/internal/entities/AbstractWebhookClient.java
@@ -29,11 +29,11 @@
import net.dv8tion.jda.api.utils.FileUpload;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageEditData;
+import net.dv8tion.jda.api.utils.messages.MessagePollData;
import net.dv8tion.jda.internal.requests.restaction.WebhookMessageCreateActionImpl;
import net.dv8tion.jda.internal.requests.restaction.WebhookMessageDeleteActionImpl;
import net.dv8tion.jda.internal.requests.restaction.WebhookMessageEditActionImpl;
import net.dv8tion.jda.internal.utils.Checks;
-import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.util.Collection;
@@ -91,7 +91,7 @@ public WebhookMessageCreateAction sendMessageEmbeds(@Nonnull Collection ext
@Nonnull
@Override
- public WebhookMessageCreateAction sendMessageComponents(@NotNull Collection extends LayoutComponent> components)
+ public WebhookMessageCreateAction sendMessageComponents(@Nonnull Collection extends LayoutComponent> components)
{
return sendRequest().setComponents(components);
}
@@ -103,6 +103,14 @@ public WebhookMessageCreateAction sendMessage(@Nonnull MessageCreateData mess
return sendRequest().applyData(message);
}
+ @Nonnull
+ @Override
+ public WebhookMessageCreateAction sendMessagePoll(@Nonnull MessagePollData poll)
+ {
+ Checks.notNull(poll, "Message Poll");
+ return sendRequest().setPoll(poll);
+ }
+
@Nonnull
@Override
public WebhookMessageCreateAction sendFiles(@Nonnull Collection extends FileUpload> files)
diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntitlementImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/EntitlementImpl.java
new file mode 100644
index 00000000000..e77d790477d
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/internal/entities/EntitlementImpl.java
@@ -0,0 +1,106 @@
+/*
+ * 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.entities.Entitlement;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.time.OffsetDateTime;
+
+public class EntitlementImpl implements Entitlement
+{
+ private long id;
+ private long skuId;
+ private long applicationId;
+ private long userId;
+ private long guildId;
+ private EntitlementType type;
+ private boolean deleted;
+ private OffsetDateTime startsAt;
+ private OffsetDateTime endsAt;
+
+ public EntitlementImpl(long id, long skuId, long applicationId, long userId, long guildId, EntitlementType type, boolean deleted, @Nullable OffsetDateTime startsAt, @Nullable OffsetDateTime endsAt)
+ {
+ this.id = id;
+ this.skuId = skuId;
+ this.applicationId = applicationId;
+ this.userId = userId;
+ this.guildId = guildId;
+ this.type = type;
+ this.deleted = deleted;
+ this.startsAt = startsAt;
+ this.endsAt = endsAt;
+ }
+
+ @Override
+ public long getIdLong()
+ {
+ return id;
+ }
+
+ @Override
+ public long getSkuIdLong()
+ {
+ return skuId;
+ }
+
+ @Override
+ public long getApplicationIdLong()
+ {
+ return applicationId;
+ }
+
+ @Override
+ public long getUserIdLong()
+ {
+ return userId;
+ }
+
+ @Override
+ public long getGuildIdLong()
+ {
+ return guildId;
+ }
+
+ @Nonnull
+ @Override
+ public EntitlementType getType()
+ {
+ return type;
+ }
+
+ @Override
+ public boolean isDeleted()
+ {
+ return deleted;
+ }
+
+ @Nullable
+ @Override
+ public OffsetDateTime getTimeStarting()
+ {
+ return startsAt;
+ }
+
+ @Nullable
+ @Override
+ public OffsetDateTime getTimeEnding()
+ {
+ return endsAt;
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java
index 6b30eab45b2..a07eeddbb54 100644
--- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java
+++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java
@@ -39,8 +39,10 @@
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.emoji.Emoji;
import net.dv8tion.jda.api.entities.emoji.EmojiUnion;
import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji;
+import net.dv8tion.jda.api.entities.messages.MessagePoll;
import net.dv8tion.jda.api.entities.sticker.*;
import net.dv8tion.jda.api.entities.templates.Template;
import net.dv8tion.jda.api.entities.templates.TemplateChannel;
@@ -65,6 +67,7 @@
import net.dv8tion.jda.internal.entities.emoji.CustomEmojiImpl;
import net.dv8tion.jda.internal.entities.emoji.RichCustomEmojiImpl;
import net.dv8tion.jda.internal.entities.emoji.UnicodeEmojiImpl;
+import net.dv8tion.jda.internal.entities.messages.MessagePollImpl;
import net.dv8tion.jda.internal.entities.sticker.*;
import net.dv8tion.jda.internal.handle.EventCache;
import net.dv8tion.jda.internal.utils.Helpers;
@@ -1836,6 +1839,8 @@ else if (MISSING_CHANNEL.equals(ex.getMessage()))
);
}
+ MessagePoll poll = jsonObject.optObject("poll").map(EntityBuilder::createMessagePoll).orElse(null);
+
// Message Components
List components = Collections.emptyList();
Optional componentsArrayOpt = jsonObject.optArray("components");
@@ -1866,7 +1871,7 @@ else if (MISSING_CHANNEL.equals(ex.getMessage()))
int position = jsonObject.getInt("position", -1);
return new ReceivedMessage(id, channelId, guildId, api, guild, channel, type, messageReference, fromWebhook, applicationId, tts, pinned,
- content, nonce, user, member, activity, editTime, mentions, reactions, attachments, embeds, stickers, components, flags,
+ content, nonce, user, member, activity, poll, editTime, mentions, reactions, attachments, embeds, stickers, components, flags,
messageInteraction, startedThread, position);
}
@@ -1897,6 +1902,47 @@ private static MessageActivity createMessageActivity(DataObject jsonObject)
return new MessageActivity(activityType, partyId, application);
}
+ public static MessagePollImpl createMessagePoll(DataObject data)
+ {
+ MessagePoll.LayoutType layout = MessagePoll.LayoutType.fromKey(data.getInt("layout_type"));
+ OffsetDateTime expiresAt = data.isNull("expiry") ? null : data.getOffsetDateTime("expiry");
+ boolean isMultiAnswer = data.getBoolean("allow_multiselect");
+
+ DataArray answersData = data.getArray("answers");
+ DataObject questionData = data.getObject("question");
+
+ DataObject resultsData = data.optObject("results").orElseGet(
+ () -> DataObject.empty().put("answer_counts", DataArray.empty()) // FIXME: Discord bug
+ );
+ boolean isFinalized = resultsData.getBoolean("is_finalized");
+
+ DataArray resultVotes = resultsData.getArray("answer_counts");
+ TLongObjectMap voteMapping = new TLongObjectHashMap<>();
+ resultVotes.stream(DataArray::getObject)
+ .forEach(votes -> voteMapping.put(votes.getLong("id"), votes));
+
+ MessagePoll.Question question = new MessagePoll.Question(
+ questionData.getString("text"),
+ questionData.optObject("emoji").map(Emoji::fromData).orElse(null));
+
+ List answers = answersData.stream(DataArray::getObject)
+ .map(answer -> {
+ long answerId = answer.getLong("answer_id");
+ DataObject media = answer.getObject("poll_media");
+ DataObject votes = voteMapping.get(answerId);
+ return new MessagePoll.Answer(
+ answerId,
+ media.getString("text"),
+ media.optObject("emoji").map(Emoji::fromData).orElse(null),
+ votes != null ? votes.getInt("count") : 0,
+ votes != null && votes.getBoolean("me_voted")
+ );
+ })
+ .collect(Helpers.toUnmodifiableList());
+
+ return new MessagePollImpl(layout, question, answers, expiresAt, isMultiAnswer, isFinalized);
+ }
+
public MessageReaction createMessageReaction(MessageChannel chan, long channelId, long messageId, DataObject obj)
{
DataObject emoji = obj.getObject("emoji");
@@ -1991,6 +2037,7 @@ public MessageEmbed createMessageEmbed(DataObject content)
{
DataObject obj = content.getObject("video");
video = new VideoInfo(obj.getString("url", null),
+ obj.getString("proxy_url", null),
obj.getInt("width", -1),
obj.getInt("height", -1));
}
@@ -2556,6 +2603,21 @@ public AuditLogChange createAuditLogChange(DataObject change)
return new AuditLogChange(oldValue, newValue, key);
}
+ public Entitlement createEntitlement(DataObject object)
+ {
+ return new EntitlementImpl(
+ object.getUnsignedLong("id"),
+ object.getUnsignedLong("sku_id"),
+ object.getUnsignedLong("application_id"),
+ object.getUnsignedLong("user_id", 0),
+ object.getUnsignedLong("guild_id", 0),
+ Entitlement.EntitlementType.fromKey(object.getInt("type")),
+ object.getBoolean("deleted"),
+ object.getOffsetDateTime("starts_at", null),
+ object.getOffsetDateTime("ends_at", null)
+ );
+ }
+
private Map changeToMap(Set changesList)
{
return changesList.stream().collect(Collectors.toMap(AuditLogChange::getKey, UnaryOperator.identity()));
diff --git a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java
index c13416bf144..9965195cd24 100644
--- a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java
@@ -86,6 +86,7 @@
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.*;
@@ -1545,6 +1546,44 @@ public AuditableRestAction ban(@Nonnull UserSnowflake user, int duration,
return new AuditableRestActionImpl<>(getJDA(), route, params);
}
+ @Nonnull
+ @Override
+ public AuditableRestAction ban(@Nonnull Collection users, @Nullable Duration deletionTime)
+ {
+ deletionTime = deletionTime == null ? Duration.ZERO : deletionTime;
+ Checks.noneNull(users, "Users");
+ Checks.notNegative(deletionTime.getSeconds(), "Deletion timeframe");
+ Checks.check(deletionTime.getSeconds() <= TimeUnit.DAYS.toSeconds(7), "Deletion timeframe must not be larger than 7 days. Provided: %d seconds", deletionTime.getSeconds());
+ Checks.check(users.size() <= 200, "Cannot ban more than 200 users at once");
+ checkPermission(Permission.BAN_MEMBERS);
+ checkPermission(Permission.MANAGE_SERVER);
+
+ for (UserSnowflake user : users)
+ {
+ checkOwner(user.getIdLong(), "ban");
+ checkPosition(user);
+ }
+
+ Set userIds = users.stream().map(UserSnowflake::getIdLong).collect(Collectors.toSet());
+ DataObject body = DataObject.empty()
+ .put("user_ids", userIds)
+ .put("delete_message_seconds", deletionTime.getSeconds());
+ Route.CompiledRoute route = Route.Guilds.BULK_BAN.compile(getId());
+
+ return new AuditableRestActionImpl<>(getJDA(), route, body, (res, req) -> {
+ DataObject responseBody = res.getObject();
+ List bannedUsers = responseBody.getArray("banned_users")
+ .stream(DataArray::getLong)
+ .map(UserSnowflake::fromId)
+ .collect(Collectors.toList());
+ List failedUsers = responseBody.getArray("failed_users")
+ .stream(DataArray::getLong)
+ .map(UserSnowflake::fromId)
+ .collect(Collectors.toList());
+ return new BulkBanResponse(bannedUsers, failedUsers);
+ });
+ }
+
@Nonnull
@Override
public AuditableRestAction unban(@Nonnull UserSnowflake user)
diff --git a/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java b/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java
index 4d5dab6962c..da9816ec95b 100644
--- a/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java
+++ b/src/main/java/net/dv8tion/jda/internal/entities/ReceivedMessage.java
@@ -33,12 +33,14 @@
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;
@@ -51,7 +53,9 @@
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;
@@ -94,6 +98,7 @@ public class ReceivedMessage implements Message
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;
@@ -115,7 +120,7 @@ public class ReceivedMessage implements Message
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, OffsetDateTime editTime,
+ 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)
@@ -148,6 +153,7 @@ public ReceivedMessage(
this.interaction = interaction;
this.startedThread = startedThread;
this.position = position;
+ this.poll = poll;
}
private void checkSystem(String comment)
@@ -610,6 +616,29 @@ public List getComponents()
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()
@@ -774,7 +803,9 @@ public AuditableRestAction delete()
if (isWebhookRequest())
{
Route.CompiledRoute route = Route.Webhooks.EXECUTE_WEBHOOK_DELETE.compile(webhook.getId(), webhook.getToken(), getId());
- return new AuditableRestActionImpl<>(getJDA(), route);
+ final AuditableRestActionImpl action = new AuditableRestActionImpl<>(getJDA(), route);
+ action.setErrorMapper(getUnknownWebhookErrorMapper());
+ return action;
}
SelfUser self = getJDA().getSelfUser();
@@ -808,7 +839,7 @@ public AuditableRestAction suppressEmbeds(boolean suppressed)
Route.CompiledRoute route;
if (isWebhookRequest())
{
- route = Route.Webhooks.EXECUTE_WEBHOOK_DELETE.compile(webhook.getId(), webhook.getToken(), getId());
+ route = Route.Webhooks.EXECUTE_WEBHOOK_EDIT.compile(webhook.getId(), webhook.getToken(), getId());
}
else
{
@@ -839,7 +870,9 @@ public AuditableRestAction suppressEmbeds(boolean suppressed)
newFlags &= ~suppressionValue;
DataObject body = DataObject.empty().put("flags", newFlags);
- return new AuditableRestActionImpl<>(api, route, body);
+ final AuditableRestActionImpl action = new AuditableRestActionImpl<>(api, route, body);
+ action.setErrorMapper(getUnknownWebhookErrorMapper());
+ return action;
}
@Nonnull
@@ -980,8 +1013,27 @@ private boolean isWebhookRequest()
@Nonnull
private MessageEditActionImpl editRequest()
{
- return hasChannel()
+ 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;
+ };
}
}
diff --git a/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModRuleImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModRuleImpl.java
index 9bfbd75cade..2f07ecc677d 100644
--- a/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModRuleImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/entities/automod/AutoModRuleImpl.java
@@ -29,8 +29,8 @@
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.utils.EntityString;
import net.dv8tion.jda.internal.utils.Helpers;
-import org.jetbrains.annotations.NotNull;
+import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
@@ -68,7 +68,7 @@ public long getIdLong()
return id;
}
- @NotNull
+ @Nonnull
@Override
public Guild getGuild()
{
@@ -84,21 +84,21 @@ public long getCreatorIdLong()
return ownerId;
}
- @NotNull
+ @Nonnull
@Override
public String getName()
{
return name;
}
- @NotNull
+ @Nonnull
@Override
public AutoModEventType getEventType()
{
return eventType;
}
- @NotNull
+ @Nonnull
@Override
public AutoModTriggerType getTriggerType()
{
@@ -111,7 +111,7 @@ public boolean isEnabled()
return enabled;
}
- @NotNull
+ @Nonnull
@Override
public List getExemptRoles()
{
@@ -126,7 +126,7 @@ public List getExemptRoles()
return Collections.unmodifiableList(roles);
}
- @NotNull
+ @Nonnull
@Override
public List getExemptChannels()
{
@@ -141,35 +141,35 @@ public List getExemptChannels()
return Collections.unmodifiableList(channels);
}
- @NotNull
+ @Nonnull
@Override
public List getActions()
{
return actions;
}
- @NotNull
+ @Nonnull
@Override
public List getFilteredKeywords()
{
return filteredKeywords;
}
- @NotNull
+ @Nonnull
@Override
public List getFilteredRegex()
{
return filteredRegex;
}
- @NotNull
+ @Nonnull
@Override
public EnumSet getFilteredPresets()
{
return Helpers.copyEnumSet(KeywordPreset.class, filteredPresets);
}
- @NotNull
+ @Nonnull
@Override
public List getAllowlist()
{
diff --git a/src/main/java/net/dv8tion/jda/internal/entities/channel/mixin/middleman/MessageChannelMixin.java b/src/main/java/net/dv8tion/jda/internal/entities/channel/mixin/middleman/MessageChannelMixin.java
index 8f33c5990dd..e2992ae8428 100644
--- a/src/main/java/net/dv8tion/jda/internal/entities/channel/mixin/middleman/MessageChannelMixin.java
+++ b/src/main/java/net/dv8tion/jda/internal/entities/channel/mixin/middleman/MessageChannelMixin.java
@@ -40,7 +40,9 @@
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.MessagePollData;
import net.dv8tion.jda.internal.requests.RestActionImpl;
+import org.jetbrains.annotations.NotNull;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
@@ -155,6 +157,33 @@ default MessageCreateAction sendMessageEmbeds(@Nonnull Collection extends Mess
return MessageChannelUnion.super.sendMessageEmbeds(embeds);
}
+ @NotNull
+ @Override
+ default MessageCreateAction sendMessageComponents(@NotNull LayoutComponent component, @NotNull LayoutComponent... other)
+ {
+ checkCanAccessChannel();
+ checkCanSendMessage();
+ return MessageChannelUnion.super.sendMessageComponents(component, other);
+ }
+
+ @Nonnull
+ @Override
+ default MessageCreateAction sendMessageComponents(@Nonnull Collection extends LayoutComponent> components)
+ {
+ checkCanAccessChannel();
+ checkCanSendMessage();
+ return MessageChannelUnion.super.sendMessageComponents(components);
+ }
+
+ @Nonnull
+ @Override
+ default MessageCreateAction sendMessagePoll(@Nonnull MessagePollData poll)
+ {
+ checkCanAccessChannel();
+ checkCanSendMessage();
+ return MessageChannelUnion.super.sendMessagePoll(poll);
+ }
+
@Nonnull
@CheckReturnValue
default MessageCreateAction sendMessage(@Nonnull MessageCreateData msg)
diff --git a/src/main/java/net/dv8tion/jda/internal/entities/messages/MessagePollImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/messages/MessagePollImpl.java
new file mode 100644
index 00000000000..5614e2b4559
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/internal/entities/messages/MessagePollImpl.java
@@ -0,0 +1,83 @@
+/*
+ * 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.messages;
+
+import net.dv8tion.jda.api.entities.messages.MessagePoll;
+
+import javax.annotation.Nonnull;
+import java.time.OffsetDateTime;
+import java.util.List;
+
+public class MessagePollImpl implements MessagePoll
+{
+ private final LayoutType layout;
+ private final Question question;
+ private final List answers;
+ private final OffsetDateTime expiresAt;
+ private final boolean isMultiAnswer;
+ private final boolean isFinalizedVotes;
+
+ public MessagePollImpl(LayoutType layout, Question question, List answers, OffsetDateTime expiresAt, boolean isMultiAnswer, boolean isFinalizedVotes)
+ {
+ this.layout = layout;
+ this.question = question;
+ this.answers = answers;
+ this.expiresAt = expiresAt;
+ this.isMultiAnswer = isMultiAnswer;
+ this.isFinalizedVotes = isFinalizedVotes;
+ }
+
+ @Nonnull
+ @Override
+ public LayoutType getLayout()
+ {
+ return layout;
+ }
+
+ @Nonnull
+ @Override
+ public Question getQuestion()
+ {
+ return question;
+ }
+
+ @Nonnull
+ @Override
+ public List getAnswers()
+ {
+ return answers;
+ }
+
+ @Nonnull
+ @Override
+ public OffsetDateTime getTimeExpiresAt()
+ {
+ return expiresAt;
+ }
+
+ @Override
+ public boolean isMultiAnswer()
+ {
+ return isMultiAnswer;
+ }
+
+ @Override
+ public boolean isFinalizedVotes()
+ {
+ return isFinalizedVotes;
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/internal/handle/EntitlementCreateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/EntitlementCreateHandler.java
new file mode 100644
index 00000000000..32367ecff4a
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/internal/handle/EntitlementCreateHandler.java
@@ -0,0 +1,36 @@
+/*
+ * 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.handle;
+
+import net.dv8tion.jda.api.events.entitlement.EntitlementCreateEvent;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.internal.JDAImpl;
+
+public class EntitlementCreateHandler extends SocketHandler
+{
+ public EntitlementCreateHandler(JDAImpl api)
+ {
+ super(api);
+ }
+
+ @Override
+ protected Long handleInternally(DataObject content)
+ {
+ getJDA().handleEvent(new EntitlementCreateEvent(getJDA(), responseNumber, getJDA().getEntityBuilder().createEntitlement(content)));
+ return null;
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/internal/handle/EntitlementDeleteHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/EntitlementDeleteHandler.java
new file mode 100644
index 00000000000..22a54e3bbd4
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/internal/handle/EntitlementDeleteHandler.java
@@ -0,0 +1,36 @@
+/*
+ * 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.handle;
+
+import net.dv8tion.jda.api.events.entitlement.EntitlementDeleteEvent;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.internal.JDAImpl;
+
+public class EntitlementDeleteHandler extends SocketHandler
+{
+ public EntitlementDeleteHandler(JDAImpl api)
+ {
+ super(api);
+ }
+
+ @Override
+ protected Long handleInternally(DataObject content)
+ {
+ getJDA().handleEvent(new EntitlementDeleteEvent(getJDA(), responseNumber, getJDA().getEntityBuilder().createEntitlement(content)));
+ return null;
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/internal/handle/EntitlementUpdateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/EntitlementUpdateHandler.java
new file mode 100644
index 00000000000..c2a86177528
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/internal/handle/EntitlementUpdateHandler.java
@@ -0,0 +1,35 @@
+/*
+ * 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.handle;
+import net.dv8tion.jda.api.events.entitlement.EntitlementUpdateEvent;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.internal.JDAImpl;
+
+public class EntitlementUpdateHandler extends SocketHandler
+{
+ public EntitlementUpdateHandler(JDAImpl api)
+ {
+ super(api);
+ }
+
+ @Override
+ protected Long handleInternally(DataObject content)
+ {
+ getJDA().handleEvent(new EntitlementUpdateEvent(getJDA(), responseNumber, getJDA().getEntityBuilder().createEntitlement(content)));
+ return null;
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/internal/handle/MessagePollVoteHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/MessagePollVoteHandler.java
new file mode 100644
index 00000000000..1e38887ce93
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/internal/handle/MessagePollVoteHandler.java
@@ -0,0 +1,84 @@
+/*
+ * 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.handle;
+
+import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
+import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
+import net.dv8tion.jda.api.events.message.poll.MessagePollVoteAddEvent;
+import net.dv8tion.jda.api.events.message.poll.MessagePollVoteRemoveEvent;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.internal.JDAImpl;
+import net.dv8tion.jda.internal.requests.WebSocketClient;
+
+public class MessagePollVoteHandler extends SocketHandler
+{
+ private final boolean add;
+
+ public MessagePollVoteHandler(JDAImpl api, boolean add)
+ {
+ super(api);
+ this.add = add;
+ }
+
+ @Override
+ protected Long handleInternally(DataObject content)
+ {
+ long answerId = content.getLong("answer_id");
+ long userId = content.getUnsignedLong("user_id");
+ long messageId = content.getUnsignedLong("message_id");
+ long channelId = content.getUnsignedLong("channel_id");
+ long guildId = content.getUnsignedLong("guild_id", 0);
+
+ if (api.getGuildSetupController().isLocked(guildId))
+ return guildId;
+
+ Guild guild = api.getGuildById(guildId);
+ MessageChannel channel = api.getChannelById(MessageChannel.class, channelId);
+ if (channel == null)
+ {
+ if (guild != null)
+ {
+ GuildChannel actual = guild.getGuildChannelById(channelId);
+ if (actual != null)
+ {
+ WebSocketClient.LOG.debug("Dropping message poll vote event for unexpected channel of type {}", actual.getType());
+ return null;
+ }
+ }
+
+ if (guildId != 0)
+ {
+ api.getEventCache().cache(EventCache.Type.CHANNEL, channelId, responseNumber, allContent, this::handle);
+ EventCache.LOG.debug("Received a vote for a channel that JDA does not currently have cached");
+ return null;
+ }
+
+ channel = getJDA().getEntityBuilder().createPrivateChannel(
+ DataObject.empty()
+ .put("id", channelId)
+ );
+ }
+
+ if (add)
+ api.handleEvent(new MessagePollVoteAddEvent(channel, responseNumber, messageId, userId, answerId));
+ else
+ api.handleEvent(new MessagePollVoteRemoveEvent(channel, responseNumber, messageId, userId, answerId));
+
+ return null;
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/CommandDataImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/CommandDataImpl.java
index c3bf85f68be..88bc202595a 100644
--- a/src/main/java/net/dv8tion/jda/internal/interactions/CommandDataImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/interactions/CommandDataImpl.java
@@ -87,8 +87,7 @@ public void checkName(@Nonnull String name)
public void checkDescription(@Nonnull String description)
{
checkType(Command.Type.SLASH, "set description");
- Checks.notEmpty(description, "Description");
- Checks.notLonger(description, MAX_DESCRIPTION_LENGTH, "Description");
+ Checks.inRange(description, 1, MAX_DESCRIPTION_LENGTH, "Description");
}
@Nonnull
diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/InteractionImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/InteractionImpl.java
index 9c334a9d380..cce804a8bdd 100644
--- a/src/main/java/net/dv8tion/jda/internal/interactions/InteractionImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/interactions/InteractionImpl.java
@@ -17,6 +17,7 @@
package net.dv8tion.jda.internal.interactions;
import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.Entitlement;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
@@ -26,15 +27,17 @@
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.interactions.DiscordLocale;
import net.dv8tion.jda.api.interactions.Interaction;
+import net.dv8tion.jda.api.utils.data.DataArray;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.JDAImpl;
-import net.dv8tion.jda.internal.entities.GuildImpl;
-import net.dv8tion.jda.internal.entities.MemberImpl;
-import net.dv8tion.jda.internal.entities.UserImpl;
+import net.dv8tion.jda.internal.entities.*;
import net.dv8tion.jda.internal.entities.channel.concrete.PrivateChannelImpl;
+import net.dv8tion.jda.internal.utils.Helpers;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.util.List;
+import java.util.stream.Collectors;
public class InteractionImpl implements Interaction
{
@@ -47,6 +50,7 @@ public class InteractionImpl implements Interaction
protected final User user;
protected final Channel channel;
protected final DiscordLocale userLocale;
+ protected final List entitlements;
protected final JDAImpl api;
//This is used to give a proper error when an interaction is ack'd twice
@@ -104,6 +108,11 @@ public InteractionImpl(JDAImpl jda, DataObject data)
}
this.user = user;
}
+
+ this.entitlements = data.optArray("entitlements").orElseGet(DataArray::empty)
+ .stream(DataArray::getObject)
+ .map(jda.getEntityBuilder()::createEntitlement)
+ .collect(Helpers.toUnmodifiableList());
}
// Used to allow interaction hook to send messages after acknowledgements
@@ -183,6 +192,13 @@ public Member getMember()
return member;
}
+ @Nonnull
+ @Override
+ public List getEntitlements()
+ {
+ return entitlements;
+ }
+
@Nonnull
@Override
public JDA getJDA()
diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/command/CommandInteractionImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/command/CommandInteractionImpl.java
index 2940a5ad99e..9d437bac2ce 100644
--- a/src/main/java/net/dv8tion/jda/internal/interactions/command/CommandInteractionImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/interactions/command/CommandInteractionImpl.java
@@ -20,11 +20,13 @@
import net.dv8tion.jda.api.interactions.commands.CommandInteractionPayload;
import net.dv8tion.jda.api.interactions.modals.Modal;
import net.dv8tion.jda.api.requests.restaction.interactions.ModalCallbackAction;
+import net.dv8tion.jda.api.requests.restaction.interactions.PremiumRequiredCallbackAction;
import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.JDAImpl;
import net.dv8tion.jda.internal.interactions.DeferrableInteractionImpl;
import net.dv8tion.jda.internal.requests.restaction.interactions.ModalCallbackActionImpl;
+import net.dv8tion.jda.internal.requests.restaction.interactions.PremiumRequiredCallbackActionImpl;
import net.dv8tion.jda.internal.requests.restaction.interactions.ReplyCallbackActionImpl;
import net.dv8tion.jda.internal.utils.Checks;
@@ -60,4 +62,11 @@ public ModalCallbackAction replyModal(@Nonnull Modal modal)
Checks.notNull(modal, "Modal");
return new ModalCallbackActionImpl(this, modal);
}
+
+ @Nonnull
+ @Override
+ public PremiumRequiredCallbackAction replyWithPremiumRequired()
+ {
+ return new PremiumRequiredCallbackActionImpl(this);
+ }
}
diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/component/ComponentInteractionImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/component/ComponentInteractionImpl.java
index e6e735eae3f..e7ec45268b3 100644
--- a/src/main/java/net/dv8tion/jda/internal/interactions/component/ComponentInteractionImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/interactions/component/ComponentInteractionImpl.java
@@ -23,6 +23,7 @@
import net.dv8tion.jda.api.interactions.components.ComponentInteraction;
import net.dv8tion.jda.api.interactions.modals.Modal;
import net.dv8tion.jda.api.requests.restaction.interactions.ModalCallbackAction;
+import net.dv8tion.jda.api.requests.restaction.interactions.PremiumRequiredCallbackAction;
import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.JDAImpl;
@@ -30,6 +31,7 @@
import net.dv8tion.jda.internal.interactions.DeferrableInteractionImpl;
import net.dv8tion.jda.internal.requests.restaction.interactions.MessageEditCallbackActionImpl;
import net.dv8tion.jda.internal.requests.restaction.interactions.ModalCallbackActionImpl;
+import net.dv8tion.jda.internal.requests.restaction.interactions.PremiumRequiredCallbackActionImpl;
import net.dv8tion.jda.internal.requests.restaction.interactions.ReplyCallbackActionImpl;
import net.dv8tion.jda.internal.utils.Checks;
@@ -115,4 +117,11 @@ public ModalCallbackAction replyModal(@Nonnull Modal modal)
return new ModalCallbackActionImpl(this, modal);
}
+
+ @Nonnull
+ @Override
+ public PremiumRequiredCallbackAction replyWithPremiumRequired()
+ {
+ return new PremiumRequiredCallbackActionImpl(this);
+ }
}
diff --git a/src/main/java/net/dv8tion/jda/internal/interactions/component/EntitySelectMenuImpl.java b/src/main/java/net/dv8tion/jda/internal/interactions/component/EntitySelectMenuImpl.java
index a760810b6e0..f2a7540aea3 100644
--- a/src/main/java/net/dv8tion/jda/internal/interactions/component/EntitySelectMenuImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/interactions/component/EntitySelectMenuImpl.java
@@ -22,7 +22,6 @@
import net.dv8tion.jda.api.utils.data.DataArray;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.utils.Helpers;
-import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.util.Collections;
@@ -99,7 +98,7 @@ public List getDefaultValues()
return defaultValues;
}
- @NotNull
+ @Nonnull
@Override
public DataObject toData()
{
diff --git a/src/main/java/net/dv8tion/jda/internal/managers/AccountManagerImpl.java b/src/main/java/net/dv8tion/jda/internal/managers/AccountManagerImpl.java
index 6e62bc1df14..4da6e543d67 100644
--- a/src/main/java/net/dv8tion/jda/internal/managers/AccountManagerImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/managers/AccountManagerImpl.java
@@ -35,6 +35,7 @@ public class AccountManagerImpl extends ManagerBase implements A
protected String name;
protected Icon avatar;
+ protected Icon banner;
/**
* Creates a new AccountManager instance
@@ -63,6 +64,8 @@ public AccountManagerImpl reset(long fields)
super.reset(fields);
if ((fields & AVATAR) == AVATAR)
avatar = null;
+ if ((fields & BANNER) == BANNER)
+ banner = null;
return this;
}
@@ -110,6 +113,16 @@ public AccountManagerImpl setAvatar(Icon avatar)
return this;
}
+ @Nonnull
+ @Override
+ @CheckReturnValue
+ public AccountManager setBanner(Icon banner)
+ {
+ this.banner = banner;
+ set |= BANNER;
+ return this;
+ }
+
@Override
protected RequestBody finalizeData()
{
@@ -123,6 +136,8 @@ protected RequestBody finalizeData()
body.put("username", name);
if (shouldUpdate(AVATAR))
body.put("avatar", avatar == null ? null : avatar.getEncoding());
+ if (shouldUpdate(BANNER))
+ body.put("banner", banner == null ? null : banner.getEncoding());
reset();
return getRequestBody(body);
diff --git a/src/main/java/net/dv8tion/jda/internal/requests/ErrorMapper.java b/src/main/java/net/dv8tion/jda/internal/requests/ErrorMapper.java
new file mode 100644
index 00000000000..92d785dbac8
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/internal/requests/ErrorMapper.java
@@ -0,0 +1,31 @@
+/*
+ * 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.requests;
+
+import net.dv8tion.jda.api.exceptions.ErrorResponseException;
+import net.dv8tion.jda.api.requests.Request;
+import net.dv8tion.jda.api.requests.Response;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+@FunctionalInterface
+public interface ErrorMapper
+{
+ @Nullable
+ Throwable apply(@Nonnull Response response, @Nonnull Request> request, @Nonnull ErrorResponseException exception);
+}
diff --git a/src/main/java/net/dv8tion/jda/internal/requests/IncomingWebhookClientImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/IncomingWebhookClientImpl.java
index 9f7bd21f561..2f32deb30c3 100644
--- a/src/main/java/net/dv8tion/jda/internal/requests/IncomingWebhookClientImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/requests/IncomingWebhookClientImpl.java
@@ -33,7 +33,6 @@
import net.dv8tion.jda.internal.requests.restaction.WebhookMessageEditActionImpl;
import net.dv8tion.jda.internal.requests.restaction.WebhookMessageRetrieveActionImpl;
import net.dv8tion.jda.internal.utils.Checks;
-import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.util.function.Function;
@@ -82,7 +81,7 @@ public WebhookMessageRetrieveAction retrieveMessageById(@Nonnull String messageI
@Nonnull
@Override
- public WebhookMessageDeleteAction deleteMessageById(@NotNull String messageId)
+ public WebhookMessageDeleteAction deleteMessageById(@Nonnull String messageId)
{
WebhookMessageDeleteActionImpl action = (WebhookMessageDeleteActionImpl) super.deleteMessageById(messageId);
action.run();
diff --git a/src/main/java/net/dv8tion/jda/internal/requests/RestActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/RestActionImpl.java
index 3184d67d1c1..48eb4fb741b 100644
--- a/src/main/java/net/dv8tion/jda/internal/requests/RestActionImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/requests/RestActionImpl.java
@@ -63,6 +63,7 @@ else if (t.getCause() != null)
private final Route.CompiledRoute route;
private final RequestBody data;
private final BiFunction, T> handler;
+ private ErrorMapper errorMapper = null;
private boolean priority = false;
private long deadline = 0;
@@ -146,6 +147,11 @@ public RestActionImpl(JDA api, Route.CompiledRoute route, RequestBody data, BiFu
this.handler = handler;
}
+ public void setErrorMapper(ErrorMapper errorMapper)
+ {
+ this.errorMapper = errorMapper;
+ }
+
public RestActionImpl priority()
{
priority = true;
@@ -277,8 +283,20 @@ public void handleResponse(Response response, Request request)
{
if (response.isOk())
handleSuccess(response, request);
+ else if (response.isRateLimit())
+ request.onRateLimited(response);
else
- request.onFailure(response);
+ {
+ final ErrorResponseException exception = request.createErrorResponseException(response);
+ final Throwable mappedThrowable = this.errorMapper != null
+ ? this.errorMapper.apply(response, request, exception)
+ : null;
+
+ if (mappedThrowable != null)
+ request.onFailure(mappedThrowable);
+ else
+ request.onFailure(exception);
+ }
}
protected void handleSuccess(Response response, Request request)
diff --git a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java
index 1a7c1fca7ff..924c45724ae 100644
--- a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java
+++ b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java
@@ -1361,6 +1361,9 @@ protected void setupHandlers()
handlers.put("CHANNEL_CREATE", new ChannelCreateHandler(api));
handlers.put("CHANNEL_DELETE", new ChannelDeleteHandler(api));
handlers.put("CHANNEL_UPDATE", new ChannelUpdateHandler(api));
+ handlers.put("ENTITLEMENT_CREATE", new EntitlementCreateHandler(api));
+ handlers.put("ENTITLEMENT_UPDATE", new EntitlementUpdateHandler(api));
+ handlers.put("ENTITLEMENT_DELETE", new EntitlementDeleteHandler(api));
handlers.put("GUILD_AUDIT_LOG_ENTRY_CREATE", new GuildAuditLogEntryCreateHandler(api));
handlers.put("GUILD_BAN_ADD", new GuildBanHandler(api, true));
handlers.put("GUILD_BAN_REMOVE", new GuildBanHandler(api, false));
@@ -1392,6 +1395,8 @@ protected void setupHandlers()
handlers.put("MESSAGE_REACTION_REMOVE", new MessageReactionHandler(api, false));
handlers.put("MESSAGE_REACTION_REMOVE_ALL", new MessageReactionBulkRemoveHandler(api));
handlers.put("MESSAGE_REACTION_REMOVE_EMOJI", new MessageReactionClearEmojiHandler(api));
+ handlers.put("MESSAGE_POLL_VOTE_ADD", new MessagePollVoteHandler(api, true));
+ handlers.put("MESSAGE_POLL_VOTE_REMOVE", new MessagePollVoteHandler(api, false));
handlers.put("MESSAGE_UPDATE", new MessageUpdateHandler(api));
handlers.put("PRESENCE_UPDATE", new PresenceUpdateHandler(api));
handlers.put("READY", new ReadyHandler(api));
diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/CommandCreateActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/CommandCreateActionImpl.java
index ba7873b7f50..fdd02e0db16 100644
--- a/src/main/java/net/dv8tion/jda/internal/requests/restaction/CommandCreateActionImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/CommandCreateActionImpl.java
@@ -34,7 +34,6 @@
import net.dv8tion.jda.internal.interactions.command.CommandImpl;
import net.dv8tion.jda.internal.requests.RestActionImpl;
import okhttp3.RequestBody;
-import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.util.List;
@@ -225,19 +224,19 @@ public LocalizationMap getDescriptionLocalizations()
}
@Override
- public boolean removeOptions(@NotNull Predicate super OptionData> condition)
+ public boolean removeOptions(@Nonnull Predicate super OptionData> condition)
{
return data.removeOptions(condition);
}
@Override
- public boolean removeSubcommands(@NotNull Predicate super SubcommandData> condition)
+ public boolean removeSubcommands(@Nonnull Predicate super SubcommandData> condition)
{
return data.removeSubcommands(condition);
}
@Override
- public boolean removeSubcommandGroups(@NotNull Predicate super SubcommandGroupData> condition)
+ public boolean removeSubcommandGroups(@Nonnull Predicate super SubcommandGroupData> condition)
{
return data.removeSubcommandGroups(condition);
}
diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageCreateActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageCreateActionImpl.java
index 395366df567..6e76e56a5b4 100644
--- a/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageCreateActionImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/MessageCreateActionImpl.java
@@ -35,6 +35,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -43,6 +44,7 @@
public class MessageCreateActionImpl extends RestActionImpl implements MessageCreateAction, MessageCreateBuilderMixin
{
+ protected static final SecureRandom nonceGenerator = new SecureRandom();
protected static boolean defaultFailOnInvalidReply = false;
private final MessageChannel channel;
@@ -77,14 +79,17 @@ protected RequestBody finalizeData()
{
if (!stickers.isEmpty())
return getRequestBody(DataObject.empty().put("sticker_ids", stickers));
- throw new IllegalStateException("Cannot build empty messages! Must provide at least one of: content, embed, file, or stickers");
+ throw new IllegalStateException("Cannot build empty messages! Must provide at least one of: content, embed, file, poll, or stickers");
}
try (MessageCreateData data = builder.build())
{
DataObject json = data.toData();
- if (nonce != null)
+ json.put("enforce_nonce", true);
+ if (nonce != null && !nonce.isEmpty())
json.put("nonce", nonce);
+ else
+ json.put("nonce", Long.toUnsignedString(nonceGenerator.nextLong()));
if (stickers != null && !stickers.isEmpty())
json.put("sticker_ids", stickers);
if (messageReferenceId != null)
diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/interactions/AutoCompleteCallbackActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/interactions/AutoCompleteCallbackActionImpl.java
index d7a4fd255f5..755cf65d8dc 100644
--- a/src/main/java/net/dv8tion/jda/internal/requests/restaction/interactions/AutoCompleteCallbackActionImpl.java
+++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/interactions/AutoCompleteCallbackActionImpl.java
@@ -69,10 +69,10 @@ public AutoCompleteCallbackAction addChoices(@Nonnull Collection
"Choice of type %s cannot be converted to INTEGER", choice.getType());
long valueLong = choice.getAsLong();
Checks.check(valueLong <= OptionData.MAX_POSITIVE_NUMBER,
- "Choice value cannot be larger than %d Provided: %d",
+ "Choice value cannot be larger than %f Provided: %d",
OptionData.MAX_POSITIVE_NUMBER, valueLong);
Checks.check(valueLong >= OptionData.MIN_NEGATIVE_NUMBER,
- "Choice value cannot be smaller than %d. Provided: %d",
+ "Choice value cannot be smaller than %f. Provided: %d",
OptionData.MIN_NEGATIVE_NUMBER, valueLong);
break;
case NUMBER:
@@ -80,10 +80,10 @@ public AutoCompleteCallbackAction addChoices(@Nonnull Collection
"Choice of type %s cannot be converted to NUMBER", choice.getType());
double valueDouble = choice.getAsDouble();
Checks.check(valueDouble <= OptionData.MAX_POSITIVE_NUMBER,
- "Choice value cannot be larger than %d Provided: %d",
+ "Choice value cannot be larger than %f Provided: %f",
OptionData.MAX_POSITIVE_NUMBER, valueDouble);
Checks.check(valueDouble >= OptionData.MIN_NEGATIVE_NUMBER,
- "Choice value cannot be smaller than %d. Provided: %d",
+ "Choice value cannot be smaller than %f. Provided: %f",
OptionData.MIN_NEGATIVE_NUMBER, valueDouble);
break;
case STRING:
diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/interactions/PremiumRequiredCallbackActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/interactions/PremiumRequiredCallbackActionImpl.java
new file mode 100644
index 00000000000..45ca3752f22
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/interactions/PremiumRequiredCallbackActionImpl.java
@@ -0,0 +1,58 @@
+/*
+ * 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.requests.restaction.interactions;
+
+import net.dv8tion.jda.api.interactions.callbacks.IPremiumRequiredReplyCallback;
+import net.dv8tion.jda.api.requests.restaction.interactions.InteractionCallbackAction;
+import net.dv8tion.jda.api.requests.restaction.interactions.PremiumRequiredCallbackAction;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.internal.interactions.InteractionImpl;
+import okhttp3.RequestBody;
+
+import javax.annotation.Nonnull;
+import java.util.function.BooleanSupplier;
+
+public class PremiumRequiredCallbackActionImpl extends InteractionCallbackImpl implements PremiumRequiredCallbackAction
+{
+
+ public PremiumRequiredCallbackActionImpl(IPremiumRequiredReplyCallback interaction)
+ {
+ super((InteractionImpl) interaction);
+ }
+
+ @Override
+ protected RequestBody finalizeData()
+ {
+ return getRequestBody(DataObject.empty()
+ .put("type", InteractionCallbackAction.ResponseType.PREMIUM_REQUIRED.getRaw())
+ .put("data", DataObject.empty()));
+ }
+
+ @Nonnull
+ @Override
+ public PremiumRequiredCallbackAction setCheck(BooleanSupplier checks)
+ {
+ return (PremiumRequiredCallbackAction) super.setCheck(checks);
+ }
+
+ @Nonnull
+ @Override
+ public PremiumRequiredCallbackAction deadline(long timestamp)
+ {
+ return (PremiumRequiredCallbackAction) super.deadline(timestamp);
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/operator/FlatMapRestAction.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/operator/FlatMapRestAction.java
index 11f49098066..84db460f730 100644
--- a/src/main/java/net/dv8tion/jda/internal/requests/restaction/operator/FlatMapRestAction.java
+++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/operator/FlatMapRestAction.java
@@ -21,6 +21,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -61,7 +62,13 @@ public void queue(@Nullable Consumer super O> success, @Nullable Consumer su
@Override
public O complete(boolean shouldQueue) throws RateLimitedException
{
- return supply(action.complete(shouldQueue)).complete(shouldQueue);
+ I complete = action.complete(shouldQueue);
+
+ if (this.condition != null && !this.condition.test(complete))
+ {
+ throw new CancellationException("FlatMap condition failed");
+ }
+ return supply(complete).complete(shouldQueue);
}
@Nonnull
@@ -69,6 +76,16 @@ public O complete(boolean shouldQueue) throws RateLimitedException
public CompletableFuture submit(boolean shouldQueue)
{
return action.submit(shouldQueue)
- .thenCompose((result) -> supply(result).submit(shouldQueue));
+ .thenCompose((result) ->
+ {
+ if (condition != null && !condition.test(result))
+ {
+ CompletableFuture future = new CompletableFuture<>();
+ future.cancel(true);
+
+ return future;
+ }
+ return supply(result).submit(shouldQueue);
+ });
}
}
diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/EntitlementPaginationActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/EntitlementPaginationActionImpl.java
new file mode 100644
index 00000000000..c51e54c534b
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/EntitlementPaginationActionImpl.java
@@ -0,0 +1,183 @@
+/*
+ * 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.requests.restaction.pagination;
+
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.Entitlement;
+import net.dv8tion.jda.api.entities.UserSnowflake;
+import net.dv8tion.jda.api.exceptions.ParsingException;
+import net.dv8tion.jda.api.requests.Request;
+import net.dv8tion.jda.api.requests.Response;
+import net.dv8tion.jda.api.requests.Route;
+import net.dv8tion.jda.api.requests.restaction.pagination.EntitlementPaginationAction;
+import net.dv8tion.jda.api.utils.data.DataArray;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.internal.entities.EntityBuilder;
+import net.dv8tion.jda.internal.utils.Checks;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.*;
+
+public class EntitlementPaginationActionImpl
+ extends PaginationActionImpl
+ implements EntitlementPaginationAction
+{
+ protected List skuIds;
+ protected long guildId;
+ protected long userId;
+ protected boolean excludeEnded;
+
+ public EntitlementPaginationActionImpl(JDA api)
+ {
+ super(api, Route.Applications.GET_ENTITLEMENTS.compile(api.getSelfUser().getApplicationId()), 1, 100, 100);
+ this.skuIds = new ArrayList<>();
+ this.guildId = 0;
+ this.userId = 0;
+ }
+
+ @Nonnull
+ @Override
+ public EnumSet getSupportedOrders()
+ {
+ return EnumSet.of(PaginationOrder.BACKWARD, PaginationOrder.FORWARD);
+ }
+
+ @Nonnull
+ @Override
+ public EntitlementPaginationAction user(@Nullable UserSnowflake user)
+ {
+ if (user == null)
+ userId = 0;
+ else
+ userId = user.getIdLong();
+ return this;
+ }
+
+ @Nonnull
+ @Override
+ public EntitlementPaginationAction skuIds(long... skuIds)
+ {
+ this.skuIds.clear();
+ for (long skuId : skuIds)
+ this.skuIds.add(Long.toUnsignedString(skuId));
+ return this;
+ }
+
+ @Nonnull
+ @Override
+ public EntitlementPaginationAction skuIds(@Nonnull String... skuIds)
+ {
+ Checks.noneNull(skuIds, "skuIds");
+ for (String skuId : skuIds)
+ Checks.isSnowflake(skuId, "skuId");
+
+ this.skuIds.clear();
+
+ Collections.addAll(this.skuIds, skuIds);
+ return this;
+ }
+
+ @Nonnull
+ @Override
+ public EntitlementPaginationAction skuIds(@Nonnull Collection skuIds)
+ {
+ Checks.noneNull(skuIds, "skuIds");
+
+ this.skuIds.clear();
+ for (String skuId : skuIds)
+ {
+ Checks.isSnowflake(skuId, "skuId");
+ this.skuIds.add(skuId);
+ }
+
+ return this;
+ }
+
+ @Nonnull
+ @Override
+ public EntitlementPaginationAction guild(long guildId)
+ {
+ this.guildId = guildId;
+ return this;
+ }
+
+ @Nonnull
+ @Override
+ public EntitlementPaginationAction excludeEnded(boolean excludeEnded)
+ {
+ this.excludeEnded = excludeEnded;
+ return this;
+ }
+
+ @Override
+ protected Route.CompiledRoute finalizeRoute()
+ {
+ Route.CompiledRoute route = super.finalizeRoute();
+
+ if (userId != 0)
+ route = route.withQueryParams("user_id", Long.toUnsignedString(userId));
+
+ if (!skuIds.isEmpty())
+ route = route.withQueryParams("sku_ids", String.join(",", skuIds));
+
+ if (guildId != 0)
+ route = route.withQueryParams("guild_id", Long.toUnsignedString(guildId));
+
+ if (excludeEnded)
+ route = route.withQueryParams("exclude_ended", String.valueOf(true));
+
+ return route;
+ }
+
+ @Override
+ protected void handleSuccess(Response response, Request> request)
+ {
+ DataArray array = response.getArray();
+ List entitlements = new ArrayList<>(array.length());
+ EntityBuilder builder = api.getEntityBuilder();
+ for (int i = 0; i < array.length(); i++)
+ {
+ try
+ {
+ DataObject object = array.getObject(i);
+ Entitlement entitlement = builder.createEntitlement(object);
+ entitlements.add(entitlement);
+ }
+ catch(ParsingException | NullPointerException e)
+ {
+ LOG.warn("Encountered an exception in EntitlementPaginationAction", e);
+ }
+ }
+
+ if (!entitlements.isEmpty())
+ {
+ if (useCache)
+ cached.addAll(entitlements);
+ last = entitlements.get(entitlements.size() - 1);
+ lastKey = last.getIdLong();
+ }
+
+ request.onSuccess(entitlements);
+ }
+
+ @Override
+ protected long getKey(Entitlement it)
+ {
+ return it.getIdLong();
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/PollVotersPaginationActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/PollVotersPaginationActionImpl.java
new file mode 100644
index 00000000000..5ac181ee7b5
--- /dev/null
+++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/pagination/PollVotersPaginationActionImpl.java
@@ -0,0 +1,85 @@
+/*
+ * 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.requests.restaction.pagination;
+
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.User;
+import net.dv8tion.jda.api.exceptions.ParsingException;
+import net.dv8tion.jda.api.requests.Request;
+import net.dv8tion.jda.api.requests.Response;
+import net.dv8tion.jda.api.requests.Route;
+import net.dv8tion.jda.api.requests.restaction.pagination.PollVotersPaginationAction;
+import net.dv8tion.jda.api.utils.data.DataArray;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.internal.entities.EntityBuilder;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+
+public class PollVotersPaginationActionImpl extends PaginationActionImpl implements PollVotersPaginationAction
+{
+ public PollVotersPaginationActionImpl(JDA jda, String channelId, String messageId, long answerId)
+ {
+ super(jda, Route.Messages.GET_POLL_ANSWER_VOTERS.compile(channelId, messageId, Long.toString(answerId)), 1, 1000, 1000);
+ this.order = PaginationOrder.FORWARD;
+ }
+
+ @NotNull
+ @Override
+ public EnumSet getSupportedOrders()
+ {
+ return EnumSet.of(PaginationOrder.FORWARD);
+ }
+
+ @Override
+ protected long getKey(User it)
+ {
+ return it.getIdLong();
+ }
+
+ @Override
+ protected void handleSuccess(Response response, Request> request)
+ {
+ DataArray array = response.getObject().getArray("users");
+ List users = new ArrayList<>(array.length());
+ EntityBuilder builder = api.getEntityBuilder();
+ for (int i = 0; i < array.length(); i++)
+ {
+ try
+ {
+ DataObject object = array.getObject(i);
+ users.add(builder.createUser(object));
+ }
+ catch(ParsingException | NullPointerException e)
+ {
+ LOG.warn("Encountered an exception in PollVotersPaginationAction", e);
+ }
+ }
+
+ if (!users.isEmpty())
+ {
+ if (useCache)
+ cached.addAll(users);
+ last = users.get(users.size() - 1);
+ lastKey = last.getIdLong();
+ }
+
+ request.onSuccess(users);
+ }
+}
diff --git a/src/main/java/net/dv8tion/jda/internal/utils/Checks.java b/src/main/java/net/dv8tion/jda/internal/utils/Checks.java
index 4ce2bee3d88..edd1fe10fd2 100644
--- a/src/main/java/net/dv8tion/jda/internal/utils/Checks.java
+++ b/src/main/java/net/dv8tion/jda/internal/utils/Checks.java
@@ -25,9 +25,12 @@
import net.dv8tion.jda.api.interactions.components.ActionComponent;
import net.dv8tion.jda.api.interactions.components.Component;
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
+import org.intellij.lang.annotations.PrintFormat;
import org.jetbrains.annotations.Contract;
+import java.time.Duration;
import java.util.*;
+import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -63,14 +66,14 @@ public static void check(final boolean expression, final String message)
}
@Contract("false, _, _ -> fail")
- public static void check(final boolean expression, final String message, final Object... args)
+ public static void check(final boolean expression, @PrintFormat final String message, final Object... args)
{
if (!expression)
throw new IllegalArgumentException(String.format(message, args));
}
@Contract("false, _, _ -> fail")
- public static void check(final boolean expression, final String message, final Object arg)
+ public static void check(final boolean expression, @PrintFormat final String message, final Object arg)
{
if (!expression)
throw new IllegalArgumentException(String.format(message, arg));
@@ -211,6 +214,18 @@ public static void notNegative(final long n, final String name)
throw new IllegalArgumentException(name + " may not be negative");
}
+ public static void notLonger(final Duration duration, final Duration maxDuration, final TimeUnit resolutionUnit, final String name)
+ {
+ notNull(duration, name);
+ check(
+ duration.compareTo(maxDuration) <= 0,
+ "%s may not be longer than %s. Provided: %s",
+ name,
+ JDALogger.getLazyString(() -> Helpers.durationToString(maxDuration, resolutionUnit)),
+ JDALogger.getLazyString(() -> Helpers.durationToString(duration, resolutionUnit))
+ );
+ }
+
// Unique streams checks
public static void checkUnique(Stream stream, String format, BiFunction getArgs)
diff --git a/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java b/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java
index b8a70512132..703f7d1e84e 100644
--- a/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java
+++ b/src/main/java/net/dv8tion/jda/internal/utils/Helpers.java
@@ -26,6 +26,7 @@
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.*;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.ToLongFunction;
import java.util.stream.Collector;
@@ -316,4 +317,27 @@ public static boolean hasCause(Throwable throwable, Class extends Throwable> c
{
return Collector.of(DataArray::empty, DataArray::add, DataArray::addAll);
}
+
+ public static String durationToString(Duration duration, TimeUnit resolutionUnit)
+ {
+ long actual = resolutionUnit.convert(duration.getSeconds(), TimeUnit.SECONDS);
+ String raw = actual + " " + resolutionUnit.toString().toLowerCase(Locale.ROOT);
+
+ long days = duration.toDays();
+ long hours = duration.toHours() % 24;
+ long minutes = duration.toMinutes() % 60;
+ long seconds = duration.getSeconds() - TimeUnit.DAYS.toSeconds(days) - TimeUnit.HOURS.toSeconds(hours) - TimeUnit.MINUTES.toSeconds(minutes);
+
+ StringJoiner joiner = new StringJoiner(" ");
+ if (days > 0)
+ joiner.add(days + " days");
+ if (hours > 0)
+ joiner.add(hours + " hours");
+ if (minutes > 0)
+ joiner.add(minutes + " minutes");
+ if (seconds > 0)
+ joiner.add(seconds + " seconds");
+
+ return raw + " (" + joiner + ")";
+ }
}
diff --git a/src/main/java/net/dv8tion/jda/internal/utils/message/MessageCreateBuilderMixin.java b/src/main/java/net/dv8tion/jda/internal/utils/message/MessageCreateBuilderMixin.java
index af8f154de3e..7cf498d9892 100644
--- a/src/main/java/net/dv8tion/jda/internal/utils/message/MessageCreateBuilderMixin.java
+++ b/src/main/java/net/dv8tion/jda/internal/utils/message/MessageCreateBuilderMixin.java
@@ -21,6 +21,7 @@
import net.dv8tion.jda.api.utils.FileUpload;
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
import net.dv8tion.jda.api.utils.messages.MessageCreateRequest;
+import net.dv8tion.jda.api.utils.messages.MessagePollData;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -62,6 +63,21 @@ default R addFiles(@Nonnull Collection extends FileUpload> files)
return (R) this;
}
+ @Nullable
+ @Override
+ default MessagePollData getPoll()
+ {
+ return getBuilder().getPoll();
+ }
+
+ @Nonnull
+ @Override
+ default R setPoll(@Nullable MessagePollData poll)
+ {
+ getBuilder().setPoll(poll);
+ return (R) this;
+ }
+
@Nonnull
@Override
default R setTTS(boolean tts)
diff --git a/src/test/java/CommandDataTest.java b/src/test/java/CommandDataTest.java
deleted file mode 100644
index 5352c720470..00000000000
--- a/src/test/java/CommandDataTest.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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.
- */
-
-import net.dv8tion.jda.api.Permission;
-import net.dv8tion.jda.api.interactions.commands.Command;
-import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
-import net.dv8tion.jda.api.interactions.commands.OptionType;
-import net.dv8tion.jda.api.interactions.commands.build.CommandData;
-import net.dv8tion.jda.api.interactions.commands.build.OptionData;
-import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
-import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
-import net.dv8tion.jda.api.utils.data.DataArray;
-import net.dv8tion.jda.api.utils.data.DataObject;
-import net.dv8tion.jda.internal.interactions.CommandDataImpl;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class CommandDataTest
-{
- @Test
- public void testNormal()
- {
- CommandData command = new CommandDataImpl("ban", "Ban a user from this server")
- .setGuildOnly(true)
- .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.BAN_MEMBERS))
- .addOption(OptionType.USER, "user", "The user to ban", true) // required before non-required
- .addOption(OptionType.STRING, "reason", "The ban reason") // test that default is false
- .addOption(OptionType.INTEGER, "days", "The duration of the ban", false); // test with explicit false
-
- DataObject data = command.toData();
- Assertions.assertEquals("ban", data.getString("name"));
- Assertions.assertEquals("Ban a user from this server", data.getString("description"));
- Assertions.assertFalse(data.getBoolean("dm_permission"));
- Assertions.assertEquals(Permission.BAN_MEMBERS.getRawValue(), data.getUnsignedLong("default_member_permissions"));
-
- DataArray options = data.getArray("options");
-
- DataObject option = options.getObject(0);
- Assertions.assertTrue(option.getBoolean("required"));
- Assertions.assertEquals("user", option.getString("name"));
- Assertions.assertEquals("The user to ban", option.getString("description"));
-
- option = options.getObject(1);
- Assertions.assertFalse(option.getBoolean("required"));
- Assertions.assertEquals("reason", option.getString("name"));
- Assertions.assertEquals("The ban reason", option.getString("description"));
-
- option = options.getObject(2);
- Assertions.assertFalse(option.getBoolean("required"));
- Assertions.assertEquals("days", option.getString("name"));
- Assertions.assertEquals("The duration of the ban", option.getString("description"));
- }
-
- @Test
- public void testDefaultMemberPermissions()
- {
- CommandData command = new CommandDataImpl("ban", "Ban a user from this server")
- .setDefaultPermissions(DefaultMemberPermissions.DISABLED);
- DataObject data = command.toData();
-
- Assertions.assertEquals(0, data.getUnsignedLong("default_member_permissions"));
-
- command.setDefaultPermissions(DefaultMemberPermissions.ENABLED);
- data = command.toData();
- Assertions.assertTrue(data.isNull("default_member_permissions"));
- }
-
- @Test
- public void testSubcommand()
- {
- CommandDataImpl command = new CommandDataImpl("mod", "Moderation commands")
- .addSubcommands(new SubcommandData("ban", "Ban a user from this server")
- .addOption(OptionType.USER, "user", "The user to ban", true) // required before non-required
- .addOption(OptionType.STRING, "reason", "The ban reason") // test that default is false
- .addOption(OptionType.INTEGER, "days", "The duration of the ban", false)); // test with explicit false
-
- DataObject data = command.toData();
- Assertions.assertEquals("mod", data.getString("name"));
- Assertions.assertEquals("Moderation commands", data.getString("description"));
-
- DataObject subdata = data.getArray("options").getObject(0);
- Assertions.assertEquals("ban", subdata.getString("name"));
- Assertions.assertEquals("Ban a user from this server", subdata.getString("description"));
-
- DataArray options = subdata.getArray("options");
-
- DataObject option = options.getObject(0);
- Assertions.assertTrue(option.getBoolean("required"));
- Assertions.assertEquals("user", option.getString("name"));
- Assertions.assertEquals("The user to ban", option.getString("description"));
-
- option = options.getObject(1);
- Assertions.assertFalse(option.getBoolean("required"));
- Assertions.assertEquals("reason", option.getString("name"));
- Assertions.assertEquals("The ban reason", option.getString("description"));
-
- option = options.getObject(2);
- Assertions.assertFalse(option.getBoolean("required"));
- Assertions.assertEquals("days", option.getString("name"));
- Assertions.assertEquals("The duration of the ban", option.getString("description"));
- }
-
- @Test
- public void testSubcommandGroup()
- {
- CommandDataImpl command = new CommandDataImpl("mod", "Moderation commands")
- .addSubcommandGroups(new SubcommandGroupData("ban", "Ban or unban a user from this server")
- .addSubcommands(new SubcommandData("add", "Ban a user from this server")
- .addOption(OptionType.USER, "user", "The user to ban", true) // required before non-required
- .addOption(OptionType.STRING, "reason", "The ban reason") // test that default is false
- .addOption(OptionType.INTEGER, "days", "The duration of the ban", false))); // test with explicit false
-
- DataObject data = command.toData();
- Assertions.assertEquals("mod", data.getString("name"));
- Assertions.assertEquals("Moderation commands", data.getString("description"));
-
- DataObject group = data.getArray("options").getObject(0);
- Assertions.assertEquals("ban", group.getString("name"));
- Assertions.assertEquals("Ban or unban a user from this server", group.getString("description"));
-
- DataObject subdata = group.getArray("options").getObject(0);
- Assertions.assertEquals("add", subdata.getString("name"));
- Assertions.assertEquals("Ban a user from this server", subdata.getString("description"));
- DataArray options = subdata.getArray("options");
-
- DataObject option = options.getObject(0);
- Assertions.assertTrue(option.getBoolean("required"));
- Assertions.assertEquals("user", option.getString("name"));
- Assertions.assertEquals("The user to ban", option.getString("description"));
-
- option = options.getObject(1);
- Assertions.assertFalse(option.getBoolean("required"));
- Assertions.assertEquals("reason", option.getString("name"));
- Assertions.assertEquals("The ban reason", option.getString("description"));
-
- option = options.getObject(2);
- Assertions.assertFalse(option.getBoolean("required"));
- Assertions.assertEquals("days", option.getString("name"));
- Assertions.assertEquals("The duration of the ban", option.getString("description"));
- }
-
- @Test
- public void testRequiredThrows()
- {
- CommandDataImpl command = new CommandDataImpl("ban", "Simple ban command");
- command.addOption(OptionType.STRING, "opt", "desc");
-
- Assertions.assertThrows(IllegalArgumentException.class, () -> command.addOption(OptionType.STRING, "other", "desc", true));
-
- SubcommandData subcommand = new SubcommandData("sub", "Simple subcommand");
- subcommand.addOption(OptionType.STRING, "opt", "desc");
- Assertions.assertThrows(IllegalArgumentException.class, () -> subcommand.addOption(OptionType.STRING, "other", "desc", true));
- }
-
- @Test
- public void testNameChecks()
- {
- Assertions.assertThrows(IllegalArgumentException.class, () -> new CommandDataImpl("invalid name", "Valid description"));
- Assertions.assertThrows(IllegalArgumentException.class, () -> new CommandDataImpl("invalidName", "Valid description"));
- Assertions.assertThrows(IllegalArgumentException.class, () -> new CommandDataImpl("valid_name", ""));
-
- Assertions.assertThrows(IllegalArgumentException.class, () -> new SubcommandData("invalid name", "Valid description"));
- Assertions.assertThrows(IllegalArgumentException.class, () -> new SubcommandData("invalidName", "Valid description"));
- Assertions.assertThrows(IllegalArgumentException.class, () -> new SubcommandData("valid_name", ""));
-
- Assertions.assertThrows(IllegalArgumentException.class, () -> new SubcommandGroupData("invalid name", "Valid description"));
- Assertions.assertThrows(IllegalArgumentException.class, () -> new SubcommandGroupData("invalidName", "Valid description"));
- Assertions.assertThrows(IllegalArgumentException.class, () -> new SubcommandGroupData("valid_name", ""));
- }
-
- @Test
- public void testChoices()
- {
- OptionData option = new OptionData(OptionType.INTEGER, "choice", "Option with choices!");
- Assertions.assertThrows(IllegalArgumentException.class, () -> option.addChoice("invalid name", "Valid description"));
- Assertions.assertThrows(IllegalArgumentException.class, () -> option.addChoice("invalidName", "Valid description"));
- Assertions.assertThrows(IllegalArgumentException.class, () -> option.addChoice("valid_name", ""));
-
- List choices = new ArrayList<>();
- for (int i = 0; i < 25; i++)
- {
- option.addChoice("choice_" + i, i);
- choices.add(new Command.Choice("choice_" + i, i));
- }
- Assertions.assertThrows(IllegalArgumentException.class, () -> option.addChoice("name", 100));
- Assertions.assertEquals(25, option.getChoices().size());
- Assertions.assertEquals(choices, option.getChoices());
- }
-}
diff --git a/src/test/java/DataPathTest.java b/src/test/java/DataPathTest.java
deleted file mode 100644
index 9f79294722f..00000000000
--- a/src/test/java/DataPathTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.
- */
-
-import net.dv8tion.jda.api.exceptions.ParsingException;
-import net.dv8tion.jda.api.utils.data.DataArray;
-import net.dv8tion.jda.api.utils.data.DataObject;
-import net.dv8tion.jda.api.utils.data.DataPath;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-public class DataPathTest
-{
- @Test
- void testSimple()
- {
- DataObject object = DataObject.empty()
- .put("foo", "10"); // string to also test parsing
-
- Assertions.assertEquals(10, DataPath.getInt(object, "foo"));
-
- DataArray array = DataArray.empty().add("20");
- Assertions.assertEquals(20, DataPath.getInt(array, "[0]"));
- }
-
- @Test
- void testSimpleMissing()
- {
- DataObject object = DataObject.empty();
-
- Assertions.assertEquals(0L, DataPath.getLong(object, "foo?", 0));
- Assertions.assertThrows(ParsingException.class, () -> DataPath.getLong(object, "foo"));
-
- DataArray array = DataArray.empty();
-
- Assertions.assertTrue(DataPath.getBoolean(array, "[0]?", true));
- Assertions.assertThrows(ParsingException.class, () -> DataPath.getObject(array, "[0]"));
- }
-
- @Test
- void testObjectInArray()
- {
- DataObject object = DataObject.empty().put("foo", 10.0);
- DataArray array = DataArray.empty().add(object);
-
- Assertions.assertEquals(10.0, DataPath.getDouble(array, "[0].foo"));
- Assertions.assertEquals(20.0, DataPath.getDouble(array, "[1]?.foo", 20.0));
- Assertions.assertThrows(IndexOutOfBoundsException.class, () -> DataPath.getDouble(array, "[1].foo"));
- }
-
- @Test
- void testArrayInObject()
- {
- DataArray array = DataArray.empty().add("hello");
- DataObject object = DataObject.empty().put("foo", array);
-
- Assertions.assertEquals("hello", DataPath.getString(object, "foo[0]"));
- Assertions.assertEquals("world", DataPath.getString(object, "foo[1]?", "world"));
- Assertions.assertThrows(IndexOutOfBoundsException.class, () -> DataPath.getString(object, "foo[1]"));
- }
-
- @Test
- void testArrayInArray()
- {
- DataArray array = DataArray.empty().add(DataArray.empty().add("10"));
-
- Assertions.assertEquals(10, DataPath.getUnsignedInt(array, "[0][0]"));
- Assertions.assertEquals(20, DataPath.getUnsignedInt(array, "[0][1]?", 20));
- Assertions.assertEquals(20, DataPath.getUnsignedInt(array, "[1]?[0]", 20));
- Assertions.assertThrows(IndexOutOfBoundsException.class, () -> DataPath.getUnsignedInt(array, "[0][1]"));
- Assertions.assertThrows(IndexOutOfBoundsException.class, () -> DataPath.getUnsignedInt(array, "[1][0]"));
- Assertions.assertThrows(ParsingException.class, () -> DataPath.getUnsignedInt(array, "[0][1]?"));
- Assertions.assertThrows(ParsingException.class, () -> DataPath.getUnsignedInt(array, "[1]?[0]"));
- }
-
- @Test
- void testComplex()
- {
- DataObject object = DataObject.empty()
- .put("array", DataArray.empty()
- .add(DataObject.empty()
- .put("foo", DataObject.empty()
- .put("bar", "hello"))));
-
- Assertions.assertEquals("hello", DataPath.getString(object, "array[0].foo.bar"));
- Assertions.assertEquals("world", DataPath.getString(object, "array[0].wrong?.bar", "world"));
- Assertions.assertThrows(ParsingException.class, () -> DataPath.getString(object, "array[0].wrong?.bar"));
- }
-}
diff --git a/src/test/java/HelpersTest.java b/src/test/java/HelpersTest.java
deleted file mode 100644
index f5fed4bca51..00000000000
--- a/src/test/java/HelpersTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.
- */
-
-import net.dv8tion.jda.internal.utils.Helpers;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import java.util.Arrays;
-import java.util.List;
-
-public class HelpersTest
-{
- @Test
- public void testIsEmpty()
- {
- Assertions.assertTrue(Helpers.isEmpty(null));
- Assertions.assertTrue(Helpers.isEmpty(""));
- Assertions.assertFalse(Helpers.isEmpty("null"));
- Assertions.assertFalse(Helpers.isEmpty("testing with spaces"));
- }
-
- @Test
- public void testContainsWhitespace()
- {
- Assertions.assertTrue(Helpers.containsWhitespace(" "));
- Assertions.assertTrue(Helpers.containsWhitespace("testing with spaces"));
- Assertions.assertFalse(Helpers.containsWhitespace(null));
- Assertions.assertFalse(Helpers.containsWhitespace(""));
- Assertions.assertFalse(Helpers.containsWhitespace("null"));
- }
-
- @Test
- public void testIsBlank()
- {
- Assertions.assertTrue(Helpers.isBlank(" "));
- Assertions.assertTrue(Helpers.isBlank(null));
- Assertions.assertTrue(Helpers.isBlank(""));
- Assertions.assertFalse(Helpers.isBlank("testing with spaces"));
- Assertions.assertFalse(Helpers.isBlank("null"));
- }
-
- @Test
- public void testCountMatches()
- {
- Assertions.assertEquals(3, Helpers.countMatches("Hello World", 'l'));
- Assertions.assertEquals(1, Helpers.countMatches("Hello World", ' '));
- Assertions.assertEquals(0, Helpers.countMatches("Hello World", '_'));
- Assertions.assertEquals(0, Helpers.countMatches("", '!'));
- Assertions.assertEquals(0, Helpers.countMatches(null, '?'));
- }
-
- @Test
- public void testTruncate()
- {
- Assertions.assertEquals("Hello", Helpers.truncate("Hello World", 5));
- Assertions.assertEquals("Hello", Helpers.truncate("Hello", 5));
- Assertions.assertEquals("Hello", Helpers.truncate("Hello", 10));
- Assertions.assertEquals("", Helpers.truncate("", 10));
- Assertions.assertEquals("", Helpers.truncate("Test", 0));
- Assertions.assertNull(Helpers.truncate(null, 10));
- }
-
- @Test
- public void testRightPad()
- {
- Assertions.assertEquals("Hello ", Helpers.rightPad("Hello", 9));
- Assertions.assertEquals("Hello World", Helpers.rightPad("Hello World", 9));
- Assertions.assertEquals("Hello", Helpers.rightPad("Hello", 5));
- }
-
- @Test
- public void testLeftPad()
- {
- Assertions.assertEquals(" Hello", Helpers.leftPad("Hello", 9));
- Assertions.assertEquals("Hello World", Helpers.leftPad("Hello World", 9));
- Assertions.assertEquals("Hello", Helpers.leftPad("Hello", 5));
- }
-
- @Test
- public void testIsNumeric()
- {
- Assertions.assertTrue(Helpers.isNumeric("10"));
- Assertions.assertTrue(Helpers.isNumeric("1"));
- Assertions.assertTrue(Helpers.isNumeric("0"));
- Assertions.assertTrue(Helpers.isNumeric(String.valueOf(Long.MAX_VALUE)));
- Assertions.assertFalse(Helpers.isNumeric(null));
- Assertions.assertFalse(Helpers.isNumeric(""));
- Assertions.assertFalse(Helpers.isNumeric("Test"));
- Assertions.assertFalse(Helpers.isNumeric("1.0"));
- Assertions.assertFalse(Helpers.isNumeric("1e10"));
- }
-
- @Test
- public void testDeepEquals()
- {
- List a = Arrays.asList("A", "B", "C");
- List b = Arrays.asList("B", "A", "C");
- List c = Arrays.asList("A", "B");
- List d = Arrays.asList("A", "B", "C");
-
- Assertions.assertTrue(Helpers.deepEquals(a, a));
- Assertions.assertTrue(Helpers.deepEquals(a, d));
- Assertions.assertTrue(Helpers.deepEqualsUnordered(a, b));
- Assertions.assertFalse(Helpers.deepEquals(a, b));
- Assertions.assertFalse(Helpers.deepEquals(a, c));
- Assertions.assertFalse(Helpers.deepEqualsUnordered(b, c));
- }
-}
diff --git a/src/test/java/JsonTest.java b/src/test/java/JsonTest.java
deleted file mode 100644
index f147dc3cfd8..00000000000
--- a/src/test/java/JsonTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-
-import net.dv8tion.jda.api.utils.data.DataObject;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-public class JsonTest
-{
- private static final String json = "{\"int\":10,\"long\":100,\"boolean\":true,\"string\":\"test\"}";
-
- @Test
- public void testParse()
- {
- DataObject object = DataObject.fromJson(json);
- Assertions.assertEquals(10, object.getInt("int", 0));
- Assertions.assertEquals(100, object.getLong("long", 0));
- Assertions.assertEquals(true, object.getBoolean("boolean", false));
- Assertions.assertEquals("test", object.getString("string", null));
- }
-
- @Test
- public void testJsonToString()
- {
- DataObject object = DataObject.fromJson(json);
- String result = object.toString();
- DataObject symmetric = DataObject.fromJson(result);
- Assertions.assertEquals(object.toMap(), symmetric.toMap()); // lucky that this works here :)
- }
-}
diff --git a/src/test/java/LocalizationTest.java b/src/test/java/LocalizationTest.java
deleted file mode 100644
index 7708d56d9d3..00000000000
--- a/src/test/java/LocalizationTest.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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.
- */
-
-import net.dv8tion.jda.api.interactions.DiscordLocale;
-import net.dv8tion.jda.api.interactions.commands.Command;
-import net.dv8tion.jda.api.interactions.commands.OptionType;
-import net.dv8tion.jda.api.interactions.commands.build.*;
-import net.dv8tion.jda.api.interactions.commands.localization.LocalizationFunction;
-import net.dv8tion.jda.api.interactions.commands.localization.ResourceBundleLocalizationFunction;
-import net.dv8tion.jda.api.utils.data.DataArray;
-import net.dv8tion.jda.api.utils.data.DataObject;
-import net.dv8tion.jda.api.utils.data.DataPath;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-import java.util.Arrays;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-public class LocalizationTest
-{
- private static SlashCommandData slashCommandData;
- private static DataObject data;
-
- @BeforeAll
- public static void setup()
- {
- final LocalizationFunction localizationFunction = ResourceBundleLocalizationFunction
- .fromBundles("MyCommands", DiscordLocale.FRENCH)
- .build();
-
- slashCommandData = Commands.slash("ban", "Bans someone").addSubcommandGroups(
- new SubcommandGroupData("user", "Bans a member").addSubcommands(
- new SubcommandData("perm", "Bans an user permanently").addOptions(
- new OptionData(OptionType.STRING, "user", "The user to ban"),
- new OptionData(OptionType.INTEGER, "del_days", "The amount of days to delete messages")
- .addChoices(
- new Command.Choice("1 Day", "1"),
- new Command.Choice("7 Days", "7"),
- new Command.Choice("14 Days", "14")
- )
- ),
- new SubcommandData("temp", "Bans an user temporarily").addOptions(
- new OptionData(OptionType.STRING, "user", "The user to ban"),
- new OptionData(OptionType.INTEGER, "del_days", "The amount of days to delete messages")
- .addChoices(
- new Command.Choice("1 Day", "1"),
- new Command.Choice("7 Days", "7"),
- new Command.Choice("14 Days", "14")
- )
- )
- )
- ).setLocalizationFunction(localizationFunction);
-
- data = slashCommandData.toData();
- }
-
- @Test
- public void commandLocalization()
- {
- assertEquals("ban", DataPath.getString(data, "name_localizations.fr"));
- assertEquals("Bannis un utilisateur", DataPath.getString(data, "description_localizations.fr"));
- }
-
- @Test
- public void subcommandLocalization()
- {
- assertEquals("utilisateur", navigateOptions("user").getObject("name_localizations").getString("fr"));
- assertEquals("Bannis un utilisateur", navigateOptions("user").getObject("description_localizations").getString("fr"));
- }
-
- @Test
- public void subcommandGroupLocalization()
- {
- assertEquals("permanent", navigateOptions("user", "perm").getObject("name_localizations").getString("fr"));
- assertEquals("Bannis un utilisateur pour toujours", navigateOptions("user", "perm").getObject("description_localizations").getString("fr"));
- }
-
- @Test
- public void optionLocalization()
- {
- assertEquals("utilisateur", navigateOptions("user", "perm", "user").getObject("name_localizations").getString("fr"));
- assertEquals("L'utilisateur à bannir", navigateOptions("user", "perm", "user").getObject("description_localizations").getString("fr"));
-
- assertEquals("nb_jours", navigateOptions("user", "perm", "del_days").getObject("name_localizations").getString("fr"));
- assertEquals("Nombre de jours de messages à supprimer", navigateOptions("user", "perm", "del_days").getObject("description_localizations").getString("fr"));
- }
-
- @Test
- public void choiceLocalization()
- {
- assertEquals("1 jour", navigateChoice("1 Day", "user", "perm", "del_days").getObject("name_localizations").getString("fr"));
- assertEquals("7 jours", navigateChoice("7 Days", "user", "perm", "del_days").getObject("name_localizations").getString("fr"));
- assertEquals("14 jours", navigateChoice("14 Days", "user", "perm", "del_days").getObject("name_localizations").getString("fr"));
- }
-
- @Test
- public void reconstructData()
- {
- final DataObject data = slashCommandData.toData();
- final DataObject reconstitutedData = CommandData.fromData(data).toData();
- assertEquals(data.toMap(), reconstitutedData.toMap());
- }
-
- private DataObject navigateOptions(String... names)
- {
- DataObject o = data;
- for (String name : names)
- {
- o = o.getArray("options").stream(DataArray::getObject)
- .filter(s -> s.getString("name").equals(name))
- .findAny()
- .orElseThrow(() -> new IllegalArgumentException("Could not find an option with path: " + Arrays.toString(names)));
- }
- return o;
- }
-
- private DataObject navigateChoice(String choiceName, String... names)
- {
- return navigateOptions(names)
- .getArray("choices")
- .stream(DataArray::getObject)
- .filter(s -> s.getString("name").equals(choiceName))
- .findAny()
- .orElseThrow(() -> new IllegalArgumentException("Could not find choice '" + choiceName + "' with path: " + Arrays.toString(names)));
- }
-}
diff --git a/src/test/java/MarkdownTest.java b/src/test/java/MarkdownTest.java
deleted file mode 100644
index 384934491fd..00000000000
--- a/src/test/java/MarkdownTest.java
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
- * 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.
- */
-
-import net.dv8tion.jda.api.utils.MarkdownSanitizer;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class MarkdownTest
-{
- private MarkdownSanitizer markdown;
-
- @BeforeEach
- public void setup()
- {
- markdown = new MarkdownSanitizer().withStrategy(MarkdownSanitizer.SanitizationStrategy.REMOVE);
- }
-
- @Test
- public void testComplex()
- {
- Assertions.assertEquals("ABCDEF", markdown.compute("**A_B||C~~D__E`F`__~~||_**"));
- }
-
- @Test
- public void testTrivial()
- {
- Assertions.assertEquals("", markdown.compute(""));
- Assertions.assertEquals("Hello World ~~~~", markdown.compute("Hello World ~~~~"));
- Assertions.assertEquals("Hello World ~", markdown.compute("Hello World ~~~~~"));
- }
-
- @Test
- public void testBold()
- {
- Assertions.assertEquals("Hello", markdown.compute("**Hello**"));
- Assertions.assertEquals("**Hello", markdown.compute("**Hello"));
- Assertions.assertEquals("\\**Hello**", markdown.compute("\\**Hello**"));
- }
-
- @Test
- public void testItalics()
- {
- Assertions.assertEquals("Hello", markdown.compute("*Hello*"));
- Assertions.assertEquals("Hello", markdown.compute("_Hello_"));
-
- Assertions.assertEquals("*Hello", markdown.compute("*Hello"));
- Assertions.assertEquals("_Hello", markdown.compute("_Hello"));
-
- Assertions.assertEquals("\\*Hello*", markdown.compute("\\*Hello*"));
- Assertions.assertEquals("\\_Hello_", markdown.compute("\\_Hello_"));
- }
-
- @Test
- public void testBoldItalics()
- {
- Assertions.assertEquals("Hello", markdown.compute("***Hello***"));
- Assertions.assertEquals("***Hello", markdown.compute("***Hello"));
- Assertions.assertEquals("\\***Hello***", markdown.compute("\\***Hello***"));
- }
-
- @Test
- public void testUnderline()
- {
- Assertions.assertEquals("Hello", markdown.compute("__Hello__"));
- Assertions.assertEquals("__Hello", markdown.compute("__Hello"));
- Assertions.assertEquals("\\__Hello__", markdown.compute("\\__Hello__"));
- }
-
- @Test
- public void testStrike()
- {
- Assertions.assertEquals("Hello", markdown.compute("~~Hello~~"));
- Assertions.assertEquals("~~Hello", markdown.compute("~~Hello"));
- Assertions.assertEquals("\\~~Hello~~", markdown.compute("\\~~Hello~~"));
- }
-
- @Test
- public void testSpoiler()
- {
- Assertions.assertEquals("Hello", markdown.compute("||Hello||"));
- Assertions.assertEquals("||Hello", markdown.compute("||Hello"));
- Assertions.assertEquals("\\||Hello||", markdown.compute("\\||Hello||"));
- }
-
- @Test
- public void testMono()
- {
- Assertions.assertEquals("Hello", markdown.compute("`Hello`"));
- Assertions.assertEquals("`Hello", markdown.compute("`Hello"));
- Assertions.assertEquals("\\`Hello`", markdown.compute("\\`Hello`"));
-
- Assertions.assertEquals("Hello **World**", markdown.compute("`Hello **World**`"));
- Assertions.assertEquals("`Hello World", markdown.compute("`Hello **World**"));
- Assertions.assertEquals("\\`Hello World`", markdown.compute("\\`Hello **World**`"));
- }
-
- @Test
- public void testMonoTwo()
- {
- Assertions.assertEquals("Hello", markdown.compute("``Hello``"));
- Assertions.assertEquals("``Hello", markdown.compute("``Hello"));
- Assertions.assertEquals("\\``Hello``", markdown.compute("\\``Hello``"));
-
- Assertions.assertEquals("Hello **World**", markdown.compute("``Hello **World**``"));
- Assertions.assertEquals("``Hello World", markdown.compute("``Hello **World**"));
- Assertions.assertEquals("\\``Hello World``", markdown.compute("\\``Hello **World**``"));
-
- Assertions.assertEquals("Hello `to` World", markdown.compute("``Hello `to` World``"));
- Assertions.assertEquals("``Hello to World", markdown.compute("``Hello `to` World"));
- Assertions.assertEquals("\\``Hello to World``", markdown.compute("\\``Hello `to` World``"));
- }
-
- @Test
- public void testBlock()
- {
- Assertions.assertEquals("Hello", markdown.compute("```Hello```"));
- Assertions.assertEquals("```Hello", markdown.compute("```Hello"));
- Assertions.assertEquals("\\```Hello```", markdown.compute("\\```Hello```"));
-
- Assertions.assertEquals("Hello **World**", markdown.compute("```Hello **World**```"));
- Assertions.assertEquals("```Hello World", markdown.compute("```Hello **World**"));
- Assertions.assertEquals("\\```Hello World```", markdown.compute("\\```Hello **World**```"));
-
- Assertions.assertEquals("Hello `to` World", markdown.compute("```Hello `to` World```"));
- Assertions.assertEquals("```Hello to World", markdown.compute("```Hello `to` World"));
- Assertions.assertEquals("\\```Hello to World```", markdown.compute("\\```Hello `to` World```"));
-
- Assertions.assertEquals("Test", markdown.compute("```java\nTest```"));
- }
-
- @Test
- public void testQuote()
- {
- Assertions.assertEquals("Hello > World", markdown.compute("> Hello > World"));
- Assertions.assertEquals("Hello\nWorld", markdown.compute("> Hello\n> World"));
- Assertions.assertEquals("Hello\nWorld", markdown.compute(">>> Hello\nWorld"));
- Assertions.assertEquals("Hello\nWorld", markdown.compute(">>>\nHello\nWorld"));
- Assertions.assertEquals("Hello > World", markdown.compute(">>>\nHello > World"));
- Assertions.assertEquals("Hello\n World", markdown.compute("Hello\n > World"));
- }
-}
-
-class IgnoreMarkdownTest
-{
- private MarkdownSanitizer markdown;
-
- @BeforeEach
- public void setup()
- {
- markdown = new MarkdownSanitizer().withIgnored(0xFFFFFFFF);
- }
-
- @Test
- public void testComplex()
- {
- Assertions.assertEquals("**A_B||C~~D__E`F`__~~||_**", markdown.compute("**A_B||C~~D__E`F`__~~||_**"));
- }
-
- @Test
- public void testBold()
- {
- Assertions.assertEquals("**Hello**", markdown.compute("**Hello**"));
- Assertions.assertEquals("**Hello", markdown.compute("**Hello"));
- }
-
- @Test
- public void testItalics()
- {
- Assertions.assertEquals("*Hello*", markdown.compute("*Hello*"));
- Assertions.assertEquals("_Hello_", markdown.compute("_Hello_"));
-
- Assertions.assertEquals("*Hello", markdown.compute("*Hello"));
- Assertions.assertEquals("_Hello", markdown.compute("_Hello"));
- }
-
- @Test
- public void testBoldItalics()
- {
- Assertions.assertEquals("***Hello***", markdown.compute("***Hello***"));
- Assertions.assertEquals("***Hello", markdown.compute("***Hello"));
- Assertions.assertEquals("\\***Hello***", markdown.compute("\\***Hello***"));
- }
-
- @Test
- public void testUnderline()
- {
- Assertions.assertEquals("__Hello__", markdown.compute("__Hello__"));
- Assertions.assertEquals("__Hello", markdown.compute("__Hello"));
- }
-
- @Test
- public void testStrike()
- {
- Assertions.assertEquals("~~Hello~~", markdown.compute("~~Hello~~"));
- Assertions.assertEquals("~~Hello", markdown.compute("~~Hello"));
- }
-
- @Test
- public void testSpoiler()
- {
- Assertions.assertEquals("||Hello||", markdown.compute("||Hello||"));
- Assertions.assertEquals("||Hello", markdown.compute("||Hello"));
- }
-
- @Test
- public void testMono()
- {
- Assertions.assertEquals("`Hello`", markdown.compute("`Hello`"));
- Assertions.assertEquals("`Hello", markdown.compute("`Hello"));
-
- Assertions.assertEquals("`Hello **World**`", markdown.compute("`Hello **World**`"));
- Assertions.assertEquals("`Hello **World**", markdown.compute("`Hello **World**"));
- }
-
- @Test
- public void testMonoTwo()
- {
- Assertions.assertEquals("``Hello``", markdown.compute("``Hello``"));
- Assertions.assertEquals("``Hello", markdown.compute("``Hello"));
-
- Assertions.assertEquals("``Hello **World**``", markdown.compute("``Hello **World**``"));
- Assertions.assertEquals("``Hello **World**", markdown.compute("``Hello **World**"));
-
- Assertions.assertEquals("``Hello `to` World``", markdown.compute("``Hello `to` World``"));
- Assertions.assertEquals("``Hello `to` World", markdown.compute("``Hello `to` World"));
- }
-
- @Test
- public void testBlock()
- {
- Assertions.assertEquals("```Hello```", markdown.compute("```Hello```"));
- Assertions.assertEquals("```Hello", markdown.compute("```Hello"));
-
- Assertions.assertEquals("```Hello **World**```", markdown.compute("```Hello **World**```"));
- Assertions.assertEquals("```Hello **World**", markdown.compute("```Hello **World**"));
-
- Assertions.assertEquals("```Hello `to` World```", markdown.compute("```Hello `to` World```"));
- Assertions.assertEquals("```Hello `to` World", markdown.compute("```Hello `to` World"));
-
- Assertions.assertEquals("```java\nTest```", markdown.compute("```java\nTest```"));
- }
-
- @Test
- public void testQuote()
- {
- Assertions.assertEquals("> Hello > World", markdown.compute("> Hello > World"));
- Assertions.assertEquals("> Hello\n> World", markdown.compute("> Hello\n> World"));
- Assertions.assertEquals(">>> Hello\nWorld", markdown.compute(">>> Hello\nWorld"));
- Assertions.assertEquals(">>>\nHello\nWorld", markdown.compute(">>>\nHello\nWorld"));
- Assertions.assertEquals(">>>\nHello > World", markdown.compute(">>>\nHello > World"));
- Assertions.assertEquals("Hello\n > World", markdown.compute("Hello\n > World"));
- }
-}
-
-class EscapeMarkdownTest
-{
- private MarkdownSanitizer markdown;
-
- @BeforeEach
- public void setup()
- {
- markdown = new MarkdownSanitizer().withStrategy(MarkdownSanitizer.SanitizationStrategy.ESCAPE);
- }
-
- @Test
- public void testComplex()
- {
- Assertions.assertEquals("\\*\\*A\\_B\\||C\\~~D\\_\\_E\\`F\\`\\_\\_\\~~\\||\\_\\*\\*", markdown.compute("**A_B||C~~D__E`F`__~~||_**"));
- }
-
- @Test
- public void testBold()
- {
- Assertions.assertEquals("\\*\\*Hello\\*\\*", markdown.compute("**Hello**"));
- Assertions.assertEquals("**Hello", markdown.compute("**Hello"));
- Assertions.assertEquals("\\**Hello**", markdown.compute("\\**Hello**"));
- }
-
- @Test
- public void testItalics()
- {
- Assertions.assertEquals("\\*Hello\\*", markdown.compute("*Hello*"));
- Assertions.assertEquals("\\_Hello\\_", markdown.compute("_Hello_"));
-
- Assertions.assertEquals("*Hello", markdown.compute("*Hello"));
- Assertions.assertEquals("_Hello", markdown.compute("_Hello"));
-
- Assertions.assertEquals("\\*Hello*", markdown.compute("\\*Hello*"));
- Assertions.assertEquals("\\_Hello_", markdown.compute("\\_Hello_"));
- }
-
- @Test
- public void testBoldItalics()
- {
- Assertions.assertEquals("\\*\\*\\*Hello\\*\\*\\*", markdown.compute("***Hello***"));
- Assertions.assertEquals("***Hello", markdown.compute("***Hello"));
- Assertions.assertEquals("\\***Hello***", markdown.compute("\\***Hello***"));
- }
-
- @Test
- public void testUnderline()
- {
- Assertions.assertEquals("\\_\\_Hello\\_\\_", markdown.compute("__Hello__"));
- Assertions.assertEquals("__Hello", markdown.compute("__Hello"));
- Assertions.assertEquals("\\__Hello__", markdown.compute("\\__Hello__"));
- }
-
- @Test
- public void testStrike()
- {
- Assertions.assertEquals("\\~~Hello\\~~", markdown.compute("~~Hello~~"));
- Assertions.assertEquals("~~Hello", markdown.compute("~~Hello"));
- Assertions.assertEquals("\\~~Hello~~", markdown.compute("\\~~Hello~~"));
- }
-
- @Test
- public void testSpoiler()
- {
- Assertions.assertEquals("\\||Hello\\||", markdown.compute("||Hello||"));
- Assertions.assertEquals("||Hello", markdown.compute("||Hello"));
- Assertions.assertEquals("\\||Hello||", markdown.compute("\\||Hello||"));
- }
-
- @Test
- public void testMono()
- {
- Assertions.assertEquals("\\`Hello\\`", markdown.compute("`Hello`"));
- Assertions.assertEquals("`Hello", markdown.compute("`Hello"));
- Assertions.assertEquals("\\`Hello`", markdown.compute("\\`Hello`"));
-
- Assertions.assertEquals("\\`Hello **World**\\`", markdown.compute("`Hello **World**`"));
- Assertions.assertEquals("`Hello \\*\\*World\\*\\*", markdown.compute("`Hello **World**"));
- Assertions.assertEquals("\\`Hello \\*\\*World\\*\\*`", markdown.compute("\\`Hello **World**`"));
-
- }
-
- @Test
- public void testMonoTwo()
- {
- Assertions.assertEquals("\\``Hello\\``", markdown.compute("``Hello``"));
- Assertions.assertEquals("``Hello", markdown.compute("``Hello"));
- Assertions.assertEquals("\\``Hello``", markdown.compute("\\``Hello``"));
-
- Assertions.assertEquals("\\``Hello **World**\\``", markdown.compute("``Hello **World**``"));
- Assertions.assertEquals("``Hello \\*\\*World\\*\\*", markdown.compute("``Hello **World**"));
- Assertions.assertEquals("\\``Hello \\*\\*World\\*\\*``", markdown.compute("\\``Hello **World**``"));
-
- Assertions.assertEquals("\\``Hello `to` World\\``", markdown.compute("``Hello `to` World``"));
- Assertions.assertEquals("``Hello \\`to\\` World", markdown.compute("``Hello `to` World"));
- Assertions.assertEquals("\\``Hello \\`to\\` World", markdown.compute("\\``Hello `to` World"));
- }
-
- @Test
- public void testBlock()
- {
- Assertions.assertEquals("\\```Hello\\```", markdown.compute("```Hello```"));
- Assertions.assertEquals("```Hello", markdown.compute("```Hello"));
- Assertions.assertEquals("\\```Hello", markdown.compute("\\```Hello"));
-
- Assertions.assertEquals("\\```Hello **World**\\```", markdown.compute("```Hello **World**```"));
- Assertions.assertEquals("```Hello \\*\\*World\\*\\*", markdown.compute("```Hello **World**"));
- Assertions.assertEquals("\\```Hello \\*\\*World\\*\\*", markdown.compute("\\```Hello **World**"));
-
- Assertions.assertEquals("\\```Hello `to` World\\```", markdown.compute("```Hello `to` World```"));
- Assertions.assertEquals("```Hello \\`to\\` World", markdown.compute("```Hello `to` World"));
- Assertions.assertEquals("\\```Hello \\`to\\` World", markdown.compute("\\```Hello `to` World"));
-
- Assertions.assertEquals("\\```java\nTest\\```", markdown.compute("```java\nTest```"));
- }
-
- @Test
- public void testQuote()
- {
- Assertions.assertEquals("\\> Hello > World", markdown.compute("> Hello > World"));
- Assertions.assertEquals("\\> Hello\n\\> World", markdown.compute("> Hello\n> World"));
- Assertions.assertEquals("\\>>> Hello\nWorld", markdown.compute(">>> Hello\nWorld"));
- Assertions.assertEquals("\\>>>\nHello\nWorld", markdown.compute(">>>\nHello\nWorld"));
- Assertions.assertEquals("\\>>>\nHello > World", markdown.compute(">>>\nHello > World"));
- Assertions.assertEquals("\\> \\_Hello \n\\> World\\_", markdown.compute("> _Hello \n> World_"));
- Assertions.assertEquals("Hello\n \\> World", markdown.compute("Hello\n > World"));
- }
-}
-
-class EscapeMarkdownAllTest
-{
- @Test
- public void testAsterisk()
- {
- Assertions.assertEquals("Hello\\*World", MarkdownSanitizer.escape("Hello*World", true));
- Assertions.assertEquals("Hello\\*\\*World", MarkdownSanitizer.escape("Hello**World", true));
- Assertions.assertEquals("Hello\\*\\*\\*World", MarkdownSanitizer.escape("Hello***World", true));
-
- Assertions.assertEquals("Hello\\*World", MarkdownSanitizer.escape("Hello\\*World", true));
- Assertions.assertEquals("Hello\\*\\*World", MarkdownSanitizer.escape("Hello\\*\\*World", true));
- Assertions.assertEquals("Hello\\*\\*\\*World", MarkdownSanitizer.escape("Hello\\*\\*\\*World", true));
- }
-
- @Test
- public void testUnderscore()
- {
- Assertions.assertEquals("Hello\\_World", MarkdownSanitizer.escape("Hello_World", true));
- Assertions.assertEquals("Hello\\_\\_World", MarkdownSanitizer.escape("Hello__World", true));
- Assertions.assertEquals("Hello\\_\\_\\_World", MarkdownSanitizer.escape("Hello___World", true));
-
- Assertions.assertEquals("Hello\\_World", MarkdownSanitizer.escape("Hello\\_World", true));
- Assertions.assertEquals("Hello\\_\\_World", MarkdownSanitizer.escape("Hello\\_\\_World", true));
- Assertions.assertEquals("Hello\\_\\_\\_World", MarkdownSanitizer.escape("Hello\\_\\_\\_World", true));
- }
-
- @Test
- public void testCodeBlock()
- {
- Assertions.assertEquals("Hello\\`World", MarkdownSanitizer.escape("Hello`World", true));
- Assertions.assertEquals("Hello\\`\\`World", MarkdownSanitizer.escape("Hello``World", true));
- Assertions.assertEquals("Hello\\`\\`\\`World", MarkdownSanitizer.escape("Hello```World", true));
-
- Assertions.assertEquals("Hello\\`World", MarkdownSanitizer.escape("Hello\\`World", true));
- Assertions.assertEquals("Hello\\`\\`World", MarkdownSanitizer.escape("Hello\\`\\`World", true));
- Assertions.assertEquals("Hello\\`\\`\\`World", MarkdownSanitizer.escape("Hello\\`\\`\\`World", true));
- }
-
- @Test
- public void testSpoiler()
- {
- Assertions.assertEquals("Hello\\|\\|World", MarkdownSanitizer.escape("Hello||World", true));
- Assertions.assertEquals("Hello|World", MarkdownSanitizer.escape("Hello|World", true));
-
- Assertions.assertEquals("Hello\\|\\|World", MarkdownSanitizer.escape("Hello\\|\\|World", true));
- Assertions.assertEquals("Hello\\|World", MarkdownSanitizer.escape("Hello\\|World", true));
- }
-
- @Test
- public void testStrike()
- {
- Assertions.assertEquals("Hello\\~\\~World", MarkdownSanitizer.escape("Hello~~World", true));
- Assertions.assertEquals("Hello\\~\\~World", MarkdownSanitizer.escape("Hello\\~\\~World", true));
- }
-
- @Test
- public void testQuote()
- {
- Assertions.assertEquals("\\> Hello World", MarkdownSanitizer.escape("> Hello World", true));
- Assertions.assertEquals(">Hello World", MarkdownSanitizer.escape(">Hello World", true));
- Assertions.assertEquals("\\>\\>\\> Hello World", MarkdownSanitizer.escape(">>> Hello World", true));
- Assertions.assertEquals(">>>Hello World", MarkdownSanitizer.escape(">>>Hello World", true));
- Assertions.assertEquals("\\>\\>\\> Hello > World\n\\> Hello >>> World\n<@12345> > Hello\n \\> Hello world", MarkdownSanitizer.escape(">>> Hello > World\n> Hello >>> World\n<@12345> > Hello\n > Hello world", true));
-
- Assertions.assertEquals("\\> Hello World", MarkdownSanitizer.escape("\\> Hello World", true));
- Assertions.assertEquals("\\>\\>\\> Hello World", MarkdownSanitizer.escape("\\>\\>\\> Hello World", true));
- Assertions.assertEquals("Hello > World", MarkdownSanitizer.escape("Hello > World"));
- Assertions.assertEquals("Hello\n \\> World", MarkdownSanitizer.escape("Hello\n > World"));
- Assertions.assertEquals("Hello\n\\> World", MarkdownSanitizer.escape("Hello\n> World"));
- }
-}
diff --git a/src/test/java/MarkdownUtilTest.java b/src/test/java/MarkdownUtilTest.java
deleted file mode 100644
index 2501e4f2bbf..00000000000
--- a/src/test/java/MarkdownUtilTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.
- */
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import static net.dv8tion.jda.api.utils.MarkdownUtil.*;
-
-public class MarkdownUtilTest
-{
- @Test
- public void testBold()
- {
- Assertions.assertEquals("**Hello World**", bold("Hello World"));
- Assertions.assertEquals("**Hello \\*\\*Test\\*\\* World**", bold("Hello **Test** World"));
- Assertions.assertEquals("**Hello *Test* World**", bold("Hello *Test* World"));
- }
-
- @Test
- public void testItalics()
- {
- Assertions.assertEquals("_Hello World_", italics("Hello World"));
- Assertions.assertEquals("_Hello \\_Test\\_ World_", italics("Hello _Test_ World"));
- Assertions.assertEquals("_Hello __Test__ World_", italics("Hello __Test__ World"));
- }
-
- @Test
- public void testUnderline()
- {
- Assertions.assertEquals("__Hello World__", underline("Hello World"));
- Assertions.assertEquals("__Hello \\_\\_Test\\_\\_ World__", underline("Hello __Test__ World"));
- Assertions.assertEquals("__Hello _Test_ World__", underline("Hello _Test_ World"));
- }
-
- @Test
- public void testMonospace()
- {
- Assertions.assertEquals("`Hello World`", monospace("Hello World"));
- Assertions.assertEquals("`Hello \\`Test\\` World`", monospace("Hello `Test` World"));
- Assertions.assertEquals("`Hello ``Test`` World`", monospace("Hello ``Test`` World"));
- }
-
- @Test
- public void testCodeblock()
- {
- Assertions.assertEquals("```java\nHello World```", codeblock("java", "Hello World"));
- Assertions.assertEquals("```java\nHello \\```java\nTest\\``` World```", codeblock("java", "Hello ```java\nTest``` World"));
- Assertions.assertEquals("```java\nHello `Test` World```", codeblock("java", "Hello `Test` World"));
-
- Assertions.assertEquals("```Hello World```", codeblock("Hello World"));
- Assertions.assertEquals("```Hello \\```java\nTest\\``` World```", codeblock("Hello ```java\nTest``` World"));
- Assertions.assertEquals("```Hello `Test` World```", codeblock("Hello `Test` World"));
- }
-
- @Test
- public void testSpoiler()
- {
- Assertions.assertEquals("||Hello World||", spoiler("Hello World"));
- Assertions.assertEquals("||Hello \\||Test\\|| World||", spoiler("Hello ||Test|| World"));
- Assertions.assertEquals("||Hello |Test| World||", spoiler("Hello |Test| World"));
- }
-
- @Test
- public void testStrike()
- {
- Assertions.assertEquals("~~Hello World~~", strike("Hello World"));
- Assertions.assertEquals("~~Hello \\~~Test\\~~ World~~", strike("Hello ~~Test~~ World"));
- Assertions.assertEquals("~~Hello ~Test~ World~~", strike("Hello ~Test~ World"));
- }
-
- @Test
- public void testQuote()
- {
- Assertions.assertEquals("> Hello World", quote("Hello World"));
- Assertions.assertEquals("> Hello \n> \\> Test World", quote("Hello \n> Test World"));
- Assertions.assertEquals("> Hello > Test World", quote("Hello > Test World"));
- }
-
- @Test
- public void testQuoteBlock()
- {
- Assertions.assertEquals(">>> Hello World", quoteBlock("Hello World"));
- Assertions.assertEquals(">>> Hello \n>>> Test World", quoteBlock("Hello \n>>> Test World"));
- }
-
- @Test
- public void testMaskedLink()
- {
- Assertions.assertEquals("[Hello](World)", maskedLink("Hello", "World"));
- Assertions.assertEquals("[Hello](World%29)", maskedLink("Hello", "World)"));
- Assertions.assertEquals("[Hello\\]](World%29)", maskedLink("Hello]", "World)"));
- }
-}
diff --git a/src/test/java/net/dv8tion/jda/entities/MessageSerializationTest.java b/src/test/java/net/dv8tion/jda/entities/MessageSerializationTest.java
deleted file mode 100644
index d605fe5fa57..00000000000
--- a/src/test/java/net/dv8tion/jda/entities/MessageSerializationTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.entities;
-
-import net.dv8tion.jda.api.EmbedBuilder;
-import net.dv8tion.jda.api.entities.EmbedType;
-import net.dv8tion.jda.api.entities.MessageEmbed;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-public class MessageSerializationTest
-{
- @Test
- void testEmbedSerialization()
- {
- EmbedBuilder builder = new EmbedBuilder();
- builder.setDescription("Description Text");
- builder.setTitle("Title Text", "https://example.com/title");
- builder.setAuthor("Author Text", "https://example.com/author", "https://example.com/author_icon");
- builder.setFooter("Footer Text", "https://example.com/footer_icon");
- builder.setImage("https://example.com/image");
- builder.setThumbnail("https://example.com/thumbnail");
- builder.addField("Field 1", "Field 1 Text", true);
- builder.addField("Field 2", "Field 2 Text", false);
- builder.addField("Field 3", "Field 3 Text", true);
-
- MessageEmbed embed = builder.build();
-
- MessageEmbed dataEmbed = EmbedBuilder.fromData(embed.toData()).build();
-
- Assertions.assertEquals(embed.getType(), dataEmbed.getType());
- Assertions.assertEquals(EmbedType.RICH, embed.getType());
-
- Assertions.assertEquals(embed.getDescription(), dataEmbed.getDescription());
- Assertions.assertEquals(embed.getTitle(), dataEmbed.getTitle());
- Assertions.assertEquals(embed.getUrl(), dataEmbed.getUrl());
- Assertions.assertEquals(embed.getAuthor(), dataEmbed.getAuthor());
- Assertions.assertEquals(embed.getFooter(), dataEmbed.getFooter());
- Assertions.assertEquals(embed.getImage(), dataEmbed.getImage());
- Assertions.assertEquals(embed.getThumbnail(), dataEmbed.getThumbnail());
- Assertions.assertEquals(embed.getFields(), dataEmbed.getFields());
-
- Assertions.assertEquals(embed, dataEmbed);
-
- Assertions.assertEquals("Description Text", dataEmbed.getDescription());
- Assertions.assertEquals("Title Text", dataEmbed.getTitle());
- Assertions.assertEquals("https://example.com/title", dataEmbed.getUrl());
- Assertions.assertEquals("Author Text", dataEmbed.getAuthor().getName());
- Assertions.assertEquals("https://example.com/author", dataEmbed.getAuthor().getUrl());
- Assertions.assertEquals("https://example.com/author_icon", dataEmbed.getAuthor().getIconUrl());
- Assertions.assertEquals("Footer Text", dataEmbed.getFooter().getText());
- Assertions.assertEquals("https://example.com/footer_icon", dataEmbed.getFooter().getIconUrl());
- Assertions.assertEquals("https://example.com/image", dataEmbed.getImage().getUrl());
- Assertions.assertEquals("https://example.com/thumbnail", dataEmbed.getThumbnail().getUrl());
- Assertions.assertEquals(3, dataEmbed.getFields().size());
- Assertions.assertEquals("Field 1", dataEmbed.getFields().get(0).getName());
- Assertions.assertEquals("Field 1 Text", dataEmbed.getFields().get(0).getValue());
- Assertions.assertTrue(dataEmbed.getFields().get(0).isInline());
- Assertions.assertEquals("Field 2", dataEmbed.getFields().get(1).getName());
- Assertions.assertEquals("Field 2 Text", dataEmbed.getFields().get(1).getValue());
- Assertions.assertFalse(dataEmbed.getFields().get(1).isInline());
- Assertions.assertEquals("Field 3", dataEmbed.getFields().get(2).getName());
- Assertions.assertEquals("Field 3 Text", dataEmbed.getFields().get(2).getValue());
- Assertions.assertTrue(dataEmbed.getFields().get(2).isInline());
- }
-}
diff --git a/src/test/java/net/dv8tion/jda/entitystring/EntityStringTest.java b/src/test/java/net/dv8tion/jda/entitystring/EntityStringTest.java
deleted file mode 100644
index 2ac411cda1b..00000000000
--- a/src/test/java/net/dv8tion/jda/entitystring/EntityStringTest.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * 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.entitystring;
-
-import net.dv8tion.jda.api.entities.channel.ChannelType;
-import net.dv8tion.jda.internal.utils.EntityString;
-import org.junit.jupiter.api.MethodOrderer;
-import org.junit.jupiter.api.Order;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestMethodOrder;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
-public class EntityStringTest
-{
- @Test
- @Order(1)
- public void testSimple()
- {
- assertEquals("AnEntity", new EntityString(new AnEntity()).toString());
- assertEquals("AnEntity:AName", new EntityString(new AnEntity()).setName("AName").toString());
- }
-
- @Test
- @Order(2)
- public void testClassNameAsString()
- {
- assertEquals("NotAnEntity", new EntityString("NotAnEntity").toString());
- assertEquals("NotAnEntity:AName", new EntityString("NotAnEntity").setName("AName").toString());
- }
-
- @Test
- @Order(3)
- public void testType()
- {
- assertEquals("AnEntity[AType]", new EntityString(new AnEntity()).setType("AType").toString());
- assertEquals("AnEntity[AType]:AName", new EntityString(new AnEntity()).setType("AType").setName("AName").toString());
- assertEquals("AnEntity[NEWS]:AName", new EntityString(new AnEntity()).setType(ChannelType.NEWS).setName("AName").toString());
- }
-
- @Test
- @Order(4)
- public void testMetadata()
- {
- assertEquals("AnEntity(Metadata1)", new EntityString(new AnEntity()).addMetadata(null, "Metadata1").toString());
- assertEquals("AnEntity(MetaKey=Metadata1)", new EntityString(new AnEntity()).addMetadata("MetaKey", "Metadata1").toString());
- assertEquals("AnEntity(MetaKey=42)", new EntityString(new AnEntity()).addMetadata("MetaKey", 42).toString());
- assertEquals("AnEntity(MetaKey1=Metadata1, MetaKey2=Metadata2)", new EntityString(new AnEntity())
- .addMetadata("MetaKey1", "Metadata1")
- .addMetadata("MetaKey2", "Metadata2")
- .toString());
- }
-
- @Test
- @Order(5)
- public void testAll()
- {
- assertEquals("AnEntity:AName(Metadata1)", new EntityString(new AnEntity())
- .setName("AName")
- .addMetadata(null, "Metadata1")
- .toString());
- assertEquals("AnEntity:AName(MetaKey=Metadata1)", new EntityString(new AnEntity())
- .setName("AName")
- .addMetadata("MetaKey", "Metadata1")
- .toString());
- assertEquals("AnEntity:AName(MetaKey=42)", new EntityString(new AnEntity())
- .setName("AName")
- .addMetadata("MetaKey", 42)
- .toString());
- assertEquals("AnEntity:AName(MetaKey1=Metadata1, MetaKey2=Metadata2)", new EntityString(new AnEntity())
- .setName("AName")
- .addMetadata("MetaKey1", "Metadata1")
- .addMetadata("MetaKey2", "Metadata2")
- .toString());
-
- assertEquals("AnEntity[Type]:AName(Metadata1)", new EntityString(new AnEntity())
- .setName("AName")
- .setType("Type")
- .addMetadata(null, "Metadata1")
- .toString());
- assertEquals("AnEntity[Type]:AName(MetaKey=Metadata1)", new EntityString(new AnEntity())
- .setName("AName")
- .setType("Type")
- .addMetadata("MetaKey", "Metadata1")
- .toString());
- assertEquals("AnEntity[Type]:AName(MetaKey=42)", new EntityString(new AnEntity())
- .setName("AName")
- .setType("Type")
- .addMetadata("MetaKey", 42)
- .toString());
- assertEquals("AnEntity[Type]:AName(MetaKey1=Metadata1, MetaKey2=Metadata2)", new EntityString(new AnEntity())
- .setName("AName")
- .setType("Type")
- .addMetadata("MetaKey1", "Metadata1")
- .addMetadata("MetaKey2", "Metadata2")
- .toString());
- }
-
- @Test
- @Order(6)
- public void testSimpleSnowflake()
- {
- assertEquals("ASnowflake(id=42)", new EntityString(new ASnowflake()).toString());
- assertEquals("ASnowflake:AName(id=42)", new EntityString(new ASnowflake()).setName("AName").toString());
- }
-
- @Test
- @Order(7)
- public void testTypeSnowflake()
- {
- assertEquals("ASnowflake[AType](id=42)", new EntityString(new ASnowflake()).setType("AType").toString());
- assertEquals("ASnowflake[AType]:AName(id=42)", new EntityString(new ASnowflake()).setType("AType").setName("AName").toString());
- assertEquals("ASnowflake[NEWS]:AName(id=42)", new EntityString(new ASnowflake()).setType(ChannelType.NEWS).setName("AName").toString());
- }
-
- @Test
- @Order(8)
- public void testMetadataSnowflake()
- {
- assertEquals("ASnowflake(id=42, Metadata1)", new EntityString(new ASnowflake()).addMetadata(null, "Metadata1").toString());
- assertEquals("ASnowflake(id=42, MetaKey=Metadata1)", new EntityString(new ASnowflake()).addMetadata("MetaKey", "Metadata1").toString());
- assertEquals("ASnowflake(id=42, MetaKey=42)", new EntityString(new ASnowflake()).addMetadata("MetaKey", 42).toString());
- assertEquals("ASnowflake(id=42, MetaKey1=Metadata1, MetaKey2=Metadata2)", new EntityString(new ASnowflake())
- .addMetadata("MetaKey1", "Metadata1")
- .addMetadata("MetaKey2", "Metadata2")
- .toString());
- }
-
- @Test
- @Order(9)
- public void testAllSnowflake()
- {
- assertEquals("ASnowflake:AName(id=42, Metadata1)", new EntityString(new ASnowflake())
- .setName("AName")
- .addMetadata(null, "Metadata1")
- .toString());
- assertEquals("ASnowflake:AName(id=42, MetaKey=Metadata1)", new EntityString(new ASnowflake())
- .setName("AName")
- .addMetadata("MetaKey", "Metadata1")
- .toString());
- assertEquals("ASnowflake:AName(id=42, MetaKey=42)", new EntityString(new ASnowflake())
- .setName("AName")
- .addMetadata("MetaKey", 42)
- .toString());
- assertEquals("ASnowflake:AName(id=42, MetaKey1=Metadata1, MetaKey2=Metadata2)", new EntityString(new ASnowflake())
- .setName("AName")
- .addMetadata("MetaKey1", "Metadata1")
- .addMetadata("MetaKey2", "Metadata2")
- .toString());
-
- assertEquals("ASnowflake[Type]:AName(id=42, Metadata1)", new EntityString(new ASnowflake())
- .setName("AName")
- .setType("Type")
- .addMetadata(null, "Metadata1")
- .toString());
- assertEquals("ASnowflake[Type]:AName(id=42, MetaKey=Metadata1)", new EntityString(new ASnowflake())
- .setName("AName")
- .setType("Type")
- .addMetadata("MetaKey", "Metadata1")
- .toString());
- assertEquals("ASnowflake[Type]:AName(id=42, MetaKey=42)", new EntityString(new ASnowflake())
- .setName("AName")
- .setType("Type")
- .addMetadata("MetaKey", 42)
- .toString());
- assertEquals("ASnowflake[Type]:AName(id=42, MetaKey1=Metadata1, MetaKey2=Metadata2)", new EntityString(new ASnowflake())
- .setName("AName")
- .setType("Type")
- .addMetadata("MetaKey1", "Metadata1")
- .addMetadata("MetaKey2", "Metadata2")
- .toString());
- }
-}
diff --git a/src/test/java/net/dv8tion/jda/test/ChecksHelper.java b/src/test/java/net/dv8tion/jda/test/ChecksHelper.java
new file mode 100644
index 00000000000..142f2792d93
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/ChecksHelper.java
@@ -0,0 +1,96 @@
+/*
+ * 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.test;
+
+import net.dv8tion.jda.test.assertions.checks.*;
+import org.junit.jupiter.api.function.ThrowingConsumer;
+
+import java.time.Duration;
+import java.util.regex.Pattern;
+
+public class ChecksHelper
+{
+ public static String tooLongError(String name, int maxLength, String value)
+ {
+ return name + " may not be longer than " + maxLength + " characters! Provided: \"" + value + "\"";
+ }
+
+ public static String notInRangeError(String name, int minLength, int maxLength, String value)
+ {
+ return name + " must be between " + minLength + " and " + maxLength + " characters long! Provided: \"" + value + "\"";
+ }
+
+ public static String isNullError(String name)
+ {
+ return name + " may not be null";
+ }
+
+ public static String isEmptyError(String name)
+ {
+ return name + " may not be empty";
+ }
+
+ public static String isBlankError(String name)
+ {
+ return name + " may not be blank";
+ }
+
+ public static String isNotLowercase(String name, String value)
+ {
+ return name + " must be lowercase only! Provided: \"" + value + "\"";
+ }
+
+ public static String notRegexMatch(String name, Pattern pattern, String value)
+ {
+ return name + " must match regex ^" + pattern + "$. Provided: \"" + value + "\"";
+ }
+
+ public static String isNegativeError(String name)
+ {
+ return name + " may not be negative";
+ }
+
+ public static String notPositiveError(String name)
+ {
+ return name + " may not be negative or zero";
+ }
+
+ public static StringChecksAssertions assertStringChecks(String name, ThrowingConsumer callable)
+ {
+ return new StringChecksAssertions(name, callable);
+ }
+
+ public static > EnumChecksAssertions assertEnumChecks(String name, ThrowingConsumer callable)
+ {
+ return new EnumChecksAssertions<>(name, callable);
+ }
+
+ public static DurationChecksAssertions assertDurationChecks(String name, ThrowingConsumer callable)
+ {
+ return new DurationChecksAssertions(name, callable);
+ }
+
+ public static LongChecksAssertions assertLongChecks(String name, ThrowingConsumer callable)
+ {
+ return new LongChecksAssertions(name, callable);
+ }
+
+ public static SimpleChecksAssertions assertChecks(String name, ThrowingConsumer callable)
+ {
+ return new SimpleChecksAssertions<>(name, callable);
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/Constants.java b/src/test/java/net/dv8tion/jda/test/Constants.java
new file mode 100644
index 00000000000..4dc3e449bf7
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/Constants.java
@@ -0,0 +1,25 @@
+/*
+ * 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.test;
+
+public interface Constants
+{
+ long GUILD_ID = 125227483518861312L;
+ long CHANNEL_ID = 125227483518861312L;
+ long MINN_USER_ID = 86699011792191488L;
+ long BUTLER_USER_ID = 150203841827045376L;
+}
diff --git a/src/test/java/net/dv8tion/jda/test/IntegrationTest.java b/src/test/java/net/dv8tion/jda/test/IntegrationTest.java
new file mode 100644
index 00000000000..ebb0952457e
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/IntegrationTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.test;
+
+import net.dv8tion.jda.api.requests.Request;
+import net.dv8tion.jda.api.requests.Response;
+import net.dv8tion.jda.api.requests.RestAction;
+import net.dv8tion.jda.api.utils.data.DataArray;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.internal.JDAImpl;
+import net.dv8tion.jda.internal.entities.EntityBuilder;
+import net.dv8tion.jda.internal.requests.Requester;
+import net.dv8tion.jda.internal.requests.RestActionImpl;
+import net.dv8tion.jda.test.assertions.restaction.RestActionAssertions;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.TestInfo;
+import org.mockito.Mock;
+
+import javax.annotation.CheckReturnValue;
+import javax.annotation.Nonnull;
+import java.util.Random;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.function.Consumer;
+
+import static org.mockito.Mockito.*;
+import static org.mockito.MockitoAnnotations.openMocks;
+
+public class IntegrationTest
+{
+ protected Random random = new Random();
+ @Mock
+ protected JDAImpl jda;
+ @Mock
+ protected Requester requester;
+ @Mock
+ protected ScheduledExecutorService scheduledExecutorService;
+
+ private AutoCloseable closeable;
+ private int expectedRequestCount;
+
+ @BeforeEach
+ protected final void setup()
+ {
+ random.setSeed(4242);
+ expectedRequestCount = 0;
+ closeable = openMocks(this);
+ when(jda.getRequester()).thenReturn(requester);
+ when(jda.getEntityBuilder()).thenReturn(new EntityBuilder(jda));
+ }
+
+ @AfterEach
+ protected final void teardown(TestInfo testInfo) throws Exception
+ {
+ verify(
+ requester,
+ times(expectedRequestCount).description("Requests sent by " + testInfo.getDisplayName())
+ ).request(any());
+ closeable.close();
+ }
+
+ @Nonnull
+ protected DataObject normalizeRequestBody(@Nonnull DataObject body)
+ {
+ return body;
+ }
+
+ @CheckReturnValue
+ protected RestActionAssertions assertThatRequestFrom(@Nonnull RestAction> action)
+ {
+ expectedRequestCount += 1;
+ return RestActionAssertions.assertThatNextAction(requester, action)
+ .withNormalizedBody(this::normalizeRequestBody);
+ }
+
+ protected void whenSuccess(RestActionImpl action, DataArray array, Consumer assertion)
+ {
+ Response response = mock();
+ Request request = mock();
+
+ when(response.isOk()).thenReturn(true);
+ when(response.getArray()).thenReturn(array);
+
+ doNothing().when(request).onSuccess(assertArg(assertion));
+
+ action.handleResponse(response, request);
+
+ verify(request, times(1)).onSuccess(any());
+ }
+
+ protected String randomSnowflake()
+ {
+ return Long.toUnsignedString(random.nextLong());
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/PrettyRepresentation.java b/src/test/java/net/dv8tion/jda/test/PrettyRepresentation.java
new file mode 100644
index 00000000000..5080ac4815d
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/PrettyRepresentation.java
@@ -0,0 +1,34 @@
+/*
+ * 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.test;
+
+import net.dv8tion.jda.api.utils.data.DataArray;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import org.assertj.core.presentation.StandardRepresentation;
+
+public class PrettyRepresentation extends StandardRepresentation
+{
+ @Override
+ protected String fallbackToStringOf(Object object)
+ {
+ if (object instanceof DataObject)
+ return ((DataObject) object).toPrettyString();
+ else if (object instanceof DataArray)
+ return ((DataArray) object).toPrettyString();
+ return object.toString();
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/assertions/checks/AbstractChecksAssertions.java b/src/test/java/net/dv8tion/jda/test/assertions/checks/AbstractChecksAssertions.java
new file mode 100644
index 00000000000..f8f964a7c4a
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/assertions/checks/AbstractChecksAssertions.java
@@ -0,0 +1,48 @@
+/*
+ * 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.test.assertions.checks;
+
+import org.junit.jupiter.api.function.ThrowingConsumer;
+
+import static net.dv8tion.jda.test.ChecksHelper.isNullError;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+public class AbstractChecksAssertions>
+{
+ protected final String name;
+ protected final ThrowingConsumer callable;
+
+ public AbstractChecksAssertions(String name, ThrowingConsumer callable)
+ {
+ this.name = name;
+ this.callable = callable;
+ }
+
+ public S checksNotNull()
+ {
+ return throwsFor(null, isNullError(name));
+ }
+
+ @SuppressWarnings("unchecked")
+ public S throwsFor(T input, String expectedError)
+ {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> callable.accept(input))
+ .withMessage(expectedError);
+ return (S) this;
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/assertions/checks/DurationChecksAssertions.java b/src/test/java/net/dv8tion/jda/test/assertions/checks/DurationChecksAssertions.java
new file mode 100644
index 00000000000..ba50121459f
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/assertions/checks/DurationChecksAssertions.java
@@ -0,0 +1,57 @@
+/*
+ * 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.test.assertions.checks;
+
+import org.junit.jupiter.api.function.ThrowingConsumer;
+
+import java.time.Duration;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+import static net.dv8tion.jda.internal.utils.Helpers.durationToString;
+import static net.dv8tion.jda.test.ChecksHelper.isNegativeError;
+import static net.dv8tion.jda.test.ChecksHelper.notPositiveError;
+
+public class DurationChecksAssertions extends AbstractChecksAssertions
+{
+ public DurationChecksAssertions(String name, ThrowingConsumer callable)
+ {
+ super(name, callable);
+ }
+
+ public DurationChecksAssertions checksNotNegative()
+ {
+ throwsFor(Duration.ofSeconds(-1), isNegativeError(name));
+ return this;
+ }
+
+ public DurationChecksAssertions checksPositive()
+ {
+ throwsFor(Duration.ofSeconds(-1), notPositiveError(name));
+ throwsFor(Duration.ZERO, notPositiveError(name));
+ return this;
+ }
+
+ public DurationChecksAssertions checksNotLonger(Duration maxDuration, TimeUnit resolution)
+ {
+ Duration input = maxDuration.plusSeconds(resolution.toSeconds(1));
+ throwsFor(input,
+ String.format(Locale.ROOT, "%s may not be longer than %s. Provided: %s",
+ name, durationToString(maxDuration, resolution), durationToString(input, resolution)));
+ return this;
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/assertions/checks/EnumChecksAssertions.java b/src/test/java/net/dv8tion/jda/test/assertions/checks/EnumChecksAssertions.java
new file mode 100644
index 00000000000..1ca7712fdf9
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/assertions/checks/EnumChecksAssertions.java
@@ -0,0 +1,33 @@
+/*
+ * 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.test.assertions.checks;
+
+import org.junit.jupiter.api.function.ThrowingConsumer;
+
+public class EnumChecksAssertions> extends AbstractChecksAssertions>
+{
+ public EnumChecksAssertions(String name, ThrowingConsumer callable)
+ {
+ super(name, callable);
+ }
+
+ public EnumChecksAssertions checkIsNot(E variant)
+ {
+ throwsFor(variant, name + " cannot be " + variant);
+ return this;
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/assertions/checks/LongChecksAssertions.java b/src/test/java/net/dv8tion/jda/test/assertions/checks/LongChecksAssertions.java
new file mode 100644
index 00000000000..6ba7b8bafc5
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/assertions/checks/LongChecksAssertions.java
@@ -0,0 +1,36 @@
+/*
+ * 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.test.assertions.checks;
+
+import org.junit.jupiter.api.function.ThrowingConsumer;
+
+import static net.dv8tion.jda.test.ChecksHelper.notPositiveError;
+
+public class LongChecksAssertions extends AbstractChecksAssertions
+{
+ public LongChecksAssertions(String name, ThrowingConsumer callable)
+ {
+ super(name, callable);
+ }
+
+ public LongChecksAssertions checksPositive()
+ {
+ throwsFor( 0L, notPositiveError(name));
+ throwsFor( -1L, notPositiveError(name));
+ return this;
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/assertions/checks/SimpleChecksAssertions.java b/src/test/java/net/dv8tion/jda/test/assertions/checks/SimpleChecksAssertions.java
new file mode 100644
index 00000000000..91441c2660a
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/assertions/checks/SimpleChecksAssertions.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.test.assertions.checks;
+
+import org.junit.jupiter.api.function.ThrowingConsumer;
+
+public class SimpleChecksAssertions extends AbstractChecksAssertions>
+{
+ public SimpleChecksAssertions(String name, ThrowingConsumer callable)
+ {
+ super(name, callable);
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/assertions/checks/StringChecksAssertions.java b/src/test/java/net/dv8tion/jda/test/assertions/checks/StringChecksAssertions.java
new file mode 100644
index 00000000000..91e89207fd0
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/assertions/checks/StringChecksAssertions.java
@@ -0,0 +1,75 @@
+/*
+ * 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.test.assertions.checks;
+
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.function.ThrowingConsumer;
+
+import java.util.regex.Pattern;
+
+import static net.dv8tion.jda.test.ChecksHelper.*;
+
+public class StringChecksAssertions extends AbstractChecksAssertions
+{
+ public StringChecksAssertions(String name, ThrowingConsumer callable)
+ {
+ super(name, callable);
+ }
+
+ public StringChecksAssertions checksNotEmpty()
+ {
+ throwsFor(null, isNullError(name));
+ throwsFor("", isEmptyError(name));
+ return this;
+ }
+
+ public StringChecksAssertions checksNotBlank()
+ {
+ throwsFor(null, isNullError(name));
+ throwsFor("", isBlankError(name));
+ throwsFor(" ", isBlankError(name));
+ return this;
+ }
+
+ public StringChecksAssertions checksNotLonger(int maxLength)
+ {
+ String invalidInput = StringUtils.repeat("s", maxLength + 1);
+ throwsFor(invalidInput, tooLongError(name, maxLength, invalidInput));
+ return this;
+ }
+
+ public StringChecksAssertions checksLowercaseOnly()
+ {
+ throwsFor("InvalidCasing", isNotLowercase(name, "InvalidCasing"));
+ return this;
+ }
+
+ public StringChecksAssertions checksRange(int minLength, int maxLength)
+ {
+ String tooLong = StringUtils.repeat("s", maxLength + 1);
+ String tooShort = StringUtils.repeat("s", minLength - 1);
+ throwsFor(tooShort, notInRangeError(name, minLength, maxLength, tooShort));
+ throwsFor(tooLong, notInRangeError(name, minLength, maxLength, tooLong));
+ return this;
+ }
+
+ public StringChecksAssertions checksRegex(String input, Pattern regex)
+ {
+ throwsFor(input, notRegexMatch(name, regex, input));
+ return this;
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/assertions/events/EventFiredAssertions.java b/src/test/java/net/dv8tion/jda/test/assertions/events/EventFiredAssertions.java
new file mode 100644
index 00000000000..3cb518678c1
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/assertions/events/EventFiredAssertions.java
@@ -0,0 +1,61 @@
+/*
+ * 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.test.assertions.events;
+
+import net.dv8tion.jda.internal.JDAImpl;
+import org.junit.jupiter.api.function.ThrowingConsumer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.assertArg;
+import static org.mockito.Mockito.*;
+
+public class EventFiredAssertions
+{
+ private final Class eventType;
+ private final JDAImpl jda;
+ private final List> assertions = new ArrayList<>();
+
+ public EventFiredAssertions(Class eventType, JDAImpl jda)
+ {
+ this.eventType = eventType;
+ this.jda = jda;
+ }
+
+ public EventFiredAssertions hasGetterWithValueEqualTo(Function getter, V value)
+ {
+ assertions.add(event -> assertThat(getter.apply(event)).isEqualTo(value));
+ return this;
+ }
+
+ public void isFiredBy(Runnable runnable)
+ {
+ doNothing().when(jda).handleEvent(assertArg(arg -> {
+ assertThat(arg).isInstanceOf(eventType);
+ T casted = eventType.cast(arg);
+ for (ThrowingConsumer assertion : assertions)
+ assertion.accept(casted);
+ }));
+
+ runnable.run();
+
+ verify(jda, times(1)).handleEvent(any());
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/assertions/restaction/RestActionAssertions.java b/src/test/java/net/dv8tion/jda/test/assertions/restaction/RestActionAssertions.java
new file mode 100644
index 00000000000..3e124582917
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/assertions/restaction/RestActionAssertions.java
@@ -0,0 +1,161 @@
+/*
+ * 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.test.assertions.restaction;
+
+import net.dv8tion.jda.api.requests.Method;
+import net.dv8tion.jda.api.requests.Request;
+import net.dv8tion.jda.api.requests.RestAction;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.internal.requests.Requester;
+import net.dv8tion.jda.internal.utils.EncodingUtil;
+import org.jetbrains.annotations.Contract;
+import org.mockito.ThrowingConsumer;
+
+import javax.annotation.CheckReturnValue;
+import javax.annotation.Nonnull;
+import java.util.*;
+import java.util.function.Consumer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.assertArg;
+import static org.mockito.Mockito.doNothing;
+
+public class RestActionAssertions implements ThrowingConsumer>
+{
+ private final RestAction> action;
+ private final List>> assertions = new ArrayList<>();
+ private Consumer super DataObject> normalizeRequestBody = (v) -> {};
+
+ public RestActionAssertions(RestAction> action)
+ {
+ this.action = action;
+ }
+
+ @CheckReturnValue
+ public static RestActionAssertions assertThatNextAction(Requester requester, RestAction> action)
+ {
+ RestActionAssertions assertions = new RestActionAssertions(action);
+ doNothing().when(requester).request(assertArg(assertions::acceptThrows));
+ return assertions;
+ }
+
+ public void whenQueueCalled()
+ {
+ action.queue();
+ }
+
+ @CheckReturnValue
+ @Contract("_->this")
+ public RestActionAssertions withNormalizedBody(@Nonnull Consumer super DataObject> normalizer)
+ {
+ this.normalizeRequestBody = normalizer;
+ return this;
+ }
+
+ @CheckReturnValue
+ @Contract("_->this")
+ public RestActionAssertions checkAssertions(@Nonnull ThrowingConsumer> assertion)
+ {
+ assertions.add(assertion);
+ return this;
+ }
+
+ @CheckReturnValue
+ @Contract("_->this")
+ public RestActionAssertions hasBodyEqualTo(@Nonnull DataObject expected)
+ {
+ return checkAssertions(request -> {
+ Object body = request.getRawBody();
+ assertThat(body)
+ .isNotNull()
+ .isInstanceOf(DataObject.class);
+
+ DataObject dataObject = (DataObject) body;
+ normalizeRequestBody.accept(dataObject);
+ normalizeRequestBody.accept(expected);
+
+ assertThat(dataObject.toPrettyString())
+ .as("RestAction should send request using expected request body")
+ .isEqualTo(expected.toPrettyString());
+ });
+ }
+
+ @CheckReturnValue
+ @Contract("_->this")
+ public RestActionAssertions hasMethod(@Nonnull Method method)
+ {
+ return checkAssertions(request ->
+ assertThat(request.getRoute().getMethod())
+ .as("RestAction should send request using expected HTTP Method")
+ .isEqualTo(method)
+ );
+ }
+
+ @CheckReturnValue
+ @Contract("_->this")
+ public RestActionAssertions hasCompiledRoute(@Nonnull String route)
+ {
+ return checkAssertions(request ->
+ assertThat(request.getRoute().getCompiledRoute())
+ .as("RestAction should send request using expected REST endpoint")
+ .isEqualTo(route)
+ );
+ }
+
+ @CheckReturnValue
+ @Contract("_->this")
+ public RestActionAssertions hasQueryParams(@Nonnull Object... params)
+ {
+ assertThat(params.length).isEven();
+
+ Map expectedQuery = new LinkedHashMap<>();
+
+ for (int i = 0; i < params.length; i += 2)
+ expectedQuery.put(String.valueOf(params[i]), EncodingUtil.encodeUTF8(String.valueOf(params[i + 1])));
+
+ return checkAssertions(request -> {
+ Map actualQuery = new LinkedHashMap<>();
+ String[] query = request.getRoute().getCompiledRoute().split("[?&=]");
+
+ for (int i = 1; i < query.length; i += 2)
+ actualQuery.put(query[i], query[i + 1]);
+
+ assertThat(actualQuery)
+ .containsExactlyEntriesOf(expectedQuery);
+ });
+ }
+
+ @CheckReturnValue
+ @Contract("_->this")
+ public RestActionAssertions hasAuditReason(@Nonnull String reason)
+ {
+ return checkAssertions(request ->
+ assertThat(request.getHeaders())
+ .as("RestAction should set header")
+ .contains(new AbstractMap.SimpleEntry<>("X-Audit-Log-Reason", reason))
+ );
+ }
+
+ @Override
+ public void acceptThrows(Request> request) throws Throwable
+ {
+ for (ThrowingConsumer> assertion : assertions)
+ {
+ assertion.acceptThrows(request);
+ }
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/ChannelConsistencyTest.java b/src/test/java/net/dv8tion/jda/test/compliance/ChannelConsistencyComplianceTest.java
similarity index 72%
rename from src/test/java/net/dv8tion/jda/ChannelConsistencyTest.java
rename to src/test/java/net/dv8tion/jda/test/compliance/ChannelConsistencyComplianceTest.java
index dac29156fa6..f2bab544279 100644
--- a/src/test/java/net/dv8tion/jda/ChannelConsistencyTest.java
+++ b/src/test/java/net/dv8tion/jda/test/compliance/ChannelConsistencyComplianceTest.java
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-package net.dv8tion.jda;
+package net.dv8tion.jda.test.compliance;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.attribute.IGuildChannelContainer;
import net.dv8tion.jda.api.entities.channel.concrete.Category;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
@@ -30,7 +29,10 @@
import java.util.Set;
import java.util.stream.Collectors;
-public class ChannelConsistencyTest
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+public class ChannelConsistencyComplianceTest
{
private static Set getMethodNames(Class> clazz)
{
@@ -39,11 +41,11 @@ private static Set getMethodNames(Class> clazz)
private static String getChannelName(ChannelType type)
{
- return type.name().substring(0, 1) + type.name().substring(1).toLowerCase(Locale.ROOT);
+ return type.name().charAt(0) + type.name().substring(1).toLowerCase(Locale.ROOT);
}
@Test
- public void checkCreateChannelMethods()
+ void checkCreateChannelMethods()
{
Set guildMethods = getMethodNames(Guild.class);
@@ -57,7 +59,7 @@ public void checkCreateChannelMethods()
{
String channelName = getChannelName(type);
String methodName = "create" + channelName + "Channel";
- Assertions.assertTrue(guildMethods.contains(methodName), "Missing method Guild#" + methodName);
+ assertThat(guildMethods).contains(methodName);
}
Set categoryMethods = getMethodNames(Category.class);
@@ -66,12 +68,12 @@ public void checkCreateChannelMethods()
{
String channelName = getChannelName(type);
String methodName = "create" + channelName + "Channel";
- Assertions.assertTrue(categoryMethods.contains(methodName), "Missing method Category#" + methodName);
+ assertThat(categoryMethods).contains(methodName);
}
}
@Test
- public void checkCacheAccessMethods()
+ void checkCacheAccessMethods()
{
Set jdaMethods = getMethodNames(IGuildChannelContainer.class);
Set categoryMethods = getMethodNames(Category.class);
@@ -87,22 +89,22 @@ public void checkCacheAccessMethods()
String channelName = getChannelName(type);
String methodName = "get" + channelName + "ChannelCache";
- Assertions.assertTrue(jdaMethods.contains(methodName), "Missing method IGuildChannelContainer#" + methodName);
+ assertThat(jdaMethods).contains(methodName);
methodName = "get" + channelName + "ChannelsByName";
- Assertions.assertTrue(jdaMethods.contains(methodName), "Missing method IGuildChannelContainer#" + methodName);
+ assertThat(jdaMethods).contains(methodName);
methodName = "get" + channelName + "ChannelById";
- Assertions.assertTrue(jdaMethods.contains(methodName), "Missing method IGuildChannelContainer#" + methodName);
+ assertThat(jdaMethods).contains(methodName);
methodName = "get" + channelName + "Channels";
- Assertions.assertTrue(jdaMethods.contains(methodName), "Missing method IGuildChannelContainer#" + methodName);
- Assertions.assertTrue(categoryMethods.contains(methodName), "Missing method Category#" + methodName);
+ assertThat(jdaMethods).contains(methodName);
+ assertThat(categoryMethods).contains(methodName);
}
}
@Test
- public void checkManagerExists()
+ void checkManagerExists()
{
EnumSet editable = EnumSet.complementOf(EnumSet.of(
ChannelType.PRIVATE, ChannelType.GROUP, ChannelType.CATEGORY,
@@ -114,9 +116,9 @@ public void checkManagerExists()
{
String channelName = getChannelName(type);
- Assertions.assertDoesNotThrow(() -> {
- Class.forName("net.dv8tion.jda.api.managers.channel.concrete." + channelName + "ChannelManager");
- }, "Missing manager interface for ChannelType." + type);
+ assertThatCode(() ->
+ Class.forName("net.dv8tion.jda.api.managers.channel.concrete." + channelName + "ChannelManager")
+ ).as("Missing manager interface for ChannelType." + type).doesNotThrowAnyException();
}
}
}
diff --git a/src/test/java/net/dv8tion/jda/EventConsistencyTest.java b/src/test/java/net/dv8tion/jda/test/compliance/EventConsistencyComplianceTest.java
similarity index 80%
rename from src/test/java/net/dv8tion/jda/EventConsistencyTest.java
rename to src/test/java/net/dv8tion/jda/test/compliance/EventConsistencyComplianceTest.java
index 73831791de0..8b246c19b7a 100644
--- a/src/test/java/net/dv8tion/jda/EventConsistencyTest.java
+++ b/src/test/java/net/dv8tion/jda/test/compliance/EventConsistencyComplianceTest.java
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package net.dv8tion.jda;
+package net.dv8tion.jda.test.compliance;
import net.dv8tion.jda.api.events.Event;
import net.dv8tion.jda.api.events.GenericEvent;
import net.dv8tion.jda.api.events.UpdateEvent;
import net.dv8tion.jda.api.events.self.SelfUpdateDiscriminatorEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.reflections.Reflections;
@@ -31,7 +30,10 @@
import java.util.HashSet;
import java.util.Set;
-public class EventConsistencyTest
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+public class EventConsistencyComplianceTest
{
static Set> eventTypes;
static Set> excludedTypes;
@@ -61,7 +63,9 @@ void testListenerAdapter()
continue;
String name = type.getSimpleName();
String methodName = "on" + name.substring(0, name.length() - "Event".length());
- Assertions.assertDoesNotThrow(() -> adapter.getDeclaredMethod(methodName, type), "Method for event " + type + " is missing!");
+ assertThatCode(() -> adapter.getDeclaredMethod(methodName, type))
+ .as("Method for event " + type + " is missing!")
+ .doesNotThrowAnyException();
found.add(methodName);
}
@@ -69,7 +73,9 @@ void testListenerAdapter()
{
if (!method.isAccessible() || method.getAnnotation(Deprecated.class) != null)
continue;
- Assertions.assertTrue(found.contains(method.getName()), "Dangling method found in ListenerAdapter " + method.getName());
+ assertThat(found.contains(method.getName()))
+ .as("Dangling method found in ListenerAdapter " + method.getName())
+ .isTrue();
}
}
}
diff --git a/src/test/java/net/dv8tion/jda/test/data/DataPathTest.java b/src/test/java/net/dv8tion/jda/test/data/DataPathTest.java
new file mode 100644
index 00000000000..39a22b3cc2d
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/data/DataPathTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.test.data;
+
+import net.dv8tion.jda.api.exceptions.ParsingException;
+import net.dv8tion.jda.api.utils.data.DataArray;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.api.utils.data.DataPath;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.*;
+
+public class DataPathTest
+{
+ @Test
+ void testSimple()
+ {
+ DataObject object = DataObject.empty()
+ .put("foo", "10"); // string to also test parsing
+
+ assertThat(DataPath.getInt(object, "foo")).isEqualTo(10);
+
+ DataArray array = DataArray.empty().add("20");
+ assertThat(DataPath.getInt(array, "[0]")).isEqualTo(20);
+ }
+
+ @Test
+ void testSimpleMissing()
+ {
+ DataObject object = DataObject.empty();
+
+ assertThat(DataPath.getLong(object, "foo?", 0)).isEqualTo(0L);
+ assertThatThrownBy(() -> DataPath.getLong(object, "foo"))
+ .hasMessage("Unable to resolve value with key foo to type long: null")
+ .isInstanceOf(ParsingException.class);
+
+ DataArray array = DataArray.empty();
+
+ assertThat(DataPath.getBoolean(array, "[0]?", true)).isTrue();
+ assertThatThrownBy(() -> DataPath.getObject(array, "[0]"))
+ .hasMessage("Could not resolve value of type Object at path \"[0]\"")
+ .isInstanceOf(ParsingException.class);
+ }
+
+ @Test
+ void testObjectInArray()
+ {
+ DataObject object = DataObject.empty().put("foo", 10.0);
+ DataArray array = DataArray.empty().add(object);
+
+ assertThat(DataPath.getDouble(array, "[0].foo")).isEqualTo(10.0);
+ assertThat(DataPath.getDouble(array, "[1]?.foo", 20.0)).isEqualTo(20.0);
+
+ assertThatIndexOutOfBoundsException()
+ .isThrownBy(() -> DataPath.getDouble(array, "[1].foo"));
+ }
+
+ @Test
+ void testArrayInObject()
+ {
+ DataArray array = DataArray.empty().add("hello");
+ DataObject object = DataObject.empty().put("foo", array);
+
+ assertThat(DataPath.getString(object, "foo[0]")).isEqualTo("hello");
+ assertThat(DataPath.getString(object, "foo[1]?", "world")).isEqualTo("world");
+ assertThatIndexOutOfBoundsException()
+ .isThrownBy(() -> DataPath.getString(object, "foo[1]"));
+ }
+
+ @Test
+ void testArrayInArray()
+ {
+ DataArray array = DataArray.empty().add(DataArray.empty().add("10"));
+
+ assertThat(DataPath.getUnsignedInt(array, "[0][0]")).isEqualTo(10);
+ assertThat(DataPath.getUnsignedInt(array, "[0][1]?", 20)).isEqualTo(20);
+ assertThat(DataPath.getUnsignedInt(array, "[1]?[0]", 20)).isEqualTo(20);
+ assertThatIndexOutOfBoundsException().isThrownBy(() -> DataPath.getUnsignedInt(array, "[0][1]"));
+ assertThatIndexOutOfBoundsException().isThrownBy(() -> DataPath.getUnsignedInt(array, "[1][0]"));
+ assertThatThrownBy(() -> DataPath.getUnsignedInt(array, "[0][1]?"))
+ .hasMessage("Could not resolve value of type unsigned int at path \"[0][1]?\"")
+ .isInstanceOf(ParsingException.class);
+ assertThatThrownBy(() -> DataPath.getUnsignedInt(array, "[1]?[0]"))
+ .hasMessage("Could not resolve value of type unsigned int at path \"[1]?[0]\"")
+ .isInstanceOf(ParsingException.class);
+ }
+
+ @Test
+ void testComplex()
+ {
+ DataObject object = DataObject.empty()
+ .put("array", DataArray.empty()
+ .add(DataObject.empty()
+ .put("foo", DataObject.empty()
+ .put("bar", "hello"))));
+
+ assertThat(DataPath.getString(object, "array[0].foo.bar")).isEqualTo("hello");
+ assertThat(DataPath.getString(object, "array[0].wrong?.bar", "world")).isEqualTo("world");
+ assertThatThrownBy(() -> DataPath.getString(object, "array[0].wrong?.bar"))
+ .hasMessage("Could not resolve value of type String at path \"array[0].wrong?.bar\"")
+ .isInstanceOf(ParsingException.class);
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/data/JsonTest.java b/src/test/java/net/dv8tion/jda/test/data/JsonTest.java
new file mode 100644
index 00000000000..581e4be5d64
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/data/JsonTest.java
@@ -0,0 +1,353 @@
+/*
+ * 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.test.data;
+
+import net.dv8tion.jda.api.exceptions.ParsingException;
+import net.dv8tion.jda.api.utils.data.DataArray;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.internal.utils.Helpers;
+import net.dv8tion.jda.test.PrettyRepresentation;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.time.OffsetDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.DoubleStream;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class JsonTest
+{
+ private static final String TEST_TIME_STRING = "2024-01-01T12:34:56.789Z";
+ private static final OffsetDateTime TEST_TIME = OffsetDateTime.parse(TEST_TIME_STRING);
+ private static final String testJson = jsonOf(
+ kv("int", 10),
+ kv("long", 100),
+ kv("boolean", true),
+ kv("string", "test"),
+ kv("double", 4.2),
+ kv("time", TEST_TIME_STRING)
+ );
+ private static final String testJsonArray = Stream
+ .of(10, 100L, true, "\"test\"", 4.2, 5.2f)
+ .map(String::valueOf)
+ .collect(Collectors.joining(",\n", "[", "]"));
+
+ private static final byte[] simpleEtfArray = {-125, 108, 0, 0, 0, 6, 97, 10, 97, 100, 100, 0, 4, 116, 114, 117, 101, 109, 0, 0, 0, 4, 116, 101, 115, 116, 70, 64, 16, -52, -52, -52, -52, -52, -51, 70, 64, 20, -52, -52, -52, -52, -52, -51, 106};
+ private static final byte[] complexEtfArray = {-125, 108, 0, 0, 0, 6, 109, 0, 0, 0, 3, 111, 110, 101, 97, 2, 70, 64, 11, -103, -103, -103, -103, -103, -102, 97, 7, 116, 0, 0, 0, 2, 109, 0, 0, 0, 5, 102, 105, 114, 115, 116, 109, 0, 0, 0, 5, 101, 105, 103, 104, 116, 109, 0, 0, 0, 6, 115, 101, 99, 111, 110, 100, 106, 108, 0, 0, 0, 2, 109, 0, 0, 0, 4, 110, 105, 110, 101, 116, 0, 0, 0, 1, 109, 0, 0, 0, 3, 107, 101, 121, 109, 0, 0, 0, 3, 116, 101, 110, 106, 106};
+
+ @Nested
+ class DataObjectTest
+ {
+ @Test
+ void testParse()
+ {
+ DataObject object = DataObject.fromJson(testJson);
+ assertThat(object.getInt("int")).isEqualTo(10);
+ assertThat(object.getLong("long")).isEqualTo(100);
+ assertThat(object.getDouble("double")).isEqualTo(4.2);
+ assertThat(object.getBoolean("boolean")).isTrue();
+ assertThat(object.getString("string")).isEqualTo("test");
+ assertThat(object.getOffsetDateTime("time")).isEqualTo(TEST_TIME);
+ }
+
+ @Test
+ void testCoerce()
+ {
+ DataObject data = DataObject.empty()
+ .put("stringified_int", "42")
+ .put("stringified_boolean", "true")
+ .put("stringified_long", "86699011792191488")
+ .put("stringified_datetime", TEST_TIME_STRING)
+ .put("stringified_double", "123.456");
+
+ assertThat(data.toMap()).containsOnly(
+ entry("stringified_int", "42"),
+ entry("stringified_boolean", "true"),
+ entry("stringified_long", "86699011792191488"),
+ entry("stringified_datetime", TEST_TIME_STRING),
+ entry("stringified_double", "123.456")
+ );
+
+ assertThat(data.getInt("stringified_int")).isEqualTo(42);
+ assertThat(data.getBoolean("stringified_boolean")).isTrue();
+ assertThat(data.getLong("stringified_long")).isEqualTo(86699011792191488L);
+ assertThat(data.getUnsignedLong("stringified_long")).isEqualTo(86699011792191488L);
+ assertThat(data.getDouble("stringified_double")).isEqualTo(123.456);
+ assertThat(data.getString("stringified_datetime")).isEqualTo(TEST_TIME_STRING);
+ }
+
+ @Test
+ void testFallback()
+ {
+ DataObject data = DataObject.fromJson(jsonOf());
+
+ assertThat(data).isEqualTo(DataObject.empty());
+ assertThat(data).hasToString("{}");
+
+ assertThat(data.isNull("key")).isTrue();
+ assertThat(data.hasKey("key")).isFalse();
+
+ assertThat(data.getDouble("key", 5.3)).isEqualTo(5.3);
+ assertThat(data.getInt("key", 4)).isEqualTo(4);
+ assertThat(data.getUnsignedInt("key", 7)).isEqualTo(7);
+ assertThat(data.getLong("key", 123L)).isEqualTo(123);
+ assertThat(data.getUnsignedLong("key", 321L)).isEqualTo(321L);
+ assertThat(data.getBoolean("key")).isFalse();
+ assertThat(data.getBoolean("key", true)).isTrue();
+ assertThat(data.getOffsetDateTime("key", TEST_TIME)).isEqualTo(TEST_TIME);
+ assertThat(data.opt("key")).isEmpty();
+ assertThat(data.optObject("key")).isEmpty();
+ assertThat(data.optArray("key")).isEmpty();
+ }
+
+ @Test
+ void testJsonToString()
+ {
+ DataObject object = DataObject.fromJson(testJson);
+ String result = object.toString();
+ DataObject symmetric = DataObject.fromJson(result);
+
+ assertThat(symmetric.toMap()).isNotSameAs(object.toMap());
+ assertThat(symmetric.toMap()).isEqualTo(object.toMap());
+ assertThat(symmetric.toMap()).hasSize(6);
+ assertThat(symmetric.toMap()).containsOnly(
+ entry("int", 10),
+ entry("long", 100),
+ entry("boolean", true),
+ entry("string", "test"),
+ entry("double", 4.2),
+ entry("time", TEST_TIME_STRING)
+ );
+ }
+
+ @Test
+ void testFactories()
+ {
+ DataObject reference = DataObject.fromJson(testJson);
+
+ assertThat(DataObject.fromJson(testJson.getBytes(StandardCharsets.UTF_8)))
+ .withRepresentation(new PrettyRepresentation())
+ .isEqualTo(reference);
+ assertThat(DataObject.fromJson(new StringReader(testJson)))
+ .withRepresentation(new PrettyRepresentation())
+ .isEqualTo(reference);
+ assertThat(DataObject.fromJson(new ByteArrayInputStream(testJson.getBytes(StandardCharsets.UTF_8))))
+ .withRepresentation(new PrettyRepresentation())
+ .isEqualTo(reference);
+ }
+ }
+
+ @Nested
+ class DataArrayTest
+ {
+ @Test
+ void testParse()
+ {
+ DataArray object = DataArray.fromJson(testJsonArray);
+ assertThat(object.getInt(0)).isEqualTo(10);
+ assertThat(object.getLong(1)).isEqualTo(100);
+ assertThat(object.getBoolean(2)).isTrue();
+ assertThat(object.getString(3)).isEqualTo("test");
+ assertThat(object.getDouble(4)).isEqualTo(4.2);
+ assertThat(object.getDouble(5)).isEqualTo(5.2);
+ }
+
+ @Test
+ void testCoerce()
+ {
+ DataArray array = DataArray.empty()
+ .add("42")
+ .add("true")
+ .add("86699011792191488")
+ .add(TEST_TIME_STRING)
+ .add("123.456");
+
+ assertThat(array.toList()).containsExactly(
+ "42", "true", "86699011792191488", TEST_TIME_STRING, "123.456"
+ );
+
+ assertThat(array.getInt(0)).isEqualTo(42);
+ assertThat(array.getBoolean(1)).isTrue();
+ assertThat(array.getLong(2)).isEqualTo(86699011792191488L);
+ assertThat(array.getUnsignedLong(2)).isEqualTo(86699011792191488L);
+ assertThat(array.getString(3)).isEqualTo(TEST_TIME_STRING);
+ assertThat(array.getDouble(4)).isEqualTo(123.456);
+ }
+
+ @Test
+ void testFallback()
+ {
+ DataArray data = DataArray.fromJson("[]");
+
+ assertThat(data).isEqualTo(DataArray.empty());
+ assertThat(data).hasToString("[]");
+
+ assertThat(data.isNull(0)).isTrue();
+ assertThat(data.length()).isEqualTo(0);
+ assertThat(data.isEmpty()).isTrue();
+
+ assertThat(data.getDouble(0, 5.3)).isEqualTo(5.3);
+ assertThat(data.getInt(0, 4)).isEqualTo(4);
+ assertThat(data.getUnsignedInt(0, 7)).isEqualTo(7);
+ assertThat(data.getLong(0, 123L)).isEqualTo(123);
+ assertThat(data.getUnsignedLong(0, 321L)).isEqualTo(321L);
+ assertThat(data.getBoolean(0)).isFalse();
+ assertThat(data.getBoolean(0, true)).isTrue();
+ assertThat(data.getOffsetDateTime(0, TEST_TIME)).isEqualTo(TEST_TIME);
+ }
+
+ @Test
+ void testJsonToString()
+ {
+ DataArray object = DataArray.fromJson(testJsonArray);
+ String result = object.toString();
+ DataArray symmetric = DataArray.fromJson(result);
+
+ assertThat(symmetric.toList()).isNotSameAs(object.toList());
+ assertThat(symmetric.toList()).isEqualTo(object.toList());
+ assertThat(symmetric.toList()).hasSize(6);
+ assertThat(symmetric.toList()).containsExactly(
+ 10, 100, true, "test", 4.2, 5.2
+ );
+ }
+
+ @Test
+ void testStream()
+ {
+ DataArray intArray = IntStream.range(0, 3).boxed().collect(Helpers.toDataArray());
+ assertThat(intArray.stream(DataArray::getInt))
+ .containsExactly(0, 1, 2);
+ assertThat(intArray.stream(DataArray::getLong))
+ .containsExactly(0L, 1L, 2L);
+
+ DataArray doubleArray = DoubleStream.of(0.1, 0.5, 1.2, 4.2).boxed().collect(Helpers.toDataArray());
+ assertThat(doubleArray.stream(DataArray::getDouble))
+ .containsExactly(0.1, 0.5, 1.2, 4.2);
+
+ DataArray stringArray = DataArray.empty().add("foo").add("bar");
+ assertThat(stringArray.stream(DataArray::getString))
+ .containsExactly("foo", "bar");
+
+ DataArray polyTypedArray = DataArray.empty().add(1).add(2.3).add("four");
+ assertThatThrownBy(() -> polyTypedArray.stream(DataArray::getInt).toArray())
+ .isInstanceOf(NumberFormatException.class);
+ assertThatThrownBy(() -> polyTypedArray.stream(DataArray::getDouble).toArray())
+ .isInstanceOf(NumberFormatException.class);
+ assertThatThrownBy(() -> polyTypedArray.stream(DataArray::getObject).toArray())
+ .isInstanceOf(ParsingException.class)
+ .hasMessage("Cannot parse value for index 0 into type Map: 1 instance of Integer");
+ assertThat(polyTypedArray.stream(DataArray::getString))
+ .containsExactly("1", "2.3", "four");
+
+ DataArray objectArray = DataArray.empty()
+ .add(DataObject.empty().put("foo", 1))
+ .add(DataObject.empty().put("foo", 2));
+ assertThat(objectArray.stream(DataArray::getObject).map(obj -> obj.getInt("foo")))
+ .containsExactly(1, 2);
+
+ objectArray.add(DataArray.empty());
+ assertThatThrownBy(() ->
+ objectArray.stream(DataArray::getObject).map(obj -> obj.getInt("foo")).toArray()
+ )
+ .isInstanceOf(ParsingException.class)
+ .hasMessage("Cannot parse value for index 2 into type Map: [] instance of ArrayList");
+ }
+
+ @Test
+ void testFactories()
+ {
+ assertThat(DataArray.fromJson(new StringReader(testJsonArray)))
+ .withRepresentation(new PrettyRepresentation())
+ .containsExactly(10, 100, true, "test", 4.2, 5.2);
+ assertThat(DataArray.fromJson(new ByteArrayInputStream(testJsonArray.getBytes(StandardCharsets.UTF_8))))
+ .withRepresentation(new PrettyRepresentation())
+ .containsExactly(10, 100, true, "test", 4.2, 5.2);
+ assertThat(DataArray.fromJson(testJsonArray))
+ .withRepresentation(new PrettyRepresentation())
+ .containsExactly(10, 100, true, "test", 4.2, 5.2);
+
+ List> inputList = Arrays.asList(10, 100L, true, "test", 4.2, 5.2f);
+ DataArray array = DataArray.fromCollection(inputList);
+ assertThat(array)
+ .withRepresentation(new PrettyRepresentation())
+ .as("Do not lose types from input collections")
+ .containsExactly(10, 100L, true, "test", 4.2, 5.2f);
+ assertThat(array.toList()).isNotSameAs(inputList);
+ assertThat(array.toList()).isEqualTo(inputList);
+ assertThat(array.add("foo").toList()).isNotEqualTo(inputList);
+
+ assertThat(DataArray.fromETF(simpleEtfArray))
+ .withRepresentation(new PrettyRepresentation())
+ .containsExactly(10, 100, true, "test", 4.2, 5.2);
+ assertThat(DataArray.fromETF(simpleEtfArray).toETF())
+ .isEqualTo(simpleEtfArray);
+ }
+
+ @Test
+ void testExTerm()
+ {
+ DataArray array = DataArray.empty()
+ .add("one")
+ .add(2)
+ .add(3.45)
+ .add(7L)
+ .add(DataObject.empty()
+ .put("first", "eight")
+ .put("second", DataArray.empty()))
+ .add(DataArray.empty()
+ .add("nine")
+ .add(DataObject.empty()
+ .put("key", "ten")));
+
+ assertThat(array).hasSize(6);
+ assertThat(array.toETF()).isEqualTo(complexEtfArray);
+ assertThat(DataArray.fromETF(complexEtfArray).toETF())
+ .isEqualTo(complexEtfArray);
+ assertThat(DataArray.fromETF(complexEtfArray).toPrettyString())
+ .isEqualToIgnoringWhitespace(array.toPrettyString());
+ }
+ }
+
+ private static Map.Entry entry(K key, V value)
+ {
+ return new AbstractMap.SimpleEntry<>(key, value);
+ }
+
+ private static String kv(String key, Object value)
+ {
+ return String.format(Locale.ROOT, "\"%s\": %s", key, value);
+ }
+
+ private static String kv(String key, String value)
+ {
+ return String.format(Locale.ROOT, "\"%s\": \"%s\"", key, value);
+ }
+
+ private static String jsonOf(String... keyValueMapping)
+ {
+ return Stream.of(keyValueMapping)
+ .collect(Collectors.joining(",\n", "{", "}"));
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/entities/ActivityTest.java b/src/test/java/net/dv8tion/jda/test/entities/ActivityTest.java
similarity index 69%
rename from src/test/java/net/dv8tion/jda/entities/ActivityTest.java
rename to src/test/java/net/dv8tion/jda/test/entities/ActivityTest.java
index d6150f9217b..96565c900d6 100644
--- a/src/test/java/net/dv8tion/jda/entities/ActivityTest.java
+++ b/src/test/java/net/dv8tion/jda/test/entities/ActivityTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package net.dv8tion.jda.entities;
+package net.dv8tion.jda.test.entities;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.RichPresence;
@@ -22,11 +22,12 @@
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.entities.EntityBuilder;
import net.dv8tion.jda.internal.managers.PresenceImpl;
+import net.dv8tion.jda.test.PrettyRepresentation;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.assertj.core.api.Assertions.assertThat;
public class ActivityTest
{
@@ -43,8 +44,15 @@ private static DataObject formatActivity(int type, String name, String state)
return json;
}
+ private static void assertEquals(DataObject expected, DataObject actual)
+ {
+ assertThat(actual)
+ .withRepresentation(new PrettyRepresentation())
+ .isEqualTo(expected);
+ }
+
@Test
- public void activitySerializationTest()
+ void activitySerializationTest()
{
assertEquals(
formatActivity(0, "playing test", null),
@@ -102,7 +110,7 @@ public void activitySerializationTest()
}
@Test
- public void activityBasicDeserializationTest()
+ void activityBasicDeserializationTest()
{
Activity activity = EntityBuilder.createActivity(
DataObject.empty()
@@ -110,15 +118,15 @@ public void activityBasicDeserializationTest()
.put("name", "Games")
);
- assertFalse(activity.isRich());
- assertEquals(Activity.ActivityType.PLAYING, activity.getType());
- assertEquals("Games", activity.getName());;
- assertNull(activity.getState());
- assertNull(activity.getUrl());
+ assertThat(activity.isRich()).isFalse();
+ assertThat(activity.getType()).isEqualTo(Activity.ActivityType.PLAYING);
+ assertThat(activity.getName()).isEqualTo("Games");
+ assertThat(activity.getState()).isNull();
+ assertThat(activity.getUrl()).isNull();
}
@Test
- public void activityRichDeserializationTest()
+ void activityRichDeserializationTest()
{
Activity activity = EntityBuilder.createActivity(
DataObject.empty()
@@ -127,11 +135,11 @@ public void activityRichDeserializationTest()
.put("state", "Active")
);
- assertFalse(activity.isRich(), "isRich()");
- assertEquals(Activity.ActivityType.PLAYING, activity.getType());
- assertEquals("Games", activity.getName());;
- assertEquals("Active", activity.getState());
- assertNull(activity.getUrl(), "url");
+ assertThat(activity.isRich()).isFalse();
+ assertThat(activity.getType()).isEqualTo(Activity.ActivityType.PLAYING);
+ assertThat(activity.getName()).isEqualTo("Games");;
+ assertThat(activity.getState()).isEqualTo("Active");
+ assertThat(activity.getUrl()).isNull();
activity = EntityBuilder.createActivity(
DataObject.empty()
@@ -155,30 +163,31 @@ public void activityRichDeserializationTest()
);
RichPresence rich = activity.asRichPresence();
- assertEquals(activity, rich);
+ assertThat(rich).isNotNull();
+ assertThat(rich).isEqualTo(activity);
- assertEquals(Activity.ActivityType.PLAYING, rich.getType());
- assertEquals("The Best Game Ever", rich.getName());
- assertEquals("In a Group", rich.getState());
+ assertThat(rich.getType()).isEqualTo(Activity.ActivityType.PLAYING);
+ assertThat(rich.getName()).isEqualTo("The Best Game Ever");
+ assertThat(rich.getState()).isEqualTo("In a Group");
- assertNotNull(rich.getParty(), "party");
- assertEquals("1234", rich.getParty().getId());
- assertEquals(3, rich.getParty().getSize());
- assertEquals(6, rich.getParty().getMax());
+ assertThat(rich.getParty()).isNotNull();
+ assertThat(rich.getParty().getId()).isEqualTo("1234");
+ assertThat(rich.getParty().getSize()).isEqualTo(3);
+ assertThat(rich.getParty().getMax()).isEqualTo(6);
- assertNotNull(rich.getTimestamps());
- assertEquals(1507665886, rich.getTimestamps().getStart());
- assertEquals(1507666000, rich.getTimestamps().getEnd());
+ assertThat(rich.getTimestamps()).isNotNull();
+ assertThat(rich.getTimestamps().getStart()).isEqualTo(1507665886);
+ assertThat(rich.getTimestamps().getEnd()).isEqualTo(1507666000);
- assertNotNull(rich.getLargeImage(), "assets.large_image");
- assertEquals("canary-large", rich.getLargeImage().getKey());
- assertNull(rich.getLargeImage().getText(), "assets.large_text");
+ assertThat(rich.getLargeImage()).isNotNull();
+ assertThat(rich.getLargeImage().getKey()).isEqualTo("canary-large");
+ assertThat(rich.getLargeImage().getText()).isNull();
- assertNotNull(rich.getSmallImage(), "assets.small_image");
- assertEquals("ptb-large", rich.getSmallImage().getKey());
- assertEquals("Small icon", rich.getSmallImage().getText());
+ assertThat(rich.getSmallImage()).isNotNull();
+ assertThat(rich.getSmallImage().getKey()).isEqualTo("ptb-large");
+ assertThat(rich.getSmallImage().getText()).isEqualTo("Small icon");
- assertEquals("4b2fdce12f639de8bfa7e3591b71a0d679d7c93f", rich.getSessionId());
- assertEquals("e7eb30d2ee025ed05c71ea495f770b76454ee4e0", rich.getSyncId());
+ assertThat(rich.getSessionId()).isEqualTo("4b2fdce12f639de8bfa7e3591b71a0d679d7c93f");
+ assertThat(rich.getSyncId()).isEqualTo("e7eb30d2ee025ed05c71ea495f770b76454ee4e0");
}
}
diff --git a/src/test/java/net/dv8tion/jda/test/entities/MessageSerializationTest.java b/src/test/java/net/dv8tion/jda/test/entities/MessageSerializationTest.java
new file mode 100644
index 00000000000..87e28a2072a
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/entities/MessageSerializationTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.test.entities;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.entities.MessageEmbed;
+import net.dv8tion.jda.api.utils.data.DataArray;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.test.PrettyRepresentation;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MessageSerializationTest
+{
+ private static final String DESCRIPTION_TEXT = "Description Text";
+ private static final String TITLE_TEXT = "Title Text";
+ private static final String TITLE_URL = "https://example.com/title";
+ private static final String AUTHOR_TEXT = "Author Text";
+ private static final String AUTHOR_URL = "https://example.com/author";
+ private static final String AUTHOR_ICON = "https://example.com/author_icon";
+ private static final String FOOTER_TEXT = "Footer Text";
+ private static final String FOOTER_ICON = "https://example.com/footer_icon";
+ private static final String IMAGE_URL = "https://example.com/image";
+ private static final String THUMBNAIL_URL = "https://example.com/thumbnail";
+ private static final String FIELD_1_NAME = "Field 1";
+ private static final String FIELD_1_TEXT = "Field 1 Text";
+ private static final String FIELD_2_NAME = "Field 2";
+ private static final String FIELD_2_TEXT = "Field 2 Text";
+ private static final String FIELD_3_NAME = "Field 3";
+ private static final String FIELD_3_TEXT = "Field 3 Text";
+
+ @Test
+ void testEmbedSerialization()
+ {
+ MessageEmbed embed = getTestEmbed();
+
+ MessageEmbed dataEmbed = EmbedBuilder.fromData(embed.toData()).build();
+
+ assertThat(dataEmbed).isNotSameAs(embed);
+ assertThat(dataEmbed).isEqualTo(embed);
+
+ assertThat(dataEmbed.toData())
+ .withRepresentation(new PrettyRepresentation())
+ .isEqualTo(DataObject.empty()
+ .put("title", TITLE_TEXT)
+ .put("url", TITLE_URL)
+ .put("description", DESCRIPTION_TEXT)
+ .put("image", DataObject.empty()
+ .put("url", IMAGE_URL))
+ .put("thumbnail", DataObject.empty()
+ .put("url", THUMBNAIL_URL))
+ .put("footer", DataObject.empty()
+ .put("icon_url", FOOTER_ICON)
+ .put("text", FOOTER_TEXT))
+ .put("author", DataObject.empty()
+ .put("icon_url", AUTHOR_ICON)
+ .put("name", AUTHOR_TEXT)
+ .put("url", AUTHOR_URL))
+ .put("fields", DataArray.empty()
+ .add(DataObject.empty()
+ .put("inline", true)
+ .put("name", FIELD_1_NAME)
+ .put("value", FIELD_1_TEXT))
+ .add(DataObject.empty()
+ .put("inline", false)
+ .put("name", FIELD_2_NAME)
+ .put("value", FIELD_2_TEXT))
+ .add(DataObject.empty()
+ .put("inline", true)
+ .put("name", FIELD_3_NAME)
+ .put("value", FIELD_3_TEXT))));
+ }
+
+ @NotNull
+ private static MessageEmbed getTestEmbed()
+ {
+ return new EmbedBuilder()
+ .setDescription(DESCRIPTION_TEXT)
+ .setTitle(TITLE_TEXT, TITLE_URL)
+ .setAuthor(AUTHOR_TEXT, AUTHOR_URL, AUTHOR_ICON)
+ .setFooter(FOOTER_TEXT, FOOTER_ICON)
+ .setImage(IMAGE_URL)
+ .setThumbnail(THUMBNAIL_URL)
+ .addField(FIELD_1_NAME, FIELD_1_TEXT, true)
+ .addField(FIELD_2_NAME, FIELD_2_TEXT, false)
+ .addField(FIELD_3_NAME, FIELD_3_TEXT, true)
+ .build();
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/entities/channel/ChannelCacheViewTest.java b/src/test/java/net/dv8tion/jda/test/entities/channel/ChannelCacheViewTest.java
similarity index 83%
rename from src/test/java/net/dv8tion/jda/entities/channel/ChannelCacheViewTest.java
rename to src/test/java/net/dv8tion/jda/test/entities/channel/ChannelCacheViewTest.java
index 70b4bd4eac6..9391e395b14 100644
--- a/src/test/java/net/dv8tion/jda/entities/channel/ChannelCacheViewTest.java
+++ b/src/test/java/net/dv8tion/jda/test/entities/channel/ChannelCacheViewTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package net.dv8tion.jda.entities.channel;
+package net.dv8tion.jda.test.entities.channel;
import net.dv8tion.jda.api.entities.channel.Channel;
import net.dv8tion.jda.api.entities.channel.ChannelType;
@@ -40,7 +40,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
class ChannelCacheViewTest
@@ -182,10 +182,10 @@ void testSortedStream()
{
SortedChannelCacheView cache = getMockedGuildCache();
String output = toListString(cache.stream());
- assertEquals(VALID_SORT_ORDER, output);
+ assertThat(output).isEqualTo(VALID_SORT_ORDER);
output = toListString(cache.parallelStream());
- assertEquals(VALID_SORT_ORDER, output);
+ assertThat(output).isEqualTo(VALID_SORT_ORDER);
}
@Test
@@ -193,13 +193,13 @@ void testUnsortedStream()
{
SortedChannelCacheView cache = getMockedGuildCache();
String output = toListString(cache.streamUnordered());
- assertNotEquals(VALID_SORT_ORDER, output);
+ assertThat(output).isNotEqualTo(VALID_SORT_ORDER);
output = toListString(cache.parallelStreamUnordered());
- assertNotEquals(VALID_SORT_ORDER, output);
+ assertThat(output).isNotEqualTo(VALID_SORT_ORDER);
output = cache.applyStream(ChannelCacheViewTest::toListString);
- assertNotEquals(VALID_SORT_ORDER, output);
+ assertThat(output).isNotEqualTo(VALID_SORT_ORDER);
}
@Test
@@ -208,15 +208,16 @@ void testAsListWorks()
SortedChannelCacheView cache = getMockedGuildCache();
String output = toListString(cache.asList().stream());
- assertEquals(VALID_SORT_ORDER, output);
+ assertThat(output).isEqualTo(VALID_SORT_ORDER);
SortedChannelCacheView voiceView = cache.ofType(VoiceChannel.class);
List fromOfType = voiceView.asList();
List voiceChannelFilter = cache.applyStream(stream -> stream.filter(VoiceChannel.class::isInstance).collect(Collectors.toList()));
- assertEquals(voiceView.size(), voiceChannelFilter.size());
- assertTrue(fromOfType.containsAll(voiceChannelFilter), "The filtered CacheView must contain all of VoiceChannel");
- assertTrue(voiceChannelFilter.containsAll(fromOfType), "The filtered CacheView must contain exactly all of VoiceChannel");
+ assertThat(voiceChannelFilter)
+ .hasSameSizeAs(voiceView);
+ assertThat(voiceChannelFilter)
+ .hasSameElementsAs(fromOfType);
}
@Test
@@ -225,15 +226,16 @@ void testAsSetWorks()
SortedChannelCacheView cache = getMockedGuildCache();
String output = toListString(cache.asSet().stream());
- assertEquals(VALID_SORT_ORDER, output);
+ assertThat(output).isEqualTo(VALID_SORT_ORDER);
SortedChannelCacheView voiceView = cache.ofType(VoiceChannel.class);
Set fromOfType = voiceView.asSet();
Set voiceChannelFilter = cache.applyStream(stream -> stream.filter(VoiceChannel.class::isInstance).collect(Collectors.toSet()));
- assertEquals(voiceView.size(), voiceChannelFilter.size());
- assertTrue(fromOfType.containsAll(voiceChannelFilter), "The filtered CacheView must contain all of VoiceChannel");
- assertTrue(voiceChannelFilter.containsAll(fromOfType), "The filtered CacheView must contain exactly all of VoiceChannel");
+ assertThat(voiceChannelFilter)
+ .hasSize((int) voiceView.size());
+ assertThat(voiceChannelFilter)
+ .hasSameElementsAs(fromOfType);
}
@Test
@@ -242,12 +244,12 @@ void testSizeWorks()
SortedChannelCacheView cache = getMockedGuildCache();
NavigableSet asSet = cache.asSet();
- assertEquals(asSet.size(), cache.size());
+ assertThat(cache).hasSameSizeAs(asSet);
SortedChannelCacheView ofTypeMessage = cache.ofType(GuildMessageChannel.class);
Set filterMessageType = asSet.stream().filter(GuildMessageChannel.class::isInstance).collect(Collectors.toSet());
- assertEquals(filterMessageType.size(), ofTypeMessage.size());
+ assertThat(ofTypeMessage).hasSameSizeAs(filterMessageType);
}
@Test
@@ -255,16 +257,22 @@ void testEmptyWorks()
{
SortedChannelCacheView empty = new SortedChannelCacheViewImpl<>(GuildChannel.class);
- assertTrue(empty.isEmpty(), "New cache must be empty");
+ assertThat(empty).isEmpty();
SortedChannelCacheViewImpl filled = getMockedGuildCache();
- assertFalse(filled.ofType(GuildMessageChannel.class).isEmpty(), "Filtered cache must not be empty before remove");
+ assertThat(filled.ofType(GuildMessageChannel.class))
+ .as("Filtered cache must not be empty before remove")
+ .isNotEmpty();
filled.removeIf(GuildMessageChannel.class, (c) -> true);
- assertFalse(filled.isEmpty(), "Filled cache must not be empty");
- assertTrue(filled.ofType(GuildMessageChannel.class).isEmpty(), "Filtered cache must be empty");
+ assertThat(filled)
+ .as("Filled cache must not be empty")
+ .isNotEmpty();
+ assertThat(filled.ofType(GuildMessageChannel.class))
+ .as("Filtered cache must be empty")
+ .isEmpty();
}
@Test
@@ -276,17 +284,21 @@ void testRemoveWorks()
GuildChannel textWithoutParent = getByName.get().get(0);
- assertSame(textWithoutParent, cache.remove(textWithoutParent), "Remove returns instance");
- assertTrue(getByName.get().isEmpty(), "Channel should be removed");
+ assertThat(textWithoutParent)
+ .as("Remove returns instance")
+ .isSameAs(cache.remove(textWithoutParent));
+ assertThat(getByName.get())
+ .as("Channel should be removed")
+ .isEmpty();
List messageChannels = getOfType.get();
- assertFalse(messageChannels.isEmpty(), "Message channels should not be removed");
+ assertThat(messageChannels).isNotEmpty();
cache.removeIf(GuildChannel.class, GuildMessageChannel.class::isInstance);
messageChannels = getOfType.get();
- assertTrue(messageChannels.isEmpty(), "Message channels should be removed");
+ assertThat(messageChannels).isEmpty();
}
}
diff --git a/src/test/java/net/dv8tion/jda/test/entities/guild/AbstractGuildTest.java b/src/test/java/net/dv8tion/jda/test/entities/guild/AbstractGuildTest.java
new file mode 100644
index 00000000000..0dd7263ff3e
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/entities/guild/AbstractGuildTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.test.entities.guild;
+
+import net.dv8tion.jda.api.Permission;
+import net.dv8tion.jda.api.utils.cache.CacheFlag;
+import net.dv8tion.jda.internal.entities.GuildImpl;
+import net.dv8tion.jda.internal.entities.MemberImpl;
+import net.dv8tion.jda.internal.entities.SelfUserImpl;
+import net.dv8tion.jda.internal.utils.UnlockHook;
+import net.dv8tion.jda.internal.utils.cache.MemberCacheViewImpl;
+import net.dv8tion.jda.test.Constants;
+import net.dv8tion.jda.test.IntegrationTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.mockito.Mock;
+
+import java.util.EnumSet;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+public abstract class AbstractGuildTest extends IntegrationTest
+{
+ @Mock
+ protected SelfUserImpl selfUser;
+ @Mock
+ protected MemberImpl selfMember;
+
+ protected GuildImpl guild;
+
+ @BeforeEach
+ final void setupGuild()
+ {
+ when(selfUser.getIdLong()).thenReturn(Constants.MINN_USER_ID);
+ when(jda.getSelfUser()).thenReturn(selfUser);
+ when(jda.getCacheFlags()).thenReturn(EnumSet.allOf(CacheFlag.class));
+
+ guild = new GuildImpl(jda, Constants.GUILD_ID);
+
+ MemberCacheViewImpl members = guild.getMembersView();
+ try (UnlockHook ignored = members.writeLock())
+ {
+ members.getMap().put(Constants.MINN_USER_ID, selfMember);
+ }
+ }
+
+ protected void hasPermission(boolean has)
+ {
+ when(selfMember.hasPermission(any(Permission[].class))).thenReturn(has);
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/entities/guild/BulkBanTest.java b/src/test/java/net/dv8tion/jda/test/entities/guild/BulkBanTest.java
new file mode 100644
index 00000000000..d5d126d338d
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/entities/guild/BulkBanTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.test.entities.guild;
+
+import net.dv8tion.jda.api.Permission;
+import net.dv8tion.jda.api.entities.User;
+import net.dv8tion.jda.api.entities.UserSnowflake;
+import net.dv8tion.jda.api.exceptions.HierarchyException;
+import net.dv8tion.jda.api.exceptions.InsufficientPermissionException;
+import net.dv8tion.jda.api.requests.Method;
+import net.dv8tion.jda.api.utils.data.DataArray;
+import net.dv8tion.jda.api.utils.data.DataObject;
+import net.dv8tion.jda.internal.utils.Helpers;
+import net.dv8tion.jda.test.Constants;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.LongStream;
+
+import static net.dv8tion.jda.test.ChecksHelper.assertDurationChecks;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class BulkBanTest extends AbstractGuildTest
+{
+ @Test
+ void testMissingPermissions()
+ {
+ hasPermission(false);
+
+ assertThatThrownBy(() -> guild.ban(Collections.emptyList(), Duration.ZERO))
+ .isInstanceOf(InsufficientPermissionException.class)
+ .hasMessage("Cannot perform action due to a lack of Permission. Missing permission: " + Permission.BAN_MEMBERS);
+ }
+
+ @Test
+ void testBanOwner()
+ {
+ hasPermission(true);
+
+ guild.setOwnerId(Constants.BUTLER_USER_ID);
+
+ Set users = Collections.singleton(User.fromId(Constants.BUTLER_USER_ID));
+
+ assertThatThrownBy(() -> guild.ban(users, Duration.ZERO))
+ .isInstanceOf(HierarchyException.class)
+ .hasMessage("Cannot ban the owner of a guild.");
+ }
+
+ @Test
+ void testInvalidInputs()
+ {
+ hasPermission(true);
+
+ assertDurationChecks("Deletion timeframe", duration -> guild.ban(Collections.emptyList(), duration))
+ .checksNotNegative()
+ .throwsFor(Duration.ofDays(100), "Deletion timeframe must not be larger than 7 days. Provided: 8640000 seconds");
+
+ Set users = Collections.singleton(null);
+
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> guild.ban(users, Duration.ZERO).queue())
+ .withMessage("Users may not be null");
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> guild.ban(null, null).queue())
+ .withMessage("Users may not be null");
+
+ assertThatIllegalArgumentException()
+ .isThrownBy(() ->
+ guild.ban(
+ LongStream.range(1, 300)
+ .map(i -> random.nextLong())
+ .mapToObj(User::fromId)
+ .collect(Collectors.toList()),
+ null
+ ).queue()
+ ).withMessage("Cannot ban more than 200 users at once");
+ }
+
+ @Test
+ void testDuplicates()
+ {
+ hasPermission(true);
+
+ Duration duration = Duration.ofSeconds(random.nextInt(10000));
+ String reason = Helpers.format("User %d was banned by %d for %s", Constants.BUTLER_USER_ID, Constants.MINN_USER_ID, duration);
+ List users = Arrays.asList(
+ User.fromId(Constants.BUTLER_USER_ID),
+ User.fromId(Constants.BUTLER_USER_ID)
+ );
+
+ assertThatRequestFrom(guild.ban(users, duration).reason(reason))
+ .hasMethod(Method.POST)
+ .hasCompiledRoute("guilds/" + Constants.GUILD_ID + "/bulk-ban")
+ .hasAuditReason(reason)
+ .hasBodyEqualTo(DataObject.empty()
+ .put("delete_message_seconds", duration.getSeconds())
+ .put("user_ids", DataArray.empty().add(Constants.BUTLER_USER_ID)))
+ .whenQueueCalled();
+ }
+}
diff --git a/src/test/java/net/dv8tion/jda/test/entities/message/MessagePollDataTest.java b/src/test/java/net/dv8tion/jda/test/entities/message/MessagePollDataTest.java
new file mode 100644
index 00000000000..6cd1f67b5b7
--- /dev/null
+++ b/src/test/java/net/dv8tion/jda/test/entities/message/MessagePollDataTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.test.entities.message;
+
+import net.dv8tion.jda.api.entities.messages.MessagePoll;
+import net.dv8tion.jda.api.utils.messages.MessagePollBuilder;
+import net.dv8tion.jda.test.ChecksHelper;
+import org.junit.jupiter.api.Test;
+
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+
+import static net.dv8tion.jda.test.ChecksHelper.*;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+
+public class MessagePollDataTest
+{
+ @Test
+ void testInvalidInputs()
+ {
+ assertStringChecks("Title", MessagePollBuilder::new)
+ .checksNotNull()
+ .checksNotBlank()
+ .checksNotLonger(300);
+
+ MessagePollBuilder builder = new MessagePollBuilder("test title");
+
+ assertEnumChecks("Layout", builder::setLayout)
+ .checksNotNull()
+ .checkIsNot(MessagePoll.LayoutType.UNKNOWN);
+
+ assertDurationChecks("Duration", builder::setDuration)
+ .checksNotNull()
+ .checksPositive()
+ .checksNotLonger(Duration.ofHours(7 * 24), TimeUnit.HOURS);
+
+ ChecksHelper.