magmastream 2.9.3-dev.22 → 2.9.3-dev.24

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/index.d.ts CHANGED
@@ -915,6 +915,10 @@ interface ManagerOptions {
915
915
  * @default UseNodeOptions.LeastPlayers
916
916
  */
917
917
  useNode?: UseNodeOptions.LeastLoad | UseNodeOptions.LeastPlayers;
918
+ /** Whether the manager should listen to SIGINT and SIGTERM events.
919
+ * @default true
920
+ */
921
+ listenToSIGEvents?: boolean;
918
922
  /**
919
923
  * Function to send data to the websocket.
920
924
  * @param id The ID of the node to send the data to.
@@ -2435,6 +2439,11 @@ declare class Player {
2435
2439
  * @returns {Promise<void>} - A promise that resolves when the voice receiver error is handled.
2436
2440
  */
2437
2441
  private onVoiceReceiverError;
2442
+ /**
2443
+ * Updates the voice state for the player.
2444
+ * @returns {Promise<void>} - A promise that resolves when the voice state is updated.
2445
+ */
2446
+ updateVoice(): Promise<void>;
2438
2447
  }
2439
2448
 
2440
2449
  /** Handles the requests sent to the Lavalink REST API. */
@@ -3146,6 +3155,7 @@ declare class Manager extends EventEmitter {
3146
3155
  private get priorityNode();
3147
3156
  protected send(packet: GatewayVoiceStateUpdate): unknown;
3148
3157
  protected getUserFromCache(id: string): AnyUser | undefined;
3158
+ protected getGuildFromCache(id: string): AnyGuild | undefined;
3149
3159
  sendPacket(packet: GatewayVoiceStateUpdate): unknown;
3150
3160
  /**
3151
3161
  * Resolves a PortableUser or ID to a real user object.
@@ -3698,6 +3708,7 @@ declare class DiscordenoManager extends Manager {
3698
3708
  * Uses user-provided cache getter if available, otherwise falls back to minimal info.
3699
3709
  */
3700
3710
  resolveUser(user: AnyUser | string): Promise<User$2 | AnyUser>;
3711
+ resolveGuild(guildId: string): AnyGuild;
3701
3712
  }
3702
3713
 
