From 2d0fe42e09eb5e7f80861b2a430e00389fd25762 Mon Sep 17 00:00:00 2001 From: MeLike2D Date: Sat, 27 Mar 2021 04:30:43 -0700 Subject: [PATCH 1/4] axios -> petitio and add Node#makeRequest --- package.json | 4 +-- src/structures/Manager.ts | 64 ++++++++++++--------------------------- src/structures/Node.ts | 34 +++++++++++++++++++++ 3 files changed, 56 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index c27fb5a..6be8509 100644 --- a/package.json +++ b/package.json @@ -34,11 +34,11 @@ "repository": "MenuDocs/erela.js", "bugs": "https://github.com/MenuDocs/erela.js", "devDependencies": { - "@types/axios": "^0.14.0", "@types/node": "^14.6.0", "@types/ws": "^6.0.4", "@typescript-eslint/eslint-plugin": "^3.10.1", "@typescript-eslint/parser": "^3.10.1", + "discord.js": "^12.5.1", "dts-bundle": "^0.7.3", "eslint": "^7.7.0", "tslint": "^5.20.1", @@ -48,7 +48,7 @@ }, "dependencies": { "@discordjs/collection": "^0.1.6", - "axios": "^0.21.0", + "petitio": "^1.1.0", "ws": "^7.3.1" }, "eslintConfig": { diff --git a/src/structures/Manager.ts b/src/structures/Manager.ts index e83678d..7a38307 100644 --- a/src/structures/Manager.ts +++ b/src/structures/Manager.ts @@ -1,6 +1,5 @@ /* eslint-disable no-async-promise-executor */ import Collection from "@discordjs/collection"; -import Axios from "axios"; import { EventEmitter } from "events"; import { Node, NodeOptions } from "./Node"; import { Player, PlayerOptions, Track, UnresolvedTrack } from "./Player"; @@ -316,39 +315,30 @@ export class Manager extends EventEmitter { search = `${source}search:${search}`; } - const url = `http${node.options.secure ? "s" : ""}://${ - node.options.host - }:${node.options.port}/loadtracks`; - - const res = await Axios.get(url, { - headers: { Authorization: node.options.password }, - params: { identifier: search }, - timeout: 10000, - timeoutErrorMessage: `Node ${node.options.identifier} search timed out.`, - }).catch((err) => { - return reject(err); - }); - - node.calls++; + const res = await node.makeRequest(`/loadtracks?identifier=${encodeURIComponent(search)}`, r => { + if (node.options.requestTimeout) { + r.timeout(node.options.requestTimeout) + } + }).catch(err => reject(err)); - if (!res || !res.data) { + if (!res) { return reject(new Error("Query not found.")); } const result: SearchResult = { - loadType: res.data.loadType, - exception: res.data.exception ?? null, - tracks: res.data.tracks.map((track: TrackData) => + loadType: res.loadType, + exception: res.exception ?? null, + tracks: res.tracks.map((track: TrackData) => TrackUtils.build(track, requester) ), }; if (result.loadType === "PLAYLIST_LOADED") { result.playlist = { - name: res.data.playlistInfo.name, - selectedTrack: res.data.playlistInfo.selectedTrack === -1 ? null : + name: res.playlistInfo.name, + selectedTrack: res.playlistInfo.selectedTrack === -1 ? null : TrackUtils.build( - res.data.tracks[res.data.playlistInfo.selectedTrack], + res.tracks[res.playlistInfo.selectedTrack], requester ), duration: result.tracks @@ -368,23 +358,16 @@ export class Manager extends EventEmitter { return new Promise(async (resolve, reject) => { const node = this.nodes.first(); if (!node) throw new Error("No available nodes."); - const url = `http${node.options.secure ? "s" : ""}://${ - node.options.host - }:${node.options.port}/decodetracks`; - - const res = await Axios.post(url, tracks, { - headers: { Authorization: node.options.password }, - }).catch((err) => { - return reject(err); - }); - node.calls++; + const res = await node.makeRequest(`/decodetracks`, r => r + .body(tracks, "json")) + .catch(err => reject(err)); - if (!res || !res.data) { + if (!res) { return reject(new Error("No data returned from query.")); } - return resolve(res.data); + return resolve(res); }); } @@ -392,15 +375,9 @@ export class Manager extends EventEmitter { * Decodes the base64 encoded track and returns a TrackData. * @param track */ - public decodeTrack(track: string): Promise { - return new Promise(async (resolve, reject) => { - try { - const res = await this.decodeTracks([track]); - return resolve(res[0]); - } catch (e) { - return reject(e); - } - }); + public async decodeTrack(track: string): Promise { + const res = await this.decodeTracks([ track ]); + return res[0]; } /** @@ -513,7 +490,6 @@ export interface ManagerOptions { autoPlay?: boolean; /** An array of track properties to keep. `track` will always be present. */ trackPartial?: string[]; - /** * Function to send data to the websocket. * @param id diff --git a/src/structures/Node.ts b/src/structures/Node.ts index bc89226..19b8159 100644 --- a/src/structures/Node.ts +++ b/src/structures/Node.ts @@ -1,5 +1,6 @@ /* eslint-disable no-case-declarations */ import WebSocket from "ws"; +import fetch from "petitio"; import { Manager } from "./Manager"; import { Player, Track, UnresolvedTrack } from "./Player"; import { @@ -13,6 +14,8 @@ import { WebSocketClosedEvent, } from "./Utils"; +import type { PetitioRequest } from "petitio/dist/lib/PetitioRequest"; + function check(options: NodeOptions) { if (!options) throw new TypeError("NodeOptions must not be empty."); @@ -58,6 +61,12 @@ function check(options: NodeOptions) { typeof options.retryDelay !== "number" ) throw new TypeError('Node option "retryDelay" must be a number.'); + + if ( + typeof options.requestTimeout !== "undefined" && + typeof options.requestTimeout !== "number" + ) + throw new TypeError('Node option "requestTimeout" must be a number.'); } export class Node { @@ -173,6 +182,26 @@ export class Node { this.manager.destroyNode(this.options.identifier); } + /** + * Makes an API call to the Node + * @param endpoint The endpoint that we will make the call to + * @param modify Used to modify the request before being sent + * @returns The returned data + */ + public async makeRequest(endpoint: string, modify?: ModifyRequest): Promise { + endpoint = endpoint.replace(/^\//gm, ""); + + const request = fetch(`http${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}/${endpoint}`) + .header("Authorization", this.options.password); + + if (modify) { + await modify(request); + } + + this.calls++; + return await request.json(); + } + /** * Sends data to the Node. * @param data @@ -375,6 +404,9 @@ export class Node { } } +/** Modifies any outgoing REST requests. */ +export type ModifyRequest = (request: PetitioRequest) => any | Promise; + export interface NodeOptions { /** The host for the node. */ host: string; @@ -390,6 +422,8 @@ export interface NodeOptions { retryAmount?: number; /** The retryDelay for the node. */ retryDelay?: number; + /** The timeout used for api calls */ + requestTimeout?: number; } export interface NodeStats { From 74c2ef63e09b54c3bc65ee85345c10c72045a45c Mon Sep 17 00:00:00 2001 From: MeLike2D Date: Sat, 27 Mar 2021 04:36:55 -0700 Subject: [PATCH 2/4] add support for the "Client-Name" header --- src/structures/Manager.ts | 9 +++++++++ src/structures/Node.ts | 1 + 2 files changed, 10 insertions(+) diff --git a/src/structures/Manager.ts b/src/structures/Manager.ts index 7a38307..c03953b 100644 --- a/src/structures/Manager.ts +++ b/src/structures/Manager.ts @@ -60,6 +60,12 @@ function check(options: ManagerOptions) { !Array.isArray(options.trackPartial) ) throw new TypeError('Manager option "trackPartial" must be a string array.'); + + if ( + typeof options.clientName !== "undefined" && + typeof options.clientName !== "string" + ) + throw new TypeError('Manager option "clientName" must be a string.'); } export interface Manager { @@ -250,6 +256,7 @@ export class Manager extends EventEmitter { nodes: [{ identifier: "default", host: "localhost" }], shards: 1, autoPlay: true, + clientName: "erela.js", ...options, }; @@ -482,6 +489,8 @@ export interface ManagerOptions { nodes?: NodeOptions[]; /** The client ID to use. */ clientId?: string; + /** Value to use for the `Client-Name` header. */ + clientName?: string; /** The shard count. */ shards?: number; /** A array of plugins to use. */ diff --git a/src/structures/Node.ts b/src/structures/Node.ts index 19b8159..d2be282 100644 --- a/src/structures/Node.ts +++ b/src/structures/Node.ts @@ -150,6 +150,7 @@ export class Node { Authorization: this.options.password, "Num-Shards": String(this.manager.options.shards), "User-Id": this.manager.options.clientId, + "Client-Name": this.manager.options.clientName, }; this.socket = new WebSocket( From dd449214428d729608b2d69cd9fede4e8e829bf0 Mon Sep 17 00:00:00 2001 From: MeLike2D Date: Sat, 27 Mar 2021 04:37:04 -0700 Subject: [PATCH 3/4] other things --- .gitignore | 1 + tsconfig.json | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 8fa17e5..7f0326f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules dist .idea typings +test # files package-lock.json diff --git a/tsconfig.json b/tsconfig.json index e374fc8..ac3e5a1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "declaration": true, "outDir": "./dist", "rootDir": "./src", + "skipLibCheck": true, /* Strict Type-Checking Options */ // "strict": true, From 9afaa94fe1fbb521b36cca8cb65395ee2fee36dd Mon Sep 17 00:00:00 2001 From: MeLike2D Date: Sat, 27 Mar 2021 04:43:20 -0700 Subject: [PATCH 4/4] remove discord.js from the dev dependencies --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 6be8509..2baca97 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "@types/ws": "^6.0.4", "@typescript-eslint/eslint-plugin": "^3.10.1", "@typescript-eslint/parser": "^3.10.1", - "discord.js": "^12.5.1", "dts-bundle": "^0.7.3", "eslint": "^7.7.0", "tslint": "^5.20.1",