Skip to content

Commit

Permalink
feat(Polls): Support polls (#1493)
Browse files Browse the repository at this point in the history
Co-authored-by: bsian03 <[email protected]>
  • Loading branch information
conorwastakenwastaken and bsian03 authored Jul 20, 2024
1 parent a1a449f commit 31c4608
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 22 deletions.
69 changes: 59 additions & 10 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ declare namespace Eris {
// Interaction
type AnyInteraction = PingInteraction | CommandInteraction | ComponentInteraction | AutocompleteInteraction;
type InteractionCallbackData = InteractionAutocomplete | InteractionContent | InteractionModal;
type InteractionContent = Pick<WebhookPayload, "content" | "embeds" | "allowedMentions" | "tts" | "flags" | "components">;
type InteractionContent = Pick<WebhookPayload, "content" | "embeds" | "allowedMentions" | "tts" | "flags" | "components" | "poll">;
type InteractionContentEdit = Pick<WebhookPayload, "content" | "embeds" | "allowedMentions" | "components">;
type InteractionDataOptions = InteractionDataOptionsSubCommand | InteractionDataOptionsSubCommandGroup | InteractionDataOptionsWithValue;
type InteractionDataOptionsBoolean = InteractionDataOptionWithValue<Constants["ApplicationCommandOptionTypes"]["BOOLEAN"], boolean>;
Expand Down Expand Up @@ -159,6 +159,7 @@ declare namespace Eris {
type MessageActivityTypes = Constants["MessageActivityTypes"][keyof Constants["MessageActivityTypes"]];
type MessageContent = string | AdvancedMessageContent;
type MessageContentEdit = string | AdvancedMessageContentEdit;
type PollLayoutTypes = Constants["PollLayoutTypes"][keyof Constants["PollLayoutTypes"]];
type PossiblyUncachedMessage = Message | { channel: TextableChannel | { id: string; guild?: Uncached }; guildID?: string; id: string };
type ReactionTypes = Constants["ReactionTypes"][keyof Constants["ReactionTypes"]];

Expand Down Expand Up @@ -852,6 +853,7 @@ declare namespace Eris {
mentionedBy?: unknown;
mentions: User[];
pinned: boolean;
poll?: Poll;
roleMentions: string[];
tts: boolean;
}
Expand Down Expand Up @@ -949,6 +951,8 @@ declare namespace Eris {
messageCreate: [message: Message<PossiblyUncachedTextableChannel>];
messageDelete: [message: PossiblyUncachedMessage];
messageDeleteBulk: [messages: PossiblyUncachedMessage[]];
messagePollVoteAdd: [message: PossiblyUncachedMessage, user: User | Uncached, answerID: number];
messagePollVoteRemove: [message: PossiblyUncachedMessage, user: User | Uncached, answerID: number];
messageReactionAdd: [message: PossiblyUncachedMessage, emoji: PartialEmoji, reactor: Member | Uncached, burst: boolean];
messageReactionRemove: [message: PossiblyUncachedMessage, emoji: PartialEmoji, userID: string, burst: boolean];
messageReactionRemoveAll: [message: PossiblyUncachedMessage];
Expand Down Expand Up @@ -1476,6 +1480,7 @@ declare namespace Eris {
messageReference?: MessageReferenceReply;
/** @deprecated */
messageReferenceID?: string;
poll?: PollCreateOptions;
stickerIDs?: string[];
tts?: boolean;
}
Expand Down Expand Up @@ -1546,6 +1551,10 @@ declare namespace Eris {
limit?: number;
type?: ReactionTypes;
}
interface GetPollAnswerVotersOptions {
after?: string;
limit?: number;
}
interface InteractionButton extends ButtonBase {
custom_id: string;
style: Exclude<ButtonStyles, Constants["ButtonStyles"]["LINK"]>;
Expand Down Expand Up @@ -1585,6 +1594,34 @@ declare namespace Eris {
filename?: string;
id: string | number;
}
interface Poll {
allow_multiselect: boolean;
answers: PollAnswer[];
expiry: number | null;
layout_type: PollLayoutTypes;
question: Pick<PollMedia, "text">;
results?: PollResult;
}
interface PollAnswer {
answer_id: number;
poll_media: PollMedia;
}
interface PollCreateOptions extends Omit<Poll, "expiry" | "results"> {
duration: number;
}
interface PollMedia {
emoji?: PartialEmoji;
text: string;
}
interface PollResult {
answer_counts: PollAnswerCount[];
is_finalized: boolean;
}
interface PollAnswerCount {
count: number;
id: number;
me_voted: boolean;
}
interface Reaction {
burst_colors: string[];
count: number;
Expand Down Expand Up @@ -1890,6 +1927,7 @@ declare namespace Eris {
embeds?: EmbedOptions[];
file?: FileContent | FileContent[];
flags?: number;
poll?: PollCreateOptions;
threadID?: string;
tts?: boolean;
username?: string;
Expand Down Expand Up @@ -2229,9 +2267,11 @@ declare namespace Eris {
guildScheduledEvents: 65536;
autoModerationConfiguration: 1048576;
autoModerationExecution: 2097152;
allNonPrivileged: 3243773;
guildMessagePolls: 16777216;
directMessagePolls: 33554432;
allNonPrivileged: 53575421;
allPrivileged: 33026;
all: 3276799;
all: 53608447;
};
InteractionResponseTypes: {
PONG: 1;
Expand Down Expand Up @@ -2449,10 +2489,14 @@ declare namespace Eris {
useExternalSounds: 35184372088832n;
sendVoiceMessages: 70368744177664n;
setVoiceChannelStatus: 281474976710656n;
allGuild: 311172461494462n;
allText: 70904273435729n;
allVoice: 391980524766993n;
all: 422212465065983n;
sendPolls: 562949953421312n;
allGuild: 29697484783806n;
allText: 633854226857041n;
allVoice: 954930478188305n;
all: 985162418487295n;
};
PollLayoutTypes: {
DEFAULT: 1;
};
PremiumTiers: {
NONE: 0;
Expand Down Expand Up @@ -2785,7 +2829,7 @@ declare namespace Eris {
editPermission(overwriteID: string, allow: PermissionValueTypes, deny: PermissionValueTypes, type: PermissionType, reason?: string): Promise<PermissionOverwrite>;
edit(options: EditChannelOptionsBase, reason?: string): Promise<this>;
}

export class Channel extends Base {
mention: string;
type: ChannelTypes;
Expand Down Expand Up @@ -2974,6 +3018,7 @@ declare namespace Eris {
secret: string,
code: string
): Promise<{ backup_codes: { code: string; consumed: boolean }[]; token: string }>;
endPoll(channelID: string, messageID: string): Promise<Message<AnyGuildTextableChannel>>;
executeSlackWebhook(webhookID: string, token: string, options: Record<string, unknown> & { auth?: boolean; threadID?: string }): Promise<void>;
executeSlackWebhook(webhookID: string, token: string, options: Record<string, unknown> & { auth?: boolean; threadID?: string; wait: true }): Promise<Message<AnyGuildTextableChannel>>;
executeWebhook(webhookID: string, token: string, options: WebhookPayload & { wait: true }): Promise<Message<AnyGuildTextableChannel>>;
Expand Down Expand Up @@ -3033,6 +3078,7 @@ declare namespace Eris {
getNitroStickerPacks(): Promise<{ sticker_packs: StickerPack[] }>;
getOAuthApplication(appID?: string): Promise<OAuthApplicationInfo>;
getPins(channelID: string): Promise<Message[]>;
getPollAnswerVoters(channelID: string, messageID: string, answerID: string, options?: GetPollAnswerVotersOptions): Promise<User[]>;
getPruneCount(guildID: string, options?: GetPruneOptions): Promise<number>;
getRESTChannel(channelID: string): Promise<AnyChannel>;
getRESTGuild(guildID: string, withCounts?: boolean): Promise<Guild>;
Expand Down Expand Up @@ -3277,7 +3323,7 @@ declare namespace Eris {
export class ForumChannel extends MediaChannel {
defaultForumLayout: ForumLayoutTypes;
}

export class GroupChannel extends Channel {
applicationID: string;
icon: string | null;
Expand Down Expand Up @@ -3595,13 +3641,15 @@ declare namespace Eris {
deleteMessages(messageIDs: string[], reason?: string): Promise<void>;
edit(options: EditGuildTextableChannelOptions, reason?: string): Promise<this>;
editMessage(messageID: string, content: MessageContentEdit): Promise<Message<this>>;
endPoll(messageID: string): Promise<Message<this>>;
getMessage(messageID: string): Promise<Message<this>>;
getMessageReaction(messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise<User[]>;
/** @deprecated */
getMessageReaction(messageID: string, reaction: string, limit?: number, before?: string, after?: string): Promise<User[]>;
getMessages(options?: GetMessagesOptions): Promise<Message<this>[]>;
/** @deprecated */
getMessages(limit?: number, before?: string, after?: string, around?: string): Promise<Message<this>[]>;
getPollAnswerVoters(messageID: string, answerID: string, options?: GetPollAnswerVotersOptions): Promise<User[]>;
purge(options: PurgeChannelOptions): Promise<number>;
/** @deprecated */
purge(limit: number, filter?: (message: Message<this>) => boolean, before?: string, after?: string, reason?: string): Promise<number>;
Expand Down Expand Up @@ -3675,7 +3723,7 @@ declare namespace Eris {
user?: User;
acknowledge(flags?: number): Promise<void>;
createFollowup(content: string | InteractionContent, file?: FileContent | FileContent[]): Promise<Message>;
createMessage(content: string | InteractionContent , file?: FileContent | FileContent[]): Promise<void>;
createMessage(content: string | InteractionContent, file?: FileContent | FileContent[]): Promise<void>;
createModal(content: InteractionModal): Promise<void>;
defer(flags?: number): Promise<void>;
deleteMessage(messageID: string): Promise<void>;
Expand Down Expand Up @@ -3843,6 +3891,7 @@ declare namespace Eris {
mentions: User[];
messageReference: MessageReference | null;
pinned: boolean;
poll?: Poll;
prefix?: string;
reactions: { [s: string]: Reaction };
referencedMessage?: Message | null;
Expand Down
29 changes: 29 additions & 0 deletions lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,7 @@ class Client extends EventEmitter {
* @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message
* @arg {String} [content.messageReferenceID] [DEPRECATED] The ID of the message should be replied to. Use `messageReference` instead
* @arg {String | Number} [content.nonce] A nonce value which will also appear in the messageCreate event
* @arg {Object} [content.poll] A poll object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/poll#poll-object) for object structure
* @arg {Array<String>} [content.stickerIDs] An array of IDs corresponding to stickers to send
* @arg {Boolean} [content.tts] Set the message TTS flag
* @arg {Object | Array<Object>} [file] A file object (or an Array of them)
Expand Down Expand Up @@ -2455,6 +2456,16 @@ class Client extends EventEmitter {
});
}

/**
* Immediately end a poll. Note: You cannot end polls created by another user.
* @arg {String} channelID The ID of the channel
* @arg {String} messageID The ID of the message that the poll is in
* @returns {Promise<Message>}
*/
endPoll(channelID, messageID) {
return this.requestHandler.request("POST", Endpoints.POLL_END(channelID, messageID), true).then((message) => new Message(message, this));
}

/**
* Execute a slack-style webhook
* @arg {String} webhookID The ID of the webhook
Expand Down Expand Up @@ -2521,6 +2532,7 @@ class Client extends EventEmitter {
* @arg {Buffer} options.file.file A buffer containing file data
* @arg {String} options.file.name What to name the file
* @arg {Number} [options.flags] Flags to execute the webhook with, 64 for ephemeral (Interaction webhooks only)
* @arg {Object} [options.poll] A poll object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/poll#poll-object) for object structure
* @arg {String} [options.threadID] The ID of the thread channel in the webhook's channel to send the message to
* @arg {Boolean} [options.tts=false] Whether the message should be a TTS message or not
* @arg {String} [options.username] A custom username, defaults to webhook default username if not specified
Expand Down Expand Up @@ -3211,6 +3223,23 @@ class Client extends EventEmitter {
return this.requestHandler.request("GET", Endpoints.CHANNEL_PINS(channelID), true).then((messages) => messages.map((message) => new Message(message, this)));
}

/**
* Get a list of users that voted for a specific poll answer
* @arg {String} channelID The ID of the channel
* @arg {String} messageID The ID of the message that the poll is in
* @arg {Number} answerID The ID of the answer to get voters for
* @arg {Object} [options] Options for the request
* @arg {String} [options.after] Get users after this user ID
* @arg {Number} [options.limit=25] The max number of users to get
* @returns {Promise<Array<User>>} An array of users who voted for this answer
*/
getPollAnswerVoters(channelID, messageID, answerID, options = {}) {
return this.requestHandler.request("GET", Endpoints.POLL_ANSWER_VOTERS(channelID, messageID, answerID), true, {
after: options.after,
limit: options.limit
}).then((voters) => voters.users.map((user) => new User(user, this)));
}

/**
* Get the prune count for a guild
* @arg {String} guildID The ID of the guild
Expand Down
22 changes: 17 additions & 5 deletions lib/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,10 @@ const Intents = {
guildScheduledEvents: 1 << 16,

autoModerationConfiguration: 1 << 20,
autoModerationExecution: 1 << 21
autoModerationExecution: 1 << 21,

guildMessagePolls: 1 << 24,
directMessagePolls: 1 << 25
};

Intents.allNonPrivileged = Intents.guilds
Expand All @@ -381,7 +384,9 @@ Intents.allNonPrivileged = Intents.guilds
| Intents.directMessageTyping
| Intents.guildScheduledEvents
| Intents.autoModerationConfiguration
| Intents.autoModerationExecution;
| Intents.autoModerationExecution
| Intents.guildMessagePolls
| Intents.directMessagePolls;
Intents.allPrivileged = Intents.guildMembers
| Intents.guildPresences
| Intents.messageContent;
Expand Down Expand Up @@ -581,7 +586,8 @@ const Permissions = {
useExternalSounds: 1n << 45n,
sendVoiceMessages: 1n << 46n,

setVoiceChannelStatus: 1n << 48n
setVoiceChannelStatus: 1n << 48n,
sendPolls: 1n << 49n
};
Permissions.allGuild = Permissions.kickMembers
| Permissions.banMembers
Expand Down Expand Up @@ -620,7 +626,8 @@ Permissions.allText = Permissions.createInstantInvite
| Permissions.createPrivateThreads
| Permissions.useExternalStickers
| Permissions.sendMessagesInThreads
| Permissions.sendVoiceMessages;
| Permissions.sendVoiceMessages
| Permissions.sendPolls;
Permissions.allVoice = Permissions.createInstantInvite
| Permissions.manageChannels
| Permissions.prioritySpeaker
Expand All @@ -638,10 +645,15 @@ Permissions.allVoice = Permissions.createInstantInvite
| Permissions.useSoundboard
| Permissions.useExternalSounds
| Permissions.sendVoiceMessages
| Permissions.setVoiceChannelStatus;
| Permissions.setVoiceChannelStatus
| Permissions.sendPolls;
Permissions.all = Permissions.allGuild | Permissions.allText | Permissions.allVoice;
module.exports.Permissions = Permissions;

module.exports.PollLayoutTypes = {
DEFAULT: 1
};

module.exports.PremiumTiers = {
NONE: 0,
TIER_1: 1,
Expand Down
Loading

0 comments on commit 31c4608

Please sign in to comment.