UNPKG

lavacord

Version:

A simple by design lavalink wrapper made for any discord library.

260 lines 8.69 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LavalinkNode = void 0; const ws_1 = __importDefault(require("ws")); const Rest_1 = require("./Rest"); // eslint-disable-next-line @typescript-eslint/no-var-requires const { version } = require("../../package.json"); /** * The class for handling everything to do with connecting to Lavalink */ class LavalinkNode { manager; /** * The id of the LavalinkNode so Nodes are better organized */ id; /** * The host of the LavalinkNode, this could be a ip or domain. */ host = "localhost"; /** * The port of the LavalinkNode */ port = 2333; /** * The interval that the node will try to reconnect to lavalink at in milliseconds */ reconnectInterval = 10000; /** * The password of the lavalink node */ password = "youshallnotpass"; /** * The WebSocket instance for this LavalinkNode */ ws = null; /** * The statistics of the LavalinkNode */ stats; /** * If the LavalinkNode should allow resuming */ resuming = false; /** * The resume timeout */ resumeTimeout = 120; /** * Extra info attached to your node, not required and is not sent to lavalink, purely for you. */ state; /** * The major version of the LavaLink node as indicated by /version */ version; /** * The session ID sent by LavaLink on connect. Used for some REST routes */ sessionId; /** * The reconnect timeout * @private */ _reconnect; _sessionUpdated = false; /** * The base of the connection to lavalink * @param manager The manager that created the LavalinkNode * @param options The options of the LavalinkNode {@link LavalinkNodeOptions} */ constructor(manager, options) { this.manager = manager; this.id = options.id; if (options.host) Object.defineProperty(this, "host", { value: options.host }); if (options.port) Object.defineProperty(this, "port", { value: options.port }); if (options.password) Object.defineProperty(this, "password", { value: options.password }); if (options.reconnectInterval) this.reconnectInterval = options.reconnectInterval; if (options.sessionId) this.sessionId = options.sessionId; if (options.resuming !== undefined) this.resuming = options.resuming; if (options.resumeTimeout) this.resumeTimeout = options.resumeTimeout; if (options.state) this.state = options.state; this.stats = { players: 0, playingPlayers: 0, uptime: 0, memory: { free: 0, used: 0, allocated: 0, reservable: 0 }, cpu: { cores: 0, systemLoad: 0, lavalinkLoad: 0 }, frameStats: { sent: 0, nulled: 0, deficit: 0 } }; } /** * Connects the node to Lavalink */ async connect() { return new Promise((resolve, reject) => { if (this.connected) this.ws.close(); return Rest_1.Rest.version(this) .then(nodeVersion => { // defaults to v4 which is reasonable. Actually supports snapshot versions now const major = typeof nodeVersion === "string" && nodeVersion.indexOf(".") !== -1 ? nodeVersion.split(".")[0] : "4"; if (!major || isNaN(Number(major))) return reject(new Error("Node didn't respond to /version with a major.minor.patch version string")); const numMajor = Number(major); this.version = numMajor; const headers = { Authorization: this.password, "User-Id": this.manager.user, "Client-Name": `Lavacord/${version}` }; if (this.sessionId && this.resuming) headers["Session-Id"] = this.sessionId; const ws = new ws_1.default(`ws://${this.host}:${this.port}/v${numMajor}/websocket`, { headers }); const onEvent = (event) => { ws.removeAllListeners(); reject(event instanceof Error ? event : new Error("Premature close")); }; const onOpen = () => { this.ws = ws; this.onOpen(); ws.removeListener("open", onOpen) .removeListener("error", onEvent) .removeListener("close", onEvent) .on("error", error => this.onError(error)) .on("close", (code, reason) => this.onClose(code, reason)); resolve(ws); }; ws .on("message", data => this.onMessage(data)) .once("open", onOpen) .once("error", onEvent) .once("close", onEvent); }).catch(reject); }); } /** * Destroys the connection to the Lavalink Websocket */ destroy() { if (!this.connected) return false; this.ws.close(1000, "destroy"); return true; } /** * Whether or not the node is connected */ get connected() { if (!this.ws) return false; return this.ws.readyState === ws_1.default.OPEN; } /** * A private function for handling the open event from WebSocket */ onOpen() { if (this._reconnect) clearTimeout(this._reconnect); this.manager.emit("ready", this); if (!this._sessionUpdated && this.sessionId) { this._sessionUpdated = true; Rest_1.Rest.updateSession(this).catch(e => this.manager.emit("error", e, this)); } } /** * Private function for handling the message event from WebSocket * @param data The data that came from lavalink */ onMessage(data) { let str = ""; if (Array.isArray(data)) str = Buffer.concat(data).toString(); else if (data instanceof ArrayBuffer) str = Buffer.from(data).toString(); else str = data.toString(); const msg = JSON.parse(str); switch (msg.op) { case "ready": if (msg.sessionId) this.sessionId = msg.sessionId; if (!this._sessionUpdated) { this._sessionUpdated = true; Rest_1.Rest.updateSession(this).catch(e => this.manager.emit("error", e, this)); } break; case "stats": this.stats = { ...msg }; delete this.stats.op; break; case "event": case "playerUpdate": if (!this.manager.players.has(msg.guildId)) break; this.manager.players.get(msg.guildId).emit(msg.op, msg); break; default: break; } this.manager.emit("raw", msg, this); } /** * Private function for handling the error event from WebSocket * @param error WebSocket error */ onError(error) { if (!error) return; this.manager.emit("error", error, this); this.reconnect(); } /** * Private function for handling the close event from WebSocket * @param code WebSocket close code * @param reason WebSocket close reason */ onClose(code, reason) { this._sessionUpdated = false; this.manager.emit("disconnect", code, reason.toString(), this); if (code !== 1000 || reason.toString() !== "destroy") return this.reconnect(); } /** * Handles reconnecting if something happens and the node discounnects */ reconnect() { this._reconnect = setTimeout(() => { this.ws.removeAllListeners(); this.ws = null; this.manager.emit("reconnecting", this); this.connect(); }, this.reconnectInterval); } } exports.LavalinkNode = LavalinkNode; //# sourceMappingURL=LavalinkNode.js.map