magmastream 2.9.3-dev.3 → 2.9.3-dev.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/config/blockedWords.d.ts +1 -0
  2. package/dist/index.d.ts +19 -3687
  3. package/dist/index.js +1 -1
  4. package/dist/statestorage/JsonQueue.d.ts +173 -0
  5. package/dist/statestorage/JsonQueue.js +32 -4
  6. package/dist/statestorage/MemoryQueue.d.ts +154 -0
  7. package/dist/statestorage/MemoryQueue.js +56 -36
  8. package/dist/statestorage/RedisQueue.d.ts +178 -0
  9. package/dist/statestorage/RedisQueue.js +29 -7
  10. package/dist/structures/Enums.d.ts +310 -0
  11. package/dist/structures/Enums.js +6 -0
  12. package/dist/structures/Filters.d.ts +352 -0
  13. package/dist/structures/Filters.js +5 -4
  14. package/dist/structures/MagmastreamError.d.ts +14 -0
  15. package/dist/structures/Manager.d.ts +259 -0
  16. package/dist/structures/Manager.js +296 -555
  17. package/dist/structures/Node.d.ts +390 -0
  18. package/dist/structures/Node.js +100 -145
  19. package/dist/structures/Player.d.ts +347 -0
  20. package/dist/structures/Player.js +55 -132
  21. package/dist/structures/Plugin.d.ts +23 -0
  22. package/dist/structures/Rest.d.ts +93 -0
  23. package/dist/structures/Rest.js +41 -21
  24. package/dist/structures/Types.d.ts +1315 -0
  25. package/dist/structures/Utils.d.ts +169 -0
  26. package/dist/structures/Utils.js +145 -71
  27. package/dist/utils/filtersEqualizers.d.ts +16 -0
  28. package/dist/utils/managerCheck.d.ts +7 -0
  29. package/dist/utils/nodeCheck.d.ts +7 -0
  30. package/dist/utils/playerCheck.d.ts +7 -0
  31. package/dist/wrappers/discord.js.d.ts +15 -0
  32. package/dist/wrappers/discord.js.js +19 -4
  33. package/dist/wrappers/discordeno.d.ts +19 -0
  34. package/dist/wrappers/discordeno.js +77 -0
  35. package/dist/wrappers/eris.d.ts +15 -0
  36. package/dist/wrappers/eris.js +20 -3
  37. package/dist/wrappers/oceanic.d.ts +15 -0
  38. package/dist/wrappers/oceanic.js +22 -4
  39. package/dist/wrappers/seyfert.d.ts +37 -0
  40. package/dist/wrappers/seyfert.js +25 -1
  41. package/package.json +106 -98
  42. package/dist/wrappers/detritus.js +0 -52
