discord.js 15.0.0-djs-file-upload.1761302390-5ae769c9e → 15.0.0-pr-11006.1765450224-e636950b2

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.
Files changed (44) hide show
  1. package/package.json +18 -20
  2. package/src/client/Client.js +110 -24
  3. package/src/client/websocket/handlers/RATE_LIMITED.js +24 -0
  4. package/src/client/websocket/handlers/READY.js +4 -0
  5. package/src/client/websocket/handlers/index.js +1 -0
  6. package/src/errors/DJSError.js +7 -3
  7. package/src/errors/ErrorCodes.js +2 -4
  8. package/src/errors/Messages.js +2 -3
  9. package/src/index.js +0 -3
  10. package/src/managers/CachedManager.js +5 -5
  11. package/src/managers/ChannelManager.js +10 -7
  12. package/src/managers/GuildBanManager.js +3 -3
  13. package/src/managers/GuildEmojiManager.js +2 -2
  14. package/src/managers/GuildEmojiRoleManager.js +9 -1
  15. package/src/managers/GuildMemberManager.js +44 -23
  16. package/src/managers/GuildMemberRoleManager.js +11 -2
  17. package/src/managers/MessageManager.js +25 -18
  18. package/src/structures/ApplicationCommand.js +4 -5
  19. package/src/structures/ClientApplication.js +2 -0
  20. package/src/structures/Embed.js +1 -1
  21. package/src/structures/Guild.js +11 -1
  22. package/src/structures/GuildInvite.js +1 -1
  23. package/src/structures/GuildMember.js +12 -9
  24. package/src/structures/Message.js +18 -15
  25. package/src/structures/MessagePayload.js +21 -17
  26. package/src/structures/ModalComponentResolver.js +1 -1
  27. package/src/structures/ModalSubmitInteraction.js +6 -6
  28. package/src/structures/PermissionOverwrites.js +1 -1
  29. package/src/structures/Role.js +1 -1
  30. package/src/structures/ThreadChannel.js +1 -1
  31. package/src/structures/Webhook.js +6 -11
  32. package/src/structures/interfaces/TextBasedChannel.js +7 -9
  33. package/src/util/APITypes.js +10 -0
  34. package/src/util/Components.js +52 -42
  35. package/src/util/Constants.js +2 -0
  36. package/src/util/DataResolver.js +5 -2
  37. package/src/util/GuildMemberFlagsBitField.js +1 -1
  38. package/src/util/Options.js +9 -5
  39. package/src/util/Util.js +18 -13
  40. package/typings/index.d.mts +148 -173
  41. package/typings/index.d.ts +148 -173
  42. package/src/client/BaseClient.js +0 -131
  43. package/src/client/WebhookClient.js +0 -119
  44. package/src/structures/AttachmentBuilder.js +0 -185
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { Collection } = require('@discordjs/collection');
4
4
  const { makeURLSearchParams } = require('@discordjs/rest');
5
+ const { isFileBodyEncodable, isJSONEncodable } = require('@discordjs/util');
5
6
  const { Routes } = require('discord-api-types/v10');
