mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
feat(GuildMemberManager): handle gateway request rate limit (#11253)
* feat(GuildMemberManager): handle gateway request ratelimit * fix: account for ws abstraction layer * chore: yada yada * refactor: use new error class * fix: linter --------- Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
214c6cbdd6
commit
a3c6bce3a8
@@ -0,0 +1,25 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const process = require('node:process');
|
||||||
|
const { GatewayOpcodes } = require('discord-api-types/v10');
|
||||||
|
|
||||||
|
const emittedFor = new Set();
|
||||||
|
|
||||||
|
module.exports = (_, { d: data }) => {
|
||||||
|
switch (data.opcode) {
|
||||||
|
case GatewayOpcodes.RequestGuildMembers: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
if (!emittedFor.has(data.opcode)) {
|
||||||
|
process.emitWarning(
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
`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.`,
|
||||||
|
);
|
||||||
|
|
||||||
|
emittedFor.add(data.opcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -52,6 +52,7 @@ const handlers = Object.fromEntries([
|
|||||||
['MESSAGE_REACTION_REMOVE_EMOJI', require('./MESSAGE_REACTION_REMOVE_EMOJI')],
|
['MESSAGE_REACTION_REMOVE_EMOJI', require('./MESSAGE_REACTION_REMOVE_EMOJI')],
|
||||||
['MESSAGE_UPDATE', require('./MESSAGE_UPDATE')],
|
['MESSAGE_UPDATE', require('./MESSAGE_UPDATE')],
|
||||||
['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')],
|
['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')],
|
||||||
|
['RATE_LIMITED', require('./RATE_LIMITED')],
|
||||||
['READY', require('./READY')],
|
['READY', require('./READY')],
|
||||||
['RESUMED', require('./RESUMED')],
|
['RESUMED', require('./RESUMED')],
|
||||||
['SOUNDBOARD_SOUNDS', require('./SOUNDBOARD_SOUNDS')],
|
['SOUNDBOARD_SOUNDS', require('./SOUNDBOARD_SOUNDS')],
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ const { process } = require('node:process');
|
|||||||
const { setTimeout, clearTimeout } = require('node:timers');
|
const { setTimeout, clearTimeout } = require('node:timers');
|
||||||
const { Collection } = require('@discordjs/collection');
|
const { Collection } = require('@discordjs/collection');
|
||||||
const { makeURLSearchParams } = require('@discordjs/rest');
|
const { makeURLSearchParams } = require('@discordjs/rest');
|
||||||
|
const { GatewayRateLimitError } = require('@discordjs/util');
|
||||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||||
const { Routes, GatewayOpcodes } = require('discord-api-types/v10');
|
const { Routes, GatewayOpcodes, GatewayDispatchEvents } = require('discord-api-types/v10');
|
||||||
const CachedManager = require('./CachedManager');
|
const CachedManager = require('./CachedManager');
|
||||||
const { DiscordjsError, DiscordjsTypeError, DiscordjsRangeError, ErrorCodes } = require('../errors');
|
const { DiscordjsError, DiscordjsTypeError, DiscordjsRangeError, ErrorCodes } = require('../errors');
|
||||||
const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel');
|
const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel');
|
||||||
@@ -239,19 +240,25 @@ class GuildMemberManager extends CachedManager {
|
|||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!query && !users) query = '';
|
if (!query && !users) query = '';
|
||||||
this.guild.shard.send({
|
|
||||||
op: GatewayOpcodes.RequestGuildMembers,
|
|
||||||
d: {
|
|
||||||
guild_id: this.guild.id,
|
|
||||||
presences,
|
|
||||||
user_ids: users,
|
|
||||||
query,
|
|
||||||
nonce,
|
|
||||||
limit,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const fetchedMembers = new Collection();
|
const fetchedMembers = new Collection();
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
/* eslint-disable no-use-before-define */
|
||||||
|
clearTimeout(timeout);
|
||||||
|
|
||||||
|
this.client.removeListener(Events.Raw, rateLimitHandler);
|
||||||
|
this.client.decrementMaxListeners();
|
||||||
|
this.client.removeListener(Events.GuildMembersChunk, handler);
|
||||||
|
this.client.decrementMaxListeners();
|
||||||
|
/* eslint-enable no-use-before-define */
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
cleanup();
|
||||||
|
reject(new DiscordjsError(ErrorCodes.GuildMembersTimeout));
|
||||||
|
}, time).unref();
|
||||||
|
|
||||||
const handler = (members, _, chunk) => {
|
const handler = (members, _, chunk) => {
|
||||||
if (chunk.nonce !== nonce) return;
|
if (chunk.nonce !== nonce) return;
|
||||||
timeout.refresh();
|
timeout.refresh();
|
||||||
@@ -260,19 +267,37 @@ class GuildMemberManager extends CachedManager {
|
|||||||
fetchedMembers.set(member.id, member);
|
fetchedMembers.set(member.id, member);
|
||||||
}
|
}
|
||||||
if (members.size < 1_000 || (limit && fetchedMembers.size >= limit) || i === chunk.count) {
|
if (members.size < 1_000 || (limit && fetchedMembers.size >= limit) || i === chunk.count) {
|
||||||
clearTimeout(timeout);
|
cleanup();
|
||||||
this.client.removeListener(Events.GuildMembersChunk, handler);
|
|
||||||
this.client.decrementMaxListeners();
|
|
||||||
resolve(users && !Array.isArray(users) && fetchedMembers.size ? fetchedMembers.first() : fetchedMembers);
|
resolve(users && !Array.isArray(users) && fetchedMembers.size ? fetchedMembers.first() : fetchedMembers);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
this.client.removeListener(Events.GuildMembersChunk, handler);
|
const requestData = {
|
||||||
this.client.decrementMaxListeners();
|
guild_id: this.guild.id,
|
||||||
reject(new DiscordjsError(ErrorCodes.GuildMembersTimeout));
|
presences,
|
||||||
}, time).unref();
|
user_ids: users,
|
||||||
|
query,
|
||||||
|
nonce,
|
||||||
|
limit,
|
||||||
|
};
|
||||||
|
|
||||||
|
const rateLimitHandler = payload => {
|
||||||
|
if (payload.t === GatewayDispatchEvents.RateLimited && payload.d.meta.nonce === nonce) {
|
||||||
|
cleanup();
|
||||||
|
reject(new GatewayRateLimitError(payload.d, requestData));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.client.incrementMaxListeners();
|
||||||
|
this.client.on(Events.Raw, rateLimitHandler);
|
||||||
|
|
||||||
this.client.incrementMaxListeners();
|
this.client.incrementMaxListeners();
|
||||||
this.client.on(Events.GuildMembersChunk, handler);
|
this.client.on(Events.GuildMembersChunk, handler);
|
||||||
|
|
||||||
|
this.guild.shard.send({
|
||||||
|
op: GatewayOpcodes.RequestGuildMembers,
|
||||||
|
d: requestData,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user