discord.js 15.0.0-dev.1739621068-b7fd2d105 → 15.0.0-dev.1740010343-19d48f6d6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "discord.js",
4
- "version": "15.0.0-dev.1739621068-b7fd2d105",
4
+ "version": "15.0.0-dev.1740010343-19d48f6d6",
5
5
  "description": "A powerful library for interacting with the Discord API",
6
6
  "main": "./src/index.js",
7
7
  "types": "./typings/index.d.ts",
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const { Poll } = require('../../structures/Poll.js');
4
+ const { PollAnswer } = require('../../structures/PollAnswer.js');
3
5
  const { Partials } = require('../../util/Partials.js');
4
6
 
5
7
  /*
@@ -63,6 +65,23 @@ class Action {
63
65
  );
64
66
  }
65
67
 
68
+ getPoll(data, message, channel) {
69
+ const includePollPartial = this.client.options.partials.includes(Partials.Poll);
70
+ const includePollAnswerPartial = this.client.options.partials.includes(Partials.PollAnswer);
71
+ if (message.partial && (!includePollPartial || !includePollAnswerPartial)) return null;
72
+
73
+ if (!message.poll && includePollPartial) {
74
+ message.poll = new Poll(this.client, data, message, channel);
75
+ }
76
+
77
+ if (message.poll && !message.poll.answers.has(data.answer_id) && includePollAnswerPartial) {
78
+ const pollAnswer = new PollAnswer(this.client, data, message.poll);
79
+ message.poll.answers.set(data.answer_id, pollAnswer);
80
+ }
81
+
82
+ return message.poll;
83
+ }
84
+
66
85
  getReaction(data, message, user) {
67
86
  const id = data.emoji.id ?? decodeURIComponent(data.emoji.name);
68
87
  return this.getPayload(
@@ -11,11 +11,18 @@ class MessagePollVoteAddAction extends Action {
11
11
  const message = this.getMessage(data, channel);
12
12
  if (!message) return false;
13
13
 
14
- const { poll } = message;
14
+ const poll = this.getPoll(data, message, channel);
15
+ if (!poll) return false;
15
16
 
16
- const answer = poll?.answers.get(data.answer_id);
17
+ const answer = poll.answers.get(data.answer_id);
17
18
  if (!answer) return false;
18
19
 
20
+ const user = this.getUser(data);
21
+
22
+ if (user) {
23
+ answer.voters._add(user);
24
+ }
25
+
19
26
  answer.voteCount++;
20
27
 
21
28
  /**
@@ -11,12 +11,17 @@ class MessagePollVoteRemoveAction extends Action {
11
11
  const message = this.getMessage(data, channel);
12
12
  if (!message) return false;
13
13
 
14
- const { poll } = message;
14
+ const poll = this.getPoll(data, message, channel);
15
+ if (!poll) return false;
15
16
 
16
- const answer = poll?.answers.get(data.answer_id);
17
+ const answer = poll.answers.get(data.answer_id);
17
18
  if (!answer) return false;
18
19
 
19
- answer.voteCount--;
20
+ answer.voters.cache.delete(data.user_id);
21
+
22
+ if (answer.voteCount > 0) {
23
+ answer.voteCount--;
24
+ }
20
25
 
21
26
  /**
22
27
  * Emitted whenever a user removes their vote in a poll.
package/src/index.js CHANGED
@@ -81,6 +81,7 @@ exports.GuildStickerManager = require('./managers/GuildStickerManager.js').Guild
81
81
  exports.GuildTextThreadManager = require('./managers/GuildTextThreadManager.js').GuildTextThreadManager;
82
82
  exports.MessageManager = require('./managers/MessageManager.js').MessageManager;
83
83
  exports.PermissionOverwriteManager = require('./managers/PermissionOverwriteManager.js').PermissionOverwriteManager;
84
+ exports.PollAnswerVoterManager = require('./managers/PollAnswerVoterManager.js').PollAnswerVoterManager;
84
85
  exports.PresenceManager = require('./managers/PresenceManager.js').PresenceManager;
85
86
  exports.ReactionManager = require('./managers/ReactionManager.js').ReactionManager;
86
87
  exports.ReactionUserManager = require('./managers/ReactionUserManager.js').ReactionUserManager;
@@ -0,0 +1,50 @@
1
+ 'use strict';
2
+
3
+ const { Collection } = require('@discordjs/collection');
4
+ const { makeURLSearchParams } = require('@discordjs/rest');
5
+ const { Routes } = require('discord-api-types/v10');
6
+ const { CachedManager } = require('./CachedManager.js');
7
+ const { User } = require('../structures/User.js');
8
+
9
+ /**
10
+ * Manages API methods for users who voted on a poll and stores their cache.
11
+ * @extends {CachedManager}
12
+ */
13
+ class PollAnswerVoterManager extends CachedManager {
14
+ constructor(answer) {
15
+ super(answer.client, User);
16
+
17
+ /**
18
+ * The poll answer that this manager belongs to
19
+ * @type {PollAnswer}
20
+ */
21
+ this.answer = answer;
22
+ }
23
+
24
+ /**
25
+ * The cache of this manager
26
+ * @type {Collection<Snowflake, User>}
27
+ * @name PollAnswerVoterManager#cache
28
+ */
29
+
30
+ /**
31
+ * Fetches the users that voted on this poll answer. Resolves with a collection of users, mapped by their ids.
32
+ * @param {BaseFetchPollAnswerVotersOptions} [options={}] Options for fetching the users
33
+ * @returns {Promise<Collection<Snowflake, User>>}
34
+ */
35
+ async fetch({ after, limit } = {}) {
36
+ const poll = this.answer.poll;
37
+ const query = makeURLSearchParams({ limit, after });
38
+ const data = await this.client.rest.get(Routes.pollAnswerVoters(poll.channelId, poll.messageId, this.answer.id), {
39
+ query,
40
+ });
41
+
42
+ return data.users.reduce((coll, rawUser) => {
43
+ const user = this.client.users._add(rawUser);
44
+ this.cache.set(user.id, user);
45
+ return coll.set(user.id, user);
46
+ }, new Collection());
47
+ }
48
+ }
49
+
50
+ exports.PollAnswerVoterManager = PollAnswerVoterManager;
@@ -414,11 +414,15 @@ class Message extends Base {
414
414
  }
