diff --git a/package-lock.json b/package-lock.json index da13204..b9a7c97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "MIT", "dependencies": { "@sapphire/async-queue": "^1.1.4", - "axios": "^0.21.1", "ms": "^2.1.3", + "node-fetch": "^2.6.7", "update-notifier": "^5.1.0" }, "devDependencies": { @@ -163,14 +163,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/axios": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.2.tgz", - "integrity": "sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg==", - "dependencies": { - "follow-redirects": "^1.14.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -594,25 +586,6 @@ "flat": "cli.js" } }, - "node_modules/follow-redirects": { - "version": "1.14.8", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", - "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1143,6 +1116,25 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1517,6 +1509,11 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -1598,6 +1595,20 @@ "node": ">=4" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1842,14 +1853,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "axios": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.2.tgz", - "integrity": "sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg==", - "requires": { - "follow-redirects": "^1.14.0" - } - }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2164,11 +2167,6 @@ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, - "follow-redirects": { - "version": "1.14.8", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", - "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==" - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2548,6 +2546,14 @@ "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "dev": true }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -2815,6 +2821,11 @@ "is-number": "^7.0.0" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -2871,6 +2882,20 @@ "prepend-http": "^2.0.0" } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index f55a582..6fe1a20 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,8 @@ }, "dependencies": { "@sapphire/async-queue": "^1.1.4", - "axios": "^0.21.1", "ms": "^2.1.3", + "node-fetch": "^2.6.7", "update-notifier": "^5.1.0" }, "engines": { diff --git a/src/RequestHandler.js b/src/RequestHandler.js index 9f76a9e..dd549c0 100644 --- a/src/RequestHandler.js +++ b/src/RequestHandler.js @@ -1,4 +1,4 @@ -const axios = require("axios") +const fetch = require('node-fetch'); const { RatelimitError, APIError } = require("./errors") const { AsyncQueue } = require("@sapphire/async-queue") const queue = new AsyncQueue() @@ -7,40 +7,66 @@ class RequestHandler { this._client = client } - async request(endpoint, query = {}, method, body, _attempts = 0) { + async request(endpoint, query = {}, method = "GET", body, _attempts = 0) { return new Promise(async (resolve, reject) => { await queue.wait() + // The following function (encode) is stolen from Axios ;p + function encode(str) { + var charMap = { + '!': '%21', + "'": '%27', + '(': '%28', + ')': '%29', + '~': '%7E', + '%20': '+', + '%00': '\x00' + }; + return encodeURIComponent(str).replace(/[!'\(\)~]|%20|%00/g, function replacer(match) { + return charMap[match]; + }); + } + function toQueryString (data) { + if (Object.entries(data).length == 0) return ""; + let result = ""; + for (const [key, value] of Object.entries(data)) { + result.length == 0 ? result = "?" : result += "&"; + result += `${encode(key)}=${encode(value)}`; + } + return result; + } + const url = `${this._client.baseURL}${this._client.version}${endpoint}${toQueryString(query)}`; const options = { - validateStatus: null, + method, headers: { - Authorization: this._client.token, - "Content-Type": "application/json", + "Authorization": this._client.token, + "Content-Type": "application/json" }, - baseURL: this._client.baseURL + this._client.version, - url: endpoint, - method: method, - data: body, - params: query, - timeout: 15000, - } - + body: (body == null || Object.entries(body).length == 0) ? undefined : JSON.stringify(body), + timeout: 15000 + }; if (this._client.debug) console.debug(`Sending request to ${options.url}\nMethod:\n ${options.method}\nParams:\n ${query}`) try { - axios.request(options).then((res) => { - // Increase the number of attempts - ++_attempts - - if (res.status >= 200 && res.status < 300) { - resolve(res.data) - if (this._client.debug) console.debug("Success: \n", res.data) - } else if (res.status === 429) { - if (this._client.debug) console.debug("Ratelimited: \n", res) - reject(new RatelimitError(res)) - } else { - if (this._client.debug) console.debug("API Error: \n", res) - reject(new APIError(res)) - } - }) + const res = await fetch(url, options); + if (res.status >= 200 && res.status < 300) { + const json = await res.json(); + resolve(json); + if (this._client.debug) console.debug("Success: \n", json); + } else if (res.status === 429) { + const json = await res.json(); + if (this._client.debug) console.debug("Ratelimited: \n", res, json); + reject(new RatelimitError(res, json)); + } else { + try { + const json = await res.json(); + if (this._client.debug) console.debug("API Error: \n", res, json) + reject(new APIError(res, json)); + } catch (err) { + if (this._client.debug) console.debug("API Error: \n", res) + reject(new APIError(res, null)) + } + } + } catch (err) { + reject(err); } finally { queue.shift() } diff --git a/src/errors/APIError.js b/src/errors/APIError.js index 147d4ce..f195581 100644 --- a/src/errors/APIError.js +++ b/src/errors/APIError.js @@ -6,11 +6,11 @@ */ class APIError extends Error { - constructor(response) { + constructor(response, data) { super(); this.name = this.constructor.name; this.status = response.status; - this.message = response.data ? response.data.error : undefined + this.message = data ? data.error : undefined } } diff --git a/src/errors/RatelimitError.js b/src/errors/RatelimitError.js index cf74aee..a1201a6 100644 --- a/src/errors/RatelimitError.js +++ b/src/errors/RatelimitError.js @@ -9,11 +9,11 @@ const ms = require("ms") */ class RatelimitError extends Error { - constructor(response) { + constructor(response, data) { super(); this.name = this.constructor.name; this.status = response.status; - this.remaining = response.data["Ratelimit-Remaining"] + this.remaining = data["Ratelimit-Remaining"] this.message = 'You are currently ratelimited! Try again in ' + ms(this.remaining) } }