magmastream 2.9.0-dev.9 → 2.9.1-dev.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.
@@ -3,13 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Player = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const Filters_1 = require("./Filters");
6
- const Manager_1 = require("./Manager");
7
- const Node_1 = require("./Node");
8
- const Queue_1 = require("./Queue");
6
+ const MemoryQueue_1 = require("../statestorage/MemoryQueue");
9
7
  const Utils_1 = require("./Utils");
10
8
  const _ = tslib_1.__importStar(require("lodash"));
11
9
  const playerCheck_1 = tslib_1.__importDefault(require("../utils/playerCheck"));
12
- const RedisQueue_1 = require("./RedisQueue");
10
+ const RedisQueue_1 = require("../statestorage/RedisQueue");
11
+ const Enums_1 = require("./Enums");
12
+ const ws_1 = require("ws");
13
+ const JsonQueue_1 = require("../statestorage/JsonQueue");
13
14
  class Player {
14
15
  options;
15
16
  /** The Queue for the Player. */
@@ -41,7 +42,7 @@ class Player {
41
42
  /**The now playing message. */
42
43
  nowPlayingMessage;
43
44
  /** The current state of the player. */
44
- state = Utils_1.StateTypes.Disconnected;
45
+ state = Enums_1.StateTypes.Disconnected;
45
46
  /** The equalizer bands array. */
46
47
  bands = new Array(15).fill(0.0);
47
48
  /** The voice state object from Discord. */
@@ -52,10 +53,18 @@ class Player {
52
53
  isAutoplay = false;
53
54
  /** The number of times to try autoplay before emitting queueEnd. */
54
55
  autoplayTries = 3;
55
- static _manager;
56
+ /** The cluster ID for the player. */
57
+ clusterId = 0;
56
58
  data = {};
57
59
  dynamicLoopInterval = null;
58
60
  dynamicRepeatIntervalMs = null;
61
+ static _manager;
62
+ /** Should only be used when the node is a NodeLink */
63
+ voiceReceiverWsClient;
64
+ isConnectToVoiceReceiver;
65
+ voiceReceiverReconnectTimeout;
66
+ voiceReceiverAttempt;
67
+ voiceReceiverReconnectTries;
59
68
  /**
60
69
  * Creates a new player, returns one if it already exists.
61
70
  * @param options The player options.
@@ -68,6 +77,7 @@ class Player {
68
77
  this.manager = Utils_1.Structure.get("Player")._manager;
69
78
  if (!this.manager)
70
79
  throw new RangeError("Manager has not been initiated.");
80
+ this.clusterId = this.manager.options.clusterId || 0;
71
81
  // Check the player options for errors.
72
82
  (0, playerCheck_1.default)(options);
73
83
  // Set the guild ID and voice state.
@@ -82,29 +92,40 @@ class Player {
82
92
  if (options.textChannelId)
83
93
  this.textChannelId = options.textChannelId;
84
94
  // Set the node to use, either the specified node or the first available node.
85
- const node = this.manager.nodes.get(options.node);
95
+ const node = this.manager.nodes.get(options.nodeIdentifier);
86
96
  this.node = node || this.manager.useableNode;
87
97
  // If no node is available, throw an error.
88
98
  if (!this.node)
89
99
  throw new RangeError("No available nodes.");
90
100
  // Initialize the queue with the guild ID and manager.
91
- if (this.manager.options.stateStorage.type === Manager_1.StateStorageType.Redis) {
92
- this.queue = new RedisQueue_1.RedisQueue(this.guildId, this.manager);
93
- }
94
- else {
95
- this.queue = new Queue_1.Queue(this.guildId, this.manager);
96
- }
97
- if (this.queue instanceof Queue_1.Queue) {
98
- this.queue.previous = [];
101
+ switch (this.manager.options.stateStorage.type) {
102
+ case Enums_1.StateStorageType.Redis:
103
+ this.queue = new RedisQueue_1.RedisQueue(this.guildId, this.manager);
104
+ break;
105
+ case Enums_1.StateStorageType.Memory:
106
+ this.queue = new MemoryQueue_1.MemoryQueue(this.guildId, this.manager);
107
+ break;
108
+ case Enums_1.StateStorageType.JSON:
109
+ this.queue = new JsonQueue_1.JsonQueue(this.guildId, this.manager);
110
+ break;
99
111
  }
100
112
  // Add the player to the manager's player collection.
101
113
  this.manager.players.set(options.guildId, this);
102
114
  // Set the initial volume.
103
115
  this.setVolume(options.volume ?? 100);
104
116
  // Initialize the filters.
105
- this.filters = new Filters_1.Filters(this);
117
+ this.filters = new Filters_1.Filters(this, this.manager);
106
118
  // Emit the playerCreate event.
107
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerCreate, this);
119
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerCreate, this);
120
+ }
121
+ /**
122
+ * Initializes the static properties of the Player class.
123
+ * @hidden
124
+ * @param manager The Manager to use.
125
+ */
126
+ static init(manager) {
127
+ // Set the Manager to use.
128
+ this._manager = manager;
108
129
  }
109
130
  /**
110
131
  * Set custom data.
@@ -125,15 +146,6 @@ class Player {
125
146
  // Access the data object using the key and cast it to the specified type T.
126
147
  return this.data[key];
127
148
  }
128
- /**
129
- * Initializes the static properties of the Player class.
130
- * @hidden
131
- * @param manager The Manager to use.
132
- */
133
- static init(manager) {
134
- // Set the Manager to use.
135
- this._manager = manager;
136
- }
137
149
  /**
138
150
  * Same as Manager#search() but a shortcut on the player itself.
139
151
  * @param query
@@ -153,11 +165,10 @@ class Player {
153
165
  throw new RangeError("No voice channel has been set. You must use the `setVoiceChannelId()` method to set the voice channel before connecting.");
154
166
  }
155
167
  // Set the player state to connecting.
156
- this.state = Utils_1.StateTypes.Connecting;
168
+ this.state = Enums_1.StateTypes.Connecting;
157
169
  // Clone the current player state for comparison.
158
170
  const oldPlayer = this ? { ...this } : null;
159
- // Send the voice state update to the gateway.
160
- this.manager.options.send(this.guildId, {
171
+ this.manager.sendPacket({
161
172
  op: 4,
162
173
  d: {
163
174
  guild_id: this.guildId,
@@ -167,49 +178,50 @@ class Player {
167
178
  },
168
179
  });
169
180
  // Set the player state to connected.
170
- this.state = Utils_1.StateTypes.Connected;
181
+ this.state = Enums_1.StateTypes.Connected;
171
182
  // Emit the player state update event.
172
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
173
- changeType: Manager_1.PlayerStateEventTypes.ConnectionChange,
183
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
184
+ changeType: Enums_1.PlayerStateEventTypes.ConnectionChange,
174
185
  details: {
175
- changeType: "connect",
176
- previousConnection: oldPlayer?.state === Utils_1.StateTypes.Connected,
186
+ type: "connection",
187
+ action: "connect",
188
+ previousConnection: oldPlayer?.state === Enums_1.StateTypes.Connected,
177
189
  currentConnection: true,
178
190
  },
179
191
  });
180
192
  }
181
193
  /**
182
194
  * Disconnects the player from the voice channel.
183
- * @throws {TypeError} If the player is not connected.
184
- * @returns {this} - The current instance of the Player class for method chaining.
195
+ * @returns {this} The player instance.
185
196
  */
186
197
  async disconnect() {
187
198
  // Set the player state to disconnecting.
188
- this.state = Utils_1.StateTypes.Disconnecting;
199
+ this.state = Enums_1.StateTypes.Disconnecting;
189
200
  // Clone the current player state for comparison.
190
201
  const oldPlayer = this ? { ...this } : null;
191
202
  // Pause the player.
192
203
  await this.pause(true);
193
204
  // Send the voice state update to the gateway.
194
- this.manager.options.send(this.guildId, {
205
+ this.manager.sendPacket({
195
206
  op: 4,
196
207
  d: {
197
208
  guild_id: this.guildId,
198
209
  channel_id: null,
199
- self_mute: false,
200
- self_deaf: false,
210
+ self_mute: null,
211
+ self_deaf: null,
201
212
  },
202
213
  });
203
214
  // Set the player voice channel to null.
204
215
  this.voiceChannelId = null;
205
216
  // Set the player state to disconnected.
206
- this.state = Utils_1.StateTypes.Disconnected;
217
+ this.state = Enums_1.StateTypes.Disconnected;
207
218
  // Emit the player state update event.
208
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
209
- changeType: Manager_1.PlayerStateEventTypes.ConnectionChange,
219
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
220
+ changeType: Enums_1.PlayerStateEventTypes.ConnectionChange,
210
221
  details: {
211
- changeType: "disconnect",
212
- previousConnection: oldPlayer.state === Utils_1.StateTypes.Connected,
222
+ type: "connection",
223
+ action: "disconnect",
224
+ previousConnection: oldPlayer.state === Enums_1.StateTypes.Connected,
213
225
  currentConnection: false,
214
226
  },
215
227
  });
@@ -223,21 +235,22 @@ class Player {
223
235
  * @emits {PlayerStateUpdate} - Emitted when the player state is updated.
224
236
  */
225
237
  async destroy(disconnect = true) {
226
- const oldPlayer = this ? { ...this } : null;
227
- this.state = Utils_1.StateTypes.Destroying;
238
+ this.state = Enums_1.StateTypes.Destroying;
228
239
  if (disconnect) {
229
- await this.disconnect();
240
+ await this.disconnect().catch((err) => {
241
+ console.warn(`[Player#destroy] Failed to disconnect player ${this.guildId}:`, err);
242
+ });
230
243
  }
231
- await this.node.rest.destroyPlayer(this.guildId);
232
- await this.queue.clear();
233
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, null, {
234
- changeType: Manager_1.PlayerStateEventTypes.PlayerDestroy,
244
+ await this.node.rest.destroyPlayer(this.guildId).catch((err) => {
245
+ console.warn(`[Player#destroy] REST failed to destroy player ${this.guildId}:`, err);
235
246
  });
236
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerDestroy, this);
247
+ await this.queue.clear();
248
+ await this.queue.clearPrevious();
249
+ await this.queue.setCurrent(null);
250
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerDestroy, this);
237
251
  const deleted = this.manager.players.delete(this.guildId);
238
- if (!deleted) {
239
- console.warn(`Failed to delete player with guildId: ${this.guildId}`);
240
- }
252
+ if (this.manager.options.stateStorage.deleteInactivePlayers)
253
+ await this.manager.cleanupInactivePlayer(this.guildId);
241
254
  return deleted;
242
255
  }
243
256
  /**
@@ -254,12 +267,14 @@ class Player {
254
267
  const oldPlayer = this ? { ...this } : null;
255
268
  // Update the player voice channel
256
269
  this.voiceChannelId = channel;
270
+ this.options.voiceChannelId = channel;
257
271
  this.connect();
258
272
  // Emit a player state update event
259
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
260
- changeType: Manager_1.PlayerStateEventTypes.ChannelChange,
273
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
274
+ changeType: Enums_1.PlayerStateEventTypes.ChannelChange,
261
275
  details: {
262
- changeType: "voice",
276
+ type: "channel",
277
+ action: "voice",
263
278
  previousChannel: oldPlayer.voiceChannelId || null,
264
279
  currentChannel: this.voiceChannelId,
265
280
  },
@@ -284,11 +299,13 @@ class Player {
284
299
  const oldPlayer = this ? { ...this } : null;
285
300
  // Update the text channel property
286
301
  this.textChannelId = channel;
302
+ this.options.textChannelId = channel;
287
303
  // Emit a player state update event with channel change details
288
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
289
- changeType: Manager_1.PlayerStateEventTypes.ChannelChange,
304
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
305
+ changeType: Enums_1.PlayerStateEventTypes.ChannelChange,
290
306
  details: {
291
- changeType: "text",
307
+ type: "channel",
308
+ action: "text",
292
309
  previousChannel: oldPlayer.textChannelId || null,
293
310
  currentChannel: this.textChannelId,
294
311
  },
@@ -364,9 +381,11 @@ class Player {
364
381
  this.set("Internal_BotUser", null);
365
382
  }
366
383
  const oldPlayer = { ...this };
367
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
368
- changeType: Manager_1.PlayerStateEventTypes.AutoPlayChange,
384
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
385
+ changeType: Enums_1.PlayerStateEventTypes.AutoPlayChange,
369
386
  details: {
387
+ type: "autoplay",
388
+ action: "toggle",
370
389
  previousAutoplay: oldPlayer.isAutoplay,
371
390
  currentAutoplay: this.isAutoplay,
372
391
  },
@@ -391,23 +410,28 @@ class Player {
391
410
  * @emits {PlayerStateUpdate} - Emitted when the volume is changed.
392
411
  * @example
393
412
  * player.setVolume(50);
413
+ * player.setVolume(50, { gradual: true, interval: 50, step: 5 });
394
414
  */
395
415
  async setVolume(volume) {
396
416
  if (isNaN(volume))
397
417
  throw new TypeError("Volume must be a number.");
398
418
  if (volume < 0 || volume > 1000)
399
419
  throw new RangeError("Volume must be between 0 and 1000.");
400
- const oldPlayer = this ? { ...this } : null;
420
+ const oldVolume = this.volume;
421
+ const oldPlayer = { ...this };
401
422
  await this.node.rest.updatePlayer({
402
423
  guildId: this.options.guildId,
403
- data: {
404
- volume,
405
- },
424
+ data: { volume },
406
425
  });
407
426
  this.volume = volume;
408
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
409
- changeType: Manager_1.PlayerStateEventTypes.VolumeChange,
410
- details: { previousVolume: oldPlayer.volume || null, currentVolume: this.volume },
427
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
428
+ changeType: Enums_1.PlayerStateEventTypes.VolumeChange,
429
+ details: {
430
+ type: "volume",
431
+ action: "adjust",
432
+ previousVolume: oldVolume,
433
+ currentVolume: this.volume,
434
+ },
411
435
  });
412
436
  return this;
413
437
  }
@@ -416,7 +440,7 @@ class Player {
416
440
  * @param {SponsorBlockSegment[]} segments - The sponsorblock segments to set. Defaults to `[SponsorBlockSegment.Sponsor, SponsorBlockSegment.SelfPromo]` if not provided.
417
441
  * @returns {Promise<void>} The promise is resolved when the operation is complete.
418
442
  */
419
- async setSponsorBlock(segments = [Node_1.SponsorBlockSegment.Sponsor, Node_1.SponsorBlockSegment.SelfPromo]) {
443
+ async setSponsorBlock(segments = [Enums_1.SponsorBlockSegment.Sponsor, Enums_1.SponsorBlockSegment.SelfPromo]) {
420
444
  return this.node.setSponsorBlock(this, segments);
421
445
  }
422
446
  /**
@@ -461,10 +485,11 @@ class Player {
461
485
  this.dynamicRepeat = false;
462
486
  }
463
487
  // Emit an event indicating the repeat mode has changed
464
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
465
- changeType: Manager_1.PlayerStateEventTypes.RepeatChange,
466
- detail: {
467
- changeType: "track",
488
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
489
+ changeType: Enums_1.PlayerStateEventTypes.RepeatChange,
490
+ details: {
491
+ type: "repeat",
492
+ action: "track",
468
493
  previousRepeat: this.getRepeatState(oldPlayer),
469
494
  currentRepeat: this.getRepeatState(this),
470
495
  },
@@ -495,10 +520,11 @@ class Player {
495
520
  this.dynamicRepeat = false;
496
521
  }
497
522
  // Emit the player state update event
498
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
499
- changeType: Manager_1.PlayerStateEventTypes.RepeatChange,
500
- detail: {
501
- changeType: "queue",
523
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
524
+ changeType: Enums_1.PlayerStateEventTypes.RepeatChange,
525
+ details: {
526
+ type: "repeat",
527
+ action: "queue",
502
528
  previousRepeat: this.getRepeatState(oldPlayer),
503
529
  currentRepeat: this.getRepeatState(this),
504
530
  },
@@ -551,10 +577,11 @@ class Player {
551
577
  this.dynamicRepeat = false;
552
578
  }
553
579
  // Emit a player state update event
554
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
555
- changeType: Manager_1.PlayerStateEventTypes.RepeatChange,
556
- detail: {
557
- changeType: "dynamic",
580
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
581
+ changeType: Enums_1.PlayerStateEventTypes.RepeatChange,
582
+ details: {
583
+ type: "repeat",
584
+ action: "dynamic",
558
585
  previousRepeat: this.getRepeatState(oldPlayer),
559
586
  currentRepeat: this.getRepeatState(this),
560
587
  },
@@ -605,10 +632,11 @@ class Player {
605
632
  encodedTrack: null,
606
633
  },
607
634
  });
608
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
609
- changeType: Manager_1.PlayerStateEventTypes.QueueChange,
635
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
636
+ changeType: Enums_1.PlayerStateEventTypes.QueueChange,
610
637
  details: {
611
- changeType: "remove",
638
+ type: "queue",
639
+ action: "remove",
612
640
  tracks: removedTracks,
613
641
  },
614
642
  });
@@ -640,9 +668,11 @@ class Player {
640
668
  },
641
669
  });
642
670
  // Emit an event indicating the pause state has changed.
643
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
644
- changeType: Manager_1.PlayerStateEventTypes.PauseChange,
671
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
672
+ changeType: Enums_1.PlayerStateEventTypes.PauseChange,
645
673
  details: {
674
+ type: "pause",
675
+ action: "pause",
646
676
  previousPause: oldPlayer.paused,
647
677
  currentPause: this.paused,
648
678
  },
@@ -656,31 +686,26 @@ class Player {
656
686
  * @emits {PlayerStateUpdate} - With {@link PlayerStateEventTypes.TrackChange} as the change type.
657
687
  */
658
688
  async previous() {
659
- // Check if there are previous tracks in the queue.
660
- if (!(await this.queue.getPrevious()).length) {
689
+ // Get and remove the most recent previous track
690
+ const lastTrack = await this.queue.popPrevious();
691
+ if (!lastTrack) {
692
+ await this.queue.clearPrevious();
661
693
  throw new Error("No previous track available.");
662
694
  }
663
695
  // Capture the current state of the player before making changes.
664
696
  const oldPlayer = { ...this };
665
- // Store the current track before changing it.
666
- // let currentTrackBeforeChange: Track | null = this.queue.current ? (this.queue.current as Track) : null;
667
- // Get the last played track and remove it from the history
668
- const lastTrack = (await this.queue.getPrevious()).pop();
669
- // Set the skip flag to true to prevent the onTrackEnd event from playing the next track.
697
+ // Set skip flag so trackEnd doesn't add current to previous
670
698
  this.set("skipFlag", true);
671
699
  await this.play(lastTrack);
672
- // Add the current track back to the end of the previous queue
673
- // if (currentTrackBeforeChange) this.queue.push(currentTrackBeforeChange);
674
- // Emit a player state update event indicating the track change to previous.
675
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
676
- changeType: Manager_1.PlayerStateEventTypes.TrackChange,
700
+ // Emit state update
701
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
702
+ changeType: Enums_1.PlayerStateEventTypes.TrackChange,
677
703
  details: {
678
- changeType: "previous",
704
+ type: "track",
705
+ action: "previous",
679
706
  track: lastTrack,
680
707
  },
681
708
  });
682
- // Reset the skip flag.
683
- this.set("skipFlag", false);
684
709
  return this;
685
710
  }
686
711
  /**
@@ -714,10 +739,11 @@ class Player {
714
739
  },
715
740
  });
716
741
  // Emit an event to notify the manager of the track change.
717
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
718
- changeType: Manager_1.PlayerStateEventTypes.TrackChange,
742
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
743
+ changeType: Enums_1.PlayerStateEventTypes.TrackChange,
719
744
  details: {
720
- changeType: "timeUpdate",
745
+ type: "track",
746
+ action: "timeUpdate",
721
747
  previousTime: oldPlayer.position,
722
748
  currentTime: this.position,
723
749
  },
@@ -761,13 +787,22 @@ class Player {
761
787
  const node = this.manager.nodes.get(identifier);
762
788
  if (!node)
763
789
  throw new Error(`Node with identifier ${identifier} not found`);
790
+ if (this.state !== Enums_1.StateTypes.Connected) {
791
+ return this;
792
+ }
764
793
  if (node.options.identifier === this.node.options.identifier) {
765
794
  return this;
766
795
  }
767
796
  try {
768
797
  const playerPosition = this.position;
769
- const { sessionId, event: { token, endpoint }, } = this.voiceState;
770
798
  const currentTrack = (await this.queue.getCurrent()) ? await this.queue.getCurrent() : null;
799
+ // Safely get voice state properties with null checks
800
+ const sessionId = this.voiceState?.sessionId;
801
+ const token = this.voiceState?.event?.token;
802
+ const endpoint = this.voiceState?.event?.endpoint;
803
+ if (!sessionId || !token || !endpoint) {
804
+ throw new Error(`Voice state is not properly initialized for player ${this.guildId}. The bot might not be connected to a voice channel.`);
805
+ }
771
806
  await this.node.rest.destroyPlayer(this.guildId).catch(() => { });
772
807
  this.manager.players.delete(this.guildId);
773
808
  this.node = node;
@@ -798,7 +833,7 @@ class Player {
798
833
  if (!newOptions.textChannelId)
799
834
  throw new Error("Text channel ID is required");
800
835
  // Check if a player already exists for the new guild
801
- let newPlayer = await this.manager.getPlayer(newOptions.guildId);
836
+ let newPlayer = this.manager.getPlayer(newOptions.guildId);
802
837
  // If the player already exists and force is false, return the existing player
803
838
  if (newPlayer && !force)
804
839
  return newPlayer;
@@ -826,12 +861,12 @@ class Player {
826
861
  if (force && newPlayer) {
827
862
  await newPlayer.destroy();
828
863
  }
829
- newOptions.node = newOptions.node ?? this.options.node;
864
+ newOptions.nodeIdentifier = newOptions.nodeIdentifier ?? this.options.nodeIdentifier;
830
865
  newOptions.selfDeafen = newOptions.selfDeafen ?? oldPlayerProperties.selfDeafen;
831
866
  newOptions.selfMute = newOptions.selfMute ?? oldPlayerProperties.selfMute;
832
867
  newOptions.volume = newOptions.volume ?? oldPlayerProperties.volume;
833
868
  // Deep clone the current player
834
- const clonedPlayer = await this.manager.create(newOptions);
869
+ const clonedPlayer = this.manager.create(newOptions);
835
870
  // Connect the cloned player to the new voice channel
836
871
  clonedPlayer.connect();
837
872
  // Update the player's state on the Lavalink node
@@ -871,7 +906,7 @@ class Player {
871
906
  queueSize: clonedPlayer.queue.size,
872
907
  },
873
908
  };
874
- this.manager.emit(Manager_1.ManagerEventTypes.Debug, `[PLAYER] Transferred player to a new server: ${JSON.stringify(debugInfo)}.`);
909
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[PLAYER] Transferred player to a new server: ${JSON.stringify(debugInfo)}.`);
875
910
  // Return the cloned player
876
911
  return clonedPlayer;
877
912
  }
@@ -901,5 +936,133 @@ class Player {
901
936
  }
902
937
  return result;
903
938
  }
939
+ /**
940
+ * Sets up the voice receiver for the player.
941
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is set up.
942
+ * @throws {Error} - If the node is not a NodeLink.
943
+ */
944
+ async setupVoiceReceiver() {
945
+ if (!this.node.isNodeLink)
946
+ throw new Error("This function is only available for NodeLinks");
947
+ if (this.voiceReceiverWsClient)
948
+ await this.removeVoiceReceiver();
949
+ const headers = {
950
+ Authorization: this.node.options.password,
951
+ "User-Id": this.manager.options.clientId,
952
+ "Guild-Id": this.guildId,
953
+ "Client-Name": this.manager.options.clientName,
954
+ };
955
+ const { host, useSSL, port } = this.node.options;
956
+ this.voiceReceiverWsClient = new ws_1.WebSocket(`${useSSL ? "wss" : "ws"}://${host}:${port}/connection/data`, { headers });
957
+ this.voiceReceiverWsClient.on("open", () => this.openVoiceReceiver());
958
+ this.voiceReceiverWsClient.on("error", (err) => this.onVoiceReceiverError(err));
959
+ this.voiceReceiverWsClient.on("message", (data) => this.onVoiceReceiverMessage(data.toString()));
960
+ this.voiceReceiverWsClient.on("close", (code, reason) => this.closeVoiceReceiver(code, reason.toString()));
961
+ }
962
+ /**
963
+ * Removes the voice receiver for the player.
964
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is removed.
965
+ * @throws {Error} - If the node is not a NodeLink.
966
+ */
967
+ async removeVoiceReceiver() {
968
+ if (!this.node.isNodeLink)
969
+ throw new Error("This function is only available for NodeLinks");
970
+ if (this.voiceReceiverWsClient) {
971
+ this.voiceReceiverWsClient.close(1000, "destroy");
972
+ this.voiceReceiverWsClient.removeAllListeners();
973
+ this.voiceReceiverWsClient = null;
974
+ }
975
+ this.isConnectToVoiceReceiver = false;
976
+ }
977
+ /**
978
+ * Closes the voice receiver for the player.
979
+ * @param {number} code - The code to close the voice receiver with.
980
+ * @param {string} reason - The reason to close the voice receiver with.
981
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is closed.
982
+ */
983
+ async closeVoiceReceiver(code, reason) {
984
+ await this.disconnectVoiceReceiver();
985
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[PLAYER] Closed voice receiver for player ${this.guildId} with code ${code} and reason ${reason}`);
986
+ if (code !== 1000)
987
+ await this.reconnectVoiceReceiver();
988
+ }
989
+ /**
990
+ * Reconnects the voice receiver for the player.
991
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is reconnected.
992
+ */
993
+ async reconnectVoiceReceiver() {
994
+ this.voiceReceiverReconnectTimeout = setTimeout(async () => {
995
+ if (this.voiceReceiverAttempt > this.voiceReceiverReconnectTries)
996
+ throw new Error("Failed to reconnect to voice receiver");
997
+ this.voiceReceiverWsClient?.removeAllListeners();
998
+ this.voiceReceiverWsClient = null;
999
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[PLAYER] Reconnecting to voice receiver for player ${this.guildId}`);
1000
+ await this.setupVoiceReceiver();
1001
+ this.voiceReceiverAttempt++;
1002
+ }, this.node.options.retryDelayMs);
1003
+ }
1004
+ /**
1005
+ * Disconnects the voice receiver for the player.
1006
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is disconnected.
1007
+ */
1008
+ async disconnectVoiceReceiver() {
1009
+ if (!this.isConnectToVoiceReceiver)
1010
+ return;
1011
+ this.voiceReceiverWsClient?.close(1000, "destroy");
1012
+ this.voiceReceiverWsClient?.removeAllListeners();
1013
+ this.voiceReceiverWsClient = null;
1014
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[PLAYER] Disconnected from voice receiver for player ${this.guildId}`);
1015
+ this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverDisconnect, this);
1016
+ }
1017
+ /**
1018
+ * Opens the voice receiver for the player.
1019
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is opened.
1020
+ */
1021
+ async openVoiceReceiver() {
1022
+ if (this.voiceReceiverReconnectTimeout)
1023
+ clearTimeout(this.voiceReceiverReconnectTimeout);
1024
+ this.voiceReceiverReconnectTimeout = null;
1025
+ this.isConnectToVoiceReceiver = true;
1026
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[PLAYER] Opened voice receiver for player ${this.guildId}`);
1027
+ this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverConnect, this);
1028
+ }
1029
+ /**
1030
+ * Handles a voice receiver message.
1031
+ * @param {string} payload - The payload to handle.
1032
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver message is handled.
1033
+ */
1034
+ async onVoiceReceiverMessage(payload) {
1035
+ const packet = JSON.parse(payload);
1036
+ if (!packet?.op)
1037
+ return;
1038
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `VoiceReceiver recieved a payload: ${JSON.stringify(payload)}`);
1039
+ switch (packet.type) {
1040
+ case "startSpeakingEvent": {
1041
+ this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverStartSpeaking, this, packet.data);
1042
+ break;
1043
+ }
1044
+ case "endSpeakingEvent": {
1045
+ const data = {
1046
+ ...packet.data,
1047
+ data: Buffer.from(packet.data.data, "base64"),
1048
+ };
1049
+ this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverEndSpeaking, this, data);
1050
+ break;
1051
+ }
1052
+ default: {
1053
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `VoiceReceiver recieved an unknown payload: ${JSON.stringify(payload)}`);
1054
+ break;
1055
+ }
1056
+ }
1057
+ }
1058
+ /**
1059
+ * Handles a voice receiver error.
1060
+ * @param {Error} error - The error to handle.
1061
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver error is handled.
1062
+ */
1063
+ async onVoiceReceiverError(error) {
1064
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `VoiceReceiver error for player ${this.guildId}: ${error.message}`);
1065
+ this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverError, this, error);
1066
+ }
904
1067
  }
905
1068
  exports.Player = Player;
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Plugin = void 0;
4
+ /**
5
+ * Base abstract class for all plugins.
6
+ * Users must extend this and implement load and unload methods.
7
+ */
4
8
  class Plugin {
5
9
  name;
6
10
  /**
@@ -9,6 +13,5 @@ class Plugin {
9
13
  constructor(name) {
10
14
  this.name = name;
11
15
  }
12
- load(manager) { }
13
16
  }
14
17
  exports.Plugin = Plugin;