lavacord 2.2.0 → 3.0.0

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 (59) hide show
  1. package/LICENSE +24 -201
  2. package/README.md +473 -69
  3. package/dist/cjs/index.cjs +44 -0
  4. package/dist/cjs/index.cjs.map +1 -0
  5. package/dist/cjs/index.d.cts +1211 -0
  6. package/dist/cjs/lib/LavalinkNode.cjs +423 -0
  7. package/dist/cjs/lib/LavalinkNode.cjs.map +1 -0
  8. package/dist/cjs/lib/Manager.cjs +405 -0
  9. package/dist/cjs/lib/Manager.cjs.map +1 -0
  10. package/dist/cjs/lib/Player.cjs +220 -0
  11. package/dist/cjs/lib/Player.cjs.map +1 -0
  12. package/dist/cjs/lib/Rest.cjs +192 -0
  13. package/dist/cjs/lib/Rest.cjs.map +1 -0
  14. package/dist/cjs/lib/Types.cjs +4 -0
  15. package/dist/cjs/lib/Types.cjs.map +1 -0
  16. package/dist/cjs/wrappers/cloudstorm.cjs +54 -0
  17. package/dist/cjs/wrappers/cloudstorm.cjs.map +1 -0
  18. package/dist/cjs/wrappers/detritus.cjs +57 -0
  19. package/dist/cjs/wrappers/detritus.cjs.map +1 -0
  20. package/dist/cjs/wrappers/discord.js.cjs +37 -0
  21. package/dist/cjs/wrappers/discord.js.cjs.map +1 -0
  22. package/dist/cjs/wrappers/eris.cjs +51 -0
  23. package/dist/cjs/wrappers/eris.cjs.map +1 -0
  24. package/dist/cjs/wrappers/oceanic.cjs +52 -0
  25. package/dist/cjs/wrappers/oceanic.cjs.map +1 -0
  26. package/dist/esm/chunk-PAWJFY3S.mjs +6 -0
  27. package/dist/esm/chunk-PAWJFY3S.mjs.map +1 -0
  28. package/dist/esm/index.d.mts +1211 -0
  29. package/dist/esm/index.mjs +12 -0
  30. package/dist/esm/index.mjs.map +1 -0
  31. package/dist/esm/lib/LavalinkNode.mjs +420 -0
  32. package/dist/esm/lib/LavalinkNode.mjs.map +1 -0
  33. package/dist/esm/lib/Manager.mjs +402 -0
  34. package/dist/esm/lib/Manager.mjs.map +1 -0
  35. package/dist/esm/lib/Player.mjs +217 -0
  36. package/dist/esm/lib/Player.mjs.map +1 -0
  37. package/dist/esm/lib/Rest.mjs +188 -0
  38. package/dist/esm/lib/Rest.mjs.map +1 -0
  39. package/dist/esm/lib/Types.mjs +3 -0
  40. package/dist/esm/lib/Types.mjs.map +1 -0
  41. package/dist/esm/wrappers/cloudstorm.mjs +45 -0
  42. package/dist/esm/wrappers/cloudstorm.mjs.map +1 -0
  43. package/dist/esm/wrappers/detritus.mjs +48 -0
  44. package/dist/esm/wrappers/detritus.mjs.map +1 -0
  45. package/dist/esm/wrappers/discord.js.mjs +28 -0
  46. package/dist/esm/wrappers/discord.js.mjs.map +1 -0
  47. package/dist/esm/wrappers/eris.mjs +42 -0
  48. package/dist/esm/wrappers/eris.mjs.map +1 -0
  49. package/dist/esm/wrappers/oceanic.mjs +43 -0
  50. package/dist/esm/wrappers/oceanic.mjs.map +1 -0
  51. package/dist/lib/LavalinkNode.d.ts +3 -3
  52. package/dist/lib/LavalinkNode.js +16 -26
  53. package/dist/lib/LavalinkNode.js.map +1 -1
  54. package/dist/lib/Manager.d.ts +2 -2
  55. package/dist/lib/Manager.js.map +1 -1
  56. package/dist/lib/Rest.js +5 -7
  57. package/dist/lib/Rest.js.map +1 -1
  58. package/dist/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +109 -64
