mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
Experimental active ratelimits (adapted from Eris)
This commit is contained in:
@@ -7,6 +7,7 @@ import qs from "querystring";
|
||||
|
||||
import {Endpoints, PacketType, Permissions} from "../Constants";
|
||||
|
||||
import Bucket from "../Util/Bucket";
|
||||
import Cache from "../Util/Cache";
|
||||
import Resolver from "./Resolver/Resolver";
|
||||
|
||||
@@ -52,64 +53,81 @@ export default class InternalClient {
|
||||
}
|
||||
|
||||
apiRequest(method, url, useAuth, data, file) {
|
||||
var endpoint = url.replace(/\/[0-9]+/g, "/:id");
|
||||
if (this.retryAfters[endpoint]) {
|
||||
if (this.retryAfters[endpoint] < Date.now()) {
|
||||
delete this.retryAfters[endpoint];
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
this.apiRequest.apply(this, arguments).then(resolve).catch(reject);
|
||||
}, this.retryAfters[endpoint] - Date.now());
|
||||
});
|
||||
var resolve, reject;
|
||||
var promise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
})
|
||||
var buckets = [];
|
||||
var match = url.match(/\/channels\/([0-9]+)\/messages(\/[0-9]+)?$/);
|
||||
if(match) {
|
||||
if(this.user.bot) {
|
||||
if(method === "post" || method === "patch") {
|
||||
if(this.private_channels.get("id", match[1])) {
|
||||
buckets = ["bot:msg:dm", "bot:msg:global"];
|
||||
} else if((match[1] = this.channels.get("id", match[1]))) {
|
||||
buckets = ["bot:msg:guild:" + match[1].id, "bot:msg:global"];
|
||||
}
|
||||
} else if(method === "del" && (match[1] = this.channels.get("id", match[1]))) {
|
||||
buckets = ["dmsg:" + match[1].id];
|
||||
}
|
||||
} else {
|
||||
buckets = ["msg"];
|
||||
}
|
||||
} else if(method === "patch") {
|
||||
if(url === "/users/@me" && this.user && data.username && data.username !== this.user.username) {
|
||||
buckets = ["username"];
|
||||
} else if((match = url.match(/\/guilds\/([0-9]+)\/members\/[0-9]+$/))) {
|
||||
buckets = ["guild_member:" + match[1]];
|
||||
} else if((match = url.match(/\/guilds\/([0-9]+)\/members\/@me\/nick$/))) {
|
||||
buckets = ["guild_member_nick:" + match[1]];
|
||||
}
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
var actualCall = function() {
|
||||
var ret = request[method](url);
|
||||
if (useAuth) {
|
||||
ret.set("authorization", self.token);
|
||||
}
|
||||
}
|
||||
let ret = request[method](url);
|
||||
if (useAuth) {
|
||||
ret.set("authorization", this.token);
|
||||
}
|
||||
if (file) {
|
||||
ret.attach("file", file.file, file.name);
|
||||
if (data) {
|
||||
for (var i in data) {
|
||||
if (data[i] !== undefined) {
|
||||
ret.field(i, data[i]);
|
||||
if (file) {
|
||||
ret.attach("file", file.file, file.name);
|
||||
if (data) {
|
||||
for (var i in data) {
|
||||
if (data[i] !== undefined) {
|
||||
ret.field(i, data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (data) {
|
||||
ret.send(data);
|
||||
}
|
||||
} else if (data) {
|
||||
ret.send(data);
|
||||
}
|
||||
ret.set('User-Agent', this.userAgentInfo.full);
|
||||
return new Promise((resolve, reject) => {
|
||||
ret.set('User-Agent', self.userAgentInfo.full);
|
||||
ret.end((error, data) => {
|
||||
if (error) {
|
||||
if (!this.client.options.rateLimitAsError &&
|
||||
error.response &&
|
||||
error.response.error &&
|
||||
error.response.error.status
|
||||
&& error.response.error.status === 429
|
||||
) {
|
||||
|
||||
if (data.headers["retry-after"] || data.headers["Retry-After"]) {
|
||||
var toWait = data.headers["retry-after"] || data.headers["Retry-After"];
|
||||
if (!this.retryAfters[endpoint])
|
||||
this.retryAfters[endpoint] = Date.now() + parseInt(toWait);
|
||||
setTimeout(() => {
|
||||
this.apiRequest.apply(this, arguments).then(resolve).catch(reject);
|
||||
}, this.retryAfters[endpoint] - Date.now());
|
||||
} else {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
} else {
|
||||
return reject(error);
|
||||
if(data.status === 429) {
|
||||
self.emit("debug", "Encountered 429 at " + url)
|
||||
}
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(data.body);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
var waitFor = 1;
|
||||
var i = 0;
|
||||
var done = function() {
|
||||
if(++i === waitFor) {
|
||||
actualCall();
|
||||
}
|
||||
};
|
||||
for(var bucket of buckets) {
|
||||
++waitFor;
|
||||
this.buckets[bucket].queue(done);
|
||||
}
|
||||
done();
|
||||
return promise;
|
||||
}
|
||||
|
||||
setup(discordClient) {
|
||||
@@ -148,7 +166,12 @@ export default class InternalClient {
|
||||
this.resolver = new Resolver(this);
|
||||
this.readyTime = null;
|
||||
this.messageAwaits = {};
|
||||
this.retryAfters = {};
|
||||
this.buckets = {
|
||||
"bot:msg:dm": new Bucket(5, 5000),
|
||||
"bot:msg:global": new Bucket(50, 10000),
|
||||
"msg": new Bucket(10, 10000),
|
||||
"username": new Bucket(2, 3600000)
|
||||
};
|
||||
|
||||
if (!this.tokenCacher) {
|
||||
this.tokenCacher = new TokenCacher(this.client);
|
||||
@@ -1807,6 +1830,11 @@ export default class InternalClient {
|
||||
} else {
|
||||
client.emit("debug", "server was unavailable, could not update");
|
||||
}
|
||||
self.buckets["bot:msg:guild:" + packet.d.id] =
|
||||
self.buckets["dmsg:" + packet.d.id] =
|
||||
self.buckets["bdmsg:" + packet.d.id] =
|
||||
self.buckets["guild_member:" + packet.d.id] =
|
||||
self.buckets["guild_member_nick:" + packet.d.id] = undefined;
|
||||
} else {
|
||||
client.emit("warn", "server was deleted but it was not in the cache");
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* @typedef {(string)} region
|
||||
*/
|
||||
|
||||
import Bucket from "../Util/Bucket";
|
||||
import Equality from "../Util/Equality";
|
||||
import {Endpoints} from "../Constants";
|
||||
import Cache from "../Util/Cache";
|
||||
@@ -24,13 +25,20 @@ export default class Server extends Equality {
|
||||
|
||||
super();
|
||||
|
||||
var self = this;
|
||||
this.client = client;
|
||||
this.id = data.id;
|
||||
|
||||
if(data.owner_id) { // new server data
|
||||
client.internal.buckets["bot:msg:guild:" + this.id] = new Bucket(5, 5000);
|
||||
client.internal.buckets["dmsg:" + this.id] = new Bucket(5, 1000);
|
||||
client.internal.buckets["bdmsg:" + this.id] = new Bucket(1, 1000);
|
||||
client.internal.buckets["guild_member:" + this.id] = new Bucket(10, 10000);
|
||||
client.internal.buckets["guild_member_nick:" + this.id] = new Bucket(1, 1000);
|
||||
}
|
||||
|
||||
this.region = data.region;
|
||||
this.ownerID = data.owner_id || data.ownerID;
|
||||
this.name = data.name;
|
||||
this.id = data.id;
|
||||
this.members = new Cache();
|
||||
this.channels = new Cache();
|
||||
this.roles = new Cache();
|
||||
@@ -41,8 +49,6 @@ export default class Server extends Equality {
|
||||
this.memberCount = data.member_count || data.memberCount;
|
||||
this.large = data.large || this.memberCount > 250;
|
||||
|
||||
var self = this;
|
||||
|
||||
if (data.roles instanceof Cache) {
|
||||
data.roles.forEach((role) => this.roles.add(role));
|
||||
} else {
|
||||
|
||||
40
src/Util/Bucket.js
Normal file
40
src/Util/Bucket.js
Normal file
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
|
||||
export default class Bucket { // Adapted from Eris
|
||||
constructor(tokenLimit, interval) {
|
||||
this.tokenLimit = tokenLimit;
|
||||
this.interval = interval;
|
||||
this.extraTime = 500;
|
||||
this.lastReset = this.tokens = this.lastSend = 0;
|
||||
this._queue = [];
|
||||
}
|
||||
|
||||
queue(func) {
|
||||
this._queue.push(func);
|
||||
this.check();
|
||||
}
|
||||
|
||||
check() {
|
||||
if(this.timeout || this._queue.length === 0) {
|
||||
return;
|
||||
}
|
||||
if(this.lastReset + this.interval + this.extraTime < Date.now()) {
|
||||
this.lastReset = Date.now();
|
||||
this.tokens = Math.max(0, this.tokens - this.tokenLimit);
|
||||
}
|
||||
|
||||
var val;
|
||||
while(this._queue.length > 0 && this.tokens < this.tokenLimit) {
|
||||
this.tokens++;
|
||||
this._queue.shift()();
|
||||
this.lastSend = Date.now();
|
||||
}
|
||||
|
||||
if(this._queue.length > 0 && !this.timeout) {
|
||||
this.timeout = setTimeout(() => {
|
||||
this.timeout = null;
|
||||
this.check();
|
||||
}, this.tokens < this.tokenLimit ? 1 : Math.max(0, this.lastReset + this.interval + this.extraTime - Date.now()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,8 @@
|
||||
|
||||
Instead, use objectThatExtendsEquality.equals()
|
||||
*/
|
||||
export default class Equality{
|
||||
export default class Equality {
|
||||
constructor(){
|
||||
|
||||
}
|
||||
|
||||
get eqDiscriminator(){
|
||||
|
||||
Reference in New Issue
Block a user