6
7
  const { DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
7
8
  const { Message } = require('../structures/Message.js');
@@ -115,12 +116,12 @@ class MessageManager extends CachedManager {
115
116
  return this._add(data, cache);
116
117
  }
117
118
 
118
- async _fetchMany(options = {}) {
119
+ async _fetchMany({ cache, ...apiOptions } = {}) {
119
120
  const data = await this.client.rest.get(Routes.channelMessages(this.channel.id), {
120
- query: makeURLSearchParams(options),
121
+ query: makeURLSearchParams(apiOptions),
121
122
  });
122
123
 
123
- return data.reduce((_data, message) => _data.set(message.id, this._add(message, options.cache)), new Collection());
124
+ return data.reduce((_data, message) => _data.set(message.id, this._add(message, cache)), new Collection());
124
125
  }
125
126
 
126
127
  /**
@@ -150,8 +151,8 @@ class MessageManager extends CachedManager {
150
151
  */
151
152
 
152
153
  /**
153
- * Fetches the pinned messages of this channel and returns a collection of them.
154
- * <info>The returned Collection does not contain any reaction data of the messages.
154
+ * Fetches the pinned messages of this channel, returning a paginated result.
155
+ * <info>The returned messages do not contain any reaction data.
155
156
  * Those need to be fetched separately.</info>
156
157
  *
157
158
  * @param {FetchPinnedMessagesOptions} [options={}] Options for fetching pinned messages
@@ -162,11 +163,11 @@ class MessageManager extends CachedManager {
162
163
  * .then(messages => console.log(`Received ${messages.items.length} messages`))
163
164
  * .catch(console.error);
164
165
  */
165
- async fetchPins(options = {}) {
166
+ async fetchPins({ cache, ...apiOptions } = {}) {
166
167
  const data = await this.client.rest.get(Routes.channelMessagesPins(this.channel.id), {
167
168
  query: makeURLSearchParams({
168
- ...options,
169
- before: options.before && new Date(options.before).toISOString(),
169
+ ...apiOptions,
170
+ before: apiOptions.before && new Date(apiOptions.before).toISOString(),
170
171
  }),
171
172
  });
172
173
 
@@ -176,7 +177,7 @@ class MessageManager extends CachedManager {
176
177
  get pinnedAt() {
177
178
  return new Date(this.pinnedTimestamp);
178
179
  },
179
- message: this._add(item.message, options.cache),
180
+ message: this._add(item.message, cache),
180
181
  })),
181
182
  hasMore: data.has_more,
182
183
  };
@@ -223,21 +224,27 @@ class MessageManager extends CachedManager {
223
224
  * Edits a message, even if it's not cached.
224
225
  *
225
226
  * @param {MessageResolvable} message The message to edit
226
- * @param {string|MessageEditOptions|MessagePayload} options The options to edit the message
227
+ * @param {string|MessageEditOptions|MessagePayload|FileBodyEncodable<RESTPatchAPIChannelMessageJSONBody>|JSONEncodable<RESTPatchAPIChannelMessageJSONBody>} options The options to edit the message
227
228
  * @returns {Promise<Message>}
228
229
  */
229
230
  async edit(message, options) {
230
231
  const messageId = this.resolveId(message);
231
232
  if (!messageId) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable');
232
233
 
233
- const { body, files } = await (
234
- options instanceof MessagePayload
235
- ? options
236
- : MessagePayload.create(message instanceof Message ? message : this, options)
237
- )
238
- .resolveBody()
239
- .resolveFiles();
240
- const data = await this.client.rest.patch(Routes.channelMessage(this.channel.id, messageId), { body, files });
234
+ let payload;
235
+ if (options instanceof MessagePayload) {
236
+ payload = await options.resolveBody().resolveFiles();
237
+ } else if (isFileBodyEncodable(options)) {
238
+ payload = options.toFileBody();
239
+ } else if (isJSONEncodable(options)) {
240
+ payload = { body: options.toJSON() };
241
+ } else {
242
+ payload = await MessagePayload.create(message instanceof Message ? message : this, options)
243
+ .resolveBody()
244
+ .resolveFiles();
245
+ }
246
+
247
+ const data = await this.client.rest.patch(Routes.channelMessage(this.channel.id, messageId), payload);
241
248
 
242
249
  const existing = this.cache.get(messageId);
243
250
  if (existing) {
@@ -136,11 +136,11 @@ class ApplicationCommand extends Base {
136
136
  /**
137
137
  * The options of this command
138
138
  *
139
- * @type {ApplicationCommandOption[]}
139
+ * @type {?ApplicationCommandOption[]}
140
140
  */
141
141
  this.options = data.options.map(option => this.constructor.transformOption(option, true));
142
142
  } else {
143
- this.options ??= [];
143
+ this.options ??= null;
144
144
  }
145
145
 
146
146
  if ('default_member_permissions' in data) {
@@ -436,9 +436,7 @@ class ApplicationCommand extends Base {
436
436
  ('version' in command && command.version !== this.version) ||
437
437
  (command.type && command.type !== this.type) ||
438
438
  ('nsfw' in command && command.nsfw !== this.nsfw) ||
439
- // Future proof for options being nullable
440
- // TODO: remove ?? 0 on each when nullable
441
- (command.options?.length ?? 0) !== (this.options?.length ?? 0) ||
439
+ command.options?.length !== this.options?.length ||
442
440
  defaultMemberPermissions !== (this.defaultMemberPermissions?.bitfield ?? null) ||
443
441
  !isEqual(command.nameLocalizations ?? command.name_localizations ?? {}, this.nameLocalizations ?? {}) ||
444
442
  !isEqual(
@@ -452,6 +450,7 @@ class ApplicationCommand extends Base {
452
450
  return false;
453
451
  }
454
452
 
453
+ // Don't need to check both because we already checked the lengths above
455
454
  if (command.options) {
456
455
  return this.constructor.optionsEqual(this.options, command.options, enforceOptionOrder);
457
456
  }
@@ -98,6 +98,7 @@ class ClientApplication extends Application {
98
98
  * Scopes and permissions regarding the installation context
99
99
  */
100
100
 
101
+ /* eslint-disable jsdoc/valid-types */
101
102
  /**
102
103
  * The application's supported installation context data.
103
104
  *
@@ -107,6 +108,7 @@ class ClientApplication extends Application {
107
108
  * @property {IntegrationTypesConfigurationContext} [1] Scopes and permissions
108
109
  * regarding the user-installation context
109
110
  */
111
+ /* eslint-enable jsdoc/valid-types */
110
112
 
111
113
  if ('integration_types_config' in data) {
112
114
  /**
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { embedLength } = require('@discordjs/builders');
3
+ const { embedLength } = require('@discordjs/util');
4
4
  const isEqual = require('fast-deep-equal');
5
5
 
6
6
  /**
@@ -641,7 +641,7 @@ class Guild extends AnonymousGuild {
641
641
  }
642
642
 
643
643
  /**
644
- * The maximum bitrate available for this guild
644
+ * The maximum bitrate available for a voice channel in this guild
645
645
  *
646
646
  * @type {number}
647
647
  * @readonly
@@ -663,6 +663,16 @@ class Guild extends AnonymousGuild {
663
663
  }
664
664
  }
665
665
 
666
+ /**
667
+ * The maximum bitrate available for a stage channel in this guild
668
+ *
669
+ * @type {number}
670
+ * @readonly
671
+ */
672
+ get maximumStageBitrate() {
673
+ return 64_000;
674
+ }
675
+
666
676
  /**
667
677
  * Fetches a collection of integrations to this guild.
668
678
  * Resolves with a collection mapping integrations by their ids.
@@ -192,7 +192,7 @@ class GuildInvite extends BaseInvite {
192
192
  if (!guild.members.me) throw new DiscordjsError(ErrorCodes.GuildUncachedMe);
193
193
  return Boolean(
194
194
  this.channel?.permissionsFor(this.client.user).has(PermissionFlagsBits.ManageChannels, false) ||
195
- guild.members.me.permissions.has(PermissionFlagsBits.ManageGuild),
195
+ guild.members.me.permissions.has(PermissionFlagsBits.ManageGuild),
196
196
  );
197
197
  }
198
198
 
@@ -24,13 +24,6 @@ class GuildMember extends Base {
24
24
  */
25
25
  this.guild = guild;
26
26
 
27
- /**
28
- * The timestamp the member joined the guild at
29
- *
30
- * @type {?number}
31
- */
32
- this.joinedTimestamp = null;
33
-
34
27
  /**
35
28
  * The last timestamp this member started boosting the guild
36
29
  *
@@ -68,7 +61,7 @@ class GuildMember extends Base {
68
61
  */
69
62
  Object.defineProperty(this, '_roles', { value: [], writable: true });
70
63
 
71
- if (data) this._patch(data);
64
+ this._patch(data);
72
65
  }
73
66
 
74
67
  _patch(data) {
@@ -104,7 +97,17 @@ class GuildMember extends Base {
104
97
  this.banner ??= null;
105
98
  }
106
99
 
107
- if ('joined_at' in data) this.joinedTimestamp = Date.parse(data.joined_at);
100
+ if ('joined_at' in data) {
101
+ /**
102
+ * The timestamp the member joined the guild at
103
+ *
104
+ * @type {?number}
105
+ */
106
+ this.joinedTimestamp = data.joined_at && Date.parse(data.joined_at);
107
+ } else {
108
+ this.joinedTimestamp ??= null;
109
+ }
110
+
108
111
  if ('premium_since' in data) {
109
112
  this.premiumSinceTimestamp = data.premium_since ? Date.parse(data.premium_since) : null;
110
113
  }
@@ -725,8 +725,8 @@ class Message extends Base {
725
725
  get editable() {
726
726
  const precheck = Boolean(
727
727
  this.author.id === this.client.user.id &&
728
- (!this.guild || this.channel?.viewable) &&
729
- this.reference?.type !== MessageReferenceType.Forward,
728
+ (!this.guild || this.channel?.viewable) &&
729
+ this.reference?.type !== MessageReferenceType.Forward,
730
730
  );
731
731
 
732
732
  // Regardless of permissions thread messages cannot be edited if
@@ -799,12 +799,15 @@ class Message extends Base {
799
799
  */
800
800
  get pinnable() {
801
801
  const { channel } = this;
802
- return Boolean(
803
- !this.system &&
804
- (!this.guild ||
805
- (channel?.viewable &&
806
- channel?.permissionsFor(this.client.user)?.has(PermissionFlagsBits.ManageMessages, false))),
807
- );
802
+
803
+ if (this.system) return false;
804
+ if (!this.guild) return true;
805
+ if (!channel || channel.isVoiceBased() || !channel.viewable) return false;
806
+
807
+ const permissions = channel.permissionsFor(this.client.user);
808
+ if (!permissions) return false;
809
+
810
+ return permissions.has(PermissionFlagsBits.ReadMessageHistory | PermissionFlagsBits.PinMessages);
808
811
  }
809
812
 
810
813
  /**
@@ -834,19 +837,19 @@ class Message extends Base {
834
837
  const { channel } = this;
835
838
  return Boolean(
836
839
  channel?.type === ChannelType.GuildAnnouncement &&
837
- !this.flags.has(MessageFlags.Crossposted) &&
838
- this.reference?.type !== MessageReferenceType.Forward &&
839
- this.type === MessageType.Default &&
840
- !this.poll &&
841
- channel.viewable &&
842
- channel.permissionsFor(this.client.user)?.has(bitfield, false),
840
+ !this.flags.has(MessageFlags.Crossposted) &&
841
+ this.reference?.type !== MessageReferenceType.Forward &&
842
+ this.type === MessageType.Default &&
843
+ !this.poll &&
844
+ channel.viewable &&
845
+ channel.permissionsFor(this.client.user)?.has(bitfield, false),
843
846
  );
844
847
  }
845
848
 
846
849
  /**
847
850
  * Edits the content of the message.
848
851
  *
849
- * @param {string|MessagePayload|MessageEditOptions} options The options to provide
852
+ * @param {string|MessageEditOptions|MessagePayload|FileBodyEncodable<RESTPatchAPIChannelMessageJSONBody>|JSONEncodable<RESTPatchAPIChannelMessageJSONBody>} options The options to provide
850
853
  * @returns {Promise<Message>}
851
854
  * @example
852
855
  * // Update the content of a message
@@ -1,13 +1,20 @@
1
1
  'use strict';
2
2
 
3
3
  const { Buffer } = require('node:buffer');
4
- const { isJSONEncodable } = require('@discordjs/util');
4
+ const { isJSONEncodable, lazy } = require('@discordjs/util');
5
5
  const { DiscordSnowflake } = require('@sapphire/snowflake');
6
6
  const { DiscordjsError, DiscordjsRangeError, ErrorCodes } = require('../errors/index.js');
7
7
  const { resolveFile } = require('../util/DataResolver.js');
8
8
  const { MessageFlagsBitField } = require('../util/MessageFlagsBitField.js');
9
9
  const { findName, verifyString, resolvePartialEmoji } = require('../util/Util.js');
10
10
 
11
+ // Fixes circular dependencies.
12
+ const getWebhook = lazy(() => require('./Webhook.js').Webhook);
13
+ const getUser = lazy(() => require('./User.js').User);
14
+ const getGuildMember = lazy(() => require('./GuildMember.js').GuildMember);
15
+ const getMessage = lazy(() => require('./Message.js').Message);
16
+ const getMessageManager = lazy(() => require('../managers/MessageManager.js').MessageManager);
17
+
11
18
  /**
12
19
  * Represents a message to be sent to the API.
13
20
  */
@@ -47,15 +54,13 @@ class MessagePayload {
47
54
  }
48
55
 
49
56
  /**
50
- * Whether or not the target is a {@link Webhook} or a {@link WebhookClient}
57
+ * Whether or not the target is a {@link Webhook}
51
58
  *
52
59
  * @type {boolean}
53
60
  * @readonly
54
61
  */
55
62
  get isWebhook() {
56
- const { Webhook } = require('./Webhook.js');
57
- const { WebhookClient } = require('../client/WebhookClient.js');
58
- return this.target instanceof Webhook || this.target instanceof WebhookClient;
63
+ return this.target instanceof getWebhook();
59
64
  }
60
65
 
61
66
  /**
@@ -65,9 +70,7 @@ class MessagePayload {
65
70
  * @readonly
66
71
  */
67
72
  get isUser() {
68
- const { User } = require('./User.js');
69
- const { GuildMember } = require('./GuildMember.js');
70
- return this.target instanceof User || this.target instanceof GuildMember;
73
+ return this.target instanceof getUser() || this.target instanceof getGuildMember();
71
74
  }
72
75
 
73
76
  /**
@@ -77,8 +80,7 @@ class MessagePayload {
77
80
  * @readonly
78
81
  */
79
82
  get isMessage() {
80
- const { Message } = require('./Message.js');
81
- return this.target instanceof Message;
83
+ return this.target instanceof getMessage();
82
84
  }
83
85
 
84
86
  /**
@@ -88,8 +90,7 @@ class MessagePayload {
88
90
  * @readonly
89
91
  */
90
92
  get isMessageManager() {
91
- const { MessageManager } = require('../managers/MessageManager.js');
92
- return this.target instanceof MessageManager;
93
+ return this.target instanceof getMessageManager();
93
94
  }
94
95
 
95
96
  /**
@@ -196,10 +197,13 @@ class MessagePayload {
196
197
  waveform: file.waveform,
197
198
  duration_secs: file.duration,
198
199
  }));
200
+
201
+ // Only passable during edits
199
202
  if (Array.isArray(this.options.attachments)) {
200
- this.options.attachments.push(...(attachments ?? []));
201
- } else {
202
- this.options.attachments = attachments;
203
+ attachments.push(
204
+ // Note how we don't check for file body encodable, since we aren't expecting file data here
205
+ ...this.options.attachments.map(attachment => (isJSONEncodable(attachment) ? attachment.toJSON() : attachment)),
206
+ );
203
207
  }
204
208
 
205
209
  let poll;
@@ -236,7 +240,7 @@ class MessagePayload {
236
240
  : allowedMentions,
237
241
  flags,
238
242
  message_reference,
239
- attachments: this.options.attachments,
243
+ attachments,
240
244
  sticker_ids: this.options.stickers?.map(sticker => sticker.id ?? sticker),
241
245
  thread_name: threadName,
242
246
  applied_tags: appliedTags,
@@ -302,7 +306,7 @@ exports.MessagePayload = MessagePayload;
302
306
  /**
303
307
  * A target for a message.
304
308
  *
305
- * @typedef {TextBasedChannels|ChannelManager|Webhook|WebhookClient|BaseInteraction|InteractionWebhook|
309
+ * @typedef {TextBasedChannels|ChannelManager|Webhook|BaseInteraction|InteractionWebhook|
306
310
  * Message|MessageManager} MessageTarget
307
311
  */
308
312
 
@@ -228,7 +228,7 @@ class ModalComponentResolver {
228
228
  *
229
229
  * @param {string} customId The custom id of the component
230
230
  * @param {boolean} [required=false] Whether to throw an error if the component value is not found or empty
231
- * @returns {?Collection<string, Attachment>} The uploaded files, or null if none were uploaded and not required
231
+ * @returns {?Collection<Snowflake, Attachment>} The uploaded files, or null if none were uploaded and not required
232
232
  */
233
233
  getUploadedFiles(customId, required = false) {
234
234
  return this._getTypedComponent(customId, [ComponentType.FileUpload], ['attachments'], required).attachments ?? null;
@@ -21,17 +21,17 @@ const getAttachment = lazy(() => require('./Attachment.js').Attachment);
21
21
  * @typedef {BaseModalData} SelectMenuModalData
22
22
  * @property {string} customId The custom id of the component
23
23
  * @property {string[]} values The values of the component
24
- * @property {Collection<string, GuildMember|APIGuildMember>} [members] The resolved members
25
- * @property {Collection<string, User|APIUser>} [users] The resolved users
26
- * @property {Collection<string, Role|APIRole>} [roles] The resolved roles
27
- * @property {Collection<string, BaseChannel|APIChannel>} [channels] The resolved channels
24
+ * @property {Collection<Snowflake, GuildMember|APIGuildMember>} [members] The resolved members
25
+ * @property {Collection<Snowflake, User|APIUser>} [users] The resolved users
26
+ * @property {Collection<Snowflake, Role|APIRole>} [roles] The resolved roles
27
+ * @property {Collection<Snowflake, BaseChannel|APIChannel>} [channels] The resolved channels
28
28
  */
29
29
 
30
30
  /**
31
31
  * @typedef {BaseModalData} FileUploadModalData
32
32
  * @property {string} customId The custom id of the file upload
33
- * @property {string[]} values The values of the file upload
34
- * @property {Collection<string, Attachment>} [attachments] The resolved attachments
33
+ * @property {Snowflake[]} values The values of the file upload
34
+ * @property {Collection<Snowflake, Attachment>} [attachments] The resolved attachments
35
35
  */
36
36
 
37
37
  /**
@@ -24,7 +24,7 @@ class PermissionOverwrites extends Base {
24
24
  */
25
25
  Object.defineProperty(this, 'channel', { value: channel });
26
26
 
27
- if (data) this._patch(data);
27
+ this._patch(data);
28
28
  }
29
29
 
30
30
  _patch(data) {
@@ -38,7 +38,7 @@ class Role extends Base {
38
38
  */
39
39
  this.unicodeEmoji = null;
40
40
 
41
- if (data) this._patch(data);
41
+ this._patch(data);
42
42
  }
43
43
 
44
44
  _patch(data) {
@@ -55,7 +55,7 @@ class ThreadChannel extends BaseChannel {
55
55
  * @type {ThreadMemberManager}
56
56
  */
57
57
  this.members = new ThreadMemberManager(this);
58
- if (data) this._patch(data);
58
+ this._patch(data);
59
59
  }
60
60
 
61
61
  _patch(data) {
@@ -23,7 +23,7 @@ class Webhook {
23
23
  * @readonly
24
24
  */
25
25
  Object.defineProperty(this, 'client', { value: client });
26
- if (data) this._patch(data);
26
+ this._patch(data);
27
27
  }
28
28
 
29
29
  _patch(data) {
@@ -95,9 +95,9 @@ class Webhook {
95
95
  /**
96
96
  * The owner of the webhook
97
97
  *
98
- * @type {?(User|APIUser)}
98
+ * @type {?User}
99
99
  */
100
- this.owner = this.client.users?._add(data.user) ?? data.user;
100
+ this.owner = this.client.users._add(data.user);
101
101
  } else {
102
102
  this.owner ??= null;
103
103
  }
@@ -119,7 +119,7 @@ class Webhook {
119
119
  *
120
120
  * @type {?(Guild|APIGuild)}
121
121
  */
122
- this.sourceGuild = this.client.guilds?.cache.get(data.source_guild.id) ?? data.source_guild;
122
+ this.sourceGuild = this.client.guilds.cache.get(data.source_guild.id) ?? data.source_guild;
123
123
  } else {
124
124
  this.sourceGuild ??= null;
125
125
  }
@@ -130,7 +130,7 @@ class Webhook {
130
130
  *
131
131
  * @type {?(AnnouncementChannel|APIChannel)}
132
132
  */
133
- this.sourceChannel = this.client.channels?.cache.get(data.source_channel?.id) ?? data.source_channel;
133
+ this.sourceChannel = this.client.channels.cache.get(data.source_channel?.id) ?? data.source_channel;
134
134
  } else {
135
135
  this.sourceChannel ??= null;
136
136
  }
@@ -248,7 +248,6 @@ class Webhook {
248
248
  auth: false,
249
249
  });
250
250
 
251
- if (!this.client.channels) return data;
252
251
  return (
253
252
  this.client.channels.cache.get(data.channel_id)?.messages._add(data, false) ??
254
253
  new (getMessage())(this.client, data)
@@ -345,7 +344,6 @@ class Webhook {
345
344
  auth: false,
346
345
  });
347
346
 
348
- if (!this.client.channels) return data;
349
347
  return (
350
348
  this.client.channels.cache.get(data.channel_id)?.messages._add(data, false) ??
351
349
  new (getMessage())(this.client, data)
@@ -384,10 +382,7 @@ class Webhook {
384
382
  },
385
383
  );
386
384
 
387
- const channelManager = this.client.channels;
388
- if (!channelManager) return data;
389
-
390
- const messageManager = channelManager.cache.get(data.channel_id)?.messages;
385
+ const messageManager = this.client.channels.cache.get(data.channel_id)?.messages;
391
386
  if (!messageManager) return new (getMessage())(this.client, data);
392
387
 
393
388
  const existing = messageManager.cache.get(data.id);
@@ -1,14 +1,17 @@
1
1
  'use strict';
2
2
 
3
3
  const { Collection } = require('@discordjs/collection');
4
+ const { lazy } = require('@discordjs/util');
4
5
  const { DiscordSnowflake } = require('@sapphire/snowflake');
5
6
  const { InteractionType, Routes } = require('discord-api-types/v10');
6
7
  const { DiscordjsTypeError, DiscordjsError, ErrorCodes } = require('../../errors/index.js');
7
8
  const { MaxBulkDeletableMessageAge } = require('../../util/Constants.js');
8
9
  const { InteractionCollector } = require('../InteractionCollector.js');
9
- // eslint-disable-next-line import-x/order
10
10
  const { MessageCollector } = require('../MessageCollector.js');
11
11
 
12
+ // Fixes circular dependencies.
13
+ const getGuildMessageManager = lazy(() => require('../../managers/GuildMessageManager.js').GuildMessageManager);
14
+
12
15
  /**
13
16
  * Interface for classes that have text-channel-like features.
14
17
  *
@@ -21,8 +24,7 @@ class TextBasedChannel {
21
24
  *
22
25
  * @type {GuildMessageManager}
23
26
  */
24
- // eslint-disable-next-line no-use-before-define
25
- this.messages = new GuildMessageManager(this);
27
+ this.messages = new (getGuildMessageManager())(this);
26
28
 
27
29
  /**
28
30
  * The channel's last message id, if one was sent
@@ -86,7 +88,7 @@ class TextBasedChannel {
86
88
  * @property {Array<(EmbedBuilder|Embed|APIEmbed)>} [embeds] The embeds for the message
87
89
  * @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content
88
90
  * (see {@link https://discord.com/developers/docs/resources/message#allowed-mentions-object here} for more details)
89
- * @property {Array<(AttachmentBuilder|Attachment|AttachmentPayload|BufferResolvable)>} [files]
91
+ * @property {Array<(Attachment|AttachmentPayload|BufferResolvable|FileBodyEncodable<APIAttachment>|Stream)>} [files]
90
92
  * The files to send with the message.
91
93
  * @property {Array<(ActionRowBuilder|MessageTopLevelComponent|APIMessageTopLevelComponent)>} [components]
92
94
  * Action rows containing interactive components for the message (buttons, select menus) and other
@@ -154,7 +156,7 @@ class TextBasedChannel {
154
156
  /**
155
157
  * Sends a message to this channel.
156
158
  *
157
- * @param {string|MessagePayload|MessageCreateOptions} options The options to provide
159
+ * @param {string|MessagePayload|MessageCreateOptions|JSONEncodable<RESTPostAPIChannelMessageJSONBody>|FileBodyEncodable<RESTPostAPIChannelMessageJSONBody>} options The options to provide
158
160
  * @returns {Promise<Message>}
159
161
  * @example
160
162
  * // Send a basic message
@@ -427,7 +429,3 @@ class TextBasedChannel {
427
429
  }
428
430
 
429
431
  exports.TextBasedChannel = TextBasedChannel;
430
-
431
- // Fixes Circular
432
- // eslint-disable-next-line import-x/order
433
- const { GuildMessageManager } = require('../../managers/GuildMessageManager.js');
@@ -683,3 +683,13 @@
683
683
  * @external WebhookType
684
684
  * @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/WebhookType}
685
685
  */
686
+
687
+ /**
688
+ * @external RESTPatchAPIChannelMessageJSONBody
689
+ * @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/RESTPatchAPIChannelMessageJSONBody}
690
+ */
691
+
692
+ /**
693
+ * @external RESTPostAPIChannelMessageJSONBody
694
+ * @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/RESTPostAPIChannelMessageJSONBody}
695
+ */