lavalink-client 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +93 -164
  2. package/dist/cjs/index.d.ts +9 -0
  3. package/dist/cjs/index.js +12 -0
  4. package/dist/cjs/structures/Filters.d.ts +231 -0
  5. package/dist/cjs/structures/Filters.js +481 -0
  6. package/dist/cjs/structures/LavalinkManager.d.ts +124 -0
  7. package/dist/cjs/structures/LavalinkManager.js +168 -0
  8. package/dist/cjs/structures/LavalinkManagerStatics.d.ts +3 -0
  9. package/dist/cjs/structures/LavalinkManagerStatics.js +84 -0
  10. package/dist/cjs/structures/Node.d.ts +245 -0
  11. package/dist/cjs/structures/Node.js +603 -0
  12. package/dist/cjs/structures/NodeManager.d.ts +61 -0
  13. package/dist/cjs/structures/NodeManager.js +35 -0
  14. package/dist/cjs/structures/Player.d.ts +191 -0
  15. package/dist/cjs/structures/Player.js +395 -0
  16. package/dist/cjs/structures/Queue.d.ts +107 -0
  17. package/dist/cjs/structures/Queue.js +215 -0
  18. package/dist/cjs/structures/Track.d.ts +47 -0
  19. package/dist/cjs/structures/Track.js +2 -0
  20. package/dist/cjs/structures/Utils.d.ts +258 -0
  21. package/dist/cjs/structures/Utils.js +179 -0
  22. package/dist/esm/index.d.ts +9 -0
  23. package/dist/esm/index.js +9 -0
  24. package/dist/esm/structures/Filters.d.ts +231 -0
  25. package/dist/esm/structures/Filters.js +477 -0
  26. package/dist/esm/structures/LavalinkManager.d.ts +124 -0
  27. package/dist/esm/structures/LavalinkManager.js +164 -0
  28. package/dist/esm/structures/LavalinkManagerStatics.d.ts +3 -0
  29. package/dist/esm/structures/LavalinkManagerStatics.js +81 -0
  30. package/dist/esm/structures/Node.d.ts +245 -0
  31. package/dist/esm/structures/Node.js +598 -0
  32. package/dist/esm/structures/NodeManager.d.ts +61 -0
  33. package/dist/esm/structures/NodeManager.js +31 -0
  34. package/dist/esm/structures/Player.d.ts +191 -0
  35. package/dist/esm/structures/Player.js +391 -0
  36. package/dist/esm/structures/Queue.d.ts +107 -0
  37. package/dist/esm/structures/Queue.js +208 -0
  38. package/dist/esm/structures/Track.d.ts +47 -0
  39. package/dist/esm/structures/Track.js +1 -0
  40. package/dist/esm/structures/Utils.d.ts +258 -0
  41. package/dist/esm/structures/Utils.js +173 -0
  42. package/dist/index.d.ts +10 -0
  43. package/dist/index.js +13 -0
  44. package/dist/structures/Filters.d.ts +230 -0
  45. package/dist/structures/Filters.js +472 -0
  46. package/dist/structures/LavalinkManager.d.ts +47 -0
  47. package/dist/structures/LavalinkManager.js +36 -0
  48. package/dist/structures/LavalinkManagerStatics.d.ts +3 -0
  49. package/dist/structures/LavalinkManagerStatics.js +76 -0
  50. package/dist/structures/Node.d.ts +171 -0
  51. package/dist/structures/Node.js +462 -0
  52. package/dist/structures/NodeManager.d.ts +58 -0
  53. package/dist/structures/NodeManager.js +25 -0
  54. package/dist/structures/Player.d.ts +101 -0
  55. package/dist/structures/Player.js +232 -0
  56. package/dist/structures/PlayerManager.d.ts +62 -0
  57. package/dist/structures/PlayerManager.js +26 -0
  58. package/dist/structures/Queue.d.ts +93 -0
  59. package/dist/structures/Queue.js +160 -0
  60. package/dist/structures/QueueManager.d.ts +77 -0
  61. package/dist/structures/QueueManager.js +74 -0
  62. package/dist/structures/Track.d.ts +27 -0
  63. package/dist/structures/Track.js +2 -0
  64. package/dist/structures/Utils.d.ts +183 -0
  65. package/dist/structures/Utils.js +43 -0
  66. package/dist/types/index.d.ts +9 -0
  67. package/dist/types/structures/Filters.d.ts +231 -0
  68. package/dist/types/structures/LavalinkManager.d.ts +124 -0
  69. package/dist/types/structures/LavalinkManagerStatics.d.ts +3 -0
  70. package/dist/types/structures/Node.d.ts +245 -0
  71. package/dist/types/structures/NodeManager.d.ts +61 -0
  72. package/dist/types/structures/Player.d.ts +191 -0
  73. package/dist/types/structures/Queue.d.ts +107 -0
  74. package/dist/types/structures/Track.d.ts +47 -0
  75. package/dist/types/structures/Utils.d.ts +258 -0
  76. package/package.json +63 -26
