discord.js 15.0.0-move-client-init.1761650119-a4c0a246f → 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 +107 -14
  3. package/src/client/websocket/handlers/RATE_LIMITED.js +24 -0
  4. package/src/client/websocket/handlers/index.js +1 -0
  5. package/src/errors/DJSError.js +7 -3
  6. package/src/errors/ErrorCodes.js +2 -4
  7. package/src/errors/Messages.js +2 -3
  8. package/src/index.js +0 -3
  9. package/src/managers/CachedManager.js +5 -5
  10. package/src/managers/ChannelManager.js +10 -7
  11. package/src/managers/GuildBanManager.js +3 -3
  12. package/src/managers/GuildEmojiManager.js +2 -2
  13. package/src/managers/GuildEmojiRoleManager.js +9 -1
  14. package/src/managers/GuildMemberManager.js +44 -23
  15. package/src/managers/GuildMemberRoleManager.js +11 -2
  16. package/src/managers/MessageManager.js +25 -18
  17. package/src/structures/ApplicationCommand.js +4 -5
  18. package/src/structures/ClientApplication.js +2 -0
  19. package/src/structures/CommandInteraction.js +1 -1
  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 +11 -0
  27. package/src/structures/ModalSubmitInteraction.js +23 -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 +60 -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 +163 -171
  41. package/typings/index.d.ts +163 -171
  42. package/src/client/BaseClient.js +0 -131
  43. package/src/client/WebhookClient.js +0 -119
  44. package/src/structures/AttachmentBuilder.js +0 -185
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-move-client-init.1761650119-a4c0a246f",
4
+ "version": "15.0.0-pr-11006.1765450224-e636950b2",
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",
@@ -55,36 +55,34 @@
55
55
  "dependencies": {
56
56
  "@sapphire/snowflake": "3.5.5",
57
57
  "@vladfrangu/async_event_emitter": "^2.4.7",
58
- "discord-api-types": "^0.38.30",
58
+ "discord-api-types": "^0.38.36",
59
59
  "fast-deep-equal": "3.1.3",
60
60
  "lodash.snakecase": "4.1.1",
61
61
  "magic-bytes.js": "^1.12.1",
62
62
  "tslib": "^2.8.1",
63
63
  "undici": "7.16.0",
64
- "@discordjs/builders": "^2.0.0-move-client-init.1761650119-a4c0a246f",
65
- "@discordjs/collection": "^3.0.0-move-client-init.1761650119-a4c0a246f",
66
- "@discordjs/formatters": "^1.0.0-move-client-init.1761650119-a4c0a246f",
67
- "@discordjs/rest": "^3.0.0-move-client-init.1761650119-a4c0a246f",
68
- "@discordjs/util": "^2.0.0-move-client-init.1761650119-a4c0a246f",
69
- "@discordjs/ws": "^3.0.0-move-client-init.1761650119-a4c0a246f"
64
+ "@discordjs/builders": "2.0.0-pr-11006.1765450224-e636950b2",
65
+ "@discordjs/formatters": "1.0.0-pr-11006.1765450224-e636950b2",
66
+ "@discordjs/collection": "3.0.0-pr-11006.1765450224-e636950b2",
67
+ "@discordjs/rest": "3.0.0-pr-11006.1765450224-e636950b2",
68
+ "@discordjs/util": "2.0.0-pr-11006.1765450224-e636950b2",
69
+ "@discordjs/ws": "3.0.0-pr-11006.1765450224-e636950b2"
70
70
  },
71
71
  "devDependencies": {
72
- "@favware/cliff-jumper": "^4.1.0",
73
- "@types/node": "^22.18.8",
72
+ "@favware/cliff-jumper": "^6.0.0",
73
+ "@types/node": "^22.19.1",
74
74
  "cross-env": "^10.1.0",
75
- "eslint": "^9.37.0",
76
- "eslint-config-neon": "^0.2.7",
77
- "eslint-formatter-compact": "^8.40.0",
75
+ "eslint": "^9.39.1",
76
+ "eslint-config-neon": "^0.2.9",
77
+ "eslint-formatter-compact": "^9.0.1",
78
78
  "eslint-formatter-pretty": "^7.0.0",
79
- "eslint-plugin-import": "^2.32.0",
80
- "eslint-plugin-jsdoc": "^54.7.0",
81
- "prettier": "^3.6.2",
79
+ "prettier": "^3.7.4",
82
80
  "tsd": "^0.33.0",
83
- "turbo": "^2.5.8",
81
+ "turbo": "^2.6.3",
84
82
  "typescript": "~5.9.3",
85
- "@discordjs/api-extractor": "^7.52.7",
86
- "@discordjs/docgen": "^0.12.1",
87
- "@discordjs/scripts": "^0.1.0"
83
+ "@discordjs/api-extractor": "7.52.7",
84
+ "@discordjs/docgen": "0.12.1",
85
+ "@discordjs/scripts": "0.1.0"
88
86
  },