@@ -31,7 +31,6 @@ class Node {
31
31
  reconnectTimeout;
32
32
  reconnectAttempts = 1;
33
33
  redisPrefix;
34
- sessionIdsFilePath;
35
34
  sessionIdsMap = new Map();
36
35
  /**
37
36
  * Creates an instance of Node.
@@ -97,18 +96,10 @@ class Node {
97
96
  switch (this.manager.options.stateStorage.type) {
98
97
  case Enums_1.StateStorageType.Memory:
99
98
  case Enums_1.StateStorageType.JSON:
100
- this.sessionIdsFilePath = path_1.default.join(process.cwd(), "magmastream", "sessionData", "sessionIds.json");
101
- const configDir = path_1.default.dirname(this.sessionIdsFilePath);
102
- if (!fs_1.default.existsSync(configDir)) {
103
- fs_1.default.mkdirSync(configDir, { recursive: true });
104
- }
105
- this.createSessionIdsFile();
106
99
  this.createReadmeFile();
107
100
  break;
108
101
  case Enums_1.StateStorageType.Redis:
109
- this.redisPrefix = this.manager.options.stateStorage.redisConfig.prefix?.endsWith(":")
110
- ? this.manager.options.stateStorage.redisConfig.prefix
111
- : this.manager.options.stateStorage.redisConfig.prefix ?? "magmastream:";
102
+ this.redisPrefix = Utils_1.PlayerUtils.getRedisKey();
112
103
  break;
113
104
  }
114
105
  }
@@ -125,16 +116,19 @@ class Node {
125
116
  get address() {
126
117
  return `${this.options.host}:${this.options.port}`;
127
118
  }
128
- /**
129
- * Creates the sessionIds.json file if it doesn't exist. This file is used to
130
- * store the session IDs for each node. The session IDs are used to identify
131
- * the node when resuming a session.
132
- */
133
- createSessionIdsFile() {
134
- if (!fs_1.default.existsSync(this.sessionIdsFilePath)) {
135
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Creating sessionId file at: ${this.sessionIdsFilePath}`);
136
- fs_1.default.writeFileSync(this.sessionIdsFilePath, Utils_1.JSONUtils.safe({}), "utf-8");
137
- }
119
+ getCompositeKey() {
120
+ return `${this.options.identifier}::${this.manager.options.clusterId}`;
121
+ }
122
+ getRedisSessionIdsKey() {
123
+ return `${this.redisPrefix}node:sessionIds`;
124
+ }
125
+ getNodeSessionsDir() {
126
+ return path_1.default.join(process.cwd(), "magmastream", "sessionData", "nodeSessions");
127
+ }
128
+ getNodeSessionPath() {
129
+ const safeId = String(this.options.identifier).replace(/[^a-zA-Z0-9._-]/g, "_");
130
+ const clusterId = String(this.manager.options.clusterId ?? 0);
131
+ return path_1.default.join(this.getNodeSessionsDir(), `${safeId}__${clusterId}.txt`);
138
132
  }
139
133
  /**
140
134
  * Loads session IDs from the sessionIds.json file if it exists.
@@ -148,49 +142,43 @@ class Node {
148
142
  switch (this.manager.options.stateStorage.type) {
149
143
  case Enums_1.StateStorageType.Memory:
150
144
  case Enums_1.StateStorageType.JSON: {
151
- if (fs_1.default.existsSync(this.sessionIdsFilePath)) {
152
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Loading sessionIds from file: ${this.sessionIdsFilePath}`);
153
- const sessionIdsData = fs_1.default.readFileSync(this.sessionIdsFilePath, "utf-8");
154
- this.sessionIdsMap = new Map(Object.entries(JSON.parse(sessionIdsData)));
155
- const compositeKey = `${this.options.identifier}::${this.manager.options.clusterId}`;
156
- if (this.sessionIdsMap.has(compositeKey)) {
157
- this.sessionId = this.sessionIdsMap.get(compositeKey);
158
- }
145
+ const dir = this.getNodeSessionsDir();
146
+ const filePath = this.getNodeSessionPath();
147
+ if (!fs_1.default.existsSync(dir))
148
+ fs_1.default.mkdirSync(dir, { recursive: true });
149
+ if (!fs_1.default.existsSync(filePath)) {
150
+ this.sessionId = null;
151
+ return;
152
+ }
153
+ try {
154
+ const raw = fs_1.default.readFileSync(filePath, "utf-8").trim();
155
+ this.sessionId = raw.length ? raw : null;
156
+ if (this.sessionId)
157
+ this.sessionIdsMap.set(this.getCompositeKey(), this.sessionId);
158
+ }
159
+ catch {
160
+ this.sessionId = null;
159
161
  }
160
162
  break;
161
163
  }
162
- case Enums_1.StateStorageType.Redis:
163
- const key = `${this.redisPrefix}node:sessionIds`;
164
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Loading sessionIds from Redis key: ${key}`);
165
- const currentRaw = await this.manager.redis.get(key);
166
- if (currentRaw) {
167
- try {
168
- const sessionIds = JSON.parse(currentRaw);
169
- if (typeof sessionIds !== "object" || Array.isArray(sessionIds)) {
170
- throw new MagmastreamError_1.MagmaStreamError({
171
- code: Enums_1.MagmaStreamErrorCode.NODE_SESSION_IDS_LOAD_FAILED,
172
- message: "Invalid sessionIds data type from Redis.",
173
- context: { sessionIds },
174
- });
175
- }
176
- this.sessionIdsMap = new Map(Object.entries(sessionIds));
177
- const compositeKey = `${this.options.identifier}::${this.manager.options.clusterId}`;
178
- if (this.sessionIdsMap.has(compositeKey)) {
179
- this.sessionId = this.sessionIdsMap.get(compositeKey) || null;
180
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Restored sessionId for ${compositeKey}: ${this.sessionId}`);
181
- }
182
- }
183
- catch (err) {
184
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Failed to parse Redis sessionIds: ${err.message}`);
185
- this.sessionIdsMap = new Map();
164
+ case Enums_1.StateStorageType.Redis: {
165
+ const key = this.getRedisSessionIdsKey();
166
+ const compositeKey = this.getCompositeKey();
167
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Loading sessionId from Redis hash: ${key} field: ${compositeKey}`);
168
+ try {
169
+ const sid = await this.manager.redis.hget(key, compositeKey);
170
+ this.sessionId = sid ?? null;
171
+ if (this.sessionId) {
172
+ this.sessionIdsMap.set(compositeKey, this.sessionId);
173
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Restored sessionId for ${compositeKey}: ${this.sessionId}`);
186
174
  }
187
175
  }
188
- else {
189
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] No sessionIds found in Redis creating new key.`);
190
- await this.manager.redis.set(key, Utils_1.JSONUtils.safe({}));
191
- this.sessionIdsMap = new Map();
176
+ catch (err) {
177
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Failed to load sessionId from Redis hash: ${err.message}`);
178
+ this.sessionId = null;
192
179
  }
193
180
  break;
181
+ }
194
182
  }
195
183
  }
196
184
  /**
@@ -207,84 +195,53 @@ class Node {
207
195
  async updateSessionId() {
208
196
  switch (this.manager.options.stateStorage.type) {
209
197
  case Enums_1.StateStorageType.Memory:
210
- case Enums_1.StateStorageType.JSON: {
211
- const compositeKey = `${this.options.identifier}::${this.manager.options.clusterId}`;
212
- const filePath = this.sessionIdsFilePath;
213
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Updating sessionIds to file: ${filePath}`);
214
- let updated = false;
215
- let retries = 3;
216
- while (!updated && retries > 0) {
217
- try {
218
- let fileData = {};
219
- if (fs_1.default.existsSync(filePath)) {
220
- try {
221
- const raw = fs_1.default.readFileSync(filePath, "utf-8");
222
- fileData = raw.trim() ? JSON.parse(raw) : {};
223
- }
224
- catch (err) {
225
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Failed to read/parse sessionIds.json: ${err.message}`);
226
- fileData = {};
227
- }
228
- }
229
- fileData[compositeKey] = this.sessionId;
230
- const tmpPath = `${filePath}.tmp`;
231
- fs_1.default.writeFileSync(tmpPath, Utils_1.JSONUtils.safe(fileData, 2), "utf-8");
232
- fs_1.default.renameSync(tmpPath, filePath);
233
- this.sessionIdsMap = new Map(Object.entries(fileData));
234
- updated = true;
235
- }
236
- catch (err) {
237
- retries--;
238
- if (retries === 0) {
239
- throw new MagmastreamError_1.MagmaStreamError({
240
- code: Enums_1.MagmaStreamErrorCode.NODE_SESSION_IDS_UPDATE_FAILED,
241
- message: `Failed to update sessionIds after retries.`,
242
- cause: err instanceof Error ? err : undefined,
243
- context: { filePath, compositeKey, storage: "file" },
244
- });
245
- }
246
- await new Promise((r) => setTimeout(r, 50));
247
- }
248
- }
249
- break;
198
+ case Enums_1.StateStorageType.JSON:
199
+ return this.updateSessionIdFile();
200
+ case Enums_1.StateStorageType.Redis:
201
+ return this.updateSessionIdRedis();
202
+ }
203
+ }
204
+ async updateSessionIdFile() {
205
+ const dir = this.getNodeSessionsDir();
206
+ const filePath = this.getNodeSessionPath();
207
+ const tmpPath = `${filePath}.tmp`;
208
+ if (!fs_1.default.existsSync(dir))
209
+ fs_1.default.mkdirSync(dir, { recursive: true });
210
+ if (this.sessionId) {
211
+ fs_1.default.writeFileSync(tmpPath, this.sessionId, "utf-8");
212
+ fs_1.default.renameSync(tmpPath, filePath);
213
+ this.sessionIdsMap.set(this.getCompositeKey(), this.sessionId);
214
+ }
215
+ else {
216
+ try {
217
+ if (fs_1.default.existsSync(filePath))
218
+ fs_1.default.unlinkSync(filePath);
250
219
  }
251
- case Enums_1.StateStorageType.Redis: {
252
- const key = `${this.redisPrefix}node:sessionIds`;
253
- const compositeKey = `${this.options.identifier}::${this.manager.options.clusterId}`;
254
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Updating sessionIds in Redis key: ${key}`);
255
- let sessionIds = {};
256
- try {
257
- const currentRaw = await this.manager.redis.get(key);
258
- if (currentRaw) {
259
- sessionIds = JSON.parse(currentRaw);
260
- if (typeof sessionIds !== "object" || Array.isArray(sessionIds)) {
261
- throw new Error("Invalid data type in Redis");
262
- }
263
- }
264
- else {
265
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Redis key not found — creating new sessionIds key.`);
266
- sessionIds = {};
267
- }
268
- }
269
- catch (err) {
270
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Corrupted Redis sessionIds, reinitializing: ${err.message}`);
271
- sessionIds = {};
272
- }
273
- try {
274
- sessionIds[compositeKey] = this.sessionId;
275
- this.sessionIdsMap = new Map(Object.entries(sessionIds));
276
- await this.manager.redis.set(key, Utils_1.JSONUtils.safe(sessionIds));
277
- }
278
- catch (err) {
279
- throw new MagmastreamError_1.MagmaStreamError({
280
- code: Enums_1.MagmaStreamErrorCode.NODE_SESSION_IDS_UPDATE_FAILED,
281
- message: `Failed to update sessionIds in Redis.`,
282
- cause: err instanceof Error ? err : undefined,
283
- context: { key, compositeKey, storage: "redis" },
284
- });
285
- }
286
- break;
220
+ catch { }
221
+ this.sessionIdsMap.delete(this.getCompositeKey());
222
+ }
223
+ }
224
+ async updateSessionIdRedis() {
225
+ const key = this.getRedisSessionIdsKey();
226
+ const compositeKey = this.getCompositeKey();
227
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Updating sessionId in Redis hash: ${key} field: ${compositeKey}`);
228
+ try {
229
+ if (this.sessionId) {
230
+ await this.manager.redis.hset(key, compositeKey, this.sessionId);
231
+ this.sessionIdsMap.set(compositeKey, this.sessionId);
287
232
  }
233
+ else {
234
+ await this.manager.redis.hdel(key, compositeKey);
235
+ this.sessionIdsMap.delete(compositeKey);
236
+ }
237
+ }
238
+ catch (err) {
239
+ throw new MagmastreamError_1.MagmaStreamError({
240
+ code: Enums_1.MagmaStreamErrorCode.NODE_SESSION_IDS_UPDATE_FAILED,
241
+ message: "Failed to update sessionId in Redis hash.",
242
+ cause: err instanceof Error ? err : undefined,
243
+ context: { key, compositeKey, storage: "redis-hash" },
244
+ });
288
245
  }
289
246
  }
290
247
  /**
@@ -305,12 +262,7 @@ class Node {
305
262
  "User-Id": this.manager.options.clientId,
306
263
  "Client-Name": this.manager.options.clientName,
307
264
  };
308
- const compositeKey = `${this.options.identifier}::${this.manager.options.clusterId}`;
309
- if (this.sessionId) {
310
- headers["Session-Id"] = this.sessionId;
311
- }
312
- else if (this.options.enableSessionResumeOption && this.sessionIdsMap.has(compositeKey)) {
313
- this.sessionId = this.sessionIdsMap.get(compositeKey) || null;
265
+ if (typeof this.sessionId === "string" && this.sessionId.length > 0) {
314
266
  headers["Session-Id"] = this.sessionId;
315
267
  }
316
268
  this.socket = new ws_1.default(`ws${this.options.useSSL ? "s" : ""}://${this.address}/v4/websocket`, { headers });
@@ -356,9 +308,7 @@ class Node {
356
308
  // Automove all players connected to that node
357
309
  const players = this.manager.players.filter((p) => p.node == this);
358
310
  if (players.size) {
359
- for (const player of players.values()) {
360
- await player.autoMoveNode();
361
- }
311
+ await Promise.all(Array.from(players.values(), (player) => player.autoMoveNode()));
362
312
  }
363
313
  this.socket.close(1000, "destroy");
364
314
  this.socket.removeAllListeners();
@@ -537,10 +487,11 @@ class Node {
537
487
  case "ready":
538
488
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Node message: ${Utils_1.JSONUtils.safe(payload, 2)}`);
539
489
  this.rest.setSessionId(payload.sessionId);
490
+ const hadPreviousSession = this.sessionId && this.sessionId !== payload.sessionId;
540
491
  this.sessionId = payload.sessionId;
541
492
  await this.updateSessionId();
542
493
  this.info = await this.fetchInfo();
543
- if (payload.resumed) {
494
+ if (payload.resumed || !hadPreviousSession) {
544
495
  await this.manager.loadPlayerStates(this.options.identifier);
545
496
  }
546
497
  if (this.options.enableSessionResumeOption) {
@@ -808,7 +759,7 @@ class Node {
808
759
  }
809
760
  }
810
761
  if (playNextOnEnd)
811
- await player.play();
762
+ await player.play({ startTime: 0 });
812
763
  }
813
764
  /**
814
765
  * Plays the next track in the queue.
@@ -826,7 +777,7 @@ class Node {
826
777
  await player.queue.setCurrent(await player.queue.dequeue());
827
778
  this.manager.emit(Enums_1.ManagerEventTypes.TrackEnd, player, track, payload);
828
779
  if (this.manager.options.playNextOnEnd)
829
- await player.play();
780
+ await player.play({ startTime: 0 });
830
781
  }
831
782
  /**
832
783
  * Handles the event when a queue ends.
@@ -1222,8 +1173,12 @@ class Node {
1222
1173
  * @private
1223
1174
  */
1224
1175
  createReadmeFile() {
1225
- const readmeFilePath = path_1.default.join(process.cwd(), "magmastream", "README.md");
1176
+ const baseDir = path_1.default.join(process.cwd(), "magmastream");
1177
+ const readmeFilePath = path_1.default.join(baseDir, "README.md");
1226
1178
  const message = "Please do NOT delete the magmastream/ folder as it is used to store player data for autoresume etc.";
1179
+ if (!fs_1.default.existsSync(baseDir)) {
1180
+ fs_1.default.mkdirSync(baseDir, { recursive: true });
1181
+ }
1227
1182
  if (!fs_1.default.existsSync(readmeFilePath)) {
1228
1183
  fs_1.default.writeFileSync(readmeFilePath, message, "utf-8");
1229
1184
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Created README file at: ${readmeFilePath}`);