@@ -0,0 +1,603 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LavalinkNode = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const ws_1 = tslib_1.__importDefault(require("ws"));
6
+ const undici_1 = require("undici");
7
+ const Utils_1 = require("./Utils");
8
+ const Player_1 = require("./Player");
9
+ const path_1 = require("path");
10
+ class LavalinkNode {
11
+ /** The provided Options of the Node */
12
+ options;
13
+ /** The amount of rest calls the node has made. */
14
+ calls = 0;
15
+ stats = {
16
+ players: 0,
17
+ playingPlayers: 0,
18
+ cpu: {
19
+ cores: 0,
20
+ lavalinkLoad: 0,
21
+ systemLoad: 0
22
+ },
23
+ memory: {
24
+ allocated: 0,
25
+ free: 0,
26
+ reservable: 0,
27
+ used: 0,
28
+ },
29
+ uptime: 0,
30
+ frameStats: {
31
+ deficit: 0,
32
+ nulled: 0,
33
+ sent: 0,
34
+ }
35
+ };
36
+ sessionId = null;
37
+ /** Actual Lavalink Information of the Node */
38
+ info = null;
39
+ /** The Node Manager of this Node */
40
+ NodeManager = null;
41
+ /** The Reconnection Timeout */
42
+ reconnectTimeout = undefined;
43
+ /** The Reconnection Attempt counter */
44
+ reconnectAttempts = 1;
45
+ /** The Socket of the Lavalink */
46
+ socket = null;
47
+ /** The Rest Server for this Lavalink */
48
+ rest;
49
+ /** Version of what the Lavalink Server should be */
50
+ version = "v4";
51
+ /**
52
+ * Create a new Node
53
+ * @param options
54
+ * @param manager
55
+ */
56
+ constructor(options, manager) {
57
+ this.options = {
58
+ secure: false,
59
+ retryAmount: 5,
60
+ retryDelay: 30e3,
61
+ requestTimeout: 10e3,
62
+ ...options
63
+ };
64
+ this.NodeManager = manager;
65
+ this.validate();
66
+ if (this.options.secure && this.options.port !== 443)
67
+ throw new SyntaxError("If secure is true, then the port must be 443");
68
+ this.rest = new undici_1.Pool(this.poolAddress, this.options.poolOptions);
69
+ this.options.regions = (this.options.regions || []).map(a => a.toLowerCase());
70
+ }
71
+ /**
72
+ * Makes an API call to the Node
73
+ * @param endpoint The endpoint that we will make the call to
74
+ * @param modify Used to modify the request before being sent
75
+ * @returns The returned data
76
+ */
77
+ async request(endpoint, modify, parseAsText = false) {
78
+ const options = {
79
+ path: `/${this.version}/${endpoint.replace(/^\//gm, "")}`,
80
+ method: "GET",
81
+ headers: {
82
+ Authorization: this.options.authorization
83
+ },
84
+ headersTimeout: this.options.requestTimeout,
85
+ };
86
+ modify?.(options);
87
+ const url = new URL(`${this.poolAddress}${options.path}`);
88
+ url.searchParams.append("trace", "true");
89
+ options.path = url.toString().replace(this.poolAddress, "");
90
+ const request = await this.rest.request(options);
91
+ this.calls++;
92
+ if (options.method === "DELETE")
93
+ return;
94
+ return parseAsText ? await request.body.text() : await request.body.json();
95
+ }
96
+ /**
97
+ * Update the Player State on the Lavalink Server
98
+ * @param data
99
+ * @returns
100
+ */
101
+ async updatePlayer(data) {
102
+ if (!this.sessionId)
103
+ throw new Error("The Lavalink Node is either not ready, or not up to date!");
104
+ this.syncPlayerData(data);
105
+ const res = await this.request(`/sessions/${this.sessionId}/players/${data.guildId}`, r => {
106
+ r.method = "PATCH";
107
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
108
+ r.headers["Content-Type"] = "application/json";
109
+ if (data.playerOptions.track)
110
+ delete data.playerOptions.track;
111
+ r.body = JSON.stringify(data.playerOptions);
112
+ if (data.noReplace) {
113
+ const url = new URL(`${this.poolAddress}${r.path}`);
114
+ url.searchParams.append("noReplace", data.noReplace?.toString() || "false");
115
+ r.path = url.toString().replace(this.poolAddress, "");
116
+ }
117
+ });
118
+ return this.syncPlayerData({}, res), res;
119
+ }
120
+ /**
121
+ * Destroys the Player on the Lavalink Server
122
+ * @param guildId
123
+ * @returns
124
+ */
125
+ async destroyPlayer(guildId) {
126
+ if (!this.sessionId)
127
+ throw new Error("The Lavalink-Node is either not ready, or not up to date!");
128
+ return await this.request(`/sessions/${this.sessionId}/players/${guildId}`, r => { r.method = "DELETE"; });
129
+ }
130
+ /**
131
+ * Connect to the Lavalink Node
132
+ * @param sessionId Provide the Session Id of the previous connection, to resume the node and it's player(s)
133
+ * @returns
134
+ */
135
+ connect(sessionId) {
136
+ if (this.connected)
137
+ return;
138
+ const headers = {
139
+ Authorization: this.options.authorization,
140
+ "Num-Shards": String(this.NodeManager.LavalinkManager.options.client.shards || "auto"),
141
+ "User-Id": this.NodeManager.LavalinkManager.options.client.id,
142
+ "User-Name": this.NodeManager.LavalinkManager.options.client.username || "Lavalink-Client",
143
+ };
144
+ if (typeof this.options.sessionId === "string" || typeof sessionId === "string") {
145
+ headers["Session-Id"] = this.options.sessionId || sessionId;
146
+ this.sessionId = this.options.sessionId || sessionId;
147
+ }
148
+ this.socket = new ws_1.default(`ws${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}/v4/websocket`, { headers });
149
+ this.socket.on("open", this.open.bind(this));
150
+ this.socket.on("close", this.close.bind(this));
151
+ this.socket.on("message", this.message.bind(this));
152
+ this.socket.on("error", this.error.bind(this));
153
+ }
154
+ /** Get the id of the node */
155
+ get id() {
156
+ return this.options.id || this.options.host;
157
+ }
158
+ /**
159
+ * Destroys the Node-Connection (Websocket) and all player's of the node
160
+ * @returns
161
+ */
162
+ destroy(destroyReason) {
163
+ if (!this.connected)
164
+ return;
165
+ const players = this.NodeManager.LavalinkManager.players.filter(p => p.node.id == this.id);
166
+ if (players)
167
+ players.forEach(p => p.destroy(destroyReason || Player_1.DestroyReasons.NodeDestroy));
168
+ this.socket.close(1000, "destroy");
169
+ this.socket.removeAllListeners();
170
+ this.socket = null;
171
+ this.reconnectAttempts = 1;
172
+ clearTimeout(this.reconnectTimeout);
173
+ this.NodeManager.emit("destroy", this, destroyReason);
174
+ this.NodeManager.nodes.delete(this.id);
175
+ return;
176
+ }
177
+ /** Returns if connected to the Node. */
178
+ get connected() {
179
+ if (!this.socket)
180
+ return false;
181
+ return this.socket.readyState === ws_1.default.OPEN;
182
+ }
183
+ /**
184
+ * Gets all Players of a Node
185
+ */
186
+ async fetchAllPlayers() {
187
+ if (!this.sessionId)
188
+ throw new Error("The Lavalink-Node is either not ready, or not up to date!");
189
+ const players = await this.request(`/sessions/${this.sessionId}/players`);
190
+ if (!Array.isArray(players))
191
+ return [];
192
+ else
193
+ return players;
194
+ }
195
+ /**
196
+ * Gets specific Player Information
197
+ */
198
+ async fetchPlayer(guildId) {
199
+ if (!this.sessionId)
200
+ throw new Error("The Lavalink-Node is either not ready, or not up to date!");
201
+ return await this.request(`/sessions/${this.sessionId}/players/${guildId}`);
202
+ }
203
+ /**
204
+ * Updates the session with and enables/disables resuming and timeout
205
+ * @param resuming Whether resuming is enabled for this session or not
206
+ * @param timeout The timeout in seconds (default is 60s)
207
+ */
208
+ async updateSession(resuming, timeout) {
209
+ if (!this.sessionId)
210
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
211
+ const data = {};
212
+ if (typeof resuming === "boolean")
213
+ data.resuming = resuming;
214
+ if (typeof timeout === "number" && timeout > 0)
215
+ data.timeout = timeout;
216
+ return await this.request(`/sessions/${this.sessionId}`, r => {
217
+ r.method = "PATCH";
218
+ r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
219
+ r.body = JSON.stringify(data);
220
+ });
221
+ }
222
+ /**
223
+ * Decode Track or Tracks
224
+ */
225
+ decode = {
226
+ /**
227
+ * Decode a single track into its info, where BASE64 is the encoded base64 data.
228
+ * @param encoded
229
+ * @returns
230
+ */
231
+ singleTrack: async (encoded) => {
232
+ if (!encoded)
233
+ throw new SyntaxError("No encoded (Base64 string) was provided");
234
+ return await this.request(`/decodetrack?encodedTrack=${encoded}`);
235
+ },
236
+ /**
237
+ *
238
+ * @param encodeds Decodes multiple tracks into their info
239
+ * @returns
240
+ */
241
+ multipleTracks: async (encodeds) => {
242
+ if (!Array.isArray(encodeds) || !encodeds.every(v => typeof v === "string" && v.length > 1))
243
+ throw new SyntaxError("You need to provide encodeds, which is an array of base64 strings");
244
+ return await this.request(`/decodetracks`, r => {
245
+ r.method = "POST";
246
+ r.body = JSON.stringify(encodeds);
247
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
248
+ r.headers["Content-Type"] = "application/json";
249
+ });
250
+ }
251
+ };
252
+ /**
253
+ * Request Lavalink statistics.
254
+ * @returns
255
+ */
256
+ async fetchStats() {
257
+ return await this.request(`/stats`);
258
+ }
259
+ /**
260
+ * Request Lavalink version.
261
+ * @returns
262
+ */
263
+ async fetchVersion() {
264
+ return await this.request(`/version`, r => { r.path = "/version"; }, true);
265
+ }
266
+ /**
267
+ * Request Lavalink information.
268
+ * @returns
269
+ */
270
+ async fetchInfo() {
271
+ return await this.request(`/info`);
272
+ }
273
+ /**
274
+ * Lavalink's Route Planner Api
275
+ */
276
+ routePlannerApi = {
277
+ /**
278
+ * Get routplanner Info from Lavalink
279
+ */
280
+ getStatus: async () => {
281
+ if (!this.sessionId)
282
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
283
+ return await this.request(`/routeplanner/status`);
284
+ },
285
+ /**
286
+ * Release blacklisted IP address into pool of IPs
287
+ * @param address IP address
288
+ */
289
+ unmarkFailedAddress: async (address) => {
290
+ if (!this.sessionId)
291
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
292
+ await this.request(`/routeplanner/free/address`, r => {
293
+ r.method = "POST";
294
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
295
+ r.headers["Content-Type"] = "application/json";
296
+ r.body = JSON.stringify({ address });
297
+ });
298
+ },
299
+ /**
300
+ * Release all blacklisted IP addresses into pool of IPs
301
+ */
302
+ unmarkAllFailedAddresses: async () => {
303
+ if (!this.sessionId)
304
+ throw new Error("the Lavalink-Node is either not ready, or not up to date!");
305
+ return await this.request(`/routeplanner/free/all`, r => {
306
+ r.method = "POST";
307
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
308
+ r.headers["Content-Type"] = "application/json";
309
+ });
310
+ }
311
+ };
312
+ /** Private Utils */
313
+ validate() {
314
+ if (!this.options.authorization)
315
+ throw new SyntaxError("LavalinkNode requires 'authorization'");
316
+ if (!this.options.host)
317
+ throw new SyntaxError("LavalinkNode requires 'host'");
318
+ if (!this.options.port)
319
+ throw new SyntaxError("LavalinkNode requires 'port'");
320
+ }
321
+ syncPlayerData(data, res) {
322
+ if (typeof data === "object" && typeof data?.guildId === "string" && typeof data.playerOptions === "object" && Object.keys(data.playerOptions).length > 1) {
323
+ const player = this.NodeManager.LavalinkManager.getPlayer(data.guildId);
324
+ if (!player)
325
+ return;
326
+ if (typeof data.playerOptions.paused !== "undefined") {
327
+ player.paused = data.playerOptions.paused;
328
+ player.playing = !data.playerOptions.paused;
329
+ }
330
+ if (typeof data.playerOptions.position === "number") {
331
+ player.position = data.playerOptions.position;
332
+ player.lastPosition = data.playerOptions.position;
333
+ }
334
+ if (typeof data.playerOptions.voice !== "undefined")
335
+ player.voice = data.playerOptions.voice;
336
+ if (typeof data.playerOptions.volume !== "undefined") {
337
+ if (this.NodeManager.LavalinkManager.options.playerOptions.volumeDecrementer) {
338
+ player.volume = data.playerOptions.volume / this.NodeManager.LavalinkManager.options.playerOptions.volumeDecrementer;
339
+ player.lavalinkVolume = data.playerOptions.volume;
340
+ }
341
+ else {
342
+ player.volume = data.playerOptions.volume;
343
+ player.lavalinkVolume = data.playerOptions.volume;
344
+ }
345
+ }
346
+ if (typeof data.playerOptions.filters !== "undefined") {
347
+ const oldFilterTimescale = { ...(player.filterManager.data.timescale || {}) };
348
+ Object.freeze(oldFilterTimescale);
349
+ if (data.playerOptions.filters.timescale)
350
+ player.filterManager.data.timescale = data.playerOptions.filters.timescale;
351
+ if (data.playerOptions.filters.distortion)
352
+ player.filterManager.data.distortion = data.playerOptions.filters.distortion;
353
+ if (data.playerOptions.filters.echo)
354
+ player.filterManager.data.echo = data.playerOptions.filters.echo;
355
+ if (data.playerOptions.filters.vibrato)
356
+ player.filterManager.data.vibrato = data.playerOptions.filters.vibrato;
357
+ if (data.playerOptions.filters.volume)
358
+ player.filterManager.data.volume = data.playerOptions.filters.volume;
359
+ if (data.playerOptions.filters.equalizer)
360
+ player.filterManager.data.equalizer = data.playerOptions.filters.equalizer;
361
+ if (data.playerOptions.filters.karaoke)
362
+ player.filterManager.data.karaoke = data.playerOptions.filters.karaoke;
363
+ if (data.playerOptions.filters.lowPass)
364
+ player.filterManager.data.lowPass = data.playerOptions.filters.lowPass;
365
+ if (data.playerOptions.filters.rotation)
366
+ player.filterManager.data.rotation = data.playerOptions.filters.rotation;
367
+ if (data.playerOptions.filters.tremolo)
368
+ player.filterManager.data.tremolo = data.playerOptions.filters.tremolo;
369
+ player.filterManager.checkFiltersState(oldFilterTimescale);
370
+ }
371
+ }
372
+ // just for res
373
+ if (res?.guildId === "string" && typeof res?.voice !== "undefined") {
374
+ const player = this.NodeManager.LavalinkManager.getPlayer(data.guildId);
375
+ if (!player)
376
+ return;
377
+ if (typeof res?.voice?.connected === "boolean" && res.voice.connected === false)
378
+ return player.destroy(Player_1.DestroyReasons.LavalinkNoVoice);
379
+ player.ping.ws = res?.voice?.ping || player?.ping.ws;
380
+ }
381
+ return true;
382
+ }
383
+ get poolAddress() {
384
+ return `http${this.options.secure ? "s" : ""}://${this.options.host}:${this.options.port}`;
385
+ }
386
+ reconnect() {
387
+ this.reconnectTimeout = setTimeout(() => {
388
+ if (this.reconnectAttempts >= this.options.retryAmount) {
389
+ const error = new Error(`Unable to connect after ${this.options.retryAmount} attempts.`);
390
+ this.NodeManager.emit("error", this, error);
391
+ return this.destroy(Player_1.DestroyReasons.NodeReconnectFail);
392
+ }
393
+ this.socket.removeAllListeners();
394
+ this.socket = null;
395
+ this.NodeManager.emit("reconnecting", this);
396
+ this.connect();
397
+ this.reconnectAttempts++;
398
+ }, this.options.retryDelay || 1000);
399
+ }
400
+ open() {
401
+ if (this.reconnectTimeout)
402
+ clearTimeout(this.reconnectTimeout);
403
+ // reset the reconnect attempts amount
404
+ this.reconnectAttempts = 1;
405
+ this.NodeManager.emit("connect", this);
406
+ setTimeout(() => {
407
+ this.fetchInfo().then(x => this.info = x).catch(() => null).then(() => {
408
+ if (!this.info && ["v3", "v4"].includes(this.version)) {
409
+ const errorString = `Lavalink Node (${this.poolAddress}) does not provide any /${this.version}/info`;
410
+ throw new Error(errorString);
411
+ }
412
+ });
413
+ }, 1500);
414
+ }
415
+ close(code, reason) {
416
+ this.NodeManager.emit("disconnect", this, { code, reason });
417
+ if (code !== 1000 || reason !== "destroy")
418
+ this.reconnect();
419
+ }
420
+ error(error) {
421
+ if (!error)
422
+ return;
423
+ this.NodeManager.emit("error", this, error);
424
+ }
425
+ async message(d) {
426
+ if (Array.isArray(d))
427
+ d = Buffer.concat(d);
428
+ else if (d instanceof ArrayBuffer)
429
+ d = Buffer.from(d);
430
+ const payload = JSON.parse(d.toString());
431
+ if (!payload.op)
432
+ return;
433
+ this.NodeManager.emit("raw", this, payload);
434
+ switch (payload.op) {
435
+ case "stats":
436
+ delete payload.op;
437
+ this.stats = { ...payload };
438
+ break;
439
+ case "playerUpdate":
440
+ const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
441
+ if (!player)
442
+ return;
443
+ if (player.get("internal_updateInterval"))
444
+ clearInterval(player.get("internal_updateInterval"));
445
+ // override the position
446
+ player.position = payload.state.position || 0;
447
+ player.lastPosition = payload.state.position || 0;
448
+ player.connected = payload.state.connected;
449
+ player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
450
+ if (!player.createdTimeStamp && payload.state.time)
451
+ player.createdTimeStamp = payload.state.time;
452
+ if (typeof this.NodeManager.LavalinkManager.options.playerOptions.clientBasedUpdateInterval === "number" && this.NodeManager.LavalinkManager.options.playerOptions.clientBasedUpdateInterval >= 10) {
453
+ player.set("internal_updateInterval", setInterval(() => {
454
+ player.position += this.NodeManager.LavalinkManager.options.playerOptions.clientBasedUpdateInterval || 250;
455
+ if (player.filterManager.filterUpdatedState >= 1) {
456
+ player.filterManager.filterUpdatedState++;
457
+ const maxMins = 8;
458
+ const currentDuration = player.queue.current?.info?.duration || 0;
459
+ if (currentDuration <= maxMins * 6e4 || (0, path_1.isAbsolute)(player.queue.current?.info?.uri)) {
460
+ if (player.filterManager.filterUpdatedState >= ((this.NodeManager.LavalinkManager.options.playerOptions.clientBasedUpdateInterval || 250) > 400 ? 2 : 3)) {
461
+ player.filterManager.filterUpdatedState = 0;
462
+ player.seek(player.position);
463
+ }
464
+ }
465
+ else {
466
+ player.filterManager.filterUpdatedState = 0;
467
+ }
468
+ }
469
+ }, this.NodeManager.LavalinkManager.options.playerOptions.clientBasedUpdateInterval || 250));
470
+ }
471
+ else {
472
+ if (player.filterManager.filterUpdatedState >= 1) { // if no interval but instafix available, findable via the "filterUpdatedState" property
473
+ const maxMins = 8;
474
+ const currentDuration = player.queue.current?.info?.duration || 0;
475
+ if (currentDuration <= maxMins * 6e4 || (0, path_1.isAbsolute)(player.queue.current?.info?.uri))
476
+ player.seek(player.position);
477
+ player.filterManager.filterUpdatedState = 0;
478
+ }
479
+ }
480
+ break;
481
+ case "event":
482
+ this.handleEvent(payload);
483
+ break;
484
+ case "ready": // payload: { resumed: false, sessionId: 'ytva350aevn6n9n8', op: 'ready' }
485
+ this.sessionId = payload.sessionId;
486
+ break;
487
+ default:
488
+ this.NodeManager.emit("error", this, new Error(`Unexpected op "${payload.op}" with data`), payload);
489
+ return;
490
+ }
491
+ }
492
+ async handleEvent(payload) {
493
+ if (!payload.guildId)
494
+ return;
495
+ const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
496
+ if (!player)
497
+ return;
498
+ console.log(payload.type);
499
+ switch (payload.type) {
500
+ case "TrackStartEvent":
501
+ this.trackStart(player, player.queue.current, payload);
502
+ break;
503
+ case "TrackEndEvent":
504
+ this.trackEnd(player, player.queue.current, payload);
505
+ break;
506
+ case "TrackStuckEvent":
507
+ this.trackStuck(player, player.queue.current, payload);
508
+ break;
509
+ case "TrackExceptionEvent":
510
+ this.trackError(player, player.queue.current, payload);
511
+ break;
512
+ case "WebSocketClosedEvent":
513
+ this.socketClosed(player, payload);
514
+ break;
515
+ default:
516
+ this.NodeManager.emit("error", this, new Error(`Node#event unknown event '${payload.type}'.`), payload);
517
+ break;
518
+ }
519
+ return;
520
+ }
521
+ trackStart(player, track, payload) {
522
+ player.playing = true;
523
+ player.paused = false;
524
+ return this.NodeManager.LavalinkManager.emit("trackStart", player, track, payload);
525
+ }
526
+ async trackEnd(player, track, payload) {
527
+ // If there are no songs in the queue
528
+ if (!player.queue.tracks.length && player.repeatMode === "off")
529
+ return this.queueEnd(player, track, payload);
530
+ // If a track was forcibly played
531
+ if (payload.reason === "replaced")
532
+ return this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
533
+ // If a track had an error while starting
534
+ if (["loadFailed", "cleanup"].includes(payload.reason)) {
535
+ await (0, Utils_1.queueTrackEnd)(player.queue);
536
+ // if no track available, end queue
537
+ if (!player.queue.current)
538
+ return this.queueEnd(player, track, payload);
539
+ // fire event
540
+ this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
541
+ // play track if autoSkip is true
542
+ return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ track: player.queue.current, noReplace: true });
543
+ }
544
+ // remove tracks from the queue
545
+ if (player.repeatMode !== "track")
546
+ await (0, Utils_1.queueTrackEnd)(player.queue, player.repeatMode === "queue");
547
+ // if no track available, end queue
548
+ if (!player.queue.current)
549
+ return this.queueEnd(player, track, payload);
550
+ // fire event
551
+ this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
552
+ // play track if autoSkip is true
553
+ return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ track: player.queue.current, noReplace: true });
554
+ }
555
+ async queueEnd(player, track, payload) {
556
+ player.queue.current = null;
557
+ player.playing = false;
558
+ if (payload?.reason !== "stopped") {
559
+ await player.queue.utils.save();
560
+ }
561
+ if (typeof this.NodeManager.LavalinkManager.options.playerOptions?.onEmptyQueue?.destroyAfterMs === "number" && !isNaN(this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs) && this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs >= 0) {
562
+ if (this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs === 0)
563
+ return player.destroy(Player_1.DestroyReasons.QueueEmpty);
564
+ else {
565
+ if (player.get("internal_queueempty")) {
566
+ clearTimeout(player.get("internal_queueempty"));
567
+ player.set("internal_queueempty", undefined);
568
+ }
569
+ player.set("internal_queueempty", setTimeout(() => {
570
+ player.destroy(Player_1.DestroyReasons.QueueEmpty);
571
+ }, this.NodeManager.LavalinkManager.options.playerOptions.onEmptyQueue?.destroyAfterMs));
572
+ }
573
+ }
574
+ return this.NodeManager.LavalinkManager.emit("queueEnd", player, track, payload);
575
+ }
576
+ async trackStuck(player, track, payload) {
577
+ this.NodeManager.LavalinkManager.emit("trackStuck", player, track, payload);
578
+ // If there are no songs in the queue
579
+ if (!player.queue.tracks.length && player.repeatMode === "off")
580
+ return;
581
+ // remove the current track, and enqueue the next one
582
+ await (0, Utils_1.queueTrackEnd)(player.queue, player.repeatMode === "queue");
583
+ // if no track available, end queue
584
+ if (!player.queue.current)
585
+ return this.queueEnd(player, track, payload);
586
+ // play track if autoSkip is true
587
+ return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ track: player.queue.current, noReplace: true });
588
+ }
589
+ async trackError(player, track, payload) {
590
+ this.NodeManager.LavalinkManager.emit("trackError", player, track, payload);
591
+ // remove the current track, and enqueue the next one
592
+ await (0, Utils_1.queueTrackEnd)(player.queue, player.repeatMode === "queue");
593
+ // if no track available, end queue
594
+ if (!player.queue.current)
595
+ return this.queueEnd(player, track, payload);
596
+ // play track if autoSkip is true
597
+ return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ track: player.queue.current, noReplace: true });
598
+ }
599
+ socketClosed(player, payload) {
600
+ return this.NodeManager.LavalinkManager.emit("playerSocketClosed", player, payload);
601
+ }
602
+ }
603
+ exports.LavalinkNode = LavalinkNode;
@@ -0,0 +1,61 @@
1
+ /// <reference types="node" />
2
+ import { EventEmitter } from "stream";
3
+ import { LavalinkNode, LavalinkNodeOptions } from "./Node";
4
+ import { LavalinkManager } from "./LavalinkManager";
5
+ import { MiniMap } from "./Utils";
6
+ import { DestroyReasonsType } from "./Player";
7
+ type LavalinkNodeIdentifier = string;
8
+ interface NodeManagerEvents {
9
+ /**
10
+ * Emitted when a Node is created.
11
+ * @event Manager.nodeManager#create
12
+ */
13
+ "create": (node: LavalinkNode) => void;
14
+ /**
15
+ * Emitted when a Node is destroyed.
16
+ * @event Manager.nodeManager#destroy
17
+ */
18
+ "destroy": (node: LavalinkNode, destroyReason?: DestroyReasonsType) => void;
19
+ /**
20
+ * Emitted when a Node is connected.
21
+ * @event Manager.nodeManager#connect
22
+ */
23
+ "connect": (node: LavalinkNode) => void;
24
+ /**
25
+ * Emitted when a Node is reconnecting.
26
+ * @event Manager.nodeManager#reconnecting
27
+ */
28
+ "reconnecting": (node: LavalinkNode) => void;
29
+ /**
30
+ * Emitted when a Node is disconnects.
31
+ * @event Manager.nodeManager#disconnect
32
+ */
33
+ "disconnect": (node: LavalinkNode, reason: {
34
+ code?: number;
35
+ reason?: string;
36
+ }) => void;
37
+ /**
38
+ * Emitted when a Node is error.
39
+ * @event Manager.nodeManager#error
40
+ */
41
+ "error": (node: LavalinkNode, error: Error, payload?: unknown) => void;
42
+ /**
43
+ * Emits every single Node event.
44
+ * @event Manager.nodeManager#raw
45
+ */
46
+ "raw": (node: LavalinkNode, payload: unknown) => void;
47
+ }
48
+ export declare interface NodeManager {
49
+ on<U extends keyof NodeManagerEvents>(event: U, listener: NodeManagerEvents[U]): this;
50
+ emit<U extends keyof NodeManagerEvents>(event: U, ...args: Parameters<NodeManagerEvents[U]>): boolean;
51
+ /** @private */
52
+ LavalinkManager: LavalinkManager;
53
+ }
54
+ export declare class NodeManager extends EventEmitter {
55
+ nodes: MiniMap<string, LavalinkNode>;
56
+ constructor(LavalinkManager: LavalinkManager);
57
+ createNode(options: LavalinkNodeOptions): LavalinkNode;
58
+ get leastUsedNodes(): LavalinkNode[];
59
+ deleteNode(node: LavalinkNodeIdentifier | LavalinkNode): void;
60
+ }
61
+ export {};