89
87
  "engines": {
90
88
  "node": ">=22.12.0"
@@ -3,8 +3,9 @@
3
3
  const process = require('node:process');
4
4
  const { clearTimeout, setImmediate, setTimeout } = require('node:timers');
5
5
  const { Collection } = require('@discordjs/collection');
6
- const { makeURLSearchParams } = require('@discordjs/rest');
6
+ const { REST, RESTEvents, makeURLSearchParams } = require('@discordjs/rest');
7
7
  const { WebSocketManager, WebSocketShardEvents, WebSocketShardStatus } = require('@discordjs/ws');
8
+ const { AsyncEventEmitter } = require('@vladfrangu/async_event_emitter');
8
9
  const { GatewayDispatchEvents, GatewayIntentBits, OAuth2Scopes, Routes } = require('discord-api-types/v10');
9
10
  const { DiscordjsError, DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
10
11
  const { ChannelManager } = require('../managers/ChannelManager.js');
@@ -28,7 +29,7 @@ const { Options } = require('../util/Options.js');
28
29
  const { PermissionsBitField } = require('../util/PermissionsBitField.js');
29
30
  const { Status } = require('../util/Status.js');
30
31
  const { Sweepers } = require('../util/Sweepers.js');
31
- const { BaseClient } = require('./BaseClient.js');
32
+ const { flatten } = require('../util/Util.js');
32
33
  const { ActionsManager } = require('./actions/ActionsManager.js');
33
34
  const { ClientVoiceManager } = require('./voice/ClientVoiceManager.js');
34
35
  const { PacketHandlers } = require('./websocket/handlers/index.js');
@@ -47,24 +48,66 @@ const BeforeReadyWhitelist = [
47
48
  /**
48
49
  * The main hub for interacting with the Discord API, and the starting point for any bot.
49
50
  *
50
- * @extends {BaseClient}
51
+ * @extends {AsyncEventEmitter}
51
52
  */
52
- class Client extends BaseClient {
53
+ class Client extends AsyncEventEmitter {
53
54
  /**
54
55
  * @param {ClientOptions} options Options for the client
55
56
  */
56
57
  constructor(options) {
57
- super(options);
58
+ super();
59
+
60
+ if (typeof options !== 'object' || options === null) {
61
+ throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'options', 'object', true);
62
+ }
63
+
64
+ const defaultOptions = Options.createDefault();
65
+ /**
66
+ * The options the client was instantiated with
67
+ *
68
+ * @type {ClientOptions}
69
+ */
70
+ this.options = {
71
+ ...defaultOptions,
72
+ ...options,
73
+ presence: {
74
+ ...defaultOptions.presence,
75
+ ...options.presence,
76
+ },
77
+ sweepers: {
78
+ ...defaultOptions.sweepers,
79
+ ...options.sweepers,
80
+ },
81
+ ws: {
82
+ ...defaultOptions.ws,
83
+ ...options.ws,
84
+ },
85
+ rest: {
86
+ ...defaultOptions.rest,
87
+ ...options.rest,
88
+ userAgentAppendix: options.rest?.userAgentAppendix
89
+ ? `${Options.userAgentAppendix} ${options.rest.userAgentAppendix}`
90
+ : Options.userAgentAppendix,
91
+ },
92
+ };
93
+
94
+ /**
95
+ * The REST manager of the client
96
+ *
97
+ * @type {REST}
98
+ */
99
+ this.rest = new REST(this.options.rest);
100
+
101
+ this.rest.on(RESTEvents.Debug, message => this.emit(Events.Debug, message));
58
102
 
59
103
  const data = require('node:worker_threads').workerData ?? process.env;
60
- const defaults = Options.createDefault();
61
104
 
62
- if (this.options.ws.shardIds === defaults.ws.shardIds && 'SHARDS' in data) {
105
+ if (this.options.ws.shardIds === defaultOptions.ws.shardIds && 'SHARDS' in data) {
63
106
  const shards = JSON.parse(data.SHARDS);
64
107
  this.options.ws.shardIds = Array.isArray(shards) ? shards : [shards];
65
108
  }
66
109
 
67
- if (this.options.ws.shardCount === defaults.ws.shardCount && 'SHARD_COUNT' in data) {
110
+ if (this.options.ws.shardCount === defaultOptions.ws.shardCount && 'SHARD_COUNT' in data) {
68
111
  this.options.ws.shardCount = Number(data.SHARD_COUNT);
69
112
  }
70
113
 
@@ -435,12 +478,56 @@ class Client extends BaseClient {
435
478
  }
436
479
 
437
480
  /**
438
- * Logs out, terminates the connection to Discord, and destroys the client.
481
+ * Options used for deleting a webhook.
482
+ *
483
+ * @typedef {Object} WebhookDeleteOptions
484
+ * @property {string} [token] Token of the webhook
485
+ * @property {string} [reason] The reason for deleting the webhook
486
+ */
487
+
488
+ /**
489
+ * Deletes a webhook.
490
+ *
491
+ * @param {Snowflake} id The webhook's id
492
+ * @param {WebhookDeleteOptions} [options] Options for deleting the webhook
493
+ * @returns {Promise<void>}
494
+ */
495
+ async deleteWebhook(id, { token, reason } = {}) {
496
+ await this.rest.delete(Routes.webhook(id, token), { auth: !token, reason });
497
+ }
498
+
499
+ /**
500
+ * Increments max listeners by one, if they are not zero.
501
+ *
502
+ * @private
503
+ */
504
+ incrementMaxListeners() {
505
+ const maxListeners = this.getMaxListeners();
506
+ if (maxListeners !== 0) {
507
+ this.setMaxListeners(maxListeners + 1);
508
+ }
509
+ }
510
+
511
+ /**
512
+ * Decrements max listeners by one, if they are not zero.
513
+ *
514
+ * @private
515
+ */
516
+ decrementMaxListeners() {
517
+ const maxListeners = this.getMaxListeners();
518
+ if (maxListeners !== 0) {
519
+ this.setMaxListeners(maxListeners - 1);
520
+ }
521
+ }
522
+
523
+ /**
524
+ * Destroys all assets used by the client.
439
525
  *
440
526
  * @returns {Promise<void>}
441
527
  */
442
528
  async destroy() {
443
- super.destroy();
529
+ this.rest.clearHashSweeper();
530
+ this.rest.clearHandlerSweeper();
444
531
 
445
532
  this.sweepers.destroy();
446
533
  await this.ws.destroy();
@@ -694,10 +781,7 @@ class Client extends BaseClient {
694
781
  }
695
782
 
696
783
  toJSON() {
697
- return super.toJSON({
698
- actions: false,
699
- presence: false,
700
- });
784
+ return flatten(this, { actions: false, presence: false });
701
785
  }
702
786
 
703
787
  /**
@@ -790,6 +874,10 @@ class Client extends BaseClient {
790
874
  throw new DiscordjsTypeError(ErrorCodes.ClientInvalidOption, 'jsonTransformer', 'a function');
791
875
  }
792
876
  }
877
+
878
+ async [Symbol.asyncDispose]() {
879
+ await this.destroy();
880
+ }
793
881
  }
794
882
 
795
883
  exports.Client = Client;
@@ -839,6 +927,11 @@ exports.Client = Client;
839
927
  * @see {@link https://discord.js.org/docs/packages/collection/stable/Collection:Class}
840
928
  */
841
929
 
930
+ /**
931
+ * @external REST
932
+ * @see {@link https://discord.js.org/docs/packages/rest/stable/REST:Class}
933
+ */
934
+
842
935
  /**
843
936
  * @external ImageURLOptions
844
937
  * @see {@link https://discord.js.org/docs/packages/rest/stable/ImageURLOptions:Interface}
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ const process = require('node:process');
4
+ const { GatewayOpcodes } = require('discord-api-types/v10');
5
+
6
+ const emittedFor = new Set();
7
+
8
+ module.exports = (client, { d: data }) => {
9
+ switch (data.opcode) {
10
+ case GatewayOpcodes.RequestGuildMembers: {
11
+ break;
12
+ }
13
+
14
+ default: {
15
+ if (!emittedFor.has(data.opcode)) {
16
+ process.emitWarning(
17
+ `Hit a gateway rate limit on opcode ${data.opcode} (${GatewayOpcodes[data.opcode]}). If the discord.js version you're using is up-to-date, please open an issue on GitHub.`,
18
+ );
19
+
20
+ emittedFor.add(data.opcode);
21
+ }
22
+ }
23
+ }
24
+ };
@@ -52,6 +52,7 @@ const PacketHandlers = Object.fromEntries([
52
52
  ['MESSAGE_REACTION_REMOVE_EMOJI', require('./MESSAGE_REACTION_REMOVE_EMOJI.js')],
53
53
  ['MESSAGE_UPDATE', require('./MESSAGE_UPDATE.js')],
54
54
  ['PRESENCE_UPDATE', require('./PRESENCE_UPDATE.js')],
55
+ ['RATE_LIMITED', require('./RATE_LIMITED.js')],
55
56
  ['READY', require('./READY.js')],
56
57
  ['SOUNDBOARD_SOUNDS', require('./SOUNDBOARD_SOUNDS.js')],
57
58
  ['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE.js')],
@@ -12,15 +12,19 @@ const { Messages } = require('./Messages.js');
12
12
  * @ignore
13
13
  */
14
14
  function makeDiscordjsError(Base) {
15
- return class DiscordjsError extends Base {
15
+ return class extends Base {
16
+ static {
17
+ Object.defineProperty(this, 'name', { value: `Discordjs${Base.name}` });
18
+ }
19
+
16
20
  constructor(code, ...args) {
17
21
  super(message(code, args));
18
22
  this.code = code;
19
- Error.captureStackTrace?.(this, DiscordjsError);
23
+ Error.captureStackTrace(this, this.constructor);
20
24
  }
21
25
 
22
26
  get name() {
23
- return `${super.name} [${this.code}]`;
27
+ return `${this.constructor.name} [${this.code}]`;
24
28
  }
25
29
  };
26
30
  }
@@ -77,14 +77,13 @@
77
77
  *
78
78
  * @property {'WebhookMessage'} WebhookMessage
79
79
  * @property {'WebhookTokenUnavailable'} WebhookTokenUnavailable
80
- * @property {'WebhookURLInvalid'} WebhookURLInvalid
81
80
  * @property {'WebhookApplication'} WebhookApplication
82
81
  *
83
82
  * @property {'MessageReferenceMissing'} MessageReferenceMissing
84
83
  *
85
84
  * @property {'EmojiType'} EmojiType
86
85
  * @property {'EmojiManaged'} EmojiManaged
87
- * @property {'MissingManageGuildExpressionsPermission'} MissingManageGuildExpressionsPermission
86
+ * @property {'MissingGuildExpressionsPermission'} MissingGuildExpressionsPermission
88
87
  *
89
88
  * @property {'NotGuildSoundboardSound'} NotGuildSoundboardSound
90
89
  * @property {'NotGuildSticker'} NotGuildSticker
@@ -212,14 +211,13 @@ const keys = [
212
211
 
213
212
  'WebhookMessage',
214
213
  'WebhookTokenUnavailable',
215
- 'WebhookURLInvalid',
216
214
  'WebhookApplication',
217
215
 
218
216
  'MessageReferenceMissing',
219
217
 
220
218
  'EmojiType',
221
219
  'EmojiManaged',
222
- 'MissingManageGuildExpressionsPermission',
220
+ 'MissingGuildExpressionsPermission',
223
221
 
224
222
  'NotGuildSoundboardSound',
225
223
  'NotGuildSticker',
@@ -82,15 +82,14 @@ const Messages = {
82
82
 
83
83
  [ErrorCodes.WebhookMessage]: 'The message was not sent by a webhook.',
84
84
  [ErrorCodes.WebhookTokenUnavailable]: 'This action requires a webhook token, but none is available.',
85
- [ErrorCodes.WebhookURLInvalid]: 'The provided webhook URL is not valid.',
86
85
  [ErrorCodes.WebhookApplication]: 'This message webhook belongs to an application and cannot be fetched.',
87
86
 
88
87
  [ErrorCodes.MessageReferenceMissing]: 'The message does not reference another message',
89
88
 
90
89
  [ErrorCodes.EmojiType]: 'Emoji must be a string or GuildEmoji/ReactionEmoji',
91
90
  [ErrorCodes.EmojiManaged]: 'Emoji is managed and has no Author.',
92
- [ErrorCodes.MissingManageGuildExpressionsPermission]: guild =>
93
- `Client must have Manage Guild Expressions permission in guild ${guild} to see emoji authors.`,
91
+ [ErrorCodes.MissingGuildExpressionsPermission]: guild =>
92
+ `Client must have Create Guild Expressions or Manage Guild Expressions permission in guild ${guild} to see emoji authors.`,
94
93
 
95
94
  [ErrorCodes.NotGuildSoundboardSound]: action =>
96
95
  `Soundboard sound is a default (non-guild) soundboard sound and can't be ${action}.`,
package/src/index.js CHANGED
@@ -3,12 +3,10 @@
3
3
  const { __exportStar } = require('tslib');
4
4
 
5
5
  // "Root" classes (starting points)
6
- exports.BaseClient = require('./client/BaseClient.js').BaseClient;
7
6
  exports.Client = require('./client/Client.js').Client;
8
7
  exports.Shard = require('./sharding/Shard.js').Shard;
9
8
  exports.ShardClientUtil = require('./sharding/ShardClientUtil.js').ShardClientUtil;
10
9
  exports.ShardingManager = require('./sharding/ShardingManager.js').ShardingManager;
11
- exports.WebhookClient = require('./client/WebhookClient.js').WebhookClient;
12
10
 
13
11
  // Errors
14
12
  exports.DiscordjsError = require('./errors/DJSError.js').DiscordjsError;
@@ -113,7 +111,6 @@ exports.ApplicationEmoji = require('./structures/ApplicationEmoji.js').Applicati
113
111
  exports.ApplicationRoleConnectionMetadata =
114
112
  require('./structures/ApplicationRoleConnectionMetadata.js').ApplicationRoleConnectionMetadata;
115
113
  exports.Attachment = require('./structures/Attachment.js').Attachment;
116
- exports.AttachmentBuilder = require('./structures/AttachmentBuilder.js').AttachmentBuilder;
117
114
  exports.AutocompleteInteraction = require('./structures/AutocompleteInteraction.js').AutocompleteInteraction;
118
115
  exports.AutoModerationActionExecution =
119
116
  require('./structures/AutoModerationActionExecution.js').AutoModerationActionExecution;
@@ -22,11 +22,11 @@ class CachedManager extends DataManager {
22
22
  * @name CachedManager#_cache
23
23
  */
24
24
  Object.defineProperty(this, '_cache', {
25
- value: this.client.options.makeCache(
26
- this.constructor[MakeCacheOverrideSymbol] ?? this.constructor,
27
- this.holds,
28
- this.constructor,
29
- ),
25
+ value: this.client.options.makeCache({
26
+ holds: this.holds,
27
+ manager: this.constructor,
28
+ managerType: this.constructor[MakeCacheOverrideSymbol] ?? this.constructor,
29
+ }),
30
30
  });
31
31
 
32
32
  if (iterable) {
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const process = require('node:process');
4
- const { lazy } = require('@discordjs/util');
4
+ const { lazy, isFileBodyEncodable, isJSONEncodable } = require('@discordjs/util');
5
5
  const { Routes } = require('discord-api-types/v10');
6
6
  const { BaseChannel } = require('../structures/BaseChannel.js');
7
7
  const { MessagePayload } = require('../structures/MessagePayload.js');
@@ -147,7 +147,7 @@ class ChannelManager extends CachedManager {
147
147
  * Creates a message in a channel.
148
148
  *
149
149
  * @param {TextChannelResolvable} channel The channel to send the message to
150
- * @param {string|MessagePayload|MessageCreateOptions} options The options to provide
150
+ * @param {string|MessagePayload|MessageCreateOptions|JSONEncodable<RESTPostAPIChannelMessageJSONBody>|FileBodyEncodable<RESTPostAPIChannelMessageJSONBody>} options The options to provide
151
151
  * @returns {Promise<Message>}
152
152
  * @example
153
153
  * // Send a basic message
@@ -174,18 +174,21 @@ class ChannelManager extends CachedManager {
174
174
  * .catch(console.error);
175
175
  */
176
176
  async createMessage(channel, options) {
177
- let messagePayload;
177
+ let payload;
178
178
 
179
179
  if (options instanceof MessagePayload) {
180
- messagePayload = options.resolveBody();
180
+ payload = await options.resolveBody().resolveFiles();
181
+ } else if (isFileBodyEncodable(options)) {
182
+ payload = options.toFileBody();
183
+ } else if (isJSONEncodable(options)) {
184
+ payload = { body: options.toJSON() };
181
185
  } else {
182
- messagePayload = MessagePayload.create(this, options).resolveBody();
186
+ payload = await MessagePayload.create(this, options).resolveBody().resolveFiles();
183
187
  }
184
188
 
185
189
  const resolvedChannelId = this.resolveId(channel);
186
190
  const resolvedChannel = this.resolve(channel);
187
- const { body, files } = await messagePayload.resolveFiles();
188
- const data = await this.client.rest.post(Routes.channelMessages(resolvedChannelId), { body, files });
191
+ const data = await this.client.rest.post(Routes.channelMessages(resolvedChannelId), payload);
189
192
 
190
193
  return resolvedChannel?.messages._add(data) ?? new (getMessage())(this.client, data);
191
194
  }
@@ -124,12 +124,12 @@ class GuildBanManager extends CachedManager {
124
124
  return this._add(data, cache);
125
125
  }
126
126
 
127
- async _fetchMany(options = {}) {
127
+ async _fetchMany({ cache, ...apiOptions } = {}) {
128
128
  const data = await this.client.rest.get(Routes.guildBans(this.guild.id), {
129
- query: makeURLSearchParams(options),
129
+ query: makeURLSearchParams(apiOptions),
130
130
  });
131
131
 
132
- return data.reduce((col, ban) => col.set(ban.user.id, this._add(ban, options.cache)), new Collection());
132
+ return data.reduce((col, ban) => col.set(ban.user.id, this._add(ban, cache)), new Collection());
133
133
  }
134
134
 
135
135
  /**
@@ -251,8 +251,8 @@ class GuildEmojiManager extends CachedManager {
251
251
 
252
252
  const { me } = this.guild.members;
253
253
  if (!me) throw new DiscordjsError(ErrorCodes.GuildUncachedMe);
254
- if (!me.permissions.has(PermissionFlagsBits.ManageGuildExpressions)) {
255
- throw new DiscordjsError(ErrorCodes.MissingManageGuildExpressionsPermission, this.guild);
254
+ if (!me.permissions.any(PermissionFlagsBits.CreateGuildExpressions | PermissionFlagsBits.ManageGuildExpressions)) {
255
+ throw new DiscordjsError(ErrorCodes.MissingGuildExpressionsPermission, this.guild);
256
256
  }
257
257
 
258
258
  const data = await this.client.rest.get(Routes.guildEmoji(this.guild.id, resolvedEmoji.id));
@@ -35,7 +35,15 @@ class GuildEmojiRoleManager extends DataManager {
35
35
  * @readonly
36
36
  */
37
37
  get cache() {
38
- return this.guild.roles.cache.filter(role => this.emoji._roles.includes(role.id));
38
+ const cache = new Collection();
39
+ for (const roleId of this.emoji._roles) {
40
+ const role = this.guild.roles.cache.get(roleId);
41
+ if (role !== undefined) {
42
+ cache.set(roleId, role);
43
+ }
44
+ }
45
+
46
+ return cache;
39
47
  }
40
48
 
41
49
  /**
@@ -3,8 +3,10 @@
3
3
  const { setTimeout, clearTimeout } = require('node:timers');
4
4
  const { Collection } = require('@discordjs/collection');
5
5
  const { makeURLSearchParams } = require('@discordjs/rest');
6
+ const { GatewayRateLimitError } = require('@discordjs/util');
7
+ const { WebSocketShardEvents } = require('@discordjs/ws');
6
8
  const { DiscordSnowflake } = require('@sapphire/snowflake');
7
- const { Routes, GatewayOpcodes } = require('discord-api-types/v10');
9
+ const { Routes, GatewayOpcodes, GatewayDispatchEvents } = require('discord-api-types/v10');
8
10
  const { DiscordjsError, DiscordjsTypeError, DiscordjsRangeError, ErrorCodes } = require('../errors/index.js');
9
11
  const { BaseGuildVoiceChannel } = require('../structures/BaseGuildVoiceChannel.js');
10
12
  const { GuildMember } = require('../structures/GuildMember.js');
@@ -246,24 +248,27 @@ class GuildMemberManager extends CachedManager {
246
248
  const query = initialQuery ?? (users ? undefined : '');
247
249
 
248
250
  return new Promise((resolve, reject) => {
249
- this.guild.client.ws.send(this.guild.shardId, {
250
- op: GatewayOpcodes.RequestGuildMembers,
251
- // eslint-disable-next-line id-length
252
- d: {
253
- guild_id: this.guild.id,
254
- presences,
255
- user_ids: users,
256
- query,
257
- nonce,
258
- limit,
259
- },
260
- });
261
251
  const fetchedMembers = new Collection();
262
252
  let index = 0;
253
+
254
+ const cleanup = () => {
255
+ /* eslint-disable no-use-before-define */
256
+ clearTimeout(timeout);
257
+
258
+ this.client.ws.removeListener(WebSocketShardEvents.Dispatch, rateLimitHandler);
259
+ this.client.removeListener(Events.GuildMembersChunk, handler);
260
+ this.client.decrementMaxListeners();
261
+ /* eslint-enable no-use-before-define */
262
+ };
263
+
264
+ const timeout = setTimeout(() => {
265
+ cleanup();
266
+ reject(new DiscordjsError(ErrorCodes.GuildMembersTimeout));
267
+ }, time).unref();
268
+
263
269
  const handler = (members, _, chunk) => {
264
270
  if (chunk.nonce !== nonce) return;
265
271
 
266
- // eslint-disable-next-line no-use-before-define
267
272
  timeout.refresh();
268
273
  index++;
269
274
  for (const member of members.values()) {
@@ -271,21 +276,37 @@ class GuildMemberManager extends CachedManager {
271
276
  }
272
277
 
273
278
  if (members.size < 1_000 || (limit && fetchedMembers.size >= limit) || index === chunk.count) {
274
- // eslint-disable-next-line no-use-before-define
275
- clearTimeout(timeout);
276
- this.client.removeListener(Events.GuildMembersChunk, handler);
277
- this.client.decrementMaxListeners();
279
+ cleanup();
278
280
  resolve(users && !Array.isArray(users) && fetchedMembers.size ? fetchedMembers.first() : fetchedMembers);
279
281
  }
280
282
  };
281
283
 
282
- const timeout = setTimeout(() => {
283
- this.client.removeListener(Events.GuildMembersChunk, handler);
284
- this.client.decrementMaxListeners();
285
- reject(new DiscordjsError(ErrorCodes.GuildMembersTimeout));
286
- }, time).unref();
284
+ const requestData = {
285
+ guild_id: this.guild.id,
286
+ presences,
287
+ user_ids: users,
288
+ query,
289
+ nonce,
290
+ limit,
291
+ };
292
+
293
+ const rateLimitHandler = payload => {
294
+ if (payload.t === GatewayDispatchEvents.RateLimited && payload.d.meta.nonce === nonce) {
295
+ cleanup();
296
+ reject(new GatewayRateLimitError(payload.d, requestData));
297
+ }
298
+ };
299
+
300
+ this.client.ws.on(WebSocketShardEvents.Dispatch, rateLimitHandler);
301
+
287
302
  this.client.incrementMaxListeners();
288
303
  this.client.on(Events.GuildMembersChunk, handler);
304
+
305
+ this.guild.client.ws.send(this.guild.shardId, {
306
+ op: GatewayOpcodes.RequestGuildMembers,
307
+ // eslint-disable-next-line id-length
308
+ d: requestData,
309
+ });
289
310
  });
290
311
  }
291
312
 
@@ -37,8 +37,17 @@ class GuildMemberRoleManager extends DataManager {
37
37
  * @readonly
38
38
  */
39
39
  get cache() {
40
- const everyone = this.guild.roles.everyone;
41
- return this.guild.roles.cache.filter(role => this.member._roles.includes(role.id)).set(everyone.id, everyone);
40
+ const cache = new Collection();
41
+ cache.set(this.guild.id, this.guild.roles.everyone);
42
+
43
+ for (const roleId of this.member._roles) {
44
+ const role = this.guild.roles.cache.get(roleId);
45
+ if (role !== undefined) {
46
+ cache.set(roleId, role);
47
+ }
48
+ }
49
+
50
+ return cache;
42
51
  }
43
52
 
44
53
  /**