415
415
 
416
416
  if (data.poll) {
417
- /**
418
- * The poll that was sent with the message
419
- * @type {?Poll}
420
- */
421
- this.poll = new Poll(this.client, data.poll, this);
417
+ if (this.poll) {
418
+ this.poll._patch(data.poll);
419
+ } else {
420
+ /**
421
+ * The poll that was sent with the message
422
+ * @type {?Poll}
423
+ */
424
+ this.poll = new Poll(this.client, data.poll, this, this.channel);
425
+ }
422
426
  } else {
423
427
  this.poll ??= null;
424
428
  }
@@ -10,58 +10,38 @@ const { DiscordjsError, ErrorCodes } = require('../errors/index.js');
10
10
  * @extends {Base}
11
11
  */
12
12
  class Poll extends Base {
13
- constructor(client, data, message) {
13
+ constructor(client, data, message, channel) {
14
14
  super(client);
15
15
 
16
16
  /**
17
- * The message that started this poll
18
- * @name Poll#message
19
- * @type {Message}
20
- * @readonly
17
+ * The id of the channel that this poll is in
18
+ * @type {Snowflake}
21
19
  */
22
-
23
- Object.defineProperty(this, 'message', { value: message });
20
+ this.channelId = data.channel_id ?? channel.id;
24
21
 
25
22
  /**
26
- * The media for a poll's question
27
- * @typedef {Object} PollQuestionMedia
28
- * @property {string} text The text of this question
29
- */
30
-
31
- /**
32
- * The media for this poll's question
33
- * @type {PollQuestionMedia}
23
+ * The channel that this poll is in
24
+ * @name Poll#channel
25
+ * @type {TextBasedChannel}
26
+ * @readonly
34
27
  */
35
- this.question = {
36
- text: data.question.text,
37
- };
38
28
 
39
- /**
40
- * The answers of this poll
41
- * @type {Collection<number, PollAnswer>}
42
- */
43
- this.answers = data.answers.reduce(
44
- (acc, answer) => acc.set(answer.answer_id, new PollAnswer(this.client, answer, this)),
45
- new Collection(),
46
- );
29
+ Object.defineProperty(this, 'channel', { value: channel });
47
30
 
48
31
  /**
49
- * The timestamp when this poll expires
50
- * @type {number}
32
+ * The id of the message that started this poll
33
+ * @type {Snowflake}
51
34
  */
52
- this.expiresTimestamp = Date.parse(data.expiry);
35
+ this.messageId = data.message_id ?? message.id;
53
36
 
54
37
  /**
55
- * Whether this poll allows multiple answers
56
- * @type {boolean}
38
+ * The message that started this poll
39
+ * @name Poll#message
40
+ * @type {Message}
41
+ * @readonly
57
42
  */
58
- this.allowMultiselect = data.allow_multiselect;
59
43
 
60
- /**
61
- * The layout type of this poll
62
- * @type {PollLayoutType}
63
- */
64
- this.layoutType = data.layout_type;
44
+ Object.defineProperty(this, 'message', { value: message });
65
45
 
66
46
  this._patch(data);
67
47
  }
@@ -81,15 +61,101 @@ class Poll extends Base {
81
61
  } else {
82
62
  this.resultsFinalized ??= false;
83
63
  }
64
+
65
+ if ('allow_multiselect' in data) {
66
+ /**
67
+ * Whether this poll allows multiple answers
68
+ * @type {boolean}
69
+ */
70
+ this.allowMultiselect = data.allow_multiselect;
71
+ } else {
72
+ this.allowMultiselect ??= null;
73
+ }
74
+
75
+ if ('layout_type' in data) {
76
+ /**
77
+ * The layout type of this poll
78
+ * @type {PollLayoutType}
79
+ */
80
+ this.layoutType = data.layout_type;
81
+ } else {
82
+ this.layoutType ??= null;
83
+ }
84
+
85
+ if ('expiry' in data) {
86
+ /**
87
+ * The timestamp when this poll expires
88
+ * @type {?number}
89
+ */
90
+ this.expiresTimestamp = data.expiry && Date.parse(data.expiry);
91
+ } else {
92
+ this.expiresTimestamp ??= null;
93
+ }
94
+
95
+ if (data.question) {
96
+ /**
97
+ * The media for a poll's question
98
+ * @typedef {Object} PollQuestionMedia
99
+ * @property {?string} text The text of this question
100
+ */
101
+
102
+ /**
103
+ * The media for this poll's question
104
+ * @type {PollQuestionMedia}
105
+ */
106
+ this.question = {
107
+ text: data.question.text,
108
+ };
109
+ } else {
110
+ this.question ??= {
111
+ text: null,
112
+ };
113
+ }
114
+
115
+ /**
116
+ * The answers of this poll
117
+ * @type {Collection<number, PollAnswer|PartialPollAnswer>}
118
+ */
119
+ this.answers ??= new Collection();
120
+
121
+ if (data.answers) {
122
+ for (const answer of data.answers) {
123
+ const existing = this.answers.get(answer.answer_id);
124
+ if (existing) {
125
+ existing._patch(answer);
126
+ } else {
127
+ this.answers.set(answer.answer_id, new PollAnswer(this.client, answer, this));
128
+ }
129
+ }
130
+ }
84
131
  }
85
132
 
86
133
  /**
87
134
  * The date when this poll expires
88
- * @type {Date}
135
+ * @type {?Date}
89
136
  * @readonly
90
137
  */
91
138
  get expiresAt() {
92
- return new Date(this.expiresTimestamp);
139
+ return this.expiresTimestamp && new Date(this.expiresTimestamp);
140
+ }
141
+
142
+ /**
143
+ * Whether this poll is a partial
144
+ * @type {boolean}
145
+ * @readonly
146
+ */
147
+ get partial() {
148
+ return this.allowMultiselect === null;
149
+ }
150
+
151
+ /**
152
+ * Fetches the message that started this poll, then updates the poll from the fetched message.
153
+ * @returns {Promise<Poll>}
154
+ */
155
+ async fetch() {
156
+ await this.channel.messages.fetch(this.messageId);
157
+
158
+ return this;
93
159
  }
94
160
 
95
161
  /**
@@ -101,7 +167,7 @@ class Poll extends Base {
101
167
  throw new DiscordjsError(ErrorCodes.PollAlreadyExpired);
102
168
  }
103
169
 
104
- return this.message.channel.messages.endPoll(this.message.id);
170
+ return this.channel.messages.endPoll(this.messageId);
105
171
  }
106
172
  }
107
173
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { Base } = require('./Base.js');
4
4
  const { Emoji } = require('./Emoji.js');
5
+ const { PollAnswerVoterManager } = require('../managers/PollAnswerVoterManager.js');
5
6
  const { resolveGuildEmoji } = require('../util/Util.js');
6
7
 
7
8
  /**
@@ -15,7 +16,7 @@ class PollAnswer extends Base {
15
16
  /**
16
17
  * The {@link Poll} this answer is part of
17
18
  * @name PollAnswer#poll
18
- * @type {Poll}
19
+ * @type {Poll|PartialPoll}
19
20
  * @readonly
20
21
  */
21
22
  Object.defineProperty(this, 'poll', { value: poll });
@@ -27,10 +28,10 @@ class PollAnswer extends Base {
27
28
  this.id = data.answer_id;
28
29
 
29
30
  /**
30
- * The text of this answer
31
- * @type {?string}
31
+ * The manager of the voters for this answer
32
+ * @type {PollAnswerVoterManager}
32
33
  */
33
- this.text = data.poll_media.text ?? null;
34
+ this.voters = new PollAnswerVoterManager(this);
34
35
 
35
36
  /**
36
37
  * The raw emoji of this answer
@@ -38,7 +39,7 @@ class PollAnswer extends Base {
38
39
  * @type {?APIPartialEmoji}
39
40
  * @private
40
41
  */
41
- Object.defineProperty(this, '_emoji', { value: data.poll_media.emoji ?? null });
42
+ Object.defineProperty(this, '_emoji', { value: null });
42
43
 
43
44
  this._patch(data);
44
45
  }
@@ -52,7 +53,17 @@ class PollAnswer extends Base {
52
53
  */
53
54
  this.voteCount = data.count;
54
55
  } else {
55
- this.voteCount ??= 0;
56
+ this.voteCount ??= this.voters.cache.size;
57
+ }
58
+
59
+ /**
60
+ * The text of this answer
61
+ * @type {?string}
62
+ */
63
+ this.text ??= data.poll_media?.text ?? null;
64
+
65
+ if (data.poll_media?.emoji) {
66
+ Object.defineProperty(this, '_emoji', { value: data.poll_media.emoji });
56
67
  }
57
68
  }
