magmastream 2.9.1 → 2.9.2-dev.2
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.
- package/LICENSE +176 -176
- package/README.md +144 -157
- package/dist/index.d.ts +76 -7
- package/dist/index.js +1 -0
- package/dist/statestorage/JsonQueue.js +273 -165
- package/dist/statestorage/MemoryQueue.js +261 -200
- package/dist/statestorage/RedisQueue.js +397 -198
- package/dist/structures/Enums.js +110 -1
- package/dist/structures/Filters.js +27 -13
- package/dist/structures/MagmastreamError.js +19 -0
- package/dist/structures/Manager.js +360 -234
- package/dist/structures/Node.js +222 -69
- package/dist/structures/Player.js +195 -60
- package/dist/structures/Rest.js +23 -12
- package/dist/structures/Utils.js +122 -107
- package/dist/utils/managerCheck.js +99 -21
- package/dist/utils/nodeCheck.js +59 -34
- package/dist/utils/playerCheck.js +47 -28
- package/package.json +99 -99
|
@@ -11,6 +11,7 @@ const RedisQueue_1 = require("../statestorage/RedisQueue");
|
|
|
11
11
|
const Enums_1 = require("./Enums");
|
|
12
12
|
const ws_1 = require("ws");
|
|
13
13
|
const JsonQueue_1 = require("../statestorage/JsonQueue");
|
|
14
|
+
const MagmastreamError_1 = require("./MagmastreamError");
|
|
14
15
|
class Player {
|
|
15
16
|
options;
|
|
16
17
|
/** The Queue for the Player. */
|
|
@@ -75,8 +76,12 @@ class Player {
|
|
|
75
76
|
// If the Manager is not initiated, throw an error.
|
|
76
77
|
if (!this.manager)
|
|
77
78
|
this.manager = Utils_1.Structure.get("Player")._manager;
|
|
78
|
-
if (!this.manager)
|
|
79
|
-
throw new
|
|
79
|
+
if (!this.manager) {
|
|
80
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
81
|
+
code: Enums_1.MagmaStreamErrorCode.GENERAL_INVALID_MANAGER,
|
|
82
|
+
message: "Manager instance is required.",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
80
85
|
this.clusterId = this.manager.options.clusterId || 0;
|
|
81
86
|
// Check the player options for errors.
|
|
82
87
|
(0, playerCheck_1.default)(options);
|
|
@@ -102,8 +107,13 @@ class Player {
|
|
|
102
107
|
const node = this.manager.nodes.get(options.nodeIdentifier);
|
|
103
108
|
this.node = node || this.manager.useableNode;
|
|
104
109
|
// If no node is available, throw an error.
|
|
105
|
-
if (!this.node)
|
|
106
|
-
throw new
|
|
110
|
+
if (!this.node) {
|
|
111
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
112
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_NO_NODES,
|
|
113
|
+
message: "No available nodes for the player found.",
|
|
114
|
+
context: { guildId: this.guildId },
|
|
115
|
+
});
|
|
116
|
+
}
|
|
107
117
|
// Initialize the queue with the guild ID and manager.
|
|
108
118
|
switch (this.manager.options.stateStorage.type) {
|
|
109
119
|
case Enums_1.StateStorageType.Redis:
|
|
@@ -169,7 +179,11 @@ class Player {
|
|
|
169
179
|
connect() {
|
|
170
180
|
// Check if the voice channel has been set.
|
|
171
181
|
if (!this.voiceChannelId) {
|
|
172
|
-
throw new
|
|
182
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
183
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_CONFIG,
|
|
184
|
+
message: "No voice channel has been set. You must set the voice channel before connecting.",
|
|
185
|
+
context: { voiceChannelId: this.voiceChannelId },
|
|
186
|
+
});
|
|
173
187
|
}
|
|
174
188
|
// Set the player state to connecting.
|
|
175
189
|
this.state = Enums_1.StateTypes.Connecting;
|
|
@@ -268,8 +282,12 @@ class Player {
|
|
|
268
282
|
*/
|
|
269
283
|
setVoiceChannelId(channel) {
|
|
270
284
|
// Validate the channel parameter
|
|
271
|
-
if (typeof channel !== "string")
|
|
272
|
-
throw new
|
|
285
|
+
if (typeof channel !== "string") {
|
|
286
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
287
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_CONFIG,
|
|
288
|
+
message: "Channel must be a non-empty string.",
|
|
289
|
+
});
|
|
290
|
+
}
|
|
273
291
|
// Clone the current player state for comparison
|
|
274
292
|
const oldPlayer = this ? { ...this } : null;
|
|
275
293
|
// Update the player voice channel
|
|
@@ -300,8 +318,12 @@ class Player {
|
|
|
300
318
|
*/
|
|
301
319
|
setTextChannelId(channel) {
|
|
302
320
|
// Validate the channel parameter
|
|
303
|
-
if (typeof channel !== "string")
|
|
304
|
-
throw new
|
|
321
|
+
if (typeof channel !== "string") {
|
|
322
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
323
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_CONFIG,
|
|
324
|
+
message: "Channel must be a non-empty string.",
|
|
325
|
+
});
|
|
326
|
+
}
|
|
305
327
|
// Clone the current player state for comparison
|
|
306
328
|
const oldPlayer = this ? { ...this } : null;
|
|
307
329
|
// Update the text channel property
|
|
@@ -328,7 +350,10 @@ class Player {
|
|
|
328
350
|
*/
|
|
329
351
|
setNowPlayingMessage(message) {
|
|
330
352
|
if (!message) {
|
|
331
|
-
throw new
|
|
353
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
354
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_NOW_PLAYING_MESSAGE,
|
|
355
|
+
message: "You must provide the message of the now playing message.",
|
|
356
|
+
});
|
|
332
357
|
}
|
|
333
358
|
this.nowPlayingMessage = message;
|
|
334
359
|
return this.nowPlayingMessage;
|
|
@@ -337,8 +362,12 @@ class Player {
|
|
|
337
362
|
if (typeof optionsOrTrack !== "undefined" && Utils_1.TrackUtils.validate(optionsOrTrack)) {
|
|
338
363
|
await this.queue.setCurrent(optionsOrTrack);
|
|
339
364
|
}
|
|
340
|
-
if (!(await this.queue.getCurrent()))
|
|
341
|
-
throw new
|
|
365
|
+
if (!(await this.queue.getCurrent())) {
|
|
366
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
367
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_QUEUE_EMPTY,
|
|
368
|
+
message: "The queue is empty.",
|
|
369
|
+
});
|
|
370
|
+
}
|
|
342
371
|
const finalOptions = playOptions
|
|
343
372
|
? playOptions
|
|
344
373
|
: ["startTime", "endTime", "noReplace"].every((v) => Object.keys(optionsOrTrack || {}).includes(v))
|
|
@@ -369,11 +398,17 @@ class Player {
|
|
|
369
398
|
*/
|
|
370
399
|
setAutoplay(autoplayState, AutoplayUser, tries) {
|
|
371
400
|
if (typeof autoplayState !== "boolean") {
|
|
372
|
-
throw new
|
|
401
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
402
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_AUTOPLAY,
|
|
403
|
+
message: "autoplayState must be a boolean.",
|
|
404
|
+
});
|
|
373
405
|
}
|
|
374
406
|
if (autoplayState) {
|
|
375
407
|
if (!AutoplayUser) {
|
|
376
|
-
throw new
|
|
408
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
409
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_AUTOPLAY,
|
|
410
|
+
message: "AutoplayUser must be provided when enabling autoplay.",
|
|
411
|
+
});
|
|
377
412
|
}
|
|
378
413
|
this.autoplayTries = tries && typeof tries === "number" && tries > 0 ? tries : 3; // Default to 3 if invalid
|
|
379
414
|
this.isAutoplay = true;
|
|
@@ -407,28 +442,46 @@ class Player {
|
|
|
407
442
|
}
|
|
408
443
|
/**
|
|
409
444
|
* Sets the volume of the player.
|
|
410
|
-
* @param {number} volume - The new volume. Must be between 0 and
|
|
445
|
+
* @param {number} volume - The new volume. Must be between 0 and 500 when using filter mode (100 = 100%).
|
|
411
446
|
* @returns {Promise<Player>} - The updated player.
|
|
412
447
|
* @throws {TypeError} If the volume is not a number.
|
|
413
|
-
* @throws {RangeError} If the volume is not between 0 and
|
|
448
|
+
* @throws {RangeError} If the volume is not between 0 and 500 when using filter mode (100 = 100%).
|
|
414
449
|
* @emits {PlayerStateUpdate} - Emitted when the volume is changed.
|
|
415
450
|
* @example
|
|
416
451
|
* player.setVolume(50);
|
|
417
|
-
* player.setVolume(50, { gradual: true, interval: 50, step: 5 });
|
|
418
452
|
*/
|
|
419
453
|
async setVolume(volume) {
|
|
420
|
-
if (isNaN(volume))
|
|
421
|
-
throw new
|
|
422
|
-
|
|
423
|
-
|
|
454
|
+
if (isNaN(volume)) {
|
|
455
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
456
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_VOLUME,
|
|
457
|
+
message: "Volume must be a number.",
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
if (this.options.applyVolumeAsFilter) {
|
|
461
|
+
if (volume < 0 || volume > 500) {
|
|
462
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
463
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_VOLUME,
|
|
464
|
+
message: "Volume must be between 0 and 500 when using filter mode (100 = 100%).",
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
if (volume < 0 || volume > 1000) {
|
|
470
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
471
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_VOLUME,
|
|
472
|
+
message: "Volume must be between 0 and 1000.",
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
424
476
|
const oldVolume = this.volume;
|
|
425
477
|
const oldPlayer = { ...this };
|
|
426
|
-
const data = this.options.applyVolumeAsFilter ? { filters: { volume } } : { volume };
|
|
478
|
+
const data = this.options.applyVolumeAsFilter ? { filters: { volume: volume / 100 } } : { volume };
|
|
427
479
|
await this.node.rest.updatePlayer({
|
|
428
480
|
guildId: this.options.guildId,
|
|
429
481
|
data,
|
|
430
482
|
});
|
|
431
483
|
this.volume = volume;
|
|
484
|
+
this.options.volume = volume;
|
|
432
485
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
433
486
|
changeType: Enums_1.PlayerStateEventTypes.VolumeChange,
|
|
434
487
|
details: {
|
|
@@ -473,8 +526,12 @@ class Player {
|
|
|
473
526
|
*/
|
|
474
527
|
setTrackRepeat(repeat) {
|
|
475
528
|
// Ensure the repeat parameter is a boolean
|
|
476
|
-
if (typeof repeat !== "boolean")
|
|
477
|
-
throw new
|
|
529
|
+
if (typeof repeat !== "boolean") {
|
|
530
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
531
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_REPEAT,
|
|
532
|
+
message: "Repeat must be a boolean.",
|
|
533
|
+
});
|
|
534
|
+
}
|
|
478
535
|
// Clone the current player state for event emission
|
|
479
536
|
const oldPlayer = this ? { ...this } : null;
|
|
480
537
|
if (repeat) {
|
|
@@ -509,8 +566,12 @@ class Player {
|
|
|
509
566
|
*/
|
|
510
567
|
setQueueRepeat(repeat) {
|
|
511
568
|
// Ensure the repeat parameter is a boolean
|
|
512
|
-
if (typeof repeat !== "boolean")
|
|
513
|
-
throw new
|
|
569
|
+
if (typeof repeat !== "boolean") {
|
|
570
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
571
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_REPEAT,
|
|
572
|
+
message: "Repeat must be a boolean.",
|
|
573
|
+
});
|
|
574
|
+
}
|
|
514
575
|
// Get the current player state
|
|
515
576
|
const oldPlayer = this ? { ...this } : null;
|
|
516
577
|
// Update the player state
|
|
@@ -547,11 +608,17 @@ class Player {
|
|
|
547
608
|
async setDynamicRepeat(repeat, ms) {
|
|
548
609
|
// Validate the repeat parameter
|
|
549
610
|
if (typeof repeat !== "boolean") {
|
|
550
|
-
throw new
|
|
611
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
612
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_REPEAT,
|
|
613
|
+
message: "Repeat must be a boolean.",
|
|
614
|
+
});
|
|
551
615
|
}
|
|
552
616
|
// Ensure the queue has more than one track for dynamic repeat
|
|
553
617
|
if ((await this.queue.size()) <= 1) {
|
|
554
|
-
throw new
|
|
618
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
619
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_REPEAT,
|
|
620
|
+
message: "The queue size must be greater than 1.",
|
|
621
|
+
});
|
|
555
622
|
}
|
|
556
623
|
// Clone the current player state for comparison
|
|
557
624
|
const oldPlayer = this ? { ...this } : null;
|
|
@@ -626,8 +693,12 @@ class Player {
|
|
|
626
693
|
const oldPlayer = { ...this };
|
|
627
694
|
let removedTracks = [];
|
|
628
695
|
if (typeof amount === "number" && amount > 1) {
|
|
629
|
-
if (amount > (await this.queue.size()))
|
|
630
|
-
throw new
|
|
696
|
+
if (amount > (await this.queue.size())) {
|
|
697
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
698
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_QUEUE_EMPTY,
|
|
699
|
+
message: "The queue size must be greater than 1.",
|
|
700
|
+
});
|
|
701
|
+
}
|
|
631
702
|
removedTracks = await this.queue.getSlice(0, amount - 1);
|
|
632
703
|
await this.queue.modifyAt(0, amount - 1);
|
|
633
704
|
}
|
|
@@ -655,8 +726,12 @@ class Player {
|
|
|
655
726
|
*/
|
|
656
727
|
async pause(pause) {
|
|
657
728
|
// Validate the pause parameter to ensure it's a boolean.
|
|
658
|
-
if (typeof pause !== "boolean")
|
|
659
|
-
throw new
|
|
729
|
+
if (typeof pause !== "boolean") {
|
|
730
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
731
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_PAUSE,
|
|
732
|
+
message: "Pause must be a boolean.",
|
|
733
|
+
});
|
|
734
|
+
}
|
|
660
735
|
// If the pause state is already as desired or there are no tracks, return early.
|
|
661
736
|
if (this.paused === pause || !this.queue.totalSize)
|
|
662
737
|
return this;
|
|
@@ -691,18 +766,20 @@ class Player {
|
|
|
691
766
|
* @emits {PlayerStateUpdate} - With {@link PlayerStateEventTypes.TrackChange} as the change type.
|
|
692
767
|
*/
|
|
693
768
|
async previous() {
|
|
694
|
-
//
|
|
769
|
+
// Pop the most recent previous track (from tail)
|
|
695
770
|
const lastTrack = await this.queue.popPrevious();
|
|
696
771
|
if (!lastTrack) {
|
|
697
772
|
await this.queue.clearPrevious();
|
|
698
|
-
throw new
|
|
773
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
774
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_PREVIOUS_EMPTY,
|
|
775
|
+
message: "Previous queue is empty.",
|
|
776
|
+
});
|
|
699
777
|
}
|
|
700
778
|
// Capture the current state of the player before making changes.
|
|
701
779
|
const oldPlayer = { ...this };
|
|
702
|
-
//
|
|
780
|
+
// Prevent re-adding the current track
|
|
703
781
|
this.set("skipFlag", true);
|
|
704
782
|
await this.play(lastTrack);
|
|
705
|
-
// Emit state update
|
|
706
783
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
707
784
|
changeType: Enums_1.PlayerStateEventTypes.TrackChange,
|
|
708
785
|
details: {
|
|
@@ -726,7 +803,10 @@ class Player {
|
|
|
726
803
|
position = Number(position);
|
|
727
804
|
// Check if the position is valid.
|
|
728
805
|
if (isNaN(position)) {
|
|
729
|
-
throw new
|
|
806
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
807
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_SEEK,
|
|
808
|
+
message: "Position must be a number.",
|
|
809
|
+
});
|
|
730
810
|
}
|
|
731
811
|
// Get the old player state.
|
|
732
812
|
const oldPlayer = this ? { ...this } : null;
|
|
@@ -790,8 +870,14 @@ class Player {
|
|
|
790
870
|
*/
|
|
791
871
|
async moveNode(identifier) {
|
|
792
872
|
const node = this.manager.nodes.get(identifier);
|
|
793
|
-
if (!node)
|
|
794
|
-
|
|
873
|
+
if (!node) {
|
|
874
|
+
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Tried to move to non-existent node: ${identifier}`);
|
|
875
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
876
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_NODE_NOT_FOUND,
|
|
877
|
+
message: "Node not found.",
|
|
878
|
+
context: { identifier },
|
|
879
|
+
});
|
|
880
|
+
}
|
|
795
881
|
if (this.state !== Enums_1.StateTypes.Connected) {
|
|
796
882
|
return this;
|
|
797
883
|
}
|
|
@@ -806,7 +892,12 @@ class Player {
|
|
|
806
892
|
const token = this.voiceState?.event?.token;
|
|
807
893
|
const endpoint = this.voiceState?.event?.endpoint;
|
|
808
894
|
if (!sessionId || !token || !endpoint) {
|
|
809
|
-
|
|
895
|
+
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Voice state is not properly initialized for player ${this.guildId}. The bot might not be connected to a voice channel.`);
|
|
896
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
897
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_STATE_INVALID,
|
|
898
|
+
message: `Voice state is not properly initialized. The bot might not be connected to a voice channel.`,
|
|
899
|
+
context: { guildId: this.guildId },
|
|
900
|
+
});
|
|
810
901
|
}
|
|
811
902
|
await this.node.rest.destroyPlayer(this.guildId).catch(() => { });
|
|
812
903
|
this.manager.players.delete(this.guildId);
|
|
@@ -818,8 +909,17 @@ class Player {
|
|
|
818
909
|
});
|
|
819
910
|
await this.filters.updateFilters();
|
|
820
911
|
}
|
|
821
|
-
catch (
|
|
822
|
-
|
|
912
|
+
catch (err) {
|
|
913
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
914
|
+
? err
|
|
915
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
916
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_MOVE_FAILED,
|
|
917
|
+
message: "Error moving player to node.",
|
|
918
|
+
cause: err,
|
|
919
|
+
context: { guildId: this.guildId },
|
|
920
|
+
});
|
|
921
|
+
this.manager.emit(Enums_1.ManagerEventTypes.Debug, error);
|
|
922
|
+
console.error(error);
|
|
823
923
|
}
|
|
824
924
|
}
|
|
825
925
|
/**
|
|
@@ -831,12 +931,24 @@ class Player {
|
|
|
831
931
|
* @returns {Promise<Player>} - The new player instance.
|
|
832
932
|
*/
|
|
833
933
|
async switchGuild(newOptions, force = false) {
|
|
834
|
-
if (!newOptions.guildId)
|
|
835
|
-
throw new
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
934
|
+
if (!newOptions.guildId) {
|
|
935
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
936
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_CONFIG,
|
|
937
|
+
message: "guildId is required for switchGuild",
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
if (!newOptions.voiceChannelId) {
|
|
941
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
942
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_CONFIG,
|
|
943
|
+
message: "voiceChannelId is required for switchGuild",
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
if (!newOptions.textChannelId) {
|
|
947
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
948
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_CONFIG,
|
|
949
|
+
message: "textChannelId is required for switchGuild",
|
|
950
|
+
});
|
|
951
|
+
}
|
|
840
952
|
// Check if a player already exists for the new guild
|
|
841
953
|
let newPlayer = this.manager.getPlayer(newOptions.guildId);
|
|
842
954
|
// If the player already exists and force is false, return the existing player
|
|
@@ -917,18 +1029,26 @@ class Player {
|
|
|
917
1029
|
// Return the cloned player
|
|
918
1030
|
return clonedPlayer;
|
|
919
1031
|
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Retrieves the data associated with the player.
|
|
1034
|
+
* @returns {Record<string, unknown>} - The data associated with the player.
|
|
1035
|
+
*/
|
|
1036
|
+
getData() {
|
|
1037
|
+
return this.data;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Retrieves the dynamic loop interval of the player.
|
|
1041
|
+
* @returns {NodeJS.Timeout | null} - The dynamic loop interval of the player.
|
|
1042
|
+
*/
|
|
1043
|
+
getDynamicLoopIntervalPublic() {
|
|
1044
|
+
return this.dynamicLoopInterval;
|
|
1045
|
+
}
|
|
920
1046
|
/**
|
|
921
1047
|
* Retrieves the current lyrics for the playing track.
|
|
922
1048
|
* @param skipTrackSource - Indicates whether to skip the track source when fetching lyrics.
|
|
923
1049
|
* @returns {Promise<Lyrics>} - The lyrics of the current track.
|
|
924
|
-
* @throws {RangeError} - If the 'lavalyrics-plugin' is not available on the Lavalink node.
|
|
925
1050
|
*/
|
|
926
1051
|
async getCurrentLyrics(skipTrackSource = false) {
|
|
927
|
-
// Check if the 'lavalyrics-plugin' is available on the node
|
|
928
|
-
const hasLyricsPlugin = this.node.info.plugins.some((plugin) => plugin.name === "lavalyrics-plugin");
|
|
929
|
-
if (!hasLyricsPlugin) {
|
|
930
|
-
throw new RangeError(`There is no lavalyrics-plugin available in the Lavalink node: ${this.node.options.identifier}`);
|
|
931
|
-
}
|
|
932
1052
|
// Fetch the lyrics for the current track from the Lavalink node
|
|
933
1053
|
let result = (await this.node.getLyrics(await this.queue.getCurrent(), skipTrackSource));
|
|
934
1054
|
// If no lyrics are found, return a default empty lyrics object
|
|
@@ -949,8 +1069,13 @@ class Player {
|
|
|
949
1069
|
* @throws {Error} - If the node is not a NodeLink.
|
|
950
1070
|
*/
|
|
951
1071
|
async setupVoiceReceiver() {
|
|
952
|
-
if (!this.node.isNodeLink)
|
|
953
|
-
throw new
|
|
1072
|
+
if (!this.node.isNodeLink) {
|
|
1073
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
1074
|
+
code: Enums_1.MagmaStreamErrorCode.NODE_PROTOCOL_ERROR,
|
|
1075
|
+
message: `The node is not a NodeLink, cannot setup voice receiver.`,
|
|
1076
|
+
context: { identifier: this.node.options.identifier },
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
954
1079
|
if (this.voiceReceiverWsClient)
|
|
955
1080
|
await this.removeVoiceReceiver();
|
|
956
1081
|
const headers = {
|
|
@@ -972,8 +1097,13 @@ class Player {
|
|
|
972
1097
|
* @throws {Error} - If the node is not a NodeLink.
|
|
973
1098
|
*/
|
|
974
1099
|
async removeVoiceReceiver() {
|
|
975
|
-
if (!this.node.isNodeLink)
|
|
976
|
-
throw new
|
|
1100
|
+
if (!this.node.isNodeLink) {
|
|
1101
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
1102
|
+
code: Enums_1.MagmaStreamErrorCode.NODE_PROTOCOL_ERROR,
|
|
1103
|
+
message: `The node is not a NodeLink, cannot remove voice receiver.`,
|
|
1104
|
+
context: { identifier: this.node.options.identifier },
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
977
1107
|
if (this.voiceReceiverWsClient) {
|
|
978
1108
|
this.voiceReceiverWsClient.close(1000, "destroy");
|
|
979
1109
|
this.voiceReceiverWsClient.removeAllListeners();
|
|
@@ -999,8 +1129,13 @@ class Player {
|
|
|
999
1129
|
*/
|
|
1000
1130
|
async reconnectVoiceReceiver() {
|
|
1001
1131
|
this.voiceReceiverReconnectTimeout = setTimeout(async () => {
|
|
1002
|
-
if (this.voiceReceiverAttempt > this.voiceReceiverReconnectTries)
|
|
1003
|
-
throw new
|
|
1132
|
+
if (this.voiceReceiverAttempt > this.voiceReceiverReconnectTries) {
|
|
1133
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
1134
|
+
code: Enums_1.MagmaStreamErrorCode.PLAYER_VOICE_RECEIVER_ERROR,
|
|
1135
|
+
message: `Failed to reconnect to voice receiver for player ${this.guildId}`,
|
|
1136
|
+
context: { identifier: this.node.options.identifier },
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1004
1139
|
this.voiceReceiverWsClient?.removeAllListeners();
|
|
1005
1140
|
this.voiceReceiverWsClient = null;
|
|
1006
1141
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[PLAYER] Reconnecting to voice receiver for player ${this.guildId}`);
|
package/dist/structures/Rest.js
CHANGED
|
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const axios_1 = tslib_1.__importDefault(require("axios"));
|
|
6
6
|
const Enums_1 = require("./Enums");
|
|
7
7
|
const Utils_1 = require("./Utils");
|
|
8
|
+
const MagmastreamError_1 = require("./MagmastreamError");
|
|
8
9
|
/** Handles the requests sent to the Lavalink REST API. */
|
|
9
10
|
class Rest {
|
|
10
11
|
/** The Node that this Rest instance is connected to. */
|
|
@@ -38,12 +39,12 @@ class Rest {
|
|
|
38
39
|
return this.sessionId;
|
|
39
40
|
}
|
|
40
41
|
/**
|
|
41
|
-
* Retrieves
|
|
42
|
+
* Retrieves one the player that is currently running on the node.
|
|
42
43
|
* @returns {Promise<unknown>} Returns the result of the GET request.
|
|
43
44
|
*/
|
|
44
|
-
async
|
|
45
|
+
async getPlayer(guildId) {
|
|
45
46
|
// Send a GET request to the Lavalink Node to retrieve all the players.
|
|
46
|
-
const result = await this.get(`/v4/sessions/${this.sessionId}/players`);
|
|
47
|
+
const result = (await this.get(`/v4/sessions/${this.sessionId}/players/${guildId}`));
|
|
47
48
|
// Log the result of the request.
|
|
48
49
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[REST] Getting all players on node: ${this.node.options.identifier} : ${Utils_1.JSONUtils.safe(result, 2)}`);
|
|
49
50
|
// Return the result of the request.
|
|
@@ -93,7 +94,7 @@ class Rest {
|
|
|
93
94
|
* @returns {Promise<unknown>} The response data of the request.
|
|
94
95
|
*/
|
|
95
96
|
async request(method, endpoint, body) {
|
|
96
|
-
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[REST] ${method}
|
|
97
|
+
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[REST] ${method} request to ${endpoint} with body: ${Utils_1.JSONUtils.safe(body, 2)}`);
|
|
97
98
|
const config = {
|
|
98
99
|
method,
|
|
99
100
|
url: this.url + endpoint,
|
|
@@ -108,19 +109,29 @@ class Rest {
|
|
|
108
109
|
const response = await (0, axios_1.default)(config);
|
|
109
110
|
return response.data;
|
|
110
111
|
}
|
|
111
|
-
catch (
|
|
112
|
+
catch (err) {
|
|
113
|
+
const error = err;
|
|
112
114
|
if (!error.response) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
116
|
+
code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
|
|
117
|
+
message: `No response from node ${this.node.options.identifier}: ${error.message}`,
|
|
118
|
+
});
|
|
115
119
|
}
|
|
116
|
-
|
|
120
|
+
const data = error.response.data;
|
|
121
|
+
if (data?.message === "Guild not found") {
|
|
117
122
|
return [];
|
|
118
123
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
if (error.response.status === 401) {
|
|
125
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
126
|
+
code: Enums_1.MagmaStreamErrorCode.REST_UNAUTHORIZED,
|
|
127
|
+
message: `Unauthorized access to node ${this.node.options.identifier}`,
|
|
128
|
+
});
|
|
122
129
|
}
|
|
123
|
-
|
|
130
|
+
const dataMessage = typeof data === "string" ? data : data?.message ? data.message : Utils_1.JSONUtils.safe(data, 2);
|
|
131
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
132
|
+
code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
|
|
133
|
+
message: `Request to node ${this.node.options.identifier} failed with status ${error.response.status}: ${dataMessage}`,
|
|
134
|
+
});
|
|
124
135
|
}
|
|
125
136
|
}
|
|
126
137
|
/**
|