mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
feat: polls overhaul (#11058)
* feat: polls overhaul (#10328) * feat(Managers): add PollAnswerVoterManager * feat(Partials): make Polls partial-safe * types: add typings * chore: add tests * fix: use fetch method in manager instead * chore: add tests for manager * feat: add partial support to poll actions * style: formatting * fix: change all .users references to .voters * refactor: add additional logic for partials * fix: actually add the partials * fix: fixed issue where event does not emit on first event * fix: align property type with DAPI documentation * fix: resolve additional bugs with partials * typings: update typings to reflect property type change * fix: tests * fix: adjust tests * refactor: combine partials logic into one statement * docs: mark getter as readonly * refactor: apply suggestions Co-authored-by: Almeida <github@almeidx.dev> * refactor(Actions): apply suggestions * refactor(PollAnswerVoterManager): apply suggestions * refactor(Message): check for existing poll before creating a poll * refactor(Polls): apply suggestions * revert(types): remove unused method from Poll class * refactor(Actions): consolidate poll creation logic into action class * refactor(PollAnswerVoterManager): set default for fetch parameter * refactor(Message): apply suggestion * fix: remove partial setter * refactor(Polls): apply suggestions * types: apply suggestions * refactor: remove clones * docs: spacing * refactor: move setters from constructor to _patch * types: adjust partials for poll classes * test: add more tests for polls * refactor: move updates around, more correct partial types * fix: handle more cases * refactor: requested changes * fix: missing imports * fix: update imports * fix: require file extensions --------- Co-authored-by: Almeida <github@almeidx.dev> Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> * fix(Poll): ensure `this.answers` is set before we reference it (#10809) * Ensure his.answers is set sooner if it's null during a patch * Move data.answers block up as well to ensure the patched answers are set * Ensure collection is set in constructor instead --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * fix(PollAnswer): only define _emoji property once (#10811) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --------- Co-authored-by: Kevin <uhkevinmc@eedo.app> Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> Co-authored-by: Jacob Morrison <jake.morrison24@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const Partials = require('../../util/Partials');
|
||||
const { Poll } = require('../../structures/Poll.js');
|
||||
const { PollAnswer } = require('../../structures/PollAnswer.js');
|
||||
const Partials = require('../../util/Partials.js');
|
||||
|
||||
/*
|
||||
|
||||
@@ -63,6 +65,23 @@ class GenericAction {
|
||||
);
|
||||
}
|
||||
|
||||
getPoll(data, message, channel) {
|
||||
const includePollPartial = this.client.options.partials.includes(Partials.Poll);
|
||||
const includePollAnswerPartial = this.client.options.partials.includes(Partials.PollAnswer);
|
||||
if (message.partial && (!includePollPartial || !includePollAnswerPartial)) return null;
|
||||
|
||||
if (!message.poll && includePollPartial) {
|
||||
message.poll = new Poll(this.client, data, message, channel);
|
||||
}
|
||||
|
||||
if (message.poll && !message.poll.answers.has(data.answer_id) && includePollAnswerPartial) {
|
||||
const pollAnswer = new PollAnswer(this.client, data, message.poll);
|
||||
message.poll.answers.set(data.answer_id, pollAnswer);
|
||||
}
|
||||
|
||||
return message.poll;
|
||||
}
|
||||
|
||||
getReaction(data, message, user) {
|
||||
const id = data.emoji.id ?? decodeURIComponent(data.emoji.name);
|
||||
return this.getPayload(
|
||||
|
||||
@@ -11,11 +11,18 @@ class MessagePollVoteAddAction extends Action {
|
||||
const message = this.getMessage(data, channel);
|
||||
if (!message) return false;
|
||||
|
||||
const { poll } = message;
|
||||
const poll = this.getPoll(data, message, channel);
|
||||
if (!poll) return false;
|
||||
|
||||
const answer = poll?.answers.get(data.answer_id);
|
||||
const answer = poll.answers.get(data.answer_id);
|
||||
if (!answer) return false;
|
||||
|
||||
const user = this.getUser(data);
|
||||
|
||||
if (user) {
|
||||
answer.voters._add(user);
|
||||
}
|
||||
|
||||
answer.voteCount++;
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,12 +11,17 @@ class MessagePollVoteRemoveAction extends Action {
|
||||
const message = this.getMessage(data, channel);
|
||||
if (!message) return false;
|
||||
|
||||
const { poll } = message;
|
||||
const poll = this.getPoll(data, message, channel);
|
||||
if (!poll) return false;
|
||||
|
||||
const answer = poll?.answers.get(data.answer_id);
|
||||
const answer = poll.answers.get(data.answer_id);
|
||||
if (!answer) return false;
|
||||
|
||||
answer.voteCount--;
|
||||
answer.voters.cache.delete(data.user_id);
|
||||
|
||||
if (answer.voteCount > 0) {
|
||||
answer.voteCount--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a user removes their vote in a poll.
|
||||
|
||||
@@ -80,6 +80,7 @@ exports.GuildStickerManager = require('./managers/GuildStickerManager');
|
||||
exports.GuildTextThreadManager = require('./managers/GuildTextThreadManager');
|
||||
exports.MessageManager = require('./managers/MessageManager');
|
||||
exports.PermissionOverwriteManager = require('./managers/PermissionOverwriteManager');
|
||||
exports.PollAnswerVoterManager = require('./managers/PollAnswerVoterManager.js').PollAnswerVoterManager;
|
||||
exports.PresenceManager = require('./managers/PresenceManager');
|
||||
exports.ReactionManager = require('./managers/ReactionManager');
|
||||
exports.ReactionUserManager = require('./managers/ReactionUserManager');
|
||||
|
||||
50
packages/discord.js/src/managers/PollAnswerVoterManager.js
Normal file
50
packages/discord.js/src/managers/PollAnswerVoterManager.js
Normal file
@@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { makeURLSearchParams } = require('@discordjs/rest');
|
||||
const { Routes } = require('discord-api-types/v10');
|
||||
const CachedManager = require('./CachedManager.js');
|
||||
const User = require('../structures/User.js');
|
||||
|
||||
/**
|
||||
* Manages API methods for users who voted on a poll and stores their cache.
|
||||
* @extends {CachedManager}
|
||||
*/
|
||||
class PollAnswerVoterManager extends CachedManager {
|
||||
constructor(answer) {
|
||||
super(answer.client, User);
|
||||
|
||||
/**
|
||||
* The poll answer that this manager belongs to
|
||||
* @type {PollAnswer}
|
||||
*/
|
||||
this.answer = answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache of this manager
|
||||
* @type {Collection<Snowflake, User>}
|
||||
* @name PollAnswerVoterManager#cache
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetches the users that voted on this poll answer. Resolves with a collection of users, mapped by their ids.
|
||||
* @param {BaseFetchPollAnswerVotersOptions} [options={}] Options for fetching the users
|
||||
* @returns {Promise<Collection<Snowflake, User>>}
|
||||
*/
|
||||
async fetch({ after, limit } = {}) {
|
||||
const poll = this.answer.poll;
|
||||
const query = makeURLSearchParams({ limit, after });
|
||||
const data = await this.client.rest.get(Routes.pollAnswerVoters(poll.channelId, poll.messageId, this.answer.id), {
|
||||
query,
|
||||
});
|
||||
|
||||
return data.users.reduce((coll, rawUser) => {
|
||||
const user = this.client.users._add(rawUser);
|
||||
this.cache.set(user.id, user);
|
||||
return coll.set(user.id, user);
|
||||
}, new Collection());
|
||||
}
|
||||
}
|
||||
|
||||
exports.PollAnswerVoterManager = PollAnswerVoterManager;
|
||||
@@ -442,11 +442,15 @@ class Message extends Base {
|
||||
}
|
||||
|
||||
if (data.poll) {
|
||||
/**
|
||||
* The poll that was sent with the message
|
||||
* @type {?Poll}
|
||||
*/
|
||||
this.poll = new Poll(this.client, data.poll, this);
|
||||
if (this.poll) {
|
||||
this.poll._patch(data.poll);
|
||||
} else {
|
||||
/**
|
||||
* The poll that was sent with the message
|
||||
* @type {?Poll}
|
||||
*/
|
||||
this.poll = new Poll(this.client, data.poll, this, this.channel);
|
||||
}
|
||||
} else {
|
||||
this.poll ??= null;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,30 @@ const { ErrorCodes } = require('../errors/index');
|
||||
* @extends {Base}
|
||||
*/
|
||||
class Poll extends Base {
|
||||
constructor(client, data, message) {
|
||||
constructor(client, data, message, channel) {
|
||||
super(client);
|
||||
|
||||
/**
|
||||
* The id of the channel that this poll is in
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.channelId = data.channel_id ?? channel.id;
|
||||
|
||||
/**
|
||||
* The channel that this poll is in
|
||||
* @name Poll#channel
|
||||
* @type {TextBasedChannel}
|
||||
* @readonly
|
||||
*/
|
||||
|
||||
Object.defineProperty(this, 'channel', { value: channel });
|
||||
|
||||
/**
|
||||
* The id of the message that started this poll
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.messageId = data.message_id ?? message.id;
|
||||
|
||||
/**
|
||||
* The message that started this poll
|
||||
* @name Poll#message
|
||||
@@ -23,51 +44,27 @@ class Poll extends Base {
|
||||
|
||||
Object.defineProperty(this, 'message', { value: message });
|
||||
|
||||
/**
|
||||
* The media for a poll's question
|
||||
* @typedef {Object} PollQuestionMedia
|
||||
* @property {string} text The text of this question
|
||||
*/
|
||||
|
||||
/**
|
||||
* The media for this poll's question
|
||||
* @type {PollQuestionMedia}
|
||||
*/
|
||||
this.question = {
|
||||
text: data.question.text,
|
||||
};
|
||||
|
||||
/**
|
||||
* The answers of this poll
|
||||
* @type {Collection<number, PollAnswer>}
|
||||
* @type {Collection<number, PollAnswer|PartialPollAnswer>}
|
||||
*/
|
||||
this.answers = data.answers.reduce(
|
||||
(acc, answer) => acc.set(answer.answer_id, new PollAnswer(this.client, answer, this)),
|
||||
new Collection(),
|
||||
);
|
||||
|
||||
/**
|
||||
* The timestamp when this poll expires
|
||||
* @type {number}
|
||||
*/
|
||||
this.expiresTimestamp = Date.parse(data.expiry);
|
||||
|
||||
/**
|
||||
* Whether this poll allows multiple answers
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.allowMultiselect = data.allow_multiselect;
|
||||
|
||||
/**
|
||||
* The layout type of this poll
|
||||
* @type {PollLayoutType}
|
||||
*/
|
||||
this.layoutType = data.layout_type;
|
||||
this.answers = new Collection();
|
||||
|
||||
this._patch(data);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
if (data.answers) {
|
||||
for (const answer of data.answers) {
|
||||
const existing = this.answers.get(answer.answer_id);
|
||||
if (existing) {
|
||||
existing._patch(answer);
|
||||
} else {
|
||||
this.answers.set(answer.answer_id, new PollAnswer(this.client, answer, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.results) {
|
||||
/**
|
||||
* Whether this poll's results have been precisely counted
|
||||
@@ -82,15 +79,84 @@ class Poll extends Base {
|
||||
} else {
|
||||
this.resultsFinalized ??= false;
|
||||
}
|
||||
|
||||
if ('allow_multiselect' in data) {
|
||||
/**
|
||||
* Whether this poll allows multiple answers
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.allowMultiselect = data.allow_multiselect;
|
||||
} else {
|
||||
this.allowMultiselect ??= null;
|
||||
}
|
||||
|
||||
if ('layout_type' in data) {
|
||||
/**
|
||||
* The layout type of this poll
|
||||
* @type {PollLayoutType}
|
||||
*/
|
||||
this.layoutType = data.layout_type;
|
||||
} else {
|
||||
this.layoutType ??= null;
|
||||
}
|
||||
|
||||
if ('expiry' in data) {
|
||||
/**
|
||||
* The timestamp when this poll expires
|
||||
* @type {?number}
|
||||
*/
|
||||
this.expiresTimestamp = data.expiry && Date.parse(data.expiry);
|
||||
} else {
|
||||
this.expiresTimestamp ??= null;
|
||||
}
|
||||
|
||||
if (data.question) {
|
||||
/**
|
||||
* The media for a poll's question
|
||||
* @typedef {Object} PollQuestionMedia
|
||||
* @property {?string} text The text of this question
|
||||
*/
|
||||
|
||||
/**
|
||||
* The media for this poll's question
|
||||
* @type {PollQuestionMedia}
|
||||
*/
|
||||
this.question = {
|
||||
text: data.question.text,
|
||||
};
|
||||
} else {
|
||||
this.question ??= {
|
||||
text: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The date when this poll expires
|
||||
* @type {Date}
|
||||
* @type {?Date}
|
||||
* @readonly
|
||||
*/
|
||||
get expiresAt() {
|
||||
return new Date(this.expiresTimestamp);
|
||||
return this.expiresTimestamp && new Date(this.expiresTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this poll is a partial
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get partial() {
|
||||
return this.allowMultiselect === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the message that started this poll, then updates the poll from the fetched message.
|
||||
* @returns {Promise<Poll>}
|
||||
*/
|
||||
async fetch() {
|
||||
await this.channel.messages.fetch(this.messageId);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +168,7 @@ class Poll extends Base {
|
||||
throw new DiscordjsError(ErrorCodes.PollAlreadyExpired);
|
||||
}
|
||||
|
||||
return this.message.channel.messages.endPoll(this.message.id);
|
||||
return this.channel.messages.endPoll(this.messageId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const Base = require('./Base');
|
||||
const { Emoji } = require('./Emoji');
|
||||
const process = require('node:process');
|
||||
const Base = require('./Base.js');
|
||||
const { Emoji } = require('./Emoji.js');
|
||||
const { PollAnswerVoterManager } = require('../managers/PollAnswerVoterManager.js');
|
||||
|
||||
let deprecationEmittedForFetchVoters = false;
|
||||
|
||||
/**
|
||||
* Represents an answer to a {@link Poll}
|
||||
@@ -14,7 +18,7 @@ class PollAnswer extends Base {
|
||||
/**
|
||||
* The {@link Poll} this answer is part of
|
||||
* @name PollAnswer#poll
|
||||
* @type {Poll}
|
||||
* @type {Poll|PartialPoll}
|
||||
* @readonly
|
||||
*/
|
||||
Object.defineProperty(this, 'poll', { value: poll });
|
||||
@@ -26,10 +30,10 @@ class PollAnswer extends Base {
|
||||
this.id = data.answer_id;
|
||||
|
||||
/**
|
||||
* The text of this answer
|
||||
* @type {?string}
|
||||
* The manager of the voters for this answer
|
||||
* @type {PollAnswerVoterManager}
|
||||
*/
|
||||
this.text = data.poll_media.text ?? null;
|
||||
this.voters = new PollAnswerVoterManager(this);
|
||||
|
||||
/**
|
||||
* The raw emoji of this answer
|
||||
@@ -37,7 +41,7 @@ class PollAnswer extends Base {
|
||||
* @type {?APIPartialEmoji}
|
||||
* @private
|
||||
*/
|
||||
Object.defineProperty(this, '_emoji', { value: data.poll_media.emoji ?? null });
|
||||
Object.defineProperty(this, '_emoji', { value: null, writable: true });
|
||||
|
||||
this._patch(data);
|
||||
}
|
||||
@@ -51,7 +55,17 @@ class PollAnswer extends Base {
|
||||
*/
|
||||
this.voteCount = data.count;
|
||||
} else {
|
||||
this.voteCount ??= 0;
|
||||
this.voteCount ??= this.voters.cache.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* The text of this answer
|
||||
* @type {?string}
|
||||
*/
|
||||
this.text ??= data.poll_media?.text ?? null;
|
||||
|
||||
if (data.poll_media?.emoji) {
|
||||
this._emoji = data.poll_media.emoji;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +78,15 @@ class PollAnswer extends Base {
|
||||
return this.client.emojis.cache.get(this._emoji.id) ?? new Emoji(this.client, this._emoji);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this poll answer is a partial.
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get partial() {
|
||||
return this.poll.partial || (this.text === null && this.emoji === null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Options used for fetching voters of a poll answer.
|
||||
* @typedef {Object} BaseFetchPollAnswerVotersOptions
|
||||
@@ -75,14 +98,16 @@ class PollAnswer extends Base {
|
||||
* Fetches the users that voted for this answer.
|
||||
* @param {BaseFetchPollAnswerVotersOptions} [options={}] The options for fetching voters
|
||||
* @returns {Promise<Collection<Snowflake, User>>}
|
||||
* @deprecated Use {@link PollAnswerVoterManager#fetch} instead
|
||||
*/
|
||||
fetchVoters({ after, limit } = {}) {
|
||||
return this.poll.message.channel.messages.fetchPollAnswerVoters({
|
||||
messageId: this.poll.message.id,
|
||||
answerId: this.id,
|
||||
after,
|
||||
limit,
|
||||
});
|
||||
if (!deprecationEmittedForFetchVoters) {
|
||||
process.emitWarning('PollAnswer#fetchVoters is deprecated. Use PollAnswer#voters#fetch instead.');
|
||||
|
||||
deprecationEmittedForFetchVoters = true;
|
||||
}
|
||||
|
||||
return this.voters.fetch({ after, limit });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ const { createEnum } = require('./Enums');
|
||||
* @property {number} GuildScheduledEvent The partial to receive uncached guild scheduled events.
|
||||
* @property {number} ThreadMember The partial to receive uncached thread members.
|
||||
* @property {number} SoundboardSound The partial to receive uncached soundboard sounds.
|
||||
* @property {number} Poll The partial to receive uncached polls.
|
||||
* @property {number} PollAnswer The partial to receive uncached poll answers.
|
||||
*/
|
||||
|
||||
// JSDoc for IntelliSense purposes
|
||||
@@ -43,4 +45,6 @@ module.exports = createEnum([
|
||||
'GuildScheduledEvent',
|
||||
'ThreadMember',
|
||||
'SoundboardSound',
|
||||
'Poll',
|
||||
'PollAnswer',
|
||||
]);
|
||||
|
||||
53
packages/discord.js/typings/index.d.ts
vendored
53
packages/discord.js/typings/index.d.ts
vendored
@@ -3005,19 +3005,30 @@ export class Presence extends Base {
|
||||
}
|
||||
|
||||
export interface PollQuestionMedia {
|
||||
text: string;
|
||||
text: string | null;
|
||||
}
|
||||
|
||||
export class PollAnswerVoterManager extends CachedManager<Snowflake, User, UserResolvable> {
|
||||
private constructor(answer: PollAnswer);
|
||||
public answer: PollAnswer;
|
||||
public fetch(options?: BaseFetchPollAnswerVotersOptions): Promise<Collection<Snowflake, User>>;
|
||||
}
|
||||
|
||||
export class Poll extends Base {
|
||||
private constructor(client: Client<true>, data: APIPoll, message: Message);
|
||||
private constructor(client: Client<true>, data: APIPoll, message: Message, channel: TextBasedChannel);
|
||||
public readonly channel: TextBasedChannel;
|
||||
public channelId: Snowflake;
|
||||
public readonly message: Message;
|
||||
public messageId: Snowflake;
|
||||
public question: PollQuestionMedia;
|
||||
public answers: Collection<number, PollAnswer>;
|
||||
public expiresTimestamp: number;
|
||||
public get expiresAt(): Date;
|
||||
public answers: Collection<number, PollAnswer | PartialPollAnswer>;
|
||||
public expiresTimestamp: number | null;
|
||||
public get expiresAt(): Date | null;
|
||||
public allowMultiselect: boolean;
|
||||
public layoutType: PollLayoutType;
|
||||
public resultsFinalized: boolean;
|
||||
public get partial(): false;
|
||||
public fetch(): Promise<this>;
|
||||
public end(): Promise<Message>;
|
||||
}
|
||||
|
||||
@@ -3029,11 +3040,14 @@ export interface BaseFetchPollAnswerVotersOptions {
|
||||
export class PollAnswer extends Base {
|
||||
private constructor(client: Client<true>, data: APIPollAnswer & { count?: number }, poll: Poll);
|
||||
private _emoji: APIPartialEmoji | null;
|
||||
public readonly poll: Poll;
|
||||
public readonly poll: Poll | PartialPoll;
|
||||
public id: number;
|
||||
public text: string | null;
|
||||
public voteCount: number;
|
||||
public voters: PollAnswerVoterManager;
|
||||
public get emoji(): GuildEmoji | Emoji | null;
|
||||
public get partial(): false;
|
||||
/** @deprecated Use {@link PollAnswerVoterManager.fetch} instead */
|
||||
public fetchVoters(options?: BaseFetchPollAnswerVotersOptions): Promise<Collection<Snowflake, User>>;
|
||||
}
|
||||
|
||||
@@ -5310,7 +5324,9 @@ export type AllowedPartial =
|
||||
| MessageReaction
|
||||
| GuildScheduledEvent
|
||||
| ThreadMember
|
||||
| SoundboardSound;
|
||||
| SoundboardSound
|
||||
| Poll
|
||||
| PollAnswer;
|
||||
|
||||
export type AllowedThreadTypeForNewsChannel = ChannelType.AnnouncementThread;
|
||||
|
||||
@@ -5882,8 +5898,8 @@ export interface ClientEvents {
|
||||
inviteDelete: [invite: Invite];
|
||||
messageCreate: [message: OmitPartialGroupDMChannel<Message>];
|
||||
messageDelete: [message: OmitPartialGroupDMChannel<Message | PartialMessage>];
|
||||
messagePollVoteAdd: [pollAnswer: PollAnswer, userId: Snowflake];
|
||||
messagePollVoteRemove: [pollAnswer: PollAnswer, userId: Snowflake];
|
||||
messagePollVoteAdd: [pollAnswer: PollAnswer | PartialPollAnswer, userId: Snowflake];
|
||||
messagePollVoteRemove: [pollAnswer: PollAnswer | PartialPollAnswer, userId: Snowflake];
|
||||
messageReactionRemoveAll: [
|
||||
message: OmitPartialGroupDMChannel<Message | PartialMessage>,
|
||||
reactions: ReadonlyCollection<string | Snowflake, MessageReaction>,
|
||||
@@ -7377,6 +7393,23 @@ export interface PartialMessage
|
||||
|
||||
export interface PartialMessageReaction extends Partialize<MessageReaction, 'count'> {}
|
||||
|
||||
export interface PartialPoll
|
||||
extends Partialize<
|
||||
Poll,
|
||||
'allowMultiselect' | 'layoutType' | 'expiresTimestamp',
|
||||
null,
|
||||
'question' | 'message' | 'answers'
|
||||
> {
|
||||
question: { text: null };
|
||||
message: PartialMessage;
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
answers: Collection<number, PartialPollAnswer>;
|
||||
}
|
||||
|
||||
export interface PartialPollAnswer extends Partialize<PollAnswer, 'emoji' | 'text', null, 'poll'> {
|
||||
readonly poll: PartialPoll;
|
||||
}
|
||||
|
||||
export interface PartialGuildScheduledEvent
|
||||
extends Partialize<GuildScheduledEvent, 'userCount', 'status' | 'privacyLevel' | 'name' | 'entityType'> {}
|
||||
|
||||
@@ -7406,6 +7439,8 @@ export enum Partials {
|
||||
GuildScheduledEvent,
|
||||
ThreadMember,
|
||||
SoundboardSound,
|
||||
Poll,
|
||||
PollAnswer,
|
||||
}
|
||||
|
||||
export interface PartialUser extends Partialize<User, 'username' | 'tag' | 'discriminator'> {}
|
||||
|
||||
@@ -232,7 +232,11 @@ import {
|
||||
ContainerComponentData,
|
||||
InteractionResponse,
|
||||
FetchPinnedMessagesResponse,
|
||||
} from '.';
|
||||
PartialPoll,
|
||||
PartialPollAnswer,
|
||||
PollAnswer,
|
||||
PollAnswerVoterManager,
|
||||
} from './index.js';
|
||||
import {
|
||||
expectAssignable,
|
||||
expectDeprecated,
|
||||
@@ -715,6 +719,48 @@ client.on('messageDeleteBulk', (messages, { client }) => {
|
||||
expectType<Client<true>>(client);
|
||||
});
|
||||
|
||||
client.on('messagePollVoteAdd', async (answer, userId) => {
|
||||
expectType<Client<true>>(answer.client);
|
||||
expectType<Snowflake>(userId);
|
||||
|
||||
if (answer.partial) {
|
||||
expectType<null>(answer.emoji);
|
||||
expectType<null>(answer.text);
|
||||
expectNotType<null>(answer.id);
|
||||
expectNotType<null>(answer.poll);
|
||||
|
||||
await answer.poll.fetch();
|
||||
answer = answer.poll.answers?.get(answer.id) ?? answer;
|
||||
|
||||
expectType<User>(answer.voters.cache.get(userId)!);
|
||||
}
|
||||
|
||||
expectType<string | null>(answer.text);
|
||||
expectType<GuildEmoji | Emoji | null>(answer.emoji);
|
||||
expectType<number>(answer.id);
|
||||
expectType<number>(answer.voteCount!);
|
||||
});
|
||||
|
||||
client.on('messagePollVoteRemove', async (answer, userId) => {
|
||||
expectType<Client<true>>(answer.client);
|
||||
expectType<Snowflake>(userId);
|
||||
|
||||
if (answer.partial) {
|
||||
expectType<null>(answer.emoji);
|
||||
expectType<null>(answer.text);
|
||||
expectNotType<null>(answer.id);
|
||||
expectNotType<null>(answer.poll);
|
||||
|
||||
await answer.poll.fetch();
|
||||
answer = answer.poll.answers?.get(answer.id) ?? answer;
|
||||
}
|
||||
|
||||
expectType<string | null>(answer.text);
|
||||
expectType<GuildEmoji | Emoji | null>(answer.emoji);
|
||||
expectType<number>(answer.id);
|
||||
expectType<number>(answer.voteCount!);
|
||||
});
|
||||
|
||||
client.on('messageReactionAdd', async (reaction, { client }) => {
|
||||
expectType<Client<true>>(reaction.client);
|
||||
expectType<Client<true>>(client);
|
||||
@@ -1780,6 +1826,12 @@ declare const messageManager: MessageManager;
|
||||
messageManager.fetch({ message: '1234567890', after: '1234567890', cache: true, force: false });
|
||||
}
|
||||
|
||||
declare const pollAnswerVoterManager: PollAnswerVoterManager;
|
||||
{
|
||||
expectType<Promise<Collection<Snowflake, User>>>(pollAnswerVoterManager.fetch());
|
||||
expectType<PollAnswer>(pollAnswerVoterManager.answer);
|
||||
}
|
||||
|
||||
declare const roleManager: RoleManager;
|
||||
expectType<Promise<Collection<Snowflake, Role>>>(roleManager.fetch());
|
||||
expectType<Promise<Collection<Snowflake, Role>>>(roleManager.fetch(undefined, {}));
|
||||
@@ -2802,16 +2854,42 @@ await textChannel.send({
|
||||
},
|
||||
});
|
||||
|
||||
declare const partialPoll: PartialPoll;
|
||||
{
|
||||
if (partialPoll.partial) {
|
||||
expectType<null>(partialPoll.question.text);
|
||||
expectType<PartialMessage>(partialPoll.message);
|
||||
expectType<null>(partialPoll.allowMultiselect);
|
||||
expectType<null>(partialPoll.layoutType);
|
||||
expectType<null>(partialPoll.expiresTimestamp);
|
||||
expectType<Collection<number, PartialPollAnswer>>(partialPoll.answers);
|
||||
}
|
||||
}
|
||||
|
||||
declare const partialPollAnswer: PartialPollAnswer;
|
||||
{
|
||||
if (partialPollAnswer.partial) {
|
||||
expectType<PartialPoll>(partialPollAnswer.poll);
|
||||
expectType<null>(partialPollAnswer.emoji);
|
||||
expectType<null>(partialPollAnswer.text);
|
||||
}
|
||||
}
|
||||
declare const poll: Poll;
|
||||
declare const message: Message;
|
||||
declare const pollData: PollData;
|
||||
{
|
||||
expectType<Message>(await poll.end());
|
||||
expectType<false>(poll.partial);
|
||||
expectNotType<Collection<number, PartialPollAnswer>>(poll.answers);
|
||||
|
||||
const answer = poll.answers.first()!;
|
||||
expectType<number>(answer.voteCount);
|
||||
|
||||
expectType<Collection<Snowflake, User>>(await answer.fetchVoters({ after: snowflake, limit: 10 }));
|
||||
if (!answer.partial) {
|
||||
expectType<number>(answer.voteCount);
|
||||
expectType<number>(answer.id);
|
||||
expectType<PollAnswerVoterManager>(answer.voters);
|
||||
expectType<Collection<Snowflake, User>>(await answer.voters.fetch({ after: snowflake, limit: 10 }));
|
||||
}
|
||||
|
||||
await messageManager.endPoll(snowflake);
|
||||
await messageManager.fetchPollAnswerVoters({
|
||||
|
||||
Reference in New Issue
Block a user