mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-10 08:33:30 +01:00
lots of important stuff (#1883)
* lots of important stuff * Update Constants.js
This commit is contained in:
126
src/client/BaseClient.js
Normal file
126
src/client/BaseClient.js
Normal file
@@ -0,0 +1,126 @@
|
||||
const EventEmitter = require('events');
|
||||
const RESTManager = require('../rest/RESTManager');
|
||||
const ClientDataResolver = require('./ClientDataResolver');
|
||||
const Util = require('../util/Util');
|
||||
const Constants = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* The base class for all clients.
|
||||
* @extends {EventEmitter}
|
||||
*/
|
||||
class BaseClient extends EventEmitter {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
|
||||
/**
|
||||
* The options the client was instantiated with
|
||||
* @type {ClientOptions}
|
||||
*/
|
||||
this.options = Util.mergeDefault(Constants.DefaultOptions, options);
|
||||
|
||||
/**
|
||||
* The REST manager of the client
|
||||
* @type {RESTManager}
|
||||
* @private
|
||||
*/
|
||||
this.rest = new RESTManager(this, options._tokenType);
|
||||
|
||||
/**
|
||||
* The data resolver of the client
|
||||
* @type {ClientDataResolver}
|
||||
* @private
|
||||
*/
|
||||
this.resolver = new ClientDataResolver(this);
|
||||
|
||||
/**
|
||||
* Timeouts set by {@link WebhookClient#setTimeout} that are still active
|
||||
* @type {Set<Timeout>}
|
||||
* @private
|
||||
*/
|
||||
this._timeouts = new Set();
|
||||
|
||||
/**
|
||||
* Intervals set by {@link WebhookClient#setInterval} that are still active
|
||||
* @type {Set<Timeout>}
|
||||
* @private
|
||||
*/
|
||||
this._intervals = new Set();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the client is in a browser environment
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get browser() {
|
||||
return typeof window !== 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* API shortcut
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
get api() {
|
||||
return this.rest.api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys all assets used by the base client.
|
||||
*/
|
||||
destroy() {
|
||||
for (const t of this._timeouts) clearTimeout(t);
|
||||
for (const i of this._intervals) clearInterval(i);
|
||||
this._timeouts.clear();
|
||||
this._intervals.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a timeout that will be automatically cancelled if the client is destroyed.
|
||||
* @param {Function} fn Function to execute
|
||||
* @param {number} delay Time to wait before executing (in milliseconds)
|
||||
* @param {...*} args Arguments for the function
|
||||
* @returns {Timeout}
|
||||
*/
|
||||
setTimeout(fn, delay, ...args) {
|
||||
const timeout = setTimeout(() => {
|
||||
fn(...args);
|
||||
this._timeouts.delete(timeout);
|
||||
}, delay);
|
||||
this._timeouts.add(timeout);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears a timeout.
|
||||
* @param {Timeout} timeout Timeout to cancel
|
||||
*/
|
||||
clearTimeout(timeout) {
|
||||
clearTimeout(timeout);
|
||||
this._timeouts.delete(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an interval that will be automatically cancelled if the client is destroyed.
|
||||
* @param {Function} fn Function to execute
|
||||
* @param {number} delay Time to wait before executing (in milliseconds)
|
||||
* @param {...*} args Arguments for the function
|
||||
* @returns {Timeout}
|
||||
*/
|
||||
setInterval(fn, delay, ...args) {
|
||||
const interval = setInterval(fn, delay, ...args);
|
||||
this._intervals.add(interval);
|
||||
return interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears an interval.
|
||||
* @param {Timeout} interval Interval to cancel
|
||||
*/
|
||||
clearInterval(interval) {
|
||||
clearInterval(interval);
|
||||
this._intervals.delete(interval);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseClient;
|
||||
@@ -1,10 +1,7 @@
|
||||
const EventEmitter = require('events');
|
||||
const Constants = require('../util/Constants');
|
||||
const BaseClient = require('./BaseClient');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Util = require('../util/Util');
|
||||
const RESTManager = require('./rest/RESTManager');
|
||||
const RESTManager = require('../rest/RESTManager');
|
||||
const ClientManager = require('./ClientManager');
|
||||
const ClientDataResolver = require('./ClientDataResolver');
|
||||
const ClientVoiceManager = require('./voice/ClientVoiceManager');
|
||||
const WebSocketManager = require('./websocket/WebSocketManager');
|
||||
const ActionsManager = require('./actions/ActionsManager');
|
||||
@@ -19,28 +16,24 @@ const UserStore = require('../stores/UserStore');
|
||||
const ChannelStore = require('../stores/ChannelStore');
|
||||
const GuildStore = require('../stores/GuildStore');
|
||||
const ClientPresenceStore = require('../stores/ClientPresenceStore');
|
||||
const Constants = require('../util/Constants');
|
||||
const { Error, TypeError, RangeError } = require('../errors');
|
||||
|
||||
/**
|
||||
* The main hub for interacting with the Discord API, and the starting point for any bot.
|
||||
* @extends {EventEmitter}
|
||||
* @extends {BaseClient}
|
||||
*/
|
||||
class Client extends EventEmitter {
|
||||
class Client extends BaseClient {
|
||||
/**
|
||||
* @param {ClientOptions} [options] Options for the client
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
super(Object.assign({ _tokenType: 'Bot' }, options));
|
||||
|
||||
// Obtain shard details from environment
|
||||
if (!options.shardId && 'SHARD_ID' in process.env) options.shardId = Number(process.env.SHARD_ID);
|
||||
if (!options.shardCount && 'SHARD_COUNT' in process.env) options.shardCount = Number(process.env.SHARD_COUNT);
|
||||
|
||||
/**
|
||||
* The options the client was instantiated with
|
||||
* @type {ClientOptions}
|
||||
*/
|
||||
this.options = Util.mergeDefault(Constants.DefaultOptions, options);
|
||||
this._validateOptions();
|
||||
|
||||
/**
|
||||
@@ -64,13 +57,6 @@ class Client extends EventEmitter {
|
||||
*/
|
||||
this.ws = new WebSocketManager(this);
|
||||
|
||||
/**
|
||||
* The data resolver of the client
|
||||
* @type {ClientDataResolver}
|
||||
* @private
|
||||
*/
|
||||
this.resolver = new ClientDataResolver(this);
|
||||
|
||||
/**
|
||||
* The action manager of the client
|
||||
* @type {ActionsManager}
|
||||
@@ -184,15 +170,6 @@ class Client extends EventEmitter {
|
||||
return this.ws.connection ? this.ws.connection.lastPingTimestamp : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* API shortcut
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
get api() {
|
||||
return this.rest.api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current status of the client's connection to Discord
|
||||
* @type {?Status}
|
||||
@@ -252,15 +229,6 @@ class Client extends EventEmitter {
|
||||
return this.readyAt ? this.readyAt.getTime() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the client is in a browser environment
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get browser() {
|
||||
return typeof window !== 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a voice broadcast.
|
||||
* @returns {VoiceBroadcast}
|
||||
@@ -298,10 +266,7 @@ class Client extends EventEmitter {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
destroy() {
|
||||
for (const t of this._timeouts) clearTimeout(t);
|
||||
for (const i of this._intervals) clearInterval(i);
|
||||
this._timeouts.clear();
|
||||
this._intervals.clear();
|
||||
super.destroy();
|
||||
return this.manager.destroy();
|
||||
}
|
||||
|
||||
@@ -423,53 +388,6 @@ class Client extends EventEmitter {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a timeout that will be automatically cancelled if the client is destroyed.
|
||||
* @param {Function} fn Function to execute
|
||||
* @param {number} delay Time to wait before executing (in milliseconds)
|
||||
* @param {...*} args Arguments for the function
|
||||
* @returns {Timeout}
|
||||
*/
|
||||
setTimeout(fn, delay, ...args) {
|
||||
const timeout = setTimeout(() => {
|
||||
fn(...args);
|
||||
this._timeouts.delete(timeout);
|
||||
}, delay);
|
||||
this._timeouts.add(timeout);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears a timeout.
|
||||
* @param {Timeout} timeout Timeout to cancel
|
||||
*/
|
||||
clearTimeout(timeout) {
|
||||
clearTimeout(timeout);
|
||||
this._timeouts.delete(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an interval that will be automatically cancelled if the client is destroyed.
|
||||
* @param {Function} fn Function to execute
|
||||
* @param {number} delay Time to wait before executing (in milliseconds)
|
||||
* @param {...*} args Arguments for the function
|
||||
* @returns {Timeout}
|
||||
*/
|
||||
setInterval(fn, delay, ...args) {
|
||||
const interval = setInterval(fn, delay, ...args);
|
||||
this._intervals.add(interval);
|
||||
return interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears an interval.
|
||||
* @param {Timeout} interval Interval to cancel
|
||||
*/
|
||||
clearInterval(interval) {
|
||||
clearInterval(interval);
|
||||
this._intervals.delete(interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a ping to {@link Client#pings}.
|
||||
* @param {number} startTime Starting time of the ping
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
const Webhook = require('../structures/Webhook');
|
||||
const RESTManager = require('./rest/RESTManager');
|
||||
const ClientDataResolver = require('./ClientDataResolver');
|
||||
const Constants = require('../util/Constants');
|
||||
const Util = require('../util/Util');
|
||||
const BaseClient = require('./BaseClient');
|
||||
|
||||
/**
|
||||
* The webhook client.
|
||||
* @extends {Webhook}
|
||||
* @extends {BaseClient}
|
||||
*/
|
||||
class WebhookClient extends Webhook {
|
||||
class WebhookClient extends BaseClient {
|
||||
/**
|
||||
* @param {Snowflake} id ID of the webhook
|
||||
* @param {string} token Token of the webhook
|
||||
@@ -19,109 +17,11 @@ class WebhookClient extends Webhook {
|
||||
* hook.sendMessage('This will send a message').catch(console.error);
|
||||
*/
|
||||
constructor(id, token, options) {
|
||||
super(null, id, token);
|
||||
|
||||
/**
|
||||
* The options the client was instantiated with
|
||||
* @type {ClientOptions}
|
||||
*/
|
||||
this.options = Util.mergeDefault(Constants.DefaultOptions, options);
|
||||
|
||||
/**
|
||||
* The REST manager of the client
|
||||
* @type {RESTManager}
|
||||
* @private
|
||||
*/
|
||||
this.rest = new RESTManager(this);
|
||||
|
||||
/**
|
||||
* The data resolver of the client
|
||||
* @type {ClientDataResolver}
|
||||
* @private
|
||||
*/
|
||||
this.resolver = new ClientDataResolver(this);
|
||||
|
||||
/**
|
||||
* Timeouts set by {@link WebhookClient#setTimeout} that are still active
|
||||
* @type {Set<Timeout>}
|
||||
* @private
|
||||
*/
|
||||
this._timeouts = new Set();
|
||||
|
||||
/**
|
||||
* Intervals set by {@link WebhookClient#setInterval} that are still active
|
||||
* @type {Set<Timeout>}
|
||||
* @private
|
||||
*/
|
||||
this._intervals = new Set();
|
||||
}
|
||||
|
||||
/**
|
||||
* API shortcut
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
get api() {
|
||||
return this.rest.api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a timeout that will be automatically cancelled if the client is destroyed.
|
||||
* @param {Function} fn Function to execute
|
||||
* @param {number} delay Time to wait before executing (in milliseconds)
|
||||
* @param {...*} args Arguments for the function
|
||||
* @returns {Timeout}
|
||||
*/
|
||||
setTimeout(fn, delay, ...args) {
|
||||
const timeout = setTimeout(() => {
|
||||
fn(...args);
|
||||
this._timeouts.delete(timeout);
|
||||
}, delay);
|
||||
this._timeouts.add(timeout);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears a timeout.
|
||||
* @param {Timeout} timeout Timeout to cancel
|
||||
*/
|
||||
clearTimeout(timeout) {
|
||||
clearTimeout(timeout);
|
||||
this._timeouts.delete(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an interval that will be automatically cancelled if the client is destroyed.
|
||||
* @param {Function} fn Function to execute
|
||||
* @param {number} delay Time to wait before executing (in milliseconds)
|
||||
* @param {...*} args Arguments for the function
|
||||
* @returns {Timeout}
|
||||
*/
|
||||
setInterval(fn, delay, ...args) {
|
||||
const interval = setInterval(fn, delay, ...args);
|
||||
this._intervals.add(interval);
|
||||
return interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears an interval.
|
||||
* @param {Timeout} interval Interval to cancel
|
||||
*/
|
||||
clearInterval(interval) {
|
||||
clearInterval(interval);
|
||||
this._intervals.delete(interval);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destroys the client.
|
||||
*/
|
||||
destroy() {
|
||||
for (const t of this._timeouts) clearTimeout(t);
|
||||
for (const i of this._intervals) clearInterval(i);
|
||||
this._timeouts.clear();
|
||||
this._intervals.clear();
|
||||
super(options);
|
||||
Webhook.call(this, null, id, token);
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(WebhookClient.prototype, Object.create(Webhook.prototype));
|
||||
|
||||
module.exports = WebhookClient;
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
const querystring = require('querystring');
|
||||
const snekfetch = require('snekfetch');
|
||||
const { Error } = require('../../errors');
|
||||
|
||||
class APIRequest {
|
||||
constructor(rest, method, path, options) {
|
||||
this.rest = rest;
|
||||
this.client = rest.client;
|
||||
this.method = method;
|
||||
this.path = path.toString();
|
||||
this.route = options.route;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
getAuth() {
|
||||
if (this.client.token && this.client.user && this.client.user.bot) {
|
||||
return `Bot ${this.client.token}`;
|
||||
} else if (this.client.token) {
|
||||
return this.client.token;
|
||||
}
|
||||
throw new Error('TOKEN_MISSING');
|
||||
}
|
||||
|
||||
gen() {
|
||||
const API = `${this.client.options.http.api}/v${this.client.options.http.version}`;
|
||||
|
||||
if (this.options.query) {
|
||||
const queryString = (querystring.stringify(this.options.query).match(/[^=&?]+=[^=&?]+/g) || []).join('&');
|
||||
this.path += `?${queryString}`;
|
||||
}
|
||||
|
||||
const request = snekfetch[this.method](`${API}${this.path}`);
|
||||
|
||||
if (this.options.auth !== false) request.set('Authorization', this.getAuth());
|
||||
if (this.options.reason) request.set('X-Audit-Log-Reason', encodeURIComponent(this.options.reason));
|
||||
if (!this.rest.client.browser) request.set('User-Agent', this.rest.userAgentManager.userAgent);
|
||||
|
||||
if (this.options.files) {
|
||||
for (const file of this.options.files) if (file && file.file) request.attach(file.name, file.file, file.name);
|
||||
if (typeof this.options.data !== 'undefined') request.attach('payload_json', JSON.stringify(this.options.data));
|
||||
} else if (typeof this.options.data !== 'undefined') {
|
||||
request.send(this.options.data);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = APIRequest;
|
||||
@@ -1,34 +0,0 @@
|
||||
const util = require('util');
|
||||
|
||||
const noop = () => {}; // eslint-disable-line no-empty-function
|
||||
const methods = ['get', 'post', 'delete', 'patch', 'put'];
|
||||
const reflectors = [
|
||||
'toString', 'valueOf', 'inspect', 'constructor',
|
||||
Symbol.toPrimitive, util.inspect.custom,
|
||||
];
|
||||
|
||||
function buildRoute(manager) {
|
||||
const route = [''];
|
||||
const handler = {
|
||||
get(target, name) {
|
||||
if (reflectors.includes(name)) return () => route.join('/');
|
||||
if (methods.includes(name)) {
|
||||
return options => manager.request(name, route.join('/'), Object.assign({
|
||||
route: route.map((r, i) => {
|
||||
if (/\d{16,19}/g.test(r)) return /channels|guilds/.test(route[i - 1]) ? r : ':id';
|
||||
return r;
|
||||
}).join('/'),
|
||||
}, options));
|
||||
}
|
||||
route.push(name);
|
||||
return new Proxy(noop, handler);
|
||||
},
|
||||
apply(target, _, args) {
|
||||
route.push(...args.filter(x => x != null)); // eslint-disable-line eqeqeq
|
||||
return new Proxy(noop, handler);
|
||||
},
|
||||
};
|
||||
return new Proxy(noop, handler);
|
||||
}
|
||||
|
||||
module.exports = buildRoute;
|
||||
@@ -1,54 +0,0 @@
|
||||
/**
|
||||
* Represents an error from the Discord API.
|
||||
* @extends Error
|
||||
*/
|
||||
class DiscordAPIError extends Error {
|
||||
constructor(path, error) {
|
||||
super();
|
||||
const flattened = this.constructor.flattenErrors(error.errors || error).join('\n');
|
||||
this.name = 'DiscordAPIError';
|
||||
this.message = error.message && flattened ? `${error.message}\n${flattened}` : error.message || flattened;
|
||||
|
||||
/**
|
||||
* The path of the request relative to the HTTP endpoint
|
||||
* @type {string}
|
||||
*/
|
||||
this.path = path;
|
||||
|
||||
/**
|
||||
* HTTP error code returned by Discord
|
||||
* @type {number}
|
||||
*/
|
||||
this.code = error.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens an errors object returned from the API into an array.
|
||||
* @param {Object} obj Discord errors object
|
||||
* @param {string} [key] Used internally to determine key names of nested fields
|
||||
* @returns {string[]}
|
||||
* @private
|
||||
*/
|
||||
static flattenErrors(obj, key = '') {
|
||||
let messages = [];
|
||||
|
||||
for (const [k, v] of Object.entries(obj)) {
|
||||
if (k === 'message') continue;
|
||||
const newKey = key ? isNaN(k) ? `${key}.${k}` : `${key}[${k}]` : k;
|
||||
|
||||
if (v._errors) {
|
||||
messages.push(`${newKey}: ${v._errors.map(e => e.message).join(' ')}`);
|
||||
} else if (v.code || v.message) {
|
||||
messages.push(`${v.code ? `${v.code}: ` : ''}${v.message}`.trim());
|
||||
} else if (typeof v === 'string') {
|
||||
messages.push(v);
|
||||
} else {
|
||||
messages = messages.concat(this.flattenErrors(v, newKey));
|
||||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DiscordAPIError;
|
||||
@@ -1,59 +0,0 @@
|
||||
const UserAgentManager = require('./UserAgentManager');
|
||||
const handlers = require('./handlers');
|
||||
const APIRequest = require('./APIRequest');
|
||||
const routeBuilder = require('./APIRouter');
|
||||
const Constants = require('../../util/Constants');
|
||||
const { Error } = require('../../errors');
|
||||
|
||||
class RESTManager {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
this.handlers = {};
|
||||
this.userAgentManager = new UserAgentManager(this);
|
||||
this.rateLimitedEndpoints = {};
|
||||
this.globallyRateLimited = false;
|
||||
}
|
||||
|
||||
get api() {
|
||||
return routeBuilder(this);
|
||||
}
|
||||
|
||||
get cdn() {
|
||||
return Constants.Endpoints.CDN(this.client.options.http.cdn);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
for (const handler of Object.values(this.handlers)) {
|
||||
if (handler.destroy) handler.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
push(handler, apiRequest) {
|
||||
return new Promise((resolve, reject) => {
|
||||
handler.push({
|
||||
request: apiRequest,
|
||||
resolve,
|
||||
reject,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getRequestHandler() {
|
||||
const method = this.client.options.apiRequestMethod;
|
||||
if (typeof method === 'function') return method;
|
||||
const handler = handlers[method];
|
||||
if (!handler) throw new Error('RATELIMIT_INVALID_METHOD');
|
||||
return handler;
|
||||
}
|
||||
|
||||
request(method, url, options = {}) {
|
||||
const apiRequest = new APIRequest(this, method, url, options);
|
||||
if (!this.handlers[apiRequest.route]) {
|
||||
this.handlers[apiRequest.route] = new handlers.RequestHandler(this, this.getRequestHandler());
|
||||
}
|
||||
|
||||
return this.push(this.handlers[apiRequest.route], apiRequest);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RESTManager;
|
||||
@@ -1,25 +0,0 @@
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class UserAgentManager {
|
||||
constructor() {
|
||||
this.build(this.constructor.DEFAULT);
|
||||
}
|
||||
|
||||
set({ url, version } = {}) {
|
||||
this.build({
|
||||
url: url || this.constructor.DFEAULT.url,
|
||||
version: version || this.constructor.DEFAULT.version,
|
||||
});
|
||||
}
|
||||
|
||||
build(ua) {
|
||||
this.userAgent = `DiscordBot (${ua.url}, ${ua.version}) Node.js/${process.version}`;
|
||||
}
|
||||
}
|
||||
|
||||
UserAgentManager.DEFAULT = {
|
||||
url: Constants.Package.homepage.split('#')[0],
|
||||
version: Constants.Package.version,
|
||||
};
|
||||
|
||||
module.exports = UserAgentManager;
|
||||
@@ -1,70 +0,0 @@
|
||||
const DiscordAPIError = require('../DiscordAPIError');
|
||||
|
||||
class RequestHandler {
|
||||
constructor(manager, handler) {
|
||||
this.manager = manager;
|
||||
this.client = this.manager.client;
|
||||
this.handle = handler.bind(this);
|
||||
this.limit = Infinity;
|
||||
this.resetTime = null;
|
||||
this.remaining = 1;
|
||||
this.timeDifference = 0;
|
||||
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
get limited() {
|
||||
return this.manager.globallyRateLimited || this.remaining <= 0;
|
||||
}
|
||||
|
||||
set globallyLimited(limited) {
|
||||
this.manager.globallyRateLimited = limited;
|
||||
}
|
||||
|
||||
push(request) {
|
||||
this.queue.push(request);
|
||||
this.handle();
|
||||
}
|
||||
|
||||
execute(item) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const finish = timeout => {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
if (timeout || this.limited) reject({ timeout, limited: this.limited });
|
||||
else resolve();
|
||||
};
|
||||
item.request.gen().end((err, res) => {
|
||||
if (res && res.headers) {
|
||||
if (res.headers['x-ratelimit-global']) this.globallyLimited = true;
|
||||
this.limit = Number(res.headers['x-ratelimit-limit']);
|
||||
this.resetTime = Number(res.headers['x-ratelimit-reset']) * 1000;
|
||||
this.remaining = Number(res.headers['x-ratelimit-remaining']);
|
||||
this.timeDifference = Date.now() - new Date(res.headers.date).getTime();
|
||||
}
|
||||
if (err) {
|
||||
if (err.status === 429) {
|
||||
this.queue.unshift(item);
|
||||
finish(Number(res.headers['retry-after']) + this.client.options.restTimeOffset);
|
||||
} else if (err.status >= 500 && err.status < 600) {
|
||||
this.queue.unshift(item);
|
||||
finish(1e3 + this.client.options.restTimeOffset);
|
||||
} else {
|
||||
item.reject(err.status >= 400 && err.status < 500 ? new DiscordAPIError(res.request.path, res.body) : err);
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
const data = res && res.body ? res.body : {};
|
||||
item.resolve(data);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.globallyLimited = false;
|
||||
this.remaining = 1;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RequestHandler;
|
||||
@@ -1,13 +0,0 @@
|
||||
module.exports = function burst() {
|
||||
if (this.limited || this.queue.length === 0) return;
|
||||
this.execute(this.queue.shift())
|
||||
.then(this.handle.bind(this))
|
||||
.catch(({ timeout }) => {
|
||||
this.client.setTimeout(() => {
|
||||
this.reset();
|
||||
this.handle();
|
||||
}, timeout || (this.resetTime - Date.now() + this.timeDifference + this.client.options.restTimeOffset));
|
||||
});
|
||||
this.remaining--;
|
||||
this.handle();
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
sequential: require('./sequential'),
|
||||
burst: require('./burst'),
|
||||
RequestHandler: require('./RequestHandler'),
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
module.exports = function sequential() {
|
||||
if (this.busy || this.limited || this.queue.length === 0) return;
|
||||
this.busy = true;
|
||||
this.execute(this.queue.shift())
|
||||
.then(() => {
|
||||
this.busy = false;
|
||||
this.handle();
|
||||
})
|
||||
.catch(({ timeout }) => {
|
||||
this.client.setTimeout(() => {
|
||||
this.reset();
|
||||
this.busy = false;
|
||||
this.handle();
|
||||
}, timeout || (this.resetTime - Date.now() + this.timeDifference + this.client.options.restTimeOffset));
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user