3703
3714
  /**
@@ -95,6 +95,7 @@ class Manager extends events_1.EventEmitter {
95
95
  deleteDestroyedPlayers: options.stateStorage?.deleteDestroyedPlayers ?? true,
96
96
  },
97
97
  autoPlaySearchPlatforms: options.autoPlaySearchPlatforms ?? [Enums_1.AutoPlayPlatform.YouTube],
98
+ listenToSIGEvents: options.listenToSIGEvents ?? true,
98
99
  send: this._send,
99
100
  };
100
101
  Utils_1.AutoPlayUtils.init(this);
@@ -102,49 +103,51 @@ class Manager extends events_1.EventEmitter {
102
103
  for (const nodeOptions of this.options.nodes)
103
104
  new Node_1.Node(this, nodeOptions);
104
105
  }
105
- process.on("SIGINT", async () => {
106
- console.warn("\x1b[33mSIGINT received! Graceful shutdown initiated...\x1b[0m");
107
- try {
108
- await this.handleShutdown();
109
- console.warn("\x1b[32mShutdown complete. Waiting for Node.js event loop to empty...\x1b[0m");
110
- // Prevent forced exit by Windows
111
- setTimeout(() => {
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");
112
135
  process.exit(0);
113
- }, 2000);
114
- }
115
- catch (err) {
116
- const error = err instanceof MagmastreamError_1.MagmaStreamError
117
- ? err
118
- : new MagmastreamError_1.MagmaStreamError({
119
- code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
120
- message: "An unknown error occurred.",
121
- cause: err,
122
- context: { stage: "SIGINT" },
123
- });
124
- console.error(error);
125
- process.exit(1);
126
- }
127
- });
128
- process.on("SIGTERM", async () => {
129
- console.warn("\x1b[33mSIGTERM received! Graceful shutdown initiated...\x1b[0m");
130
- try {
131
- await this.handleShutdown();
132
- console.warn("\x1b[32mShutdown complete. Exiting now...\x1b[0m");
133
- process.exit(0);
134
- }
135
- catch (err) {
136
- const error = err instanceof MagmastreamError_1.MagmaStreamError
137
- ? err
138
- : new MagmastreamError_1.MagmaStreamError({
139
- code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
140
- message: "An unknown error occurred.",
141
- cause: err,
142
- context: { stage: "SIGTERM" },
143
- });
144
- console.error(error);
145
- process.exit(1);
146
- }
147
- });
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
+ }
148
151
  }
149
152
  /**
150
153
  * Initiates the Manager.
@@ -527,10 +530,10 @@ class Manager extends events_1.EventEmitter {
527
530
  });
528
531
  // Read guild directories inside players base dir
529
532
  const guildDirs = await promises_1.default.readdir(playersBaseDir, { withFileTypes: true });
530
- for (const dirent of guildDirs) {
531
- if (!dirent.isDirectory())
533
+ for (const file of guildDirs) {
534
+ if (!file.isDirectory())
532
535
  continue;
533
- const guildId = dirent.name;
536
+ const guildId = file.name;
534
537
  const stateFilePath = Utils_1.PlayerUtils.getPlayerStatePath(guildId);
535
538
  try {
536
539
  await promises_1.default.access(stateFilePath);
@@ -556,23 +559,9 @@ class Manager extends events_1.EventEmitter {
556
559
  applyVolumeAsFilter: state.options.applyVolumeAsFilter,
557
560
  pauseOnDisconnect: state.options.pauseOnDisconnect,
558
561
  };
559
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${Utils_1.JSONUtils.safe(state.options, 2)}`);
560
562
  const player = this.create(playerOptions);
561
- await player.node.rest.updatePlayer({
562
- guildId: state.options.guildId,
563
- data: {
564
- voice: {
565
- token: state.voiceState.event.token,
566
- endpoint: state.voiceState.event.endpoint,
567
- sessionId: state.voiceState.sessionId,
568
- channelId: state.voiceState.event.channel_id,
569
- },
570
- },
571
- });
563
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${Utils_1.JSONUtils.safe(state.options, 2)}`);
572
564
  player.connect();
573
- const tracks = [];
574
- const currentTrack = state.queue.current;
575
- const queueTracks = state.queue.tracks;
576
565
  if (state.isAutoplay) {
577
566
  const savedUser = state.data.clientUser;
578
567
  if (savedUser) {
@@ -584,6 +573,9 @@ class Manager extends events_1.EventEmitter {
584
573
  if (savedNowPlayingMessage) {
585
574
  player.setNowPlayingMessage(savedNowPlayingMessage);
586
575
  }
576
+ const tracks = [];
577
+ const currentTrack = state.queue.current;
578
+ const queueTracks = state.queue.tracks;
587
579
  if (lavaPlayer.track) {
588
580
  await player.queue.clear();
589
581
  if (currentTrack) {
@@ -732,9 +724,8 @@ class Manager extends events_1.EventEmitter {
732
724
  if (!hasGuild)
733
725
  continue;
734
726
  const lavaPlayer = await node.rest.getPlayer(state.guildId);
735
- if (!lavaPlayer) {
727
+ if (!lavaPlayer)
736
728
  await this.destroy(guildId);
737
- }
738
729
  const playerOptions = {
739
730
  guildId: state.options.guildId,
740
731
  textChannelId: state.options.textChannelId,
@@ -745,24 +736,9 @@ class Manager extends events_1.EventEmitter {
745
736
  applyVolumeAsFilter: state.options.applyVolumeAsFilter,
746
737
  pauseOnDisconnect: state.options.pauseOnDisconnect,
747
738
  };
748
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
749
739
  const player = this.create(playerOptions);
750
- await player.node.rest.updatePlayer({
751
- guildId: state.options.guildId,
752
- data: {
753
- voice: {
754
- token: state.voiceState.event.token,
755
- endpoint: state.voiceState.event.endpoint,
756
- sessionId: state.voiceState.sessionId,
757
- channelId: state.voiceState.event.channel_id,
758
- },
759
- },
760
- });
740
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
761
741
  player.connect();
762
- // Rest of the player state restoration code (tracks, filters, etc.)
763
- const tracks = [];
764
- const currentTrack = state.queue.current;
765
- const queueTracks = state.queue.tracks;
766
742
  if (state.isAutoplay) {
767
743
  const savedUser = state.data.clientUser;
768
744
  if (savedUser) {
@@ -774,6 +750,10 @@ class Manager extends events_1.EventEmitter {
774
750
  if (savedNowPlayingMessage) {
775
751
  player.setNowPlayingMessage(savedNowPlayingMessage);
776
752
  }
753
+ // Rest of the player state restoration code (tracks, filters, etc.)
754
+ const tracks = [];
755
+ const currentTrack = state.queue.current;
756
+ const queueTracks = state.queue.tracks;
777
757
  if (lavaPlayer.track) {
778
758
  await player.queue.clear();
779
759
  if (currentTrack) {
@@ -1078,15 +1058,8 @@ class Manager extends events_1.EventEmitter {
1078
1058
  player.voiceState.event = update;
1079
1059
  const sessionId = player.voiceState.sessionId;
1080
1060
  const channelId = player.voiceState.channelId;
1081
- const token = update.token;
1082
- const endpoint = update.endpoint;
1083
- this.emit(Enums_1.ManagerEventTypes.Debug, `Updated voice server for player ${player.guildId} with token ${token} | endpoint ${endpoint} | sessionId ${sessionId} | channelId ${channelId}`);
1084
- if (!sessionId || !channelId)
1085
- return;
1086
- await player.node.rest.updatePlayer({
1087
- guildId: player.guildId,
1088
- data: { voice: { token, endpoint, sessionId, channelId } },
1089
- });
1061
+ this.emit(Enums_1.ManagerEventTypes.Debug, `Updated voice server for player ${player.guildId} with token ${update.token} | endpoint ${update.endpoint} | sessionId ${sessionId} | channelId ${channelId}`);
1062
+ await player.updateVoice();
1090
1063
  }
1091
1064
  /**
1092
1065
  * 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.
@@ -1113,14 +1086,7 @@ class Manager extends events_1.EventEmitter {
1113
1086
  player.voiceState.channelId = update.channel_id;
1114
1087
  player.voiceChannelId = update.channel_id;
1115
1088
  player.options.voiceChannelId = update.channel_id;
1116
- const token = player.voiceState.event?.token;
1117
- const endpoint = player.voiceState.event?.endpoint;
1118
- if (!token || !endpoint)
1119
- return;
1120
- await player.node.rest.updatePlayer({
1121
- guildId: player.guildId,
1122
- data: { voice: { token, endpoint, sessionId: update.session_id, channelId: update.channel_id } },
1123
- });
1089
+ await player.updateVoice();
1124
1090
  }
1125
1091
  /**
1126
1092
  * Cleans up an inactive player by removing its state data.
@@ -1366,6 +1332,9 @@ class Manager extends events_1.EventEmitter {
1366
1332
  getUserFromCache(id) {
1367
1333
  return this._getUser?.(id);
1368
1334
  }
1335
+ getGuildFromCache(id) {
1336
+ return this._getGuild?.(id);
1337
+ }
1369
1338
  sendPacket(packet) {
1370
1339
  return this.send(packet);
1371
1340
  }
@@ -127,7 +127,8 @@ class Node {
127
127
  }
128
128
  getNodeSessionPath() {
129
129
  const safeId = String(this.options.identifier).replace(/[^a-zA-Z0-9._-]/g, "_");
130
- return path_1.default.join(this.getNodeSessionsDir(), `${safeId}.txt`);
130
+ const clusterId = String(this.manager.options.clusterId ?? 0);
131
+ return path_1.default.join(this.getNodeSessionsDir(), `${safeId}__${clusterId}.txt`);
131
132
  }
132
133
  /**
133
134
  * Loads session IDs from the sessionIds.json file if it exists.
@@ -1171,8 +1172,12 @@ class Node {
1171
1172
  * @private
1172
1173
  */