58
69
 
@@ -65,6 +76,15 @@ class PollAnswer extends Base {
65
76
  return resolveGuildEmoji(this.client, this._emoji.id) ?? new Emoji(this.client, this._emoji);
66
77
  }
67
78
 
79
+ /**
80
+ * Whether this poll answer is a partial.
81
+ * @type {boolean}
82
+ * @readonly
83
+ */
84
+ get partial() {
85
+ return this.poll.partial || (this.text === null && this.emoji === null);
86
+ }
87
+
68
88
  /**
69
89
  * Options used for fetching voters of a poll answer.
70
90
  * @typedef {Object} BaseFetchPollAnswerVotersOptions
@@ -76,14 +96,10 @@ class PollAnswer extends Base {
76
96
  * Fetches the users that voted for this answer.
77
97
  * @param {BaseFetchPollAnswerVotersOptions} [options={}] The options for fetching voters
78
98
  * @returns {Promise<Collection<Snowflake, User>>}
99
+ * @deprecated Use {@link PollAnswerVoterManager#fetch} instead
79
100
  */
80
101
  fetchVoters({ after, limit } = {}) {
81
- return this.poll.message.channel.messages.fetchPollAnswerVoters({
82
- messageId: this.poll.message.id,
83
- answerId: this.id,
84
- after,
85
- limit,
86
- });
102
+ return this.voters.fetch({ after, limit });
87
103
  }
