Skip to content
This repository has been archived by the owner on Nov 13, 2022. It is now read-only.

switch http libraries and add support for Client-Name header #91

Merged
merged 4 commits into from
Apr 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ node_modules
dist
.idea
typings
test

# files
package-lock.json
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"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",
Expand All @@ -48,7 +47,7 @@
},
"dependencies": {
"@discordjs/collection": "^0.1.6",
"axios": "^0.21.0",
"petitio": "^1.1.0",
"ws": "^7.3.1"
},
"eslintConfig": {
Expand Down
73 changes: 29 additions & 44 deletions src/structures/Manager.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -61,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 {
Expand Down Expand Up @@ -251,6 +256,7 @@ export class Manager extends EventEmitter {
nodes: [{ identifier: "default", host: "localhost" }],
shards: 1,
autoPlay: true,
clientName: "erela.js",
...options,
};

Expand Down Expand Up @@ -316,39 +322,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<LavalinkResult>(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<LavalinkResult>(`/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
Expand All @@ -368,39 +365,26 @@ 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<TrackData[]>(url, tracks, {
headers: { Authorization: node.options.password },
}).catch((err) => {
return reject(err);
});

node.calls++;
const res = await node.makeRequest<TrackData[]>(`/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);
});
}

/**
* Decodes the base64 encoded track and returns a TrackData.
* @param track
*/
public decodeTrack(track: string): Promise<TrackData> {
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<TrackData> {
const res = await this.decodeTracks([ track ]);
return res[0];
}

/**
Expand Down Expand Up @@ -505,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. */
Expand All @@ -513,7 +499,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
Expand Down
35 changes: 35 additions & 0 deletions src/structures/Node.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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.");

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -141,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(
Expand Down Expand Up @@ -173,6 +183,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<T>(endpoint: string, modify?: ModifyRequest): Promise<T> {
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
Expand Down Expand Up @@ -375,6 +405,9 @@ export class Node {
}
}

/** Modifies any outgoing REST requests. */
export type ModifyRequest = (request: PetitioRequest) => any | Promise<any>;

export interface NodeOptions {
/** The host for the node. */
host: string;
Expand All @@ -390,6 +423,8 @@ export interface NodeOptions {
retryAmount?: number;
/** The retryDelay for the node. */
retryDelay?: number;
/** The timeout used for api calls */
requestTimeout?: number;
}

export interface NodeStats {
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"declaration": true,
"outDir": "./dist",
"rootDir": "./src",
"skipLibCheck": true,

/* Strict Type-Checking Options */
// "strict": true,
Expand Down