1173
1174
  createReadmeFile() {
1174
- const readmeFilePath = path_1.default.join(process.cwd(), "magmastream", "README.md");
1175
+ const baseDir = path_1.default.join(process.cwd(), "magmastream");
1176
+ const readmeFilePath = path_1.default.join(baseDir, "README.md");
1175
1177
  const message = "Please do NOT delete the magmastream/ folder as it is used to store player data for autoresume etc.";
1178
+ if (!fs_1.default.existsSync(baseDir)) {
1179
+ fs_1.default.mkdirSync(baseDir, { recursive: true });
1180
+ }
1176
1181
  if (!fs_1.default.existsSync(readmeFilePath)) {
1177
1182
  fs_1.default.writeFileSync(readmeFilePath, message, "utf-8");
1178
1183
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Created README file at: ${readmeFilePath}`);
@@ -1144,5 +1144,26 @@ class Player {
1144
1144
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `VoiceReceiver error for player ${this.guildId}: ${error.message}`);
1145
1145
  this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverError, this, error);
1146
1146
  }
1147
+ /**
1148
+ * Updates the voice state for the player.
1149
+ * @returns {Promise<void>} - A promise that resolves when the voice state is updated.
1150
+ */
1151
+ async updateVoice() {
1152
+ const vs = this.voiceState;
1153
+ const ev = vs?.event;
1154
+ if (!vs?.channelId || !vs?.sessionId || !ev?.token || !ev?.endpoint)
1155
+ return;
1156
+ await this.node.rest.updatePlayer({
1157
+ guildId: this.options.guildId,
1158
+ data: {
1159
+ voice: {
1160
+ token: ev.token,
1161
+ endpoint: ev.endpoint,
1162
+ sessionId: vs.sessionId,
1163
+ channelId: vs.channelId,
1164
+ },
1165
+ },
1166
+ });
1167
+ }
1147
1168
  }
1148
1169
  exports.Player = Player;
@@ -69,5 +69,9 @@ class DiscordenoManager extends Manager_1.Manager {
69
69
  username: typeof user === "string" ? undefined : user.username,
70
70
  };
71
71
  }
72
+ resolveGuild(guildId) {
73
+ // Try user-provided cache getter
74
+ return this.getGuildFromCache(guildId);
75
+ }
72
76
  }
73
77
  exports.DiscordenoManager = DiscordenoManager;
package/package.json CHANGED
@@ -1,107 +1,107 @@
1
1
  {
2
- "name": "magmastream",
3
- "version": "2.9.3-dev.22",
4
- "description": "A user-friendly Lavalink client designed for NodeJS.",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "files": [
8
- "dist"
9
- ],
10
- "scripts": {
11
- "build": "tsc",
12
- "types": "rtb --dist dist",
13
- "format": "prettier --write .",
14
- "format:check": "prettier --check .",
15
- "lint": "eslint \"src/**/*.{ts,js}\"",
16
- "lint:fix": "eslint --fix \"src/**/*.{ts,js}\"",
17
- "ci": "run-s format:check lint build types",
18
- "release:dev": "npm run format && npm run lint:fix && npm run ci && npm version prerelease --preid=dev && git push && npm publish --tag dev"
19
- },
20
- "devDependencies": {
21
- "@favware/rollup-type-bundler": "^4.0.0",
22
- "@types/jsdom": "^27.0.0",
23
- "@types/lodash": "^4.17.24",
24
- "@types/node": "^25.3.0",
25
- "@types/ws": "^8.18.1",
26
- "@typescript-eslint/eslint-plugin": "^8.56.0",
27
- "@typescript-eslint/parser": "^8.56.0",
28
- "eslint": "^10.0.1",
29
- "npm-run-all": "^4.1.5",
30
- "prettier": "^3.8.1",
31
- "typedoc": "^0.28.17",
32
- "typedoc-plugin-no-inherit": "^1.6.1",
33
- "typescript": "^5.9.3"
34
- },
35
- "dependencies": {
36
- "@discordjs/collection": "^2.1.1",
37
- "axios": "^1.13.5",
38
- "events": "^3.3.0",
39
- "ioredis": "^5.9.3",
40
- "jsdom": "^28.1.0",
41
- "lodash": "^4.17.23",
42
- "safe-stable-stringify": "^2.5.0",
43
- "tslib": "^2.8.1",
44
- "ws": "^8.19.0"
45
- },
46
- "optionalDependencies": {
47
- "discord.js": "14.x",
48
- "discordeno": "21.x",
49
- "eris": "0.18.x",
50
- "oceanic.js": "^1.13.0",
51
- "seyfert": "4.x"
52
- },
53
- "overrides": {
54
- "undici": "^6.23.0"
55
- },
56
- "engines": {
57
- "node": ">=20.19.0"
58
- },
59
- "eslintConfig": {
60
- "root": true,
61
- "parser": "@typescript-eslint/parser",
62
- "plugins": [
63
- "@typescript-eslint"
64
- ],
65
- "rules": {
66
- "object-curly-spacing": [
67
- "error",
68
- "always"
69
- ]
70
- },
71
- "extends": [
72
- "eslint:recommended",
73
- "plugin:@typescript-eslint/recommended"
74
- ]
75
- },
76
- "keywords": [
77
- "lavalink client",
78
- "wrapper",
79
- "typescript",
80
- "discord.js",
81
- "node.js",
82
- "java",
83
- "javascript",
84
- "audio streaming",
85
- "music bot",
86
- "voice chat",
87
- "discord integration",
88
- "high performance",
89
- "scalable",
90
- "easy-to-use",
91
- "feature-rich",
92
- "cross-platform",
93
- "seamless integration",
94
- "community support",
95
- "documentation",
96
- "open-source",
97
- "lavalink",
98
- "magmastream"
99
- ],
100
- "repository": {
101
- "type": "git",
102
- "url": "git+https://gitryx.com/MagmaStream/magmastream.git#main"
103
- },
104
- "homepage": "https://docs.magmastream.com",
105
- "author": "Abel Purnwasy",
106
- "license": "Apache-2.0"
2
+ "name": "magmastream",
3
+ "version": "2.9.3-dev.24",
4
+ "description": "A user-friendly Lavalink client designed for NodeJS.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "types": "rtb --dist dist",
13
+ "format": "prettier --write .",
14
+ "format:check": "prettier --check .",
15
+ "lint": "eslint \"src/**/*.{ts,js}\"",
16
+ "lint:fix": "eslint --fix \"src/**/*.{ts,js}\"",
17
+ "ci": "run-s format:check lint build types",
18
+ "release:dev": "npm run format && npm run lint:fix && npm run ci && npm version prerelease --preid=dev && git push && npm publish --tag dev"
19
+ },
20
+ "devDependencies": {
21
+ "@favware/rollup-type-bundler": "^4.0.0",
22
+ "@types/jsdom": "^28.0.0",
23
+ "@types/lodash": "^4.17.24",
24
+ "@types/node": "^25.3.0",
25
+ "@types/ws": "^8.18.1",
26
+ "@typescript-eslint/eslint-plugin": "^8.56.0",
27
+ "@typescript-eslint/parser": "^8.56.0",
28
+ "eslint": "^10.0.2",
29
+ "npm-run-all": "^4.1.5",
30
+ "prettier": "^3.8.1",
31
+ "typedoc": "^0.28.17",
32
+ "typedoc-plugin-no-inherit": "^1.6.1",
33
+ "typescript": "^5.9.3"
34
+ },
35
+ "dependencies": {
36
+ "@discordjs/collection": "^2.1.1",
37
+ "axios": "^1.13.6",
38
+ "events": "^3.3.0",
39
+ "ioredis": "^5.10.0",
40
+ "jsdom": "^28.1.0",
41
+ "lodash": "^4.17.23",
42
+ "safe-stable-stringify": "^2.5.0",
43
+ "tslib": "^2.8.1",
44
+ "ws": "^8.19.0"
45
+ },
46
+ "optionalDependencies": {
47
+ "discord.js": "14.x",
48
+ "discordeno": "21.x",
49
+ "eris": "0.18.x",
50
+ "oceanic.js": "1.13.x",
51
+ "seyfert": "4.x"
52
+ },
53
+ "overrides": {
54
+ "undici": "^7.0.0"
55
+ },
56
+ "engines": {
57
+ "node": ">=20.19.0"
58
+ },
59
+ "eslintConfig": {
60
+ "root": true,
61
+ "parser": "@typescript-eslint/parser",
62
+ "plugins": [
63
+ "@typescript-eslint"
64
+ ],
65
+ "rules": {
66
+ "object-curly-spacing": [
67
+ "error",
68
+ "always"
69
+ ]
70
+ },
71
+ "extends": [
72
+ "eslint:recommended",
73
+ "plugin:@typescript-eslint/recommended"
74
+ ]
75
+ },
76
+ "keywords": [
77
+ "lavalink client",
78
+ "wrapper",
79
+ "typescript",
80
+ "discord.js",
81
+ "node.js",
82
+ "java",
83
+ "javascript",
84
+ "audio streaming",
85
+ "music bot",
86
+ "voice chat",
87
+ "discord integration",
88
+ "high performance",
89
+ "scalable",
90
+ "easy-to-use",
91
+ "feature-rich",
92
+ "cross-platform",
93
+ "seamless integration",
94
+ "community support",
95
+ "documentation",
96
+ "open-source",
97
+ "lavalink",
98
+ "magmastream"
99
+ ],
100
+ "repository": {
101
+ "type": "git",
102
+ "url": "git+https://gitryx.com/MagmaStream/magmastream.git#main"
103
+ },
104
+ "homepage": "https://docs.magmastream.com",
105
+ "author": "Abel Purnwasy",
106
+ "license": "Apache-2.0"
107
107
  }