magmastream 2.9.3-dev.9 → 2.10.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.
- package/dist/config/blockedWords.d.ts +1 -0
- package/dist/index.d.ts +19 -3700
- package/dist/index.js +1 -1
- package/dist/statestorage/JsonQueue.d.ts +173 -0
- package/dist/statestorage/JsonQueue.js +32 -4
- package/dist/statestorage/MemoryQueue.d.ts +154 -0
- package/dist/statestorage/MemoryQueue.js +56 -36
- package/dist/statestorage/RedisQueue.d.ts +178 -0
- package/dist/statestorage/RedisQueue.js +29 -7
- package/dist/structures/Enums.d.ts +310 -0
- package/dist/structures/Enums.js +4 -0
- package/dist/structures/Filters.d.ts +352 -0
- package/dist/structures/Filters.js +5 -4
- package/dist/structures/MagmastreamError.d.ts +14 -0
- package/dist/structures/Manager.d.ts +259 -0
- package/dist/structures/Manager.js +285 -542
- package/dist/structures/Node.d.ts +390 -0
- package/dist/structures/Node.js +100 -145
- package/dist/structures/Player.d.ts +347 -0
- package/dist/structures/Player.js +54 -128
- package/dist/structures/Plugin.d.ts +23 -0
- package/dist/structures/Rest.d.ts +93 -0
- package/dist/structures/Types.d.ts +1315 -0
- package/dist/structures/Utils.d.ts +169 -0
- package/dist/structures/Utils.js +107 -56
- package/dist/utils/filtersEqualizers.d.ts +16 -0
- package/dist/utils/managerCheck.d.ts +7 -0
- package/dist/utils/nodeCheck.d.ts +7 -0
- package/dist/utils/playerCheck.d.ts +7 -0
- package/dist/wrappers/discord.js.d.ts +15 -0
- package/dist/wrappers/discord.js.js +19 -4
- package/dist/wrappers/discordeno.d.ts +19 -0
- package/dist/wrappers/discordeno.js +77 -0
- package/dist/wrappers/eris.d.ts +15 -0
- package/dist/wrappers/eris.js +20 -3
- package/dist/wrappers/oceanic.d.ts +15 -0
- package/dist/wrappers/oceanic.js +22 -4
- package/dist/wrappers/seyfert.d.ts +37 -0
- package/dist/wrappers/seyfert.js +25 -1
- package/package.json +107 -102
- package/dist/wrappers/detritus.js +0 -52
|
@@ -28,6 +28,8 @@ class Manager extends events_1.EventEmitter {
|
|
|
28
28
|
initiated = false;
|
|
29
29
|
redis;
|
|
30
30
|
_send;
|
|
31
|
+
_getUser;
|
|
32
|
+
_getGuild;
|
|
31
33
|
loadedPlugins = new Set();
|
|
32
34
|
/**
|
|
33
35
|
* Initiates the Manager class.
|
|
@@ -61,6 +63,10 @@ class Manager extends events_1.EventEmitter {
|
|
|
61
63
|
this.options.clusterId = options.clusterId;
|
|
62
64
|
if (options.send && !this._send)
|
|
63
65
|
this._send = options.send;
|
|
66
|
+
if (options.getUser && !this._getUser)
|
|
67
|
+
this._getUser = options.getUser;
|
|
68
|
+
if (options.getGuild && !this._getGuild)
|
|
69
|
+
this._getGuild = options.getGuild;
|
|
64
70
|
this.options = {
|
|
65
71
|
...options,
|
|
66
72
|
enabledPlugins: options.enabledPlugins ?? [],
|
|
@@ -86,9 +92,10 @@ class Manager extends events_1.EventEmitter {
|
|
|
86
92
|
stateStorage: {
|
|
87
93
|
...options.stateStorage,
|
|
88
94
|
type: options.stateStorage?.type ?? Enums_1.StateStorageType.Memory,
|
|
89
|
-
|
|
95
|
+
deleteDestroyedPlayers: options.stateStorage?.deleteDestroyedPlayers ?? true,
|
|
90
96
|
},
|
|
91
97
|
autoPlaySearchPlatforms: options.autoPlaySearchPlatforms ?? [Enums_1.AutoPlayPlatform.YouTube],
|
|
98
|
+
listenToSIGEvents: options.listenToSIGEvents ?? true,
|
|
92
99
|
send: this._send,
|
|
93
100
|
};
|
|
94
101
|
Utils_1.AutoPlayUtils.init(this);
|
|
@@ -96,49 +103,51 @@ class Manager extends events_1.EventEmitter {
|
|
|
96
103
|
for (const nodeOptions of this.options.nodes)
|
|
97
104
|
new Node_1.Node(this, nodeOptions);
|
|
98
105
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
if (this.options.listenToSIGEvents) {
|
|
107
|
+
process.on("SIGINT", async () => {
|
|
108
|
+
console.warn("\x1b[33mSIGINT received! Graceful shutdown initiated...\x1b[0m");
|
|
109
|
+
try {
|
|
110
|
+
await this.handleShutdown();
|
|
111
|
+
console.warn("\x1b[32mShutdown complete. Waiting for Node.js event loop to empty...\x1b[0m");
|
|
112
|
+
// Prevent forced exit by Windows
|
|
113
|
+
setTimeout(() => {
|
|
114
|
+
process.exit(0);
|
|
115
|
+
}, 2000);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
119
|
+
? err
|
|
120
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
121
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
|
|
122
|
+
message: "An unknown error occurred.",
|
|
123
|
+
cause: err,
|
|
124
|
+
context: { stage: "SIGINT" },
|
|
125
|
+
});
|
|
126
|
+
console.error(error);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
process.on("SIGTERM", async () => {
|
|
131
|
+
console.warn("\x1b[33mSIGTERM received! Graceful shutdown initiated...\x1b[0m");
|
|
132
|
+
try {
|
|
133
|
+
await this.handleShutdown();
|
|
134
|
+
console.warn("\x1b[32mShutdown complete. Exiting now...\x1b[0m");
|
|
106
135
|
process.exit(0);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
process.on("SIGTERM", async () => {
|
|
123
|
-
console.warn("\x1b[33mSIGTERM received! Graceful shutdown initiated...\x1b[0m");
|
|
124
|
-
try {
|
|
125
|
-
await this.handleShutdown();
|
|
126
|
-
console.warn("\x1b[32mShutdown complete. Exiting now...\x1b[0m");
|
|
127
|
-
process.exit(0);
|
|
128
|
-
}
|
|
129
|
-
catch (err) {
|
|
130
|
-
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
131
|
-
? err
|
|
132
|
-
: new MagmastreamError_1.MagmaStreamError({
|
|
133
|
-
code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
|
|
134
|
-
message: "An unknown error occurred.",
|
|
135
|
-
cause: err,
|
|
136
|
-
context: { stage: "SIGTERM" },
|
|
137
|
-
});
|
|
138
|
-
console.error(error);
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
139
|
+
? err
|
|
140
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
141
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
|
|
142
|
+
message: "An unknown error occurred.",
|
|
143
|
+
cause: err,
|
|
144
|
+
context: { stage: "SIGTERM" },
|
|
145
|
+
});
|
|
146
|
+
console.error(error);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
142
151
|
}
|
|
143
152
|
/**
|
|
144
153
|
* Initiates the Manager.
|
|
@@ -468,9 +477,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
468
477
|
case Enums_1.StateStorageType.Redis:
|
|
469
478
|
{
|
|
470
479
|
try {
|
|
471
|
-
const redisKey = `${
|
|
472
|
-
? this.options.stateStorage.redisConfig.prefix
|
|
473
|
-
: (this.options.stateStorage.redisConfig.prefix ?? "magmastream:")}playerstore:${guildId}`;
|
|
480
|
+
const redisKey = `${Utils_1.PlayerUtils.getRedisKey()}playerstore:${guildId}`;
|
|
474
481
|
await this.redis.set(redisKey, JSON.stringify(serializedPlayer));
|
|
475
482
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Player state saved to Redis: ${guildId}`);
|
|
476
483
|
}
|
|
@@ -494,6 +501,154 @@ class Manager extends events_1.EventEmitter {
|
|
|
494
501
|
async sleep(ms) {
|
|
495
502
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
496
503
|
}
|
|
504
|
+
async restorePlayerFromState(node, nodeId, guildId, state, cleanup) {
|
|
505
|
+
if (!state.guildId || state.node?.options?.identifier !== nodeId)
|
|
506
|
+
return;
|
|
507
|
+
const hasGuild = this.resolveGuild(state.guildId);
|
|
508
|
+
if (!hasGuild)
|
|
509
|
+
return;
|
|
510
|
+
const lavaPlayer = (await node.rest.get(`/v4/sessions/${state.node.sessionId}/players/${state.guildId}`));
|
|
511
|
+
if (!lavaPlayer)
|
|
512
|
+
return;
|
|
513
|
+
const playerOptions = {
|
|
514
|
+
guildId: state.options.guildId,
|
|
515
|
+
textChannelId: state.options.textChannelId,
|
|
516
|
+
voiceChannelId: state.options.voiceChannelId,
|
|
517
|
+
selfDeafen: state.options.selfDeafen,
|
|
518
|
+
volume: lavaPlayer.volume || state.options.volume,
|
|
519
|
+
nodeIdentifier: nodeId,
|
|
520
|
+
applyVolumeAsFilter: state.options.applyVolumeAsFilter,
|
|
521
|
+
pauseOnDisconnect: state.options.pauseOnDisconnect,
|
|
522
|
+
};
|
|
523
|
+
const player = this.create(playerOptions);
|
|
524
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId}`);
|
|
525
|
+
if (state.isAutoplay) {
|
|
526
|
+
const savedUser = state.data?.clientUser;
|
|
527
|
+
if (savedUser) {
|
|
528
|
+
const autoPlayUser = await player.manager.resolveUser(savedUser);
|
|
529
|
+
player.setAutoplay(true, autoPlayUser, state.autoplayTries);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
const savedNowPlayingMessage = state.data?.nowPlayingMessage;
|
|
533
|
+
if (savedNowPlayingMessage) {
|
|
534
|
+
player.setNowPlayingMessage(savedNowPlayingMessage);
|
|
535
|
+
}
|
|
536
|
+
await this.restoreQueue(node, player, state, lavaPlayer);
|
|
537
|
+
await this.restorePreviousQueue(player, state);
|
|
538
|
+
this.restoreRepeatState(player, state);
|
|
539
|
+
this.restorePlayerData(player, state);
|
|
540
|
+
this.restoreFilters(player, state);
|
|
541
|
+
player.connect();
|
|
542
|
+
if (lavaPlayer.track && state.clusterId !== player.clusterId) {
|
|
543
|
+
const currentTrack = state.queue.current;
|
|
544
|
+
await player.play(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay), { startTime: lavaPlayer.state.position ?? 0 });
|
|
545
|
+
await node.rest.delete(`/v4/sessions/${state.node.sessionId}/players/${state.guildId}`);
|
|
546
|
+
}
|
|
547
|
+
await player.pause(state.paused);
|
|
548
|
+
await cleanup();
|
|
549
|
+
this.emit(Enums_1.ManagerEventTypes.PlayerRestored, player, node);
|
|
550
|
+
await this.sleep(1000);
|
|
551
|
+
}
|
|
552
|
+
async restoreQueue(node, player, state, lavaPlayer) {
|
|
553
|
+
const currentTrack = state.queue.current;
|
|
554
|
+
const queueTracks = state.queue.tracks;
|
|
555
|
+
if (lavaPlayer.track) {
|
|
556
|
+
await player.queue.clear();
|
|
557
|
+
if (currentTrack) {
|
|
558
|
+
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay));
|
|
559
|
+
}
|
|
560
|
+
const remainingQueue = queueTracks.filter((t) => t.uri !== lavaPlayer.track.info.uri);
|
|
561
|
+
if (remainingQueue.length > 0)
|
|
562
|
+
await player.queue.add(remainingQueue);
|
|
563
|
+
player.playing = !lavaPlayer.paused;
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
// No active lavalink track
|
|
567
|
+
if (currentTrack) {
|
|
568
|
+
if (queueTracks.length > 0) {
|
|
569
|
+
await player.queue.clear();
|
|
570
|
+
await player.queue.add(queueTracks);
|
|
571
|
+
}
|
|
572
|
+
await node.trackEnd(player, currentTrack, {
|
|
573
|
+
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
574
|
+
type: "TrackEndEvent",
|
|
575
|
+
});
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
// No current track either — check previous
|
|
579
|
+
const previousQueue = await player.queue.getPrevious();
|
|
580
|
+
const lastTrack = previousQueue?.at(-1);
|
|
581
|
+
if (queueTracks.length > 0) {
|
|
582
|
+
await player.queue.clear();
|
|
583
|
+
await player.queue.add(queueTracks);
|
|
584
|
+
}
|
|
585
|
+
if (lastTrack || queueTracks.length > 0) {
|
|
586
|
+
await node.trackEnd(player, lastTrack, {
|
|
587
|
+
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
588
|
+
type: "TrackEndEvent",
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
async restorePreviousQueue(player, state) {
|
|
593
|
+
if (state.queue.previous.length > 0) {
|
|
594
|
+
const validPrevious = state.queue.previous.filter((t) => t !== null && typeof t.identifier === "string");
|
|
595
|
+
if (validPrevious.length > 0)
|
|
596
|
+
await player.queue.addPrevious(validPrevious);
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
await player.queue.clearPrevious();
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
restoreRepeatState(player, state) {
|
|
603
|
+
if (state.trackRepeat)
|
|
604
|
+
player.setTrackRepeat(true);
|
|
605
|
+
if (state.queueRepeat)
|
|
606
|
+
player.setQueueRepeat(true);
|
|
607
|
+
if (state.dynamicRepeat && state.dynamicLoopInterval) {
|
|
608
|
+
player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
restorePlayerData(player, state) {
|
|
612
|
+
if (!state.data)
|
|
613
|
+
return;
|
|
614
|
+
for (const [name, value] of Object.entries(state.data)) {
|
|
615
|
+
player.set(name, value);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
restoreFilters(player, state) {
|
|
619
|
+
const filterActions = {
|
|
620
|
+
bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
|
|
621
|
+
distort: (e) => player.filters.distort(e),
|
|
622
|
+
setDistortion: () => player.filters.setDistortion(state.filters.distortion),
|
|
623
|
+
eightD: (e) => player.filters.eightD(e),
|
|
624
|
+
setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
|
|
625
|
+
nightcore: (e) => player.filters.nightcore(e),
|
|
626
|
+
slowmo: (e) => player.filters.slowmo(e),
|
|
627
|
+
soft: (e) => player.filters.soft(e),
|
|
628
|
+
trebleBass: (e) => player.filters.trebleBass(e),
|
|
629
|
+
setTimescale: () => player.filters.setTimescale(state.filters.timescale),
|
|
630
|
+
tv: (e) => player.filters.tv(e),
|
|
631
|
+
vibrato: () => player.filters.setVibrato(state.filters.vibrato),
|
|
632
|
+
vaporwave: (e) => player.filters.vaporwave(e),
|
|
633
|
+
pop: (e) => player.filters.pop(e),
|
|
634
|
+
party: (e) => player.filters.party(e),
|
|
635
|
+
earrape: (e) => player.filters.earrape(e),
|
|
636
|
+
electronic: (e) => player.filters.electronic(e),
|
|
637
|
+
radio: (e) => player.filters.radio(e),
|
|
638
|
+
setRotation: () => player.filters.setRotation(state.filters.rotation),
|
|
639
|
+
tremolo: (e) => player.filters.tremolo(e),
|
|
640
|
+
china: (e) => player.filters.china(e),
|
|
641
|
+
chipmunk: (e) => player.filters.chipmunk(e),
|
|
642
|
+
darthvader: (e) => player.filters.darthvader(e),
|
|
643
|
+
daycore: (e) => player.filters.daycore(e),
|
|
644
|
+
doubletime: (e) => player.filters.doubletime(e),
|
|
645
|
+
demon: (e) => player.filters.demon(e),
|
|
646
|
+
};
|
|
647
|
+
for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
|
|
648
|
+
if (isEnabled && filterActions[filter])
|
|
649
|
+
filterActions[filter](true);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
497
652
|
/**
|
|
498
653
|
* Loads player states from the JSON file.
|
|
499
654
|
* @param nodeId The ID of the node to load player states from.
|
|
@@ -503,7 +658,6 @@ class Manager extends events_1.EventEmitter {
|
|
|
503
658
|
this.emit(Enums_1.ManagerEventTypes.Debug, "[MANAGER] Loading saved players.");
|
|
504
659
|
const node = this.nodes.get(nodeId);
|
|
505
660
|
if (!node) {
|
|
506
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Tried to load non-existent node: ${nodeId}`);
|
|
507
661
|
throw new MagmastreamError_1.MagmaStreamError({
|
|
508
662
|
code: Enums_1.MagmaStreamErrorCode.MANAGER_NODE_NOT_FOUND,
|
|
509
663
|
message: "Node not found.",
|
|
@@ -512,400 +666,65 @@ class Manager extends events_1.EventEmitter {
|
|
|
512
666
|
}
|
|
513
667
|
switch (this.options.stateStorage.type) {
|
|
514
668
|
case Enums_1.StateStorageType.Memory:
|
|
515
|
-
case Enums_1.StateStorageType.JSON:
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
const state = JSON.parse(rawData);
|
|
535
|
-
if (state.clusterId !== this.options.clusterId)
|
|
536
|
-
continue;
|
|
537
|
-
if (!state.guildId || state.node?.options?.identifier !== nodeId)
|
|
538
|
-
continue;
|
|
539
|
-
const lavaPlayer = await node.rest.getPlayer(state.guildId);
|
|
540
|
-
if (!lavaPlayer) {
|
|
541
|
-
await this.destroy(state.guildId);
|
|
542
|
-
continue;
|
|
543
|
-
}
|
|
544
|
-
const playerOptions = {
|
|
545
|
-
guildId: state.options.guildId,
|
|
546
|
-
textChannelId: state.options.textChannelId,
|
|
547
|
-
voiceChannelId: state.options.voiceChannelId,
|
|
548
|
-
selfDeafen: state.options.selfDeafen,
|
|
549
|
-
volume: lavaPlayer.volume || state.options.volume,
|
|
550
|
-
nodeIdentifier: nodeId,
|
|
551
|
-
applyVolumeAsFilter: state.options.applyVolumeAsFilter,
|
|
552
|
-
pauseOnDisconnect: state.options.pauseOnDisconnect,
|
|
553
|
-
};
|
|
554
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${Utils_1.JSONUtils.safe(state.options, 2)}`);
|
|
555
|
-
const player = this.create(playerOptions);
|
|
556
|
-
await player.node.rest.updatePlayer({
|
|
557
|
-
guildId: state.options.guildId,
|
|
558
|
-
data: {
|
|
559
|
-
voice: {
|
|
560
|
-
token: state.voiceState.event.token,
|
|
561
|
-
endpoint: state.voiceState.event.endpoint,
|
|
562
|
-
sessionId: state.voiceState.sessionId,
|
|
563
|
-
},
|
|
564
|
-
},
|
|
565
|
-
});
|
|
566
|
-
player.connect();
|
|
567
|
-
const tracks = [];
|
|
568
|
-
const currentTrack = state.queue.current;
|
|
569
|
-
const queueTracks = state.queue.tracks;
|
|
570
|
-
if (state.isAutoplay) {
|
|
571
|
-
const savedUser = state.data.clientUser;
|
|
572
|
-
if (savedUser) {
|
|
573
|
-
const autoPlayUser = await player.manager.resolveUser(savedUser);
|
|
574
|
-
player.setAutoplay(true, autoPlayUser, state.autoplayTries);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
const savedNowPlayingMessage = state.data?.nowPlayingMessage;
|
|
578
|
-
if (savedNowPlayingMessage) {
|
|
579
|
-
player.setNowPlayingMessage(savedNowPlayingMessage);
|
|
580
|
-
}
|
|
581
|
-
if (lavaPlayer.track) {
|
|
582
|
-
await player.queue.clear();
|
|
583
|
-
if (currentTrack) {
|
|
584
|
-
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay));
|
|
585
|
-
}
|
|
586
|
-
const remainingQueue = queueTracks.filter((t) => t.uri !== lavaPlayer.track.info.uri);
|
|
587
|
-
if (remainingQueue.length > 0) {
|
|
588
|
-
await player.queue.add(remainingQueue);
|
|
589
|
-
}
|
|
590
|
-
player.playing = !lavaPlayer.paused;
|
|
591
|
-
}
|
|
592
|
-
else {
|
|
593
|
-
if (currentTrack) {
|
|
594
|
-
if (queueTracks.length > 0) {
|
|
595
|
-
tracks.push(...queueTracks);
|
|
596
|
-
await player.queue.clear();
|
|
597
|
-
await player.queue.add(tracks);
|
|
598
|
-
}
|
|
599
|
-
await node.trackEnd(player, currentTrack, {
|
|
600
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
601
|
-
type: "TrackEndEvent",
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
else {
|
|
605
|
-
const previousQueue = await player.queue.getPrevious();
|
|
606
|
-
const lastTrack = previousQueue?.at(-1);
|
|
607
|
-
if (lastTrack) {
|
|
608
|
-
if (queueTracks.length === 0) {
|
|
609
|
-
await node.trackEnd(player, lastTrack, {
|
|
610
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
611
|
-
type: "TrackEndEvent",
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
else {
|
|
615
|
-
tracks.push(...queueTracks);
|
|
616
|
-
if (tracks.length > 0) {
|
|
617
|
-
await player.queue.clear();
|
|
618
|
-
await player.queue.add(tracks);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
else if (queueTracks.length > 0) {
|
|
623
|
-
tracks.push(...queueTracks);
|
|
624
|
-
if (tracks.length > 0) {
|
|
625
|
-
await player.queue.clear();
|
|
626
|
-
await player.queue.add(tracks);
|
|
627
|
-
}
|
|
628
|
-
await node.trackEnd(player, lastTrack, {
|
|
629
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
630
|
-
type: "TrackEndEvent",
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
if (state.queue.previous.length > 0) {
|
|
636
|
-
await player.queue.addPrevious(state.queue.previous);
|
|
637
|
-
}
|
|
638
|
-
else {
|
|
639
|
-
await player.queue.clearPrevious();
|
|
640
|
-
}
|
|
641
|
-
await player.pause(state.paused);
|
|
642
|
-
if (state.trackRepeat)
|
|
643
|
-
player.setTrackRepeat(true);
|
|
644
|
-
if (state.queueRepeat)
|
|
645
|
-
player.setQueueRepeat(true);
|
|
646
|
-
if (state.dynamicRepeat && state.dynamicLoopInterval) {
|
|
647
|
-
player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval);
|
|
648
|
-
}
|
|
649
|
-
if (state.data) {
|
|
650
|
-
for (const [name, value] of Object.entries(state.data)) {
|
|
651
|
-
player.set(name, value);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
const filterActions = {
|
|
655
|
-
bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
|
|
656
|
-
distort: (enabled) => player.filters.distort(enabled),
|
|
657
|
-
setDistortion: () => player.filters.setDistortion(state.filters.distortion),
|
|
658
|
-
eightD: (enabled) => player.filters.eightD(enabled),
|
|
659
|
-
setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
|
|
660
|
-
nightcore: (enabled) => player.filters.nightcore(enabled),
|
|
661
|
-
slowmo: (enabled) => player.filters.slowmo(enabled),
|
|
662
|
-
soft: (enabled) => player.filters.soft(enabled),
|
|
663
|
-
trebleBass: (enabled) => player.filters.trebleBass(enabled),
|
|
664
|
-
setTimescale: () => player.filters.setTimescale(state.filters.timescale),
|
|
665
|
-
tv: (enabled) => player.filters.tv(enabled),
|
|
666
|
-
vibrato: () => player.filters.setVibrato(state.filters.vibrato),
|
|
667
|
-
vaporwave: (enabled) => player.filters.vaporwave(enabled),
|
|
668
|
-
pop: (enabled) => player.filters.pop(enabled),
|
|
669
|
-
party: (enabled) => player.filters.party(enabled),
|
|
670
|
-
earrape: (enabled) => player.filters.earrape(enabled),
|
|
671
|
-
electronic: (enabled) => player.filters.electronic(enabled),
|
|
672
|
-
radio: (enabled) => player.filters.radio(enabled),
|
|
673
|
-
setRotation: () => player.filters.setRotation(state.filters.rotation),
|
|
674
|
-
tremolo: (enabled) => player.filters.tremolo(enabled),
|
|
675
|
-
china: (enabled) => player.filters.china(enabled),
|
|
676
|
-
chipmunk: (enabled) => player.filters.chipmunk(enabled),
|
|
677
|
-
darthvader: (enabled) => player.filters.darthvader(enabled),
|
|
678
|
-
daycore: (enabled) => player.filters.daycore(enabled),
|
|
679
|
-
doubletime: (enabled) => player.filters.doubletime(enabled),
|
|
680
|
-
demon: (enabled) => player.filters.demon(enabled),
|
|
681
|
-
};
|
|
682
|
-
for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
|
|
683
|
-
if (isEnabled && filterActions[filter]) {
|
|
684
|
-
filterActions[filter](true);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
this.emit(Enums_1.ManagerEventTypes.PlayerRestored, player, node);
|
|
688
|
-
await this.sleep(1000);
|
|
689
|
-
}
|
|
690
|
-
catch (error) {
|
|
691
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing player state for guild ${guildId}: ${error}`);
|
|
692
|
-
continue;
|
|
693
|
-
}
|
|
669
|
+
case Enums_1.StateStorageType.JSON: {
|
|
670
|
+
const playersBaseDir = Utils_1.PlayerUtils.getPlayersBaseDir();
|
|
671
|
+
try {
|
|
672
|
+
await promises_1.default.access(playersBaseDir).catch(async () => {
|
|
673
|
+
await promises_1.default.mkdir(playersBaseDir, { recursive: true });
|
|
674
|
+
});
|
|
675
|
+
const guildDirs = await promises_1.default.readdir(playersBaseDir, { withFileTypes: true });
|
|
676
|
+
for (const file of guildDirs) {
|
|
677
|
+
if (!file.isDirectory())
|
|
678
|
+
continue;
|
|
679
|
+
const guildId = file.name;
|
|
680
|
+
const stateFilePath = Utils_1.PlayerUtils.getPlayerStatePath(guildId);
|
|
681
|
+
try {
|
|
682
|
+
await promises_1.default.access(stateFilePath);
|
|
683
|
+
const state = JSON.parse(await promises_1.default.readFile(stateFilePath, "utf-8"));
|
|
684
|
+
await this.restorePlayerFromState(node, nodeId, guildId, state, async () => {
|
|
685
|
+
await promises_1.default.rm(stateFilePath, { force: true });
|
|
686
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted state for guild ${guildId}`);
|
|
687
|
+
});
|
|
694
688
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
if (!dirent.isDirectory())
|
|
698
|
-
continue;
|
|
699
|
-
const guildId = dirent.name;
|
|
700
|
-
const stateFilePath = Utils_1.PlayerUtils.getPlayerStatePath(guildId);
|
|
701
|
-
try {
|
|
702
|
-
await promises_1.default.access(stateFilePath);
|
|
703
|
-
const data = await promises_1.default.readFile(stateFilePath, "utf-8");
|
|
704
|
-
const state = JSON.parse(data);
|
|
705
|
-
if (state && typeof state === "object" && state.node?.options?.identifier === nodeId) {
|
|
706
|
-
await promises_1.default.rm(Utils_1.PlayerUtils.getPlayerStatePath(guildId), { force: true });
|
|
707
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted player state folder for guild ${guildId}`);
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
catch (error) {
|
|
711
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error deleting player state for guild ${guildId}: ${error}`);
|
|
712
|
-
continue;
|
|
713
|
-
}
|
|
689
|
+
catch (error) {
|
|
690
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing guild ${guildId}: ${error}`);
|
|
714
691
|
}
|
|
715
692
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
}
|
|
693
|
+
}
|
|
694
|
+
catch (error) {
|
|
695
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error loading player states: ${error}`);
|
|
719
696
|
}
|
|
720
697
|
break;
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
for (const key of keys) {
|
|
730
|
-
try {
|
|
731
|
-
const data = await this.redis.get(key);
|
|
732
|
-
if (!data)
|
|
733
|
-
continue;
|
|
734
|
-
const state = JSON.parse(data);
|
|
735
|
-
if (!state || typeof state !== "object" || state.clusterId !== this.options.clusterId)
|
|
736
|
-
continue;
|
|
737
|
-
const guildId = key.split(":").pop();
|
|
738
|
-
if (!guildId || state.node.options.identifier !== nodeId)
|
|
739
|
-
continue;
|
|
740
|
-
const lavaPlayer = await node.rest.getPlayer(state.guildId);
|
|
741
|
-
if (!lavaPlayer) {
|
|
742
|
-
await this.destroy(guildId);
|
|
743
|
-
}
|
|
744
|
-
const playerOptions = {
|
|
745
|
-
guildId: state.options.guildId,
|
|
746
|
-
textChannelId: state.options.textChannelId,
|
|
747
|
-
voiceChannelId: state.options.voiceChannelId,
|
|
748
|
-
selfDeafen: state.options.selfDeafen,
|
|
749
|
-
volume: lavaPlayer.volume || state.options.volume,
|
|
750
|
-
nodeIdentifier: nodeId,
|
|
751
|
-
applyVolumeAsFilter: state.options.applyVolumeAsFilter,
|
|
752
|
-
pauseOnDisconnect: state.options.pauseOnDisconnect,
|
|
753
|
-
};
|
|
754
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
|
|
755
|
-
const player = this.create(playerOptions);
|
|
756
|
-
await player.node.rest.updatePlayer({
|
|
757
|
-
guildId: state.options.guildId,
|
|
758
|
-
data: { voice: { token: state.voiceState.event.token, endpoint: state.voiceState.event.endpoint, sessionId: state.voiceState.sessionId } },
|
|
759
|
-
});
|
|
760
|
-
player.connect();
|
|
761
|
-
// Rest of the player state restoration code (tracks, filters, etc.)
|
|
762
|
-
const tracks = [];
|
|
763
|
-
const currentTrack = state.queue.current;
|
|
764
|
-
const queueTracks = state.queue.tracks;
|
|
765
|
-
if (state.isAutoplay) {
|
|
766
|
-
const savedUser = state.data.clientUser;
|
|
767
|
-
if (savedUser) {
|
|
768
|
-
const autoPlayUser = await player.manager.resolveUser(savedUser);
|
|
769
|
-
player.setAutoplay(true, autoPlayUser, state.autoplayTries);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
const savedNowPlayingMessage = state.data?.nowPlayingMessage;
|
|
773
|
-
if (savedNowPlayingMessage) {
|
|
774
|
-
player.setNowPlayingMessage(savedNowPlayingMessage);
|
|
775
|
-
}
|
|
776
|
-
if (lavaPlayer.track) {
|
|
777
|
-
await player.queue.clear();
|
|
778
|
-
if (currentTrack) {
|
|
779
|
-
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay));
|
|
780
|
-
}
|
|
781
|
-
const remainingQueue = queueTracks.filter((t) => t.uri !== lavaPlayer.track.info.uri);
|
|
782
|
-
if (remainingQueue.length > 0) {
|
|
783
|
-
await player.queue.add(remainingQueue);
|
|
784
|
-
}
|
|
785
|
-
player.playing = !lavaPlayer.paused;
|
|
786
|
-
}
|
|
787
|
-
else {
|
|
788
|
-
// LavaPlayer missing track or lavaPlayer is falsy
|
|
789
|
-
if (currentTrack) {
|
|
790
|
-
if (queueTracks.length > 0) {
|
|
791
|
-
tracks.push(...queueTracks);
|
|
792
|
-
await player.queue.clear();
|
|
793
|
-
await player.queue.add(tracks);
|
|
794
|
-
}
|
|
795
|
-
await node.trackEnd(player, currentTrack, {
|
|
796
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
797
|
-
type: "TrackEndEvent",
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
else {
|
|
801
|
-
// No current track, check previous queue for last track
|
|
802
|
-
const previousQueue = await player.queue.getPrevious();
|
|
803
|
-
const lastTrack = previousQueue?.at(-1);
|
|
804
|
-
if (lastTrack) {
|
|
805
|
-
if (queueTracks.length === 0) {
|
|
806
|
-
// If no tracks in queue, end last track
|
|
807
|
-
await node.trackEnd(player, lastTrack, {
|
|
808
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
809
|
-
type: "TrackEndEvent",
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
|
-
else {
|
|
813
|
-
// If there are queued tracks, add them
|
|
814
|
-
tracks.push(...queueTracks);
|
|
815
|
-
if (tracks.length > 0) {
|
|
816
|
-
await player.queue.clear();
|
|
817
|
-
await player.queue.add(tracks);
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
else {
|
|
822
|
-
if (queueTracks.length > 0) {
|
|
823
|
-
tracks.push(...queueTracks);
|
|
824
|
-
if (tracks.length > 0) {
|
|
825
|
-
await player.queue.clear();
|
|
826
|
-
await player.queue.add(tracks);
|
|
827
|
-
}
|
|
828
|
-
await node.trackEnd(player, lastTrack, {
|
|
829
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
830
|
-
type: "TrackEndEvent",
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
if (state.queue.previous.length > 0) {
|
|
837
|
-
await player.queue.addPrevious(state.queue.previous);
|
|
838
|
-
}
|
|
839
|
-
else {
|
|
840
|
-
await player.queue.clearPrevious();
|
|
841
|
-
}
|
|
842
|
-
await player.pause(state.paused);
|
|
843
|
-
if (state.trackRepeat)
|
|
844
|
-
player.setTrackRepeat(true);
|
|
845
|
-
if (state.queueRepeat)
|
|
846
|
-
player.setQueueRepeat(true);
|
|
847
|
-
if (state.dynamicRepeat && state.dynamicLoopInterval) {
|
|
848
|
-
player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval);
|
|
849
|
-
}
|
|
850
|
-
if (state.data) {
|
|
851
|
-
for (const [name, value] of Object.entries(state.data)) {
|
|
852
|
-
player.set(name, value);
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
const filterActions = {
|
|
856
|
-
bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
|
|
857
|
-
distort: (enabled) => player.filters.distort(enabled),
|
|
858
|
-
setDistortion: () => player.filters.setDistortion(state.filters.distortion),
|
|
859
|
-
eightD: (enabled) => player.filters.eightD(enabled),
|
|
860
|
-
setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
|
|
861
|
-
nightcore: (enabled) => player.filters.nightcore(enabled),
|
|
862
|
-
slowmo: (enabled) => player.filters.slowmo(enabled),
|
|
863
|
-
soft: (enabled) => player.filters.soft(enabled),
|
|
864
|
-
trebleBass: (enabled) => player.filters.trebleBass(enabled),
|
|
865
|
-
setTimescale: () => player.filters.setTimescale(state.filters.timescale),
|
|
866
|
-
tv: (enabled) => player.filters.tv(enabled),
|
|
867
|
-
vibrato: () => player.filters.setVibrato(state.filters.vibrato),
|
|
868
|
-
vaporwave: (enabled) => player.filters.vaporwave(enabled),
|
|
869
|
-
pop: (enabled) => player.filters.pop(enabled),
|
|
870
|
-
party: (enabled) => player.filters.party(enabled),
|
|
871
|
-
earrape: (enabled) => player.filters.earrape(enabled),
|
|
872
|
-
electronic: (enabled) => player.filters.electronic(enabled),
|
|
873
|
-
radio: (enabled) => player.filters.radio(enabled),
|
|
874
|
-
setRotation: () => player.filters.setRotation(state.filters.rotation),
|
|
875
|
-
tremolo: (enabled) => player.filters.tremolo(enabled),
|
|
876
|
-
china: (enabled) => player.filters.china(enabled),
|
|
877
|
-
chipmunk: (enabled) => player.filters.chipmunk(enabled),
|
|
878
|
-
darthvader: (enabled) => player.filters.darthvader(enabled),
|
|
879
|
-
daycore: (enabled) => player.filters.daycore(enabled),
|
|
880
|
-
doubletime: (enabled) => player.filters.doubletime(enabled),
|
|
881
|
-
demon: (enabled) => player.filters.demon(enabled),
|
|
882
|
-
};
|
|
883
|
-
// Iterate through filterStatus and apply the enabled filters
|
|
884
|
-
for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
|
|
885
|
-
if (isEnabled && filterActions[filter]) {
|
|
886
|
-
filterActions[filter](true);
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
// After processing, delete the Redis key
|
|
890
|
-
await this.redis.del(key);
|
|
891
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted player state from Redis: ${key}`);
|
|
892
|
-
this.emit(Enums_1.ManagerEventTypes.PlayerRestored, player, node);
|
|
893
|
-
await this.sleep(1000);
|
|
894
|
-
}
|
|
895
|
-
catch (error) {
|
|
896
|
-
console.log(error);
|
|
897
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing Redis key ${key}: ${error}`);
|
|
698
|
+
}
|
|
699
|
+
case Enums_1.StateStorageType.Redis: {
|
|
700
|
+
try {
|
|
701
|
+
const keys = await this.redis.keys(`${Utils_1.PlayerUtils.getRedisKey()}playerstore:*`);
|
|
702
|
+
for (const key of keys) {
|
|
703
|
+
try {
|
|
704
|
+
const data = await this.redis.get(key);
|
|
705
|
+
if (!data)
|
|
898
706
|
continue;
|
|
899
|
-
|
|
707
|
+
const state = JSON.parse(data);
|
|
708
|
+
if (!state || typeof state !== "object")
|
|
709
|
+
continue;
|
|
710
|
+
const guildId = key.split(":").pop();
|
|
711
|
+
if (!guildId)
|
|
712
|
+
continue;
|
|
713
|
+
await this.restorePlayerFromState(node, nodeId, guildId, state, async () => {
|
|
714
|
+
await this.redis.del(key);
|
|
715
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted Redis state: ${key}`);
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
catch (error) {
|
|
719
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing Redis key ${key}: ${error}`);
|
|
900
720
|
}
|
|
901
|
-
}
|
|
902
|
-
catch (error) {
|
|
903
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error loading player states from Redis: ${error}`);
|
|
904
721
|
}
|
|
905
722
|
}
|
|
723
|
+
catch (error) {
|
|
724
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error loading player states from Redis: ${error}`);
|
|
725
|
+
}
|
|
906
726
|
break;
|
|
907
|
-
|
|
908
|
-
break;
|
|
727
|
+
}
|
|
909
728
|
}
|
|
910
729
|
this.emit(Enums_1.ManagerEventTypes.Debug, "[MANAGER] Finished loading saved players.");
|
|
911
730
|
this.emit(Enums_1.ManagerEventTypes.RestoreComplete, node);
|
|
@@ -948,8 +767,6 @@ class Manager extends events_1.EventEmitter {
|
|
|
948
767
|
}
|
|
949
768
|
});
|
|
950
769
|
await Promise.allSettled(savePromises);
|
|
951
|
-
if (this.options.stateStorage.deleteInactivePlayers)
|
|
952
|
-
await this.cleanupInactivePlayers();
|
|
953
770
|
setTimeout(() => {
|
|
954
771
|
console.warn("\x1b[32m%s\x1b[0m", "MAGMASTREAM INFO: Shutting down complete, exiting...");
|
|
955
772
|
process.exit(0);
|
|
@@ -1077,13 +894,10 @@ class Manager extends events_1.EventEmitter {
|
|
|
1077
894
|
*/
|
|
1078
895
|
async handleVoiceServerUpdate(player, update) {
|
|
1079
896
|
player.voiceState.event = update;
|
|
1080
|
-
const
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
});
|
|
1085
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `Updated voice server for player ${player.guildId} with token ${token} and endpoint ${endpoint} and sessionId ${sessionId}`);
|
|
1086
|
-
return;
|
|
897
|
+
const sessionId = player.voiceState.sessionId;
|
|
898
|
+
const channelId = player.voiceState.channelId;
|
|
899
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `Updated voice server for player ${player.guildId} with token ${update.token} | endpoint ${update.endpoint} | sessionId ${sessionId} | channelId ${channelId}`);
|
|
900
|
+
await player.updateVoice();
|
|
1087
901
|
}
|
|
1088
902
|
/**
|
|
1089
903
|
* Handles a voice state update by updating the player's voice channel and session ID if provided, or by disconnecting and destroying the player if the channel ID is null.
|
|
@@ -1094,105 +908,23 @@ class Manager extends events_1.EventEmitter {
|
|
|
1094
908
|
*/
|
|
1095
909
|
async handleVoiceStateUpdate(player, update) {
|
|
1096
910
|
this.emit(Enums_1.ManagerEventTypes.Debug, `Updated voice state for player ${player.guildId} with channel id ${update.channel_id} and session id ${update.session_id}`);
|
|
1097
|
-
if (update.channel_id) {
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
player.voiceState
|
|
1102
|
-
player.
|
|
1103
|
-
|
|
911
|
+
if (!update.channel_id) {
|
|
912
|
+
this.emit(Enums_1.ManagerEventTypes.PlayerDisconnect, player, player.voiceChannelId);
|
|
913
|
+
player.voiceChannelId = null;
|
|
914
|
+
player.state = Enums_1.StateTypes.Disconnected;
|
|
915
|
+
player.voiceState = Object.assign({});
|
|
916
|
+
if (player.options.pauseOnDisconnect)
|
|
917
|
+
await player.pause(true);
|
|
1104
918
|
return;
|
|
1105
919
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
player.state = Enums_1.StateTypes.Disconnected;
|
|
1109
|
-
player.voiceState = Object.assign({});
|
|
1110
|
-
if (player.options.pauseOnDisconnect) {
|
|
1111
|
-
await player.pause(true);
|
|
1112
|
-
}
|
|
1113
|
-
return;
|
|
1114
|
-
}
|
|
1115
|
-
/**
|
|
1116
|
-
* Cleans up inactive players by removing their state files from the file system.
|
|
1117
|
-
* This is done to prevent stale state files from accumulating on the file system.
|
|
1118
|
-
*/
|
|
1119
|
-
async cleanupInactivePlayers() {
|
|
1120
|
-
switch (this.options.stateStorage.type) {
|
|
1121
|
-
case Enums_1.StateStorageType.JSON:
|
|
1122
|
-
{
|
|
1123
|
-
const playersBaseDir = Utils_1.PlayerUtils.getPlayersBaseDir();
|
|
1124
|
-
try {
|
|
1125
|
-
await promises_1.default.mkdir(playersBaseDir, { recursive: true });
|
|
1126
|
-
const activeGuildIds = new Set(this.players.keys());
|
|
1127
|
-
// Cleanup inactive guild directories inside playersBaseDir
|
|
1128
|
-
const guildDirs = await promises_1.default.readdir(playersBaseDir, { withFileTypes: true });
|
|
1129
|
-
for (const dirent of guildDirs) {
|
|
1130
|
-
if (!dirent.isDirectory())
|
|
1131
|
-
continue;
|
|
1132
|
-
const guildId = dirent.name;
|
|
1133
|
-
if (!activeGuildIds.has(guildId)) {
|
|
1134
|
-
const guildPath = Utils_1.PlayerUtils.getGuildDir(guildId);
|
|
1135
|
-
await promises_1.default.rm(guildPath, { recursive: true, force: true });
|
|
1136
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted inactive player data folder: ${guildId}`);
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
catch (err) {
|
|
1141
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error cleaning up inactive JSON players: ${err}`);
|
|
1142
|
-
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1143
|
-
? err
|
|
1144
|
-
: new MagmastreamError_1.MagmaStreamError({
|
|
1145
|
-
code: Enums_1.MagmaStreamErrorCode.MANAGER_CLEANUP_INACTIVE_PLAYERS_FAILED,
|
|
1146
|
-
message: "Error cleaning up inactive players.",
|
|
1147
|
-
cause: err,
|
|
1148
|
-
context: { stage: "CLEANUP_INACTIVE_PLAYERS" },
|
|
1149
|
-
});
|
|
1150
|
-
console.error(error);
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
break;
|
|
1154
|
-
case Enums_1.StateStorageType.Redis:
|
|
1155
|
-
{
|
|
1156
|
-
const prefix = this.options.stateStorage.redisConfig.prefix?.endsWith(":")
|
|
1157
|
-
? this.options.stateStorage.redisConfig.prefix
|
|
1158
|
-
: (this.options.stateStorage.redisConfig.prefix ?? "magmastream:");
|
|
1159
|
-
const pattern = `${prefix}queue:*:current`;
|
|
1160
|
-
try {
|
|
1161
|
-
const stream = this.redis.scanStream({
|
|
1162
|
-
match: pattern,
|
|
1163
|
-
count: 100,
|
|
1164
|
-
});
|
|
1165
|
-
for await (const keys of stream) {
|
|
1166
|
-
for (const key of keys) {
|
|
1167
|
-
// Extract guildId from queue key
|
|
1168
|
-
const match = key.match(new RegExp(`^${prefix}queue:(.+):current$`));
|
|
1169
|
-
if (!match)
|
|
1170
|
-
continue;
|
|
1171
|
-
const guildId = match[1];
|
|
1172
|
-
// If player is not active in memory, clean up all keys
|
|
1173
|
-
if (!this.players.has(guildId)) {
|
|
1174
|
-
await this.redis.del(`${prefix}playerstore:${guildId}`, `${prefix}queue:${guildId}:current`, `${prefix}queue:${guildId}:tracks`, `${prefix}queue:${guildId}:previous`);
|
|
1175
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Cleaned inactive Redis player data: ${guildId}`);
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
catch (err) {
|
|
1181
|
-
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1182
|
-
? err
|
|
1183
|
-
: new MagmastreamError_1.MagmaStreamError({
|
|
1184
|
-
code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
|
|
1185
|
-
message: "Error saving player state.",
|
|
1186
|
-
cause: err,
|
|
1187
|
-
context: { stage: "CLEANUP_INACTIVE_PLAYERS" },
|
|
1188
|
-
});
|
|
1189
|
-
console.error(error);
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
break;
|
|
1193
|
-
default:
|
|
1194
|
-
break;
|
|
920
|
+
if (player.voiceChannelId !== update.channel_id) {
|
|
921
|
+
this.emit(Enums_1.ManagerEventTypes.PlayerMove, player, player.voiceChannelId, update.channel_id);
|
|
1195
922
|
}
|
|
923
|
+
player.voiceState.sessionId = update.session_id;
|
|
924
|
+
player.voiceState.channelId = update.channel_id;
|
|
925
|
+
player.voiceChannelId = update.channel_id;
|
|
926
|
+
player.options.voiceChannelId = update.channel_id;
|
|
927
|
+
await player.updateVoice();
|
|
1196
928
|
}
|
|
1197
929
|
/**
|
|
1198
930
|
* Cleans up an inactive player by removing its state data.
|
|
@@ -1228,9 +960,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
1228
960
|
{
|
|
1229
961
|
try {
|
|
1230
962
|
if (!player) {
|
|
1231
|
-
const prefix =
|
|
1232
|
-
? this.options.stateStorage.redisConfig.prefix
|
|
1233
|
-
: `${this.options.stateStorage.redisConfig.prefix ?? "magmastream"}:`;
|
|
963
|
+
const prefix = Utils_1.PlayerUtils.getRedisKey();
|
|
1234
964
|
const keysToDelete = [
|
|
1235
965
|
`${prefix}playerstore:${guildId}`,
|
|
1236
966
|
`${prefix}queue:${guildId}:tracks`,
|
|
@@ -1338,9 +1068,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
1338
1068
|
break;
|
|
1339
1069
|
}
|
|
1340
1070
|
case Enums_1.StateStorageType.Redis: {
|
|
1341
|
-
const prefix =
|
|
1342
|
-
? this.options.stateStorage.redisConfig.prefix
|
|
1343
|
-
: (this.options.stateStorage.redisConfig.prefix ?? "magmastream:");
|
|
1071
|
+
const prefix = Utils_1.PlayerUtils.getRedisKey();
|
|
1344
1072
|
const patterns = [`${prefix}playerstore:*`, `${prefix}queue:*`];
|
|
1345
1073
|
try {
|
|
1346
1074
|
for (const pattern of patterns) {
|
|
@@ -1439,6 +1167,12 @@ class Manager extends events_1.EventEmitter {
|
|
|
1439
1167
|
}
|
|
1440
1168
|
return this._send(packet);
|
|
1441
1169
|
}
|
|
1170
|
+
getUserFromCache(id) {
|
|
1171
|
+
return this._getUser?.(id);
|
|
1172
|
+
}
|
|
1173
|
+
getGuildFromCache(id) {
|
|
1174
|
+
return this._getGuild?.(id);
|
|
1175
|
+
}
|
|
1442
1176
|
sendPacket(packet) {
|
|
1443
1177
|
return this.send(packet);
|
|
1444
1178
|
}
|
|
@@ -1453,5 +1187,14 @@ class Manager extends events_1.EventEmitter {
|
|
|
1453
1187
|
return { id: user }; // fallback by ID only
|
|
1454
1188
|
return user; // default: just return the portable user
|
|
1455
1189
|
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Resolves a Guild ID to a real guild object.
|
|
1192
|
+
* Can be overridden by wrapper managers to return wrapper-specific Guild classes.
|
|
1193
|
+
*/
|
|
1194
|
+
resolveGuild(guildId) {
|
|
1195
|
+
if (!guildId)
|
|
1196
|
+
return null;
|
|
1197
|
+
return this._getGuild?.(guildId);
|
|
1198
|
+
}
|
|
1456
1199
|
}
|
|
1457
1200
|
exports.Manager = Manager;
|