88
104
  }
89
105
 
@@ -26,6 +26,8 @@ const { createEnum } = require('./Enums.js');
26
26
  * @property {number} Reaction The partial to receive uncached reactions.
27
27
  * @property {number} GuildScheduledEvent The partial to receive uncached guild scheduled events.
28
28
  * @property {number} ThreadMember The partial to receive uncached thread members.
29
+ * @property {number} Poll The partial to receive uncached polls.
30
+ * @property {number} PollAnswer The partial to receive uncached poll answers.
29
31
  */
30
32
 
31
33
  // JSDoc for IntelliSense purposes
@@ -41,4 +43,6 @@ exports.Partials = createEnum([
41
43
  'Reaction',
42
44
  'GuildScheduledEvent',
43
45
  'ThreadMember',
46
+ 'Poll',
47
+ 'PollAnswer',
44
48
  ]);
@@ -2675,19 +2675,30 @@ export class Presence extends Base {
2675
2675
  }
2676
2676
 
2677
2677
  export interface PollQuestionMedia {
2678
- text: string;
2678
+ text: string | null;
2679
+ }
2680
+
2681
+ export class PollAnswerVoterManager extends CachedManager<Snowflake, User, UserResolvable> {
2682
+ private constructor(answer: PollAnswer);
2683
+ public answer: PollAnswer;
2684
+ public fetch(options?: BaseFetchPollAnswerVotersOptions): Promise<Collection<Snowflake, User>>;
2679
2685
  }
