Skip to content

Commit

Permalink
Poll support (#2649)
Browse files Browse the repository at this point in the history
  • Loading branch information
MinnDevelopment committed Apr 21, 2024
1 parent 0ce30fd commit e4860b4
Show file tree
Hide file tree
Showing 55 changed files with 2,509 additions and 61 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ dependencies {
testImplementation(libs.reflections)
testImplementation(libs.mockito)
testImplementation(libs.assertj)
testImplementation(libs.commons.lang3)
}

val compileJava: JavaCompile by tasks
Expand Down
1 change: 1 addition & 0 deletions src/main/java/net/dv8tion/jda/api/Permission.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,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"),
Expand Down
83 changes: 83 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/Message.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -48,17 +49,20 @@
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;
import net.dv8tion.jda.api.utils.FileUpload;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageEditData;
import net.dv8tion.jda.api.utils.messages.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;
Expand Down Expand Up @@ -681,6 +685,43 @@ default String getGuildId()
@Nonnull
List<LayoutComponent> 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<Message> 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}.
* <br>You can use {@link MessageRequest#setComponents(LayoutComponent...)} to update these.
Expand Down Expand Up @@ -1359,6 +1400,48 @@ default MessageCreateAction reply(@Nonnull MessageCreateData msg)
return getChannel().sendMessage(msg).setMessageReference(this);
}

/**
* Shortcut for {@code getChannel().sendMessagePoll(data).setMessageReference(this)}.
*
* <p>Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include:
* <ul>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* <br>if this channel was deleted</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER}
* <br>If this is a {@link PrivateChannel} and the currently logged in account
* does not share any Guilds with the recipient User</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD}
* <br>If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER}
* <br>If this message was blocked by the harmful link filter</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_INVALID_CHANNEL_TYPE POLL_INVALID_CHANNEL_TYPE}
* <br>This channel does not allow polls</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_WITH_UNUSABLE_EMOJI POLL_WITH_UNUSABLE_EMOJI}
* <br>This poll uses an external emoji that the bot is not allowed to use</li>
* </ul>
*
* @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)}.
*
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/WebhookClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -128,6 +130,43 @@ public interface WebhookClient<T> extends ISnowflake
@CheckReturnValue
WebhookMessageCreateAction<T> sendMessage(@Nonnull MessageCreateData message);

/**
* Send a message poll to this webhook.
*
* <p>If this is an {@link InteractionHook InteractionHook} this method will be delayed until the interaction is acknowledged.
*
* <p>Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include:
* <ul>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_WEBHOOK UNKNOWN_WEBHOOK}
* <br>The webhook is no longer available, either it was deleted or in case of interactions it expired.</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD}
* <br>If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER}
* <br>If this message was blocked by the harmful link filter</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_INVALID_CHANNEL_TYPE POLL_INVALID_CHANNEL_TYPE}
* <br>This channel does not allow polls</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_WITH_UNUSABLE_EMOJI POLL_WITH_UNUSABLE_EMOJI}
* <br>This poll uses an external emoji that the bot is not allowed to use</li>
* </ul>
*
* @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<T> sendMessagePoll(@Nonnull MessagePollData poll);

/**
* Send a message to this webhook.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,23 @@
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;
import net.dv8tion.jda.api.utils.MiscUtil;
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;
import net.dv8tion.jda.internal.requests.restaction.AuditableRestActionImpl;
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;

Expand Down Expand Up @@ -644,6 +647,52 @@ default MessageCreateAction sendMessageComponents(@Nonnull Collection<? extends
return new MessageCreateActionImpl(this).setComponents(components);
}

/**
* Send a message to this channel.
*
* <p>Possible {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} include:
* <ul>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_CHANNEL UNKNOWN_CHANNEL}
* <br>if this channel was deleted</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_SEND_TO_USER CANNOT_SEND_TO_USER}
* <br>If this is a {@link PrivateChannel} and the currently logged in account
* does not share any Guilds with the recipient User</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_AUTOMOD MESSAGE_BLOCKED_BY_AUTOMOD}
* <br>If this message was blocked by an {@link net.dv8tion.jda.api.entities.automod.AutoModRule AutoModRule}</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER MESSAGE_BLOCKED_BY_HARMFUL_LINK_FILTER}
* <br>If this message was blocked by the harmful link filter</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_INVALID_CHANNEL_TYPE POLL_INVALID_CHANNEL_TYPE}
* <br>This channel does not allow polls</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#POLL_WITH_UNUSABLE_EMOJI POLL_WITH_UNUSABLE_EMOJI}
* <br>This poll uses an external emoji that the bot is not allowed to use</li>
* </ul>
*
* @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.
*
Expand Down Expand Up @@ -972,6 +1021,108 @@ default AuditableRestAction<Void> deleteMessageById(long messageId)
return deleteMessageById(Long.toUnsignedString(messageId));
}

/**
* End the poll attached to this message.
*
* <p><b>A bot cannot expire the polls of other users.</b>
*
* <p>The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
* <ul>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_AUTHOR_EDIT INVALID_AUTHOR_EDIT}
* <br>If the poll was sent by another user</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_EXPIRE_MISSING_POLL CANNOT_EXPIRE_MISSING_POLL}
* <br>The message did not have a poll attached</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* <br>The message no longer exists</li>
* </ul>
*
* @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<Message> 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.
*
* <p><b>A bot cannot expire the polls of other users.</b>
*
* <p>The following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses} are possible:
* <ul>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_AUTHOR_EDIT INVALID_AUTHOR_EDIT}
* <br>If the poll was sent by another user</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#CANNOT_EXPIRE_MISSING_POLL CANNOT_EXPIRE_MISSING_POLL}
* <br>The message did not have a poll attached</li>
*
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_MESSAGE UNKNOWN_MESSAGE}
* <br>The message no longer exists</li>
* </ul>
*
* @param messageId
* The ID for the poll message
*
* @return {@link AuditableRestAction} - Type: {@link Message}
*/
@Nonnull
@CheckReturnValue
default AuditableRestAction<Message> 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.
* <br>MessageHistory is <b>NOT</b> an internal message cache, but rather it queries the Discord servers for previously sent messages.
Expand Down
Loading

0 comments on commit e4860b4

Please sign in to comment.