diff --git a/index.d.ts b/index.d.ts index d101ff368..19480f229 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,7 +1,7 @@ import { EventEmitter } from "events"; import { Duplex, Readable as ReadableStream, Stream } from "stream"; import { Agent as HTTPSAgent } from "https"; -import { IncomingMessage, ClientRequest } from "http"; +import { IncomingMessage, ClientRequest, IncomingHttpHeaders } from "http"; import OpusScript = require("opusscript"); // Thanks TypeScript import { URL } from "url"; import { Socket as DgramSocket } from "dgram"; @@ -2577,6 +2577,7 @@ declare namespace Eris { export class DiscordHTTPError extends Error { code: number; + headers: IncomingHttpHeaders; name: "DiscordHTTPError"; req: ClientRequest; res: IncomingMessage; @@ -2587,6 +2588,7 @@ declare namespace Eris { export class DiscordRESTError extends Error { code: number; + headers: IncomingHttpHeaders; name: string; req: ClientRequest; res: IncomingMessage; diff --git a/lib/errors/DiscordHTTPError.js b/lib/errors/DiscordHTTPError.js index 59e1ea9f4..3088d3978 100644 --- a/lib/errors/DiscordHTTPError.js +++ b/lib/errors/DiscordHTTPError.js @@ -38,6 +38,10 @@ class DiscordHTTPError extends Error { } } + get headers() { + return this.response.headers; + } + get name() { return this.constructor.name; } diff --git a/lib/errors/DiscordRESTError.js b/lib/errors/DiscordRESTError.js index 251675886..51145fc6c 100644 --- a/lib/errors/DiscordRESTError.js +++ b/lib/errors/DiscordRESTError.js @@ -42,6 +42,10 @@ class DiscordRESTError extends Error { } } + get headers() { + return this.response.headers; + } + get name() { return `${this.constructor.name} [${this.code}]`; } diff --git a/lib/rest/RequestHandler.js b/lib/rest/RequestHandler.js index ba5ebc76c..c6c6d2209 100644 --- a/lib/rest/RequestHandler.js +++ b/lib/rest/RequestHandler.js @@ -273,7 +273,7 @@ class RequestHandler { this.ratelimits[route].remaining = resp.headers["x-ratelimit-remaining"] === undefined ? 1 : +resp.headers["x-ratelimit-remaining"] || 0; - const retryAfter = parseInt(resp.headers["x-ratelimit-reset-after"] || resp.headers["retry-after"]) * 1000; + const retryAfter = Number(resp.headers["x-ratelimit-reset-after"] || resp.headers["retry-after"]) * 1000; if(retryAfter >= 0) { if(resp.headers["x-ratelimit-global"]) { this.globalBlock = true; @@ -299,12 +299,21 @@ class RequestHandler { if(resp.statusCode >= 300) { if(resp.statusCode === 429) { const content = typeof body === "object" ? `${body.content} ` : ""; - this._client.emit("debug", `${resp.headers["x-ratelimit-global"] ? "Global" : "Unexpected"} 429 (╯°□°)╯︵ ┻━┻: ${response}\n${content} ${now} ${route} ${resp.statusCode}: ${latency}ms (${this.latencyRef.latency}ms avg) | ${this.ratelimits[route].remaining}/${this.ratelimits[route].limit} left | Reset ${this.ratelimits[route].reset} (${this.ratelimits[route].reset - now}ms left)`); - if(retryAfter) { + let delay = retryAfter; + if(resp.headers["x-ratelimit-scope"] === "shared") { + try { + delay = JSON.parse(response).retry_after * 1000; + } catch(err) { + reject(err); + return; + } + } + this._client.emit("debug", `${resp.headers["x-ratelimit-global"] ? "Global" : "Unexpected"} 429 (╯°□°)╯︵ ┻━┻: ${response}\n${content} ${now} ${route} ${resp.statusCode}: ${latency}ms (${this.latencyRef.latency}ms avg) | ${this.ratelimits[route].remaining}/${this.ratelimits[route].limit} left | Reset ${delay} (${this.ratelimits[route].reset - now}ms left) | Scope ${resp.headers["x-ratelimit-scope"]}`); + if(delay) { setTimeout(() => { cb(); this.request(method, url, auth, body, file, route, true).then(resolve).catch(reject); - }, retryAfter); + }, delay); return; } else { cb();