2680
2686
 
2681
2687
  export class Poll extends Base {
2682
- private constructor(client: Client<true>, data: APIPoll, message: Message);
2688
+ private constructor(client: Client<true>, data: APIPoll, message: Message, channel: TextBasedChannel);
2689
+ public readonly channel: TextBasedChannel;
2690
+ public channelId: Snowflake;
2683
2691
  public readonly message: Message;
2692
+ public messageId: Snowflake;
2684
2693
  public question: PollQuestionMedia;
2685
- public answers: Collection<number, PollAnswer>;
2686
- public expiresTimestamp: number;
2687
- public get expiresAt(): Date;
2694
+ public answers: Collection<number, PollAnswer | PartialPollAnswer>;
2695
+ public expiresTimestamp: number | null;
2696
+ public get expiresAt(): Date | null;
2688
2697
  public allowMultiselect: boolean;
2689
2698
  public layoutType: PollLayoutType;
2690
2699
  public resultsFinalized: boolean;
2700
+ public get partial(): false;
2701
+ public fetch(): Promise<this>;
2691
2702
  public end(): Promise<Message>;
2692
2703
  }
2693
2704
 
@@ -2699,11 +2710,14 @@ export interface BaseFetchPollAnswerVotersOptions {
2699
2710
  export class PollAnswer extends Base {
2700
2711
  private constructor(client: Client<true>, data: APIPollAnswer & { count?: number }, poll: Poll);
2701
2712
  private _emoji: APIPartialEmoji | null;
2702
- public readonly poll: Poll;
2713
+ public readonly poll: Poll | PartialPoll;
2703
2714
  public id: number;
2704
2715
  public text: string | null;
2705
2716
  public voteCount: number;
2717
+ public voters: PollAnswerVoterManager;
2706
2718
  public get emoji(): GuildEmoji | Emoji | null;
2719
+ public get partial(): false;
2720
+ /** @deprecated Use {@link PollAnswerVoterManager.fetch} instead */
2707
2721
  public fetchVoters(options?: BaseFetchPollAnswerVotersOptions): Promise<Collection<Snowflake, User>>;
2708
2722
  }
2709
2723
 
@@ -4572,7 +4586,9 @@ export type AllowedPartial =
4572
4586
  | Message
4573
4587
  | MessageReaction
4574
4588
  | GuildScheduledEvent
4575
- | ThreadMember;
4589
+ | ThreadMember
4590
+ | Poll
4591
+ | PollAnswer;
4576
4592
 
4577
4593
  export type AllowedThreadTypeForAnnouncementChannel = ChannelType.AnnouncementThread;
4578
4594
 
@@ -5123,8 +5139,8 @@ export interface ClientEventTypes {
5123
5139
  inviteDelete: [invite: Invite];
5124
5140
  messageCreate: [message: OmitPartialGroupDMChannel<Message>];
5125
5141
  messageDelete: [message: OmitPartialGroupDMChannel<Message | PartialMessage>];
5126
- messagePollVoteAdd: [pollAnswer: PollAnswer, userId: Snowflake];
5127
- messagePollVoteRemove: [pollAnswer: PollAnswer, userId: Snowflake];
5142
+ messagePollVoteAdd: [pollAnswer: PollAnswer | PartialPollAnswer, userId: Snowflake];
5143
+ messagePollVoteRemove: [pollAnswer: PollAnswer | PartialPollAnswer, userId: Snowflake];
5128
5144
  messageReactionRemoveAll: [
5129
5145
  message: OmitPartialGroupDMChannel<Message | PartialMessage>,
5130
5146
  reactions: ReadonlyCollection<string | Snowflake, MessageReaction>,
@@ -6536,6 +6552,23 @@ export interface PartialMessage
6536
6552
 
6537
6553
  export interface PartialMessageReaction extends Partialize<MessageReaction, 'count'> {}
6538
6554
 
6555
+ export interface PartialPoll
6556
+ extends Partialize<
6557
+ Poll,
6558
+ 'allowMultiselect' | 'layoutType' | 'expiresTimestamp',
6559
+ null,
6560
+ 'question' | 'message' | 'answers'
6561
+ > {
6562
+ question: { text: null };
6563
+ message: PartialMessage;
6564
+ // eslint-disable-next-line no-restricted-syntax
6565
+ answers: Collection<number, PartialPollAnswer>;
6566
+ }
6567
+
6568
+ export interface PartialPollAnswer extends Partialize<PollAnswer, 'emoji' | 'text', null, 'poll'> {
6569
+ readonly poll: PartialPoll;
6570
+ }
6571
+
6539
6572
  export interface PartialGuildScheduledEvent
6540
6573
  extends Partialize<GuildScheduledEvent, 'userCount', 'status' | 'privacyLevel' | 'name' | 'entityType'> {}
6541
6574
 
@@ -6560,6 +6593,8 @@ export enum Partials {
6560
6593
  Reaction,
6561
6594
  GuildScheduledEvent,
6562
6595
  ThreadMember,
6596
+ Poll,
6597
+ PollAnswer,
6563
6598
  }
6564
6599
 
6565
6600
  export interface PartialUser extends Partialize<User, 'username' | 'tag' | 'discriminator'> {}
@@ -2675,19 +2675,30 @@ export class Presence extends Base {
2675
2675
  }
2676
2676
 
2677
2677
  export interface PollQuestionMedia {
2678
- text: string;
2678
+ text: string | null;
2679
+ }
2680
+
2681
+ export class PollAnswerVoterManager extends CachedManager<Snowflake, User, UserResolvable> {
2682
+ private constructor(answer: PollAnswer);
2683
+ public answer: PollAnswer;
2684
+ public fetch(options?: BaseFetchPollAnswerVotersOptions): Promise<Collection<Snowflake, User>>;
2679
2685
  }
2680
2686
 
2681
2687
  export class Poll extends Base {
2682
- private constructor(client: Client<true>, data: APIPoll, message: Message);
2688
+ private constructor(client: Client<true>, data: APIPoll, message: Message, channel: TextBasedChannel);
2689
+ public readonly channel: TextBasedChannel;
2690
+ public channelId: Snowflake;
2683
2691
  public readonly message: Message;
2692
+ public messageId: Snowflake;
2684
2693
  public question: PollQuestionMedia;
2685
- public answers: Collection<number, PollAnswer>;
2686
- public expiresTimestamp: number;
2687
- public get expiresAt(): Date;
2694
+ public answers: Collection<number, PollAnswer | PartialPollAnswer>;
2695
+ public expiresTimestamp: number | null;
2696
+ public get expiresAt(): Date | null;
2688
2697
  public allowMultiselect: boolean;
2689
2698
  public layoutType: PollLayoutType;
2690
2699
  public resultsFinalized: boolean;
2700
+ public get partial(): false;
2701
+ public fetch(): Promise<this>;
2691
2702
  public end(): Promise<Message>;
2692
2703
  }
2693
2704
 
@@ -2699,11 +2710,14 @@ export interface BaseFetchPollAnswerVotersOptions {
2699
2710
  export class PollAnswer extends Base {
2700
2711
  private constructor(client: Client<true>, data: APIPollAnswer & { count?: number }, poll: Poll);
2701
2712
  private _emoji: APIPartialEmoji | null;
2702
- public readonly poll: Poll;
2713
+ public readonly poll: Poll | PartialPoll;
2703
2714
  public id: number;
2704
2715
  public text: string | null;
2705
2716
  public voteCount: number;
2717
+ public voters: PollAnswerVoterManager;
2706
2718
  public get emoji(): GuildEmoji | Emoji | null;
2719
+ public get partial(): false;
2720
+ /** @deprecated Use {@link PollAnswerVoterManager.fetch} instead */
2707
2721
  public fetchVoters(options?: BaseFetchPollAnswerVotersOptions): Promise<Collection<Snowflake, User>>;
2708
2722
  }
2709
2723
 
@@ -4572,7 +4586,9 @@ export type AllowedPartial =
4572
4586
  | Message
4573
4587
  | MessageReaction
4574
4588
  | GuildScheduledEvent
4575
- | ThreadMember;
4589
+ | ThreadMember
4590
+ | Poll
4591
+ | PollAnswer;
4576
4592
 
4577
4593
  export type AllowedThreadTypeForAnnouncementChannel = ChannelType.AnnouncementThread;
4578
4594
 
@@ -5123,8 +5139,8 @@ export interface ClientEventTypes {
5123
5139
  inviteDelete: [invite: Invite];
5124
5140
  messageCreate: [message: OmitPartialGroupDMChannel<Message>];
5125
5141
  messageDelete: [message: OmitPartialGroupDMChannel<Message | PartialMessage>];
5126
- messagePollVoteAdd: [pollAnswer: PollAnswer, userId: Snowflake];
5127
- messagePollVoteRemove: [pollAnswer: PollAnswer, userId: Snowflake];
5142
+ messagePollVoteAdd: [pollAnswer: PollAnswer | PartialPollAnswer, userId: Snowflake];
5143
+ messagePollVoteRemove: [pollAnswer: PollAnswer | PartialPollAnswer, userId: Snowflake];
5128
5144
  messageReactionRemoveAll: [
5129
5145
  message: OmitPartialGroupDMChannel<Message | PartialMessage>,
5130
5146
  reactions: ReadonlyCollection<string | Snowflake, MessageReaction>,
@@ -6536,6 +6552,23 @@ export interface PartialMessage
6536
6552
 
6537
6553
  export interface PartialMessageReaction extends Partialize<MessageReaction, 'count'> {}
6538
6554
 
6555
+ export interface PartialPoll
6556
+ extends Partialize<
6557
+ Poll,
6558
+ 'allowMultiselect' | 'layoutType' | 'expiresTimestamp',
6559
+ null,
6560
+ 'question' | 'message' | 'answers'
6561
+ > {
6562
+ question: { text: null };
6563
+ message: PartialMessage;
6564
+ // eslint-disable-next-line no-restricted-syntax
6565
+ answers: Collection<number, PartialPollAnswer>;
6566
+ }
6567
+
6568
+ export interface PartialPollAnswer extends Partialize<PollAnswer, 'emoji' | 'text', null, 'poll'> {
6569
+ readonly poll: PartialPoll;
6570
+ }
6571
+
6539
6572
  export interface PartialGuildScheduledEvent
6540
6573
  extends Partialize<GuildScheduledEvent, 'userCount', 'status' | 'privacyLevel' | 'name' | 'entityType'> {}
6541
6574
 
@@ -6560,6 +6593,8 @@ export enum Partials {
6560
6593
  Reaction,
6561
6594
  GuildScheduledEvent,
6562
6595
  ThreadMember,
6596
+ Poll,
6597
+ PollAnswer,
6563
6598
  }
6564
6599
 
6565
6600
  export interface PartialUser extends Partialize<User, 'username' | 'tag' | 'discriminator'> {}
@@ -214,6 +214,10 @@ import {
214
214
  InteractionCallbackResponse,
215
215
  GuildScheduledEventRecurrenceRuleOptions,
216
216
  ThreadOnlyChannel,
217
+ PartialPoll,
218
+ PartialPollAnswer,
219
+ PollAnswer,
220
+ PollAnswerVoterManager,
217
221
  } from './index.js';
218
222
  import { expectAssignable, expectNotAssignable, expectNotType, expectType } from 'tsd';
219
223
  import type { ContextMenuCommandBuilder, SlashCommandBuilder } from '@discordjs/builders';
@@ -656,6 +660,48 @@ client.on('messageDeleteBulk', (messages, { client }) => {
656
660
  expectType<Client<true>>(client);
657
661
  });
658
662
 
663
+ client.on('messagePollVoteAdd', async (answer, userId) => {
664
+ expectType<Client<true>>(answer.client);
665
+ expectType<Snowflake>(userId);
666
+
667
+ if (answer.partial) {
668
+ expectType<null>(answer.emoji);
669
+ expectType<null>(answer.text);
670
+ expectNotType<null>(answer.id);
671
+ expectNotType<null>(answer.poll);
672
+
673
+ await answer.poll.fetch();
674
+ answer = answer.poll.answers?.get(answer.id) ?? answer;
675
+
676
+ expectType<User>(answer.voters.cache.get(userId)!);
677
+ }
678
+
679
+ expectType<string | null>(answer.text);
680
+ expectType<GuildEmoji | Emoji | null>(answer.emoji);
681
+ expectType<number>(answer.id);
682
+ expectType<number>(answer.voteCount!);
683
+ });
684
+
685
+ client.on('messagePollVoteRemove', async (answer, userId) => {
686
+ expectType<Client<true>>(answer.client);
687
+ expectType<Snowflake>(userId);
688
+
689
+ if (answer.partial) {
690
+ expectType<null>(answer.emoji);
691
+ expectType<null>(answer.text);
692
+ expectNotType<null>(answer.id);
693
+ expectNotType<null>(answer.poll);
694
+
695
+ await answer.poll.fetch();
696
+ answer = answer.poll.answers?.get(answer.id) ?? answer;
697
+ }
698
+
699
+ expectType<string | null>(answer.text);
700
+ expectType<GuildEmoji | Emoji | null>(answer.emoji);
701
+ expectType<number>(answer.id);
702
+ expectType<number>(answer.voteCount!);
703
+ });
704
+
659
705
  client.on('messageReactionAdd', async (reaction, { client }) => {
660
706
  expectType<Client<true>>(reaction.client);
661
707
  expectType<Client<true>>(client);
@@ -1724,6 +1770,12 @@ declare const messageManager: MessageManager;
1724
1770
  messageManager.fetch({ message: '1234567890', after: '1234567890', cache: true, force: false });
1725
1771
  }
1726
1772
 
1773
+ declare const pollAnswerVoterManager: PollAnswerVoterManager;
1774
+ {
1775
+ expectType<Promise<Collection<Snowflake, User>>>(pollAnswerVoterManager.fetch());
1776
+ expectType<PollAnswer>(pollAnswerVoterManager.answer);
1777
+ }
1778
+
1727
1779
  declare const roleManager: RoleManager;
1728
1780
  expectType<Promise<Collection<Snowflake, Role>>>(roleManager.fetch());
1729
1781
  expectType<Promise<Collection<Snowflake, Role>>>(roleManager.fetch(undefined, {}));
@@ -2663,16 +2715,42 @@ await textChannel.send({
2663
2715
  },
2664
2716
  });
2665
2717
 
2718
+ declare const partialPoll: PartialPoll;
2719
+ {
2720
+ if (partialPoll.partial) {
2721
+ expectType<null>(partialPoll.question.text);
2722
+ expectType<PartialMessage>(partialPoll.message);
2723
+ expectType<null>(partialPoll.allowMultiselect);
2724
+ expectType<null>(partialPoll.layoutType);
2725
+ expectType<null>(partialPoll.expiresTimestamp);
2726
+ expectType<Collection<number, PartialPollAnswer>>(partialPoll.answers);
2727
+ }
2728
+ }
2729
+
2730
+ declare const partialPollAnswer: PartialPollAnswer;
2731
+ {
2732
+ if (partialPollAnswer.partial) {
2733
+ expectType<PartialPoll>(partialPollAnswer.poll);
2734
+ expectType<null>(partialPollAnswer.emoji);
2735
+ expectType<null>(partialPollAnswer.text);
2736
+ }
2737
+ }
2666
2738
  declare const poll: Poll;
2667
2739
  declare const message: Message;
2668
2740
  declare const pollData: PollData;
2669
2741
  {
2670
2742
  expectType<Message>(await poll.end());
2743
+ expectType<false>(poll.partial);
2744
+ expectNotType<Collection<number, PartialPollAnswer>>(poll.answers);
2671
2745
 
2672
2746
  const answer = poll.answers.first()!;
2673
- expectType<number>(answer.voteCount);
2674
2747
 
2675
- expectType<Collection<Snowflake, User>>(await answer.fetchVoters({ after: snowflake, limit: 10 }));
2748
+ if (!answer.partial) {
2749
+ expectType<number>(answer.voteCount);
2750
+ expectType<number>(answer.id);
2751
+ expectType<PollAnswerVoterManager>(answer.voters);
2752
+ expectType<Collection<Snowflake, User>>(await answer.voters.fetch({ after: snowflake, limit: 10 }));
2753
+ }
2676
2754
 
2677
2755
  await messageManager.endPoll(snowflake);
2678
2756
  await messageManager.fetchPollAnswerVoters({