@@ -0,0 +1,405 @@
1
+ 'use strict';
2
+
3
+ var events = require('events');
4
+ var LavalinkNode_cjs = require('./LavalinkNode.cjs');
5
+ var Player_cjs = require('./Player.cjs');
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
+ var Manager = class extends events.EventEmitter {
10
+ static {
11
+ __name(this, "Manager");
12
+ }
13
+ /**
14
+ * A Map of Lavalink Nodes indexed by their IDs.
15
+ */
16
+ nodes = /* @__PURE__ */ new Map();
17
+ /**
18
+ * A Map of all active players indexed by guild ID.
19
+ */
20
+ players = /* @__PURE__ */ new Map();
21
+ /**
22
+ * A Map of voice server update states indexed by guild ID.
23
+ */
24
+ voiceServers = /* @__PURE__ */ new Map();
25
+ /**
26
+ * A Map of voice state update states indexed by guild ID.
27
+ */
28
+ voiceStates = /* @__PURE__ */ new Map();
29
+ /**
30
+ * The user ID of the bot this Manager is managing.
31
+ */
32
+ userId = null;
33
+ /**
34
+ * Function to send voice state update packets to Discord.
35
+ *
36
+ * @remarks
37
+ * This can be implemented by the user in two ways:
38
+ * 1. Via constructor options: `new Manager(nodes, { send: fn })`
39
+ * 2. Via class extension: `class MyManager extends Manager { send(packet) { ... } }`
40
+ */
41
+ _send;
42
+ /**
43
+ * The Player class constructor used when creating new players.
44
+ *
45
+ * @remarks
46
+ * Can be overridden in the manager options to use a custom Player implementation.
47
+ */
48
+ Player = Player_cjs.Player;
49
+ /**
50
+ * A Set of guild IDs that are waiting for a connection.
51
+ *
52
+ * @internal
53
+ */
54
+ expecting = /* @__PURE__ */ new Set();
55
+ /**
56
+ * Creates a new Manager instance.
57
+ *
58
+ * @param nodes - An array of Lavalink node options to connect to.
59
+ * @param options - Configuration options for the Manager.
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * // Method 1: Define send function via options
64
+ * const manager = new Manager([
65
+ * {
66
+ * id: "main",
67
+ * host: "localhost",
68
+ * port: 2333,
69
+ * password: "youshallnotpass"
70
+ * }
71
+ * ], {
72
+ * userId: "bot_user_id",
73
+ * send: (packet) => {
74
+ * const guild = client.guilds.cache.get(packet.d.guild_id);
75
+ * if (guild) guild.shard.send(packet);
76
+ * }
77
+ * });
78
+ *
79
+ * // Method 2: Extend Manager class
80
+ * class MyManager extends Manager {
81
+ * send(packet) {
82
+ * const guild = this.client.guilds.cache.get(packet.d.guild_id);
83
+ * if (guild) guild.shard.send(packet);
84
+ * }
85
+ * }
86
+ * const manager = new MyManager(nodes, { userId: "bot_user_id" });
87
+ * ```
88
+ */
89
+ constructor(nodes, options) {
90
+ super();
91
+ options ??= {};
92
+ if (options.userId) this.userId = options.userId;
93
+ if (options.player) this.Player = options.player;
94
+ if (options.send && !this._send) this._send = options.send;
95
+ for (const node of nodes) this.createNode(node);
96
+ }
97
+ /**
98
+ * Connects all Lavalink nodes to their respective Lavalink servers.
99
+ *
100
+ * @returns A promise that resolves when all connections are established.
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * // Connect all nodes
105
+ * manager.connect()
106
+ * .then(() => console.log('All nodes connected!'))
107
+ * .catch(error => console.error('Failed to connect nodes:', error));
108
+ * ```
109
+ */
110
+ async connect() {
111
+ if (!this.userId)
112
+ throw new Error(
113
+ "Lavacord requires a client user ID before connecting. \nSet the user ID when constructing the Manager or after your Discord client is ready."
114
+ );
115
+ return Promise.all(this.nodes.values().map((node) => node.connect().then(() => node)));
116
+ }
117
+ /**
118
+ * Disconnects all players and nodes, effectively cleaning up all resources.
119
+ *
120
+ * @returns A promise that resolves when all disconnections are complete.
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * // Disconnect everything
125
+ * manager.disconnect()
126
+ * .then(() => console.log('All players and nodes cleaned up'))
127
+ * .catch(error => console.error('Error during disconnection:', error));
128
+ * ```
129
+ */
130
+ async disconnect() {
131
+ for await (const player of this.players.keys()) await this.leave(player);
132
+ for (const node of this.nodes.values()) node.destroy();
133
+ }
134
+ /**
135
+ * Creates a new Lavalink node and adds it to the nodes map.
136
+ *
137
+ * @param options - Configuration options for the node.
138
+ * @returns The newly created LavalinkNode instance.
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * // Add a new node
143
+ * const newNode = manager.createNode({
144
+ * id: "node2",
145
+ * host: "example.com",
146
+ * port: 2333,
147
+ * password: "securepassword",
148
+ * resuming: true
149
+ * });
150
+ * ```
151
+ */
152
+ createNode(options) {
153
+ const node = new LavalinkNode_cjs.LavalinkNode(this, options);
154
+ this.nodes.set(options.id, node);
155
+ return node;
156
+ }
157
+ /**
158
+ * Disconnects and removes a node from the manager.
159
+ *
160
+ * @param id - The ID of the node to remove.
161
+ * @returns Whether the node was successfully removed.
162
+ *
163
+ * @example
164
+ * ```typescript
165
+ * // Remove a node
166
+ * const removed = manager.removeNode("node1");
167
+ * if (removed) {
168
+ * console.log("Node successfully removed");
169
+ * } else {
170
+ * console.log("Node not found");
171
+ * }
172
+ * ```
173
+ */
174
+ removeNode(id) {
175
+ const node = this.nodes.get(id);
176
+ if (!node) return false;
177
+ node.destroy();
178
+ return this.nodes.delete(id);
179
+ }
180
+ /**
181
+ * Joins a voice channel and creates a player for the guild.
182
+ *
183
+ * @param data - The data needed to join a voice channel.
184
+ * @param joinOptions - Options for joining the channel (selfmute, selfdeaf).
185
+ * @returns A promise resolving to the Player instance.
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * // Join a voice channel
190
+ * const player = await manager.join({
191
+ * guild: "123456789012345678", // Guild ID
192
+ * channel: "123456789012345679", // Voice Channel ID
193
+ * node: "main" // Node ID
194
+ * }, {
195
+ * selfdeaf: true // Join deafened
196
+ * });
197
+ *
198
+ * // Now you can use the player to play music
199
+ * const result = await Rest.load(player.node, "https://www.youtube.com/watch?v=dQw4w9WgXcQ");
200
+ * await player.play(result.tracks[0].track);
201
+ * ```
202
+ */
203
+ async join(data, joinOptions = {}) {
204
+ const player = this.players.get(data.guild);
205
+ if (player) return player;
206
+ await this.sendWS(data.guild, data.channel, joinOptions);
207
+ return this.spawnPlayer(data);
208
+ }
209
+ /**
210
+ * Leaves a voice channel and cleans up the player.
211
+ *
212
+ * @param guild - The ID of the guild to leave.
213
+ * @returns A promise resolving to whether the operation was successful.
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * // Leave a voice channel
218
+ * const success = await manager.leave("123456789012345678");
219
+ * if (success) {
220
+ * console.log("Successfully left the voice channel");
221
+ * } else {
222
+ * console.log("Not in a voice channel in this guild");
223
+ * }
224
+ * ```
225
+ */
226
+ async leave(guild) {
227
+ await this.sendWS(guild, null);
228
+ const player = this.players.get(guild);
229
+ if (!player) return false;
230
+ await player.destroy();
231
+ return this.players.delete(guild);
232
+ }
233
+ /**
234
+ * Switches a player from one node to another, implementing fallback capability.
235
+ *
236
+ * @param player - The player to move to another node.
237
+ * @param node - The destination node.
238
+ * @returns A promise resolving to the updated player.
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * // Switch a player to another node (e.g. if current node is failing)
243
+ * const player = manager.players.get("123456789012345678");
244
+ * const newNode = manager.nodes.get("backup-node");
245
+ *
246
+ * if (player && newNode) {
247
+ * await manager.switch(player, newNode);
248
+ * console.log("Player switched to backup node");
249
+ * }
250
+ * ```
251
+ */
252
+ async switch(player, node) {
253
+ player.node = node;
254
+ if (!player.voice) return player;
255
+ await player.destroy();
256
+ if (!player.track) return player;
257
+ await player.play(player.track.encoded, {
258
+ position: player.state.position,
259
+ volume: player.volume,
260
+ filters: player.filters,
261
+ voice: player.voice,
262
+ paused: player.paused,
263
+ userData: player.track.userData
264
+ });
265
+ return player;
266
+ }
267
+ /**
268
+ * Processes voice server update events from Discord.
269
+ *
270
+ * @param data - The voice server update data from Discord.
271
+ * @returns A promise resolving to whether a connection was established.
272
+ *
273
+ * @example
274
+ * ```typescript
275
+ * // In your Discord.js client events
276
+ * client.ws.on(GatewayDispatchEvents.VoiceServerUpdate, (data) => {
277
+ * manager.voiceServerUpdate(data);
278
+ * });
279
+ * ```
280
+ */
281
+ voiceServerUpdate(data) {
282
+ this.voiceServers.set(data.guild_id, data);
283
+ this.expecting.add(data.guild_id);
284
+ return this._attemptConnection(data.guild_id);
285
+ }
286
+ /**
287
+ * Processes voice state update events from Discord.
288
+ *
289
+ * @param data - The voice state update data from Discord.
290
+ * @returns A promise resolving to whether a connection was established.
291
+ *
292
+ * @example
293
+ * ```typescript
294
+ * // In your Discord.js client events
295
+ * client.ws.on(GatewayDispatchEvents.VoiceStateUpdate, (data) => {
296
+ * manager.voiceStateUpdate(data);
297
+ * });
298
+ * ```
299
+ */
300
+ async voiceStateUpdate(data) {
301
+ if (data.user_id !== this.userId) return false;
302
+ if (!data.guild_id) return false;
303
+ if (data.channel_id) {
304
+ this.voiceStates.set(data.guild_id, data);
305
+ return this._attemptConnection(data.guild_id);
306
+ }
307
+ this.voiceServers.delete(data.guild_id);
308
+ this.voiceStates.delete(data.guild_id);
309
+ return false;
310
+ }
311
+ /**
312
+ * Sends a voice state update packet to Discord.
313
+ *
314
+ * @param guild - The ID of the guild.
315
+ * @param channel - The ID of the voice channel to join, or null to leave.
316
+ * @param options - Options for joining (selfmute, selfdeaf).
317
+ * @returns The result from the send function.
318
+ *
319
+ * @example
320
+ * ```typescript
321
+ * // Join a voice channel
322
+ * manager.sendWS("123456789012345678", "123456789012345679", { selfdeaf: true });
323
+ *
324
+ * // Leave a voice channel
325
+ * manager.sendWS("123456789012345678", null);
326
+ * ```
327
+ */
328
+ sendWS(guild, channel, { selfmute = false, selfdeaf = false } = {}) {
329
+ return this.send({
330
+ op: 4,
331
+ d: {
332
+ guild_id: guild,
333
+ channel_id: channel,
334
+ self_mute: selfmute,
335
+ self_deaf: selfdeaf
336
+ }
337
+ });
338
+ }
339
+ /**
340
+ * Gets all connected nodes, sorted by CPU load.
341
+ *
342
+ * @returns An array of connected nodes sorted by CPU load (least to most).
343
+ *
344
+ * @example
345
+ * ```typescript
346
+ * // Get the node with the least CPU load
347
+ * const bestNode = manager.idealNodes[0];
348
+ * if (bestNode) {
349
+ * console.log(`Best node for new connections: ${bestNode.id}`);
350
+ * }
351
+ * ```
352
+ */
353
+ get idealNodes() {
354
+ return Array.from(this.nodes.values()).filter((node) => node.connected).sort((a, b) => {
355
+ const aload = a.stats.cpu ? a.stats.cpu.systemLoad / a.stats.cpu.cores * 100 : 0;
356
+ const bload = b.stats.cpu ? b.stats.cpu.systemLoad / b.stats.cpu.cores * 100 : 0;
357
+ return aload - bload;
358
+ });
359
+ }
360
+ /**
361
+ * Attempts to establish a connection for a guild using available voice data.
362
+ *
363
+ * @internal
364
+ * @param guildID - The ID of the guild to attempt connecting.
365
+ * @returns A promise resolving to whether a connection was established.
366
+ */
367
+ async _attemptConnection(guildID) {
368
+ const server = this.voiceServers.get(guildID);
369
+ const state = this.voiceStates.get(guildID);
370
+ if (!server || !state || !this.expecting.has(guildID)) return false;
371
+ const player = this.players.get(guildID);
372
+ if (!player) return false;
373
+ await player.connect({ sessionId: state.session_id, event: server });
374
+ this.expecting.delete(guildID);
375
+ return true;
376
+ }
377
+ /**
378
+ * Creates a new player instance.
379
+ *
380
+ * @internal
381
+ * @param data - The data needed to create a player.
382
+ * @returns The created Player instance.
383
+ */
384
+ spawnPlayer(data) {
385
+ const exists = this.players.get(data.guild);
386
+ if (exists) return exists;
387
+ const node = this.nodes.get(data.node);
388
+ if (!node) throw new Error(`INVALID_HOST: No available node with ${data.node}`);
389
+ const player = new this.Player(node, data.guild);
390
+ this.players.set(data.guild, player);
391
+ return player;
392
+ }
393
+ send(packet) {
394
+ if (typeof this._send !== "function") {
395
+ throw new Error(
396
+ "Lavacord requires a send function to be defined in the Manager options. This function should send voice state updates to Discord."
397
+ );
398
+ }
399
+ return this._send(packet);
400
+ }
401
+ };
402
+
403
+ exports.Manager = Manager;
404
+ //# sourceMappingURL=Manager.cjs.map
405
+ //# sourceMappingURL=Manager.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/Manager.ts"],"names":["EventEmitter","Player","LavalinkNode"],"mappings":"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","file":"Manager.cjs","sourcesContent":["import { EventEmitter } from \"events\";\nimport { LavalinkNode } from \"./LavalinkNode\";\nimport { Player } from \"./Player\";\nimport type { JoinData, ManagerOptions, JoinOptions, LavalinkNodeOptions, ManagerEvents } from \"./Types\";\nimport { GatewayVoiceServerUpdateDispatchData, GatewayVoiceStateUpdate, GatewayVoiceStateUpdateDispatchData } from \"discord-api-types/v10\";\n\n/**\n * Main class that handles Lavalink node connections and player management.\n *\n * @remarks\n * The Manager acts as the central hub for Lavacord, managing connections to Lavalink nodes,\n * handling voice state updates, and providing a unified interface for player operations.\n */\nexport class Manager extends EventEmitter<ManagerEvents> {\n\t/**\n\t * A Map of Lavalink Nodes indexed by their IDs.\n\t */\n\tpublic nodes = new Map<string, LavalinkNode>();\n\n\t/**\n\t * A Map of all active players indexed by guild ID.\n\t */\n\tpublic players = new Map<string, Player>();\n\n\t/**\n\t * A Map of voice server update states indexed by guild ID.\n\t */\n\tpublic voiceServers = new Map<string, GatewayVoiceServerUpdateDispatchData>();\n\n\t/**\n\t * A Map of voice state update states indexed by guild ID.\n\t */\n\tpublic voiceStates = new Map<string, GatewayVoiceStateUpdateDispatchData>();\n\n\t/**\n\t * The user ID of the bot this Manager is managing.\n\t */\n\tpublic userId: string | null = null;\n\n\t/**\n\t * Function to send voice state update packets to Discord.\n\t *\n\t * @remarks\n\t * This can be implemented by the user in two ways:\n\t * 1. Via constructor options: `new Manager(nodes, { send: fn })`\n\t * 2. Via class extension: `class MyManager extends Manager { send(packet) { ... } }`\n\t */\n\tprivate _send?: (packet: GatewayVoiceStateUpdate) => unknown;\n\n\t/**\n\t * The Player class constructor used when creating new players.\n\t *\n\t * @remarks\n\t * Can be overridden in the manager options to use a custom Player implementation.\n\t */\n\tprivate readonly Player: typeof Player = Player;\n\n\t/**\n\t * A Set of guild IDs that are waiting for a connection.\n\t *\n\t * @internal\n\t */\n\tprivate readonly expecting = new Set<string>();\n\n\t/**\n\t * Creates a new Manager instance.\n\t *\n\t * @param nodes - An array of Lavalink node options to connect to.\n\t * @param options - Configuration options for the Manager.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Method 1: Define send function via options\n\t * const manager = new Manager([\n\t * {\n\t * id: \"main\",\n\t * host: \"localhost\",\n\t * port: 2333,\n\t * password: \"youshallnotpass\"\n\t * }\n\t * ], {\n\t * userId: \"bot_user_id\",\n\t * send: (packet) => {\n\t * const guild = client.guilds.cache.get(packet.d.guild_id);\n\t * if (guild) guild.shard.send(packet);\n\t * }\n\t * });\n\t *\n\t * // Method 2: Extend Manager class\n\t * class MyManager extends Manager {\n\t * send(packet) {\n\t * const guild = this.client.guilds.cache.get(packet.d.guild_id);\n\t * if (guild) guild.shard.send(packet);\n\t * }\n\t * }\n\t * const manager = new MyManager(nodes, { userId: \"bot_user_id\" });\n\t * ```\n\t */\n\tpublic constructor(nodes: LavalinkNodeOptions[], options?: ManagerOptions) {\n\t\tsuper();\n\t\toptions ??= {};\n\n\t\tif (options.userId) this.userId = options.userId;\n\t\tif (options.player) this.Player = options.player;\n\t\tif (options.send && !this._send) this._send = options.send;\n\n\t\tfor (const node of nodes) this.createNode(node);\n\t}\n\n\t/**\n\t * Connects all Lavalink nodes to their respective Lavalink servers.\n\t *\n\t * @returns A promise that resolves when all connections are established.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Connect all nodes\n\t * manager.connect()\n\t * .then(() => console.log('All nodes connected!'))\n\t * .catch(error => console.error('Failed to connect nodes:', error));\n\t * ```\n\t */\n\tpublic async connect(): Promise<LavalinkNode[]> {\n\t\tif (!this.userId)\n\t\t\tthrow new Error(\n\t\t\t\t\"Lavacord requires a client user ID before connecting. \\n\" +\n\t\t\t\t\t\"Set the user ID when constructing the Manager or after your Discord client is ready.\"\n\t\t\t);\n\t\treturn Promise.all(this.nodes.values().map((node) => node.connect().then(() => node)));\n\t}\n\n\t/**\n\t * Disconnects all players and nodes, effectively cleaning up all resources.\n\t *\n\t * @returns A promise that resolves when all disconnections are complete.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Disconnect everything\n\t * manager.disconnect()\n\t * .then(() => console.log('All players and nodes cleaned up'))\n\t * .catch(error => console.error('Error during disconnection:', error));\n\t * ```\n\t */\n\tpublic async disconnect(): Promise<void> {\n\t\tfor await (const player of this.players.keys()) await this.leave(player);\n\t\tfor (const node of this.nodes.values()) node.destroy();\n\t}\n\n\t/**\n\t * Creates a new Lavalink node and adds it to the nodes map.\n\t *\n\t * @param options - Configuration options for the node.\n\t * @returns The newly created LavalinkNode instance.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Add a new node\n\t * const newNode = manager.createNode({\n\t * id: \"node2\",\n\t * host: \"example.com\",\n\t * port: 2333,\n\t * password: \"securepassword\",\n\t * resuming: true\n\t * });\n\t * ```\n\t */\n\tpublic createNode(options: LavalinkNodeOptions): LavalinkNode {\n\t\tconst node = new LavalinkNode(this, options);\n\t\tthis.nodes.set(options.id, node);\n\t\treturn node;\n\t}\n\n\t/**\n\t * Disconnects and removes a node from the manager.\n\t *\n\t * @param id - The ID of the node to remove.\n\t * @returns Whether the node was successfully removed.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Remove a node\n\t * const removed = manager.removeNode(\"node1\");\n\t * if (removed) {\n\t * console.log(\"Node successfully removed\");\n\t * } else {\n\t * console.log(\"Node not found\");\n\t * }\n\t * ```\n\t */\n\tpublic removeNode(id: string): boolean {\n\t\tconst node = this.nodes.get(id);\n\t\tif (!node) return false;\n\t\tnode.destroy();\n\t\treturn this.nodes.delete(id);\n\t}\n\n\t/**\n\t * Joins a voice channel and creates a player for the guild.\n\t *\n\t * @param data - The data needed to join a voice channel.\n\t * @param joinOptions - Options for joining the channel (selfmute, selfdeaf).\n\t * @returns A promise resolving to the Player instance.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Join a voice channel\n\t * const player = await manager.join({\n\t * guild: \"123456789012345678\", // Guild ID\n\t * channel: \"123456789012345679\", // Voice Channel ID\n\t * node: \"main\" // Node ID\n\t * }, {\n\t * selfdeaf: true // Join deafened\n\t * });\n\t *\n\t * // Now you can use the player to play music\n\t * const result = await Rest.load(player.node, \"https://www.youtube.com/watch?v=dQw4w9WgXcQ\");\n\t * await player.play(result.tracks[0].track);\n\t * ```\n\t */\n\tpublic async join(data: JoinData, joinOptions: JoinOptions = {}): Promise<Player> {\n\t\tconst player = this.players.get(data.guild);\n\t\tif (player) return player;\n\t\tawait this.sendWS(data.guild, data.channel, joinOptions);\n\t\treturn this.spawnPlayer(data);\n\t}\n\n\t/**\n\t * Leaves a voice channel and cleans up the player.\n\t *\n\t * @param guild - The ID of the guild to leave.\n\t * @returns A promise resolving to whether the operation was successful.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Leave a voice channel\n\t * const success = await manager.leave(\"123456789012345678\");\n\t * if (success) {\n\t * console.log(\"Successfully left the voice channel\");\n\t * } else {\n\t * console.log(\"Not in a voice channel in this guild\");\n\t * }\n\t * ```\n\t */\n\tpublic async leave(guild: string): Promise<boolean> {\n\t\tawait this.sendWS(guild, null);\n\t\tconst player = this.players.get(guild);\n\t\tif (!player) return false;\n\n\t\tawait player.destroy();\n\t\treturn this.players.delete(guild);\n\t}\n\n\t/**\n\t * Switches a player from one node to another, implementing fallback capability.\n\t *\n\t * @param player - The player to move to another node.\n\t * @param node - The destination node.\n\t * @returns A promise resolving to the updated player.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Switch a player to another node (e.g. if current node is failing)\n\t * const player = manager.players.get(\"123456789012345678\");\n\t * const newNode = manager.nodes.get(\"backup-node\");\n\t *\n\t * if (player && newNode) {\n\t * await manager.switch(player, newNode);\n\t * console.log(\"Player switched to backup node\");\n\t * }\n\t * ```\n\t */\n\tpublic async switch(player: Player, node: LavalinkNode): Promise<Player> {\n\t\tplayer.node = node;\n\n\t\tif (!player.voice) return player;\n\t\tawait player.destroy();\n\t\tif (!player.track) return player;\n\n\t\tawait player.play(player.track.encoded, {\n\t\t\tposition: player.state.position,\n\t\t\tvolume: player.volume,\n\t\t\tfilters: player.filters,\n\t\t\tvoice: player.voice,\n\t\t\tpaused: player.paused,\n\t\t\tuserData: player.track.userData\n\t\t});\n\n\t\treturn player;\n\t}\n\n\t/**\n\t * Processes voice server update events from Discord.\n\t *\n\t * @param data - The voice server update data from Discord.\n\t * @returns A promise resolving to whether a connection was established.\n\t *\n\t * @example\n\t * ```typescript\n\t * // In your Discord.js client events\n\t * client.ws.on(GatewayDispatchEvents.VoiceServerUpdate, (data) => {\n\t * manager.voiceServerUpdate(data);\n\t * });\n\t * ```\n\t */\n\tpublic voiceServerUpdate(data: GatewayVoiceServerUpdateDispatchData): Promise<boolean> {\n\t\tthis.voiceServers.set(data.guild_id, data);\n\t\tthis.expecting.add(data.guild_id);\n\t\treturn this._attemptConnection(data.guild_id);\n\t}\n\n\t/**\n\t * Processes voice state update events from Discord.\n\t *\n\t * @param data - The voice state update data from Discord.\n\t * @returns A promise resolving to whether a connection was established.\n\t *\n\t * @example\n\t * ```typescript\n\t * // In your Discord.js client events\n\t * client.ws.on(GatewayDispatchEvents.VoiceStateUpdate, (data) => {\n\t * manager.voiceStateUpdate(data);\n\t * });\n\t * ```\n\t */\n\tpublic async voiceStateUpdate(data: GatewayVoiceStateUpdateDispatchData): Promise<boolean> {\n\t\tif (data.user_id !== this.userId) return false;\n\t\tif (!data.guild_id) return false;\n\n\t\tif (data.channel_id) {\n\t\t\tthis.voiceStates.set(data.guild_id, data);\n\t\t\treturn this._attemptConnection(data.guild_id);\n\t\t}\n\n\t\tthis.voiceServers.delete(data.guild_id);\n\t\tthis.voiceStates.delete(data.guild_id);\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Sends a voice state update packet to Discord.\n\t *\n\t * @param guild - The ID of the guild.\n\t * @param channel - The ID of the voice channel to join, or null to leave.\n\t * @param options - Options for joining (selfmute, selfdeaf).\n\t * @returns The result from the send function.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Join a voice channel\n\t * manager.sendWS(\"123456789012345678\", \"123456789012345679\", { selfdeaf: true });\n\t *\n\t * // Leave a voice channel\n\t * manager.sendWS(\"123456789012345678\", null);\n\t * ```\n\t */\n\tpublic sendWS(guild: string, channel: string | null, { selfmute = false, selfdeaf = false }: JoinOptions = {}): unknown {\n\t\treturn this.send({\n\t\t\top: 4,\n\t\t\td: {\n\t\t\t\tguild_id: guild,\n\t\t\t\tchannel_id: channel,\n\t\t\t\tself_mute: selfmute,\n\t\t\t\tself_deaf: selfdeaf\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Gets all connected nodes, sorted by CPU load.\n\t *\n\t * @returns An array of connected nodes sorted by CPU load (least to most).\n\t *\n\t * @example\n\t * ```typescript\n\t * // Get the node with the least CPU load\n\t * const bestNode = manager.idealNodes[0];\n\t * if (bestNode) {\n\t * console.log(`Best node for new connections: ${bestNode.id}`);\n\t * }\n\t * ```\n\t */\n\tpublic get idealNodes(): LavalinkNode[] {\n\t\treturn Array.from(this.nodes.values())\n\t\t\t.filter((node) => node.connected)\n\t\t\t.sort((a, b) => {\n\t\t\t\tconst aload = a.stats.cpu ? (a.stats.cpu.systemLoad / a.stats.cpu.cores) * 100 : 0;\n\t\t\t\tconst bload = b.stats.cpu ? (b.stats.cpu.systemLoad / b.stats.cpu.cores) * 100 : 0;\n\t\t\t\treturn aload - bload;\n\t\t\t});\n\t}\n\n\t/**\n\t * Attempts to establish a connection for a guild using available voice data.\n\t *\n\t * @internal\n\t * @param guildID - The ID of the guild to attempt connecting.\n\t * @returns A promise resolving to whether a connection was established.\n\t */\n\tprivate async _attemptConnection(guildID: string): Promise<boolean> {\n\t\tconst server = this.voiceServers.get(guildID);\n\t\tconst state = this.voiceStates.get(guildID);\n\n\t\tif (!server || !state || !this.expecting.has(guildID)) return false;\n\n\t\tconst player = this.players.get(guildID);\n\t\tif (!player) return false;\n\n\t\tawait player.connect({ sessionId: state.session_id, event: server });\n\t\tthis.expecting.delete(guildID);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Creates a new player instance.\n\t *\n\t * @internal\n\t * @param data - The data needed to create a player.\n\t * @returns The created Player instance.\n\t */\n\tprivate spawnPlayer(data: JoinData): Player {\n\t\tconst exists = this.players.get(data.guild);\n\t\tif (exists) return exists;\n\t\tconst node = this.nodes.get(data.node);\n\t\tif (!node) throw new Error(`INVALID_HOST: No available node with ${data.node}`);\n\t\tconst player = new this.Player(node, data.guild);\n\t\tthis.players.set(data.guild, player);\n\t\treturn player;\n\t}\n\n\tprotected send(packet: GatewayVoiceStateUpdate): unknown {\n\t\tif (typeof this._send !== \"function\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"Lavacord requires a send function to be defined in the Manager options.\\\n\t\t\t\tThis function should send voice state updates to Discord.\"\n\t\t\t);\n\t\t}\n\t\treturn this._send(packet);\n\t}\n}\n"]}
@@ -0,0 +1,220 @@
1
+ 'use strict';
2
+
3
+ var events = require('events');
4
+ var Rest_cjs = require('./Rest.cjs');
5
+
6
+ var __defProp = Object.defineProperty;
7
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
+ var Player = class extends events.EventEmitter {
9
+ /**
10
+ * Creates a new player instance.
11
+ *
12
+ * @param node - The Lavalink node this player is connected to.
13
+ * @param guildId - The guild ID that this player is associated with.
14
+ */
15
+ constructor(node, guildId) {
16
+ super();
17
+ this.node = node;
18
+ this.guildId = guildId;
19
+ }
20
+ static {
21
+ __name(this, "Player");
22
+ }
23
+ /**
24
+ * The current state of this Player
25
+ *
26
+ * @remarks
27
+ * Contains information about the player state from Lavalink, including position, filters, etc.
28
+ */
29
+ state = {
30
+ time: 0,
31
+ position: 0,
32
+ connected: false,
33
+ ping: 0
34
+ };
35
+ /**
36
+ * The timestamp when the current track started playing
37
+ *
38
+ * @remarks
39
+ * This is a client-side timestamp, not synchronized with Lavalink.
40
+ * Can be used to calculate approximate playback position.
41
+ */
42
+ timestamp = null;
43
+ /**
44
+ * Whether the audio playback is currently paused
45
+ */
46
+ paused = false;
47
+ /**
48
+ * The current volume level (0-1000)
49
+ */
50
+ volume = 100;
51
+ /**
52
+ * The current track in Lavalink's base64 string form
53
+ *
54
+ * @remarks
55
+ * This is null when no track is loaded or when playback has ended.
56
+ */
57
+ track = null;
58
+ /**
59
+ * The voice connection state from Lavalink API
60
+ */
61
+ voice = null;
62
+ /**
63
+ * The current audio filters applied to this player
64
+ *
65
+ * @remarks
66
+ * This includes effects like equalizer, karaoke, etc.
67
+ */
68
+ filters = {};
69
+ /**
70
+ * Updates the current player on the Lavalink node.
71
+ * @see {@link https://lavalink.dev/api/rest#update-player}
72
+ *
73
+ * @param options - The update options to apply to the player.
74
+ * @param noReplace - If true, the event will be dropped if there's a currently playing track.
75
+ * @returns {Promise<APIPlayer>} The updated player information from Lavalink.
76
+ */
77
+ async update(options, noReplace = false) {
78
+ const d = await Rest_cjs.Rest.updatePlayer(this.node, this.guildId, options, noReplace);
79
+ if ("track" in d) this.track = d.track;
80
+ if ("volume" in d) this.volume = d.volume;
81
+ if ("paused" in d) this.paused = d.paused;
82
+ if ("state" in d) this.state = d.state;
83
+ if ("filters" in d) this.filters = d.filters;
84
+ if ("voice" in d) this.voice = d.voice;
85
+ return d;
86
+ }
87
+ /**
88
+ * Plays a track using its base64 encoded string.
89
+ *
90
+ * @param track - The base64 encoded track string from Lavalink.
91
+ * @param options - Additional options for playback.
92
+ * @returns A promise resolving to the updated player information.
93
+ */
94
+ async play(track, options) {
95
+ const { userData, noReplace, ...opts } = options || {};
96
+ const d = await this.update({ track: { encoded: track, userData }, ...opts }, noReplace ?? false);
97
+ this.timestamp = Date.now();
98
+ return d;
99
+ }
100
+ /**
101
+ * Stops the currently playing track.
102
+ *
103
+ * @remarks
104
+ * This will trigger a "TrackEndEvent" with reason "STOPPED".
105
+ *
106
+ * @returns A promise resolving to the updated player information.
107
+ */
108
+ stop() {
109
+ return this.update({ track: { encoded: null } });
110
+ }
111
+ /**
112
+ * Pauses or resumes the current track.
113
+ *
114
+ * @param pause - Whether to pause (true) or resume (false) playback.
115
+ * @returns A promise resolving to the updated player information.
116
+ */
117
+ async pause(pause) {
118
+ const d = await this.update({ paused: pause });
119
+ if (this.listenerCount("pause")) this.emit("pause", pause);
120
+ if (this.manager.listenerCount("playerPause")) this.manager.emit("playerPause", this, pause);
121
+ return d;
122
+ }
123
+ /**
124
+ * Changes the volume of the current playback.
125
+ *
126
+ * @param volume - The volume level as a number between 0 and 1000
127
+ * @returns A promise resolving to the updated player information.
128
+ */
129
+ async setVolume(volume) {
130
+ const d = await this.update({ volume });
131
+ if (this.listenerCount("volume")) this.emit("volume", volume);
132
+ if (this.manager.listenerCount("playerVolume")) this.manager.emit("playerVolume", this, volume);
133
+ return d;
134
+ }
135
+ /**
136
+ * Seeks to a specific position in the current track.
137
+ *
138
+ * @param position - The position to seek to in milliseconds.
139
+ * @returns A promise resolving to the updated player information.
140
+ */
141
+ async seek(position) {
142
+ const d = await this.update({ position });
143
+ if (this.listenerCount("seek")) this.emit("seek", position);
144
+ if (this.manager.listenerCount("playerSeek")) this.manager.emit("playerSeek", this, position);
145
+ return d;
146
+ }
147
+ /**
148
+ * Applies audio filters to the current playback.
149
+ *
150
+ * @param options - The filter options to apply.
151
+ * @returns A promise resolving to the updated player information.
152
+ */
153
+ async setFilters(options) {
154
+ const d = await this.update({ filters: options });
155
+ if (this.listenerCount("filters")) this.emit("filters", options);
156
+ if (this.manager.listenerCount("playerFilters")) this.manager.emit("playerFilters", this, options);
157
+ return d;
158
+ }
159
+ /**
160
+ * Sets the equalizer effect for the current playback.
161
+ *
162
+ * @param bands - An array of equalizer bands to adjust.
163
+ * @returns A promise resolving to the updated player information.
164
+ *
165
+ * @remarks
166
+ * Each band is an object with 'band' (0-14) and 'gain' (-0.25 to 1.0) properties.
167
+ */
168
+ async setEqualizer(bands) {
169
+ return this.setFilters({ equalizer: bands });
170
+ }
171
+ /**
172
+ * Destroys the player on the Lavalink node.
173
+ *
174
+ * @remarks
175
+ * This sends a destroy signal to Lavalink to clean up resources for this guild ID.
176
+ * It doesn't affect the Discord voice connection - use {@link Manager.leave} for that.
177
+ *
178
+ * @returns {Promise<DestroyPlayerResult>} A promise resolving to the destroy result.
179
+ */
180
+ async destroy() {
181
+ return Rest_cjs.Rest.destroyPlayer(this.node, this.guildId);
182
+ }
183
+ /**
184
+ * Provides voice server update information to Lavalink to establish a connection.
185
+ *
186
+ * @param data - The voice update state containing session ID and voice server information.
187
+ * @returns A promise resolving to the updated player information.
188
+ */
189
+ connect(data) {
190
+ return this.update({
191
+ voice: {
192
+ token: data.event.token,
193
+ endpoint: data.event.endpoint,
194
+ sessionId: data.sessionId
195
+ }
196
+ });
197
+ }
198
+ /**
199
+ * Switches the player to a different voice channel.
200
+ *
201
+ * @param channel - The ID of the voice channel to switch to.
202
+ * @param options - Options for joining the channel (selfMute, selfDeaf).
203
+ * @returns Does not return anything, but sends a WebSocket message to the Lavalink node.
204
+ */
205
+ switchChannel(channel, options = {}) {
206
+ return this.manager.sendWS(this.guildId, channel, options);
207
+ }
208
+ /**
209
+ * Gets the manager instance that created this player.
210
+ *
211
+ * @returns {Manager} The manager instance.
212
+ */
213
+ get manager() {
214
+ return this.node.manager;
215
+ }
216
+ };
217
+
218
+ exports.Player = Player;
219
+ //# sourceMappingURL=Player.cjs.map
220
+ //# sourceMappingURL=Player.cjs.map