magmastream 2.10.3-dev.0 → 2.10.3-dev.1
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/statestorage/JsonQueue.js +6 -6
- package/dist/statestorage/MemoryQueue.js +6 -6
- package/dist/statestorage/RedisQueue.js +4 -4
- package/dist/structures/Manager.js +40 -38
- package/dist/structures/Node.d.ts +3 -2
- package/dist/structures/Node.js +47 -30
- package/dist/structures/Player.d.ts +1 -1
- package/dist/structures/Player.js +10 -10
- package/dist/structures/Types.d.ts +2 -0
- package/dist/structures/Utils.js +16 -14
- package/dist/wrappers/cloudstorm.js +3 -3
- package/dist/wrappers/discord.js.js +4 -4
- package/dist/wrappers/discordeno.js +3 -3
- package/dist/wrappers/eris.js +3 -3
- package/dist/wrappers/oceanic.js +3 -3
- package/dist/wrappers/seyfert.js +4 -4
- package/package.json +1 -1
|
@@ -102,11 +102,11 @@ class JsonQueue {
|
|
|
102
102
|
const tracks = Array.isArray(track) ? track : [track];
|
|
103
103
|
if (!tracks.length)
|
|
104
104
|
return;
|
|
105
|
-
const current = (await this.getPrevious()).filter((
|
|
106
|
-
const validTracks = tracks.filter((
|
|
105
|
+
const current = (await this.getPrevious()).filter((previousTrack) => previousTrack !== null);
|
|
106
|
+
const validTracks = tracks.filter((track) => track !== null && typeof track.uri === "string");
|
|
107
107
|
if (!validTracks.length)
|
|
108
108
|
return;
|
|
109
|
-
const newTracks = validTracks.filter((
|
|
109
|
+
const newTracks = validTracks.filter((track) => !current.some((previousTrack) => previousTrack.uri === track.uri));
|
|
110
110
|
if (!newTracks.length)
|
|
111
111
|
return;
|
|
112
112
|
const updated = [...current, ...newTracks];
|
|
@@ -391,9 +391,9 @@ class JsonQueue {
|
|
|
391
391
|
const users = [...userMap.keys()];
|
|
392
392
|
const queues = users.map((id) => userMap.get(id));
|
|
393
393
|
const shuffledQueue = [];
|
|
394
|
-
while (queues.some((
|
|
395
|
-
for (const
|
|
396
|
-
const track =
|
|
394
|
+
while (queues.some((queue) => queue.length > 0)) {
|
|
395
|
+
for (const queue of queues) {
|
|
396
|
+
const track = queue.shift();
|
|
397
397
|
if (track)
|
|
398
398
|
shuffledQueue.push(track);
|
|
399
399
|
}
|
|
@@ -43,7 +43,7 @@ class MemoryQueue extends Array {
|
|
|
43
43
|
const isArray = Array.isArray(track);
|
|
44
44
|
const tracks = isArray ? [...track] : [track];
|
|
45
45
|
// Get the track info as a string
|
|
46
|
-
const trackInfo = isArray ? tracks.map((
|
|
46
|
+
const trackInfo = isArray ? tracks.map((track) => Utils_1.JSONUtils.safe(track, 2)).join(", ") : Utils_1.JSONUtils.safe(track, 2);
|
|
47
47
|
// Emit a debug message
|
|
48
48
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[MEMORYQUEUE] Added ${tracks.length} track(s) to queue: ${trackInfo}`);
|
|
49
49
|
const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
|
|
@@ -129,13 +129,13 @@ class MemoryQueue extends Array {
|
|
|
129
129
|
addPrevious(track) {
|
|
130
130
|
try {
|
|
131
131
|
const max = this.manager.options.maxPreviousTracks;
|
|
132
|
-
this.previous = this.previous.filter((
|
|
132
|
+
this.previous = this.previous.filter((previousTrack) => previousTrack !== null);
|
|
133
133
|
if (Array.isArray(track)) {
|
|
134
|
-
const newTracks = track.filter((
|
|
134
|
+
const newTracks = track.filter((track) => !this.previous.some((previousTrack) => previousTrack?.identifier === track.identifier));
|
|
135
135
|
this.previous.push(...newTracks);
|
|
136
136
|
}
|
|
137
137
|
else {
|
|
138
|
-
const exists = this.previous.some((
|
|
138
|
+
const exists = this.previous.some((previousTrack) => previousTrack?.identifier === track.identifier);
|
|
139
139
|
if (!exists) {
|
|
140
140
|
this.previous.push(track);
|
|
141
141
|
}
|
|
@@ -261,7 +261,7 @@ class MemoryQueue extends Array {
|
|
|
261
261
|
* @returns The previous tracks.
|
|
262
262
|
*/
|
|
263
263
|
getPrevious() {
|
|
264
|
-
this.previous = this.previous.map((
|
|
264
|
+
this.previous = this.previous.map((track) => Utils_1.TrackUtils.revive(track));
|
|
265
265
|
return [...this.previous];
|
|
266
266
|
}
|
|
267
267
|
/**
|
|
@@ -274,7 +274,7 @@ class MemoryQueue extends Array {
|
|
|
274
274
|
* @returns The tracks in the queue.
|
|
275
275
|
*/
|
|
276
276
|
getTracks() {
|
|
277
|
-
this.forEach((
|
|
277
|
+
this.forEach((track) => Utils_1.TrackUtils.revive(track));
|
|
278
278
|
return [...this]; // clone to avoid direct mutation
|
|
279
279
|
}
|
|
280
280
|
/**
|
|
@@ -44,7 +44,7 @@ class RedisQueue {
|
|
|
44
44
|
const isArray = Array.isArray(track);
|
|
45
45
|
const tracks = isArray ? track : [track];
|
|
46
46
|
// Serialize tracks
|
|
47
|
-
const serialized = tracks.map((
|
|
47
|
+
const serialized = tracks.map((track) => this.serialize(track));
|
|
48
48
|
const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
|
|
49
49
|
// Set current track if none exists
|
|
50
50
|
if (!(await this.getCurrent())) {
|
|
@@ -524,9 +524,9 @@ class RedisQueue {
|
|
|
524
524
|
const users = [...userMap.keys()];
|
|
525
525
|
const queues = users.map((id) => userMap.get(id));
|
|
526
526
|
const shuffledQueue = [];
|
|
527
|
-
while (queues.some((
|
|
528
|
-
for (const
|
|
529
|
-
const track =
|
|
527
|
+
while (queues.some((queue) => queue.length > 0)) {
|
|
528
|
+
for (const queue of queues) {
|
|
529
|
+
const track = queue.shift();
|
|
530
530
|
if (track)
|
|
531
531
|
shuffledQueue.push(track);
|
|
532
532
|
}
|
|
@@ -216,8 +216,8 @@ class Manager extends events_1.EventEmitter {
|
|
|
216
216
|
const search = isUrl ? _query.query : `${_source}:${_query.query}`;
|
|
217
217
|
this.emit(Enums_1.ManagerEventTypes.Debug, isUrl ? `[MANAGER] Performing search for: ${_query.query}` : `[MANAGER] Performing ${_source} search for: ${_query.query}`);
|
|
218
218
|
try {
|
|
219
|
-
const
|
|
220
|
-
if (!
|
|
219
|
+
const lavalinkResponse = (await node.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(search)}`));
|
|
220
|
+
if (!lavalinkResponse) {
|
|
221
221
|
throw new MagmastreamError_1.MagmaStreamError({
|
|
222
222
|
code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
|
|
223
223
|
message: `No results returned from Lavalink for query "${search}".`,
|
|
@@ -225,16 +225,16 @@ class Manager extends events_1.EventEmitter {
|
|
|
225
225
|
});
|
|
226
226
|
}
|
|
227
227
|
let result;
|
|
228
|
-
switch (
|
|
228
|
+
switch (lavalinkResponse.loadType) {
|
|
229
229
|
case Enums_1.LoadTypes.Search: {
|
|
230
|
-
const tracks =
|
|
231
|
-
result = { loadType:
|
|
230
|
+
const tracks = lavalinkResponse.data.map((track) => Utils_1.TrackUtils.build(track, requester));
|
|
231
|
+
result = { loadType: lavalinkResponse.loadType, tracks };
|
|
232
232
|
break;
|
|
233
233
|
}
|
|
234
234
|
case Enums_1.LoadTypes.Short:
|
|
235
235
|
case Enums_1.LoadTypes.Track: {
|
|
236
|
-
const track = Utils_1.TrackUtils.build(
|
|
237
|
-
result = { loadType:
|
|
236
|
+
const track = Utils_1.TrackUtils.build(lavalinkResponse.data, requester);
|
|
237
|
+
result = { loadType: lavalinkResponse.loadType, tracks: [track] };
|
|
238
238
|
break;
|
|
239
239
|
}
|
|
240
240
|
case Enums_1.LoadTypes.Album:
|
|
@@ -243,10 +243,10 @@ class Manager extends events_1.EventEmitter {
|
|
|
243
243
|
case Enums_1.LoadTypes.Podcast:
|
|
244
244
|
case Enums_1.LoadTypes.Show:
|
|
245
245
|
case Enums_1.LoadTypes.Playlist: {
|
|
246
|
-
const playlistData =
|
|
247
|
-
const tracks = playlistData.tracks.map((
|
|
246
|
+
const playlistData = lavalinkResponse.data;
|
|
247
|
+
const tracks = playlistData.tracks.map((track) => Utils_1.TrackUtils.build(track, requester));
|
|
248
248
|
result = {
|
|
249
|
-
loadType:
|
|
249
|
+
loadType: lavalinkResponse.loadType,
|
|
250
250
|
tracks,
|
|
251
251
|
playlist: {
|
|
252
252
|
name: playlistData.info.name,
|
|
@@ -259,7 +259,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
259
259
|
break;
|
|
260
260
|
}
|
|
261
261
|
default:
|
|
262
|
-
result = { loadType:
|
|
262
|
+
result = { loadType: lavalinkResponse.loadType, tracks: [] };
|
|
263
263
|
}
|
|
264
264
|
if (this.options.normalizeYouTubeTitles && "tracks" in result) {
|
|
265
265
|
const processTrack = (track) => {
|
|
@@ -275,7 +275,9 @@ class Manager extends events_1.EventEmitter {
|
|
|
275
275
|
result.playlist.tracks = result.tracks;
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
|
-
const summary = "tracks" in result
|
|
278
|
+
const summary = "tracks" in result
|
|
279
|
+
? result.tracks.map((track) => Object.fromEntries(Object.entries(track).filter(([key]) => key !== "requester")))
|
|
280
|
+
: [];
|
|
279
281
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Result search for ${_query.query}: ${Utils_1.JSONUtils.safe(summary, 2)}`);
|
|
280
282
|
return result;
|
|
281
283
|
}
|
|
@@ -412,14 +414,14 @@ class Manager extends events_1.EventEmitter {
|
|
|
412
414
|
message: "No available nodes to decode tracks.",
|
|
413
415
|
});
|
|
414
416
|
}
|
|
415
|
-
const
|
|
416
|
-
if (!
|
|
417
|
+
const decodedTrackData = (await node.rest.post("/v4/decodetracks", Utils_1.JSONUtils.safe(tracks, 2)).catch((err) => reject(err)));
|
|
418
|
+
if (!decodedTrackData) {
|
|
417
419
|
throw new MagmastreamError_1.MagmaStreamError({
|
|
418
420
|
code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
|
|
419
421
|
message: "No decoded tracks returned from node.",
|
|
420
422
|
});
|
|
421
423
|
}
|
|
422
|
-
return resolve(
|
|
424
|
+
return resolve(decodedTrackData);
|
|
423
425
|
});
|
|
424
426
|
}
|
|
425
427
|
/**
|
|
@@ -429,9 +431,9 @@ class Manager extends events_1.EventEmitter {
|
|
|
429
431
|
* @throws Will throw an error if no nodes are available or if the API request fails.
|
|
430
432
|
*/
|
|
431
433
|
async decodeTrack(track) {
|
|
432
|
-
const
|
|
434
|
+
const decodedTracks = await this.decodeTracks([track]);
|
|
433
435
|
// Since we're only decoding one track, we can just return the first element of the array
|
|
434
|
-
return
|
|
436
|
+
return decodedTracks[0];
|
|
435
437
|
}
|
|
436
438
|
/**
|
|
437
439
|
* Saves player states.
|
|
@@ -577,7 +579,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
577
579
|
}
|
|
578
580
|
async restorePreviousQueue(player, state) {
|
|
579
581
|
if (state.queue.previous.length > 0) {
|
|
580
|
-
const validPrevious = state.queue.previous.filter((
|
|
582
|
+
const validPrevious = state.queue.previous.filter((track) => track !== null && typeof track.identifier === "string");
|
|
581
583
|
if (validPrevious.length > 0)
|
|
582
584
|
await player.queue.addPrevious(validPrevious);
|
|
583
585
|
}
|
|
@@ -604,31 +606,31 @@ class Manager extends events_1.EventEmitter {
|
|
|
604
606
|
restoreFilters(player, state) {
|
|
605
607
|
const filterActions = {
|
|
606
608
|
bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
|
|
607
|
-
distort: (
|
|
609
|
+
distort: (isEnabled) => player.filters.distort(isEnabled),
|
|
608
610
|
setDistortion: () => player.filters.setDistortion(state.filters.distortion),
|
|
609
|
-
eightD: (
|
|
611
|
+
eightD: (isEnabled) => player.filters.eightD(isEnabled),
|
|
610
612
|
setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
|
|
611
|
-
nightcore: (
|
|
612
|
-
slowmo: (
|
|
613
|
-
soft: (
|
|
614
|
-
trebleBass: (
|
|
613
|
+
nightcore: (isEnabled) => player.filters.nightcore(isEnabled),
|
|
614
|
+
slowmo: (isEnabled) => player.filters.slowmo(isEnabled),
|
|
615
|
+
soft: (isEnabled) => player.filters.soft(isEnabled),
|
|
616
|
+
trebleBass: (isEnabled) => player.filters.trebleBass(isEnabled),
|
|
615
617
|
setTimescale: () => player.filters.setTimescale(state.filters.timescale),
|
|
616
|
-
tv: (
|
|
618
|
+
tv: (isEnabled) => player.filters.tv(isEnabled),
|
|
617
619
|
vibrato: () => player.filters.setVibrato(state.filters.vibrato),
|
|
618
|
-
vaporwave: (
|
|
619
|
-
pop: (
|
|
620
|
-
party: (
|
|
621
|
-
earrape: (
|
|
622
|
-
electronic: (
|
|
623
|
-
radio: (
|
|
620
|
+
vaporwave: (isEnabled) => player.filters.vaporwave(isEnabled),
|
|
621
|
+
pop: (isEnabled) => player.filters.pop(isEnabled),
|
|
622
|
+
party: (isEnabled) => player.filters.party(isEnabled),
|
|
623
|
+
earrape: (isEnabled) => player.filters.earrape(isEnabled),
|
|
624
|
+
electronic: (isEnabled) => player.filters.electronic(isEnabled),
|
|
625
|
+
radio: (isEnabled) => player.filters.radio(isEnabled),
|
|
624
626
|
setRotation: () => player.filters.setRotation(state.filters.rotation),
|
|
625
|
-
tremolo: (
|
|
626
|
-
china: (
|
|
627
|
-
chipmunk: (
|
|
628
|
-
darthvader: (
|
|
629
|
-
daycore: (
|
|
630
|
-
doubletime: (
|
|
631
|
-
demon: (
|
|
627
|
+
tremolo: (isEnabled) => player.filters.tremolo(isEnabled),
|
|
628
|
+
china: (isEnabled) => player.filters.china(isEnabled),
|
|
629
|
+
chipmunk: (isEnabled) => player.filters.chipmunk(isEnabled),
|
|
630
|
+
darthvader: (isEnabled) => player.filters.darthvader(isEnabled),
|
|
631
|
+
daycore: (isEnabled) => player.filters.daycore(isEnabled),
|
|
632
|
+
doubletime: (isEnabled) => player.filters.doubletime(isEnabled),
|
|
633
|
+
demon: (isEnabled) => player.filters.demon(isEnabled),
|
|
632
634
|
};
|
|
633
635
|
for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
|
|
634
636
|
if (isEnabled && filterActions[filter])
|
|
@@ -23,7 +23,8 @@ export declare class Node {
|
|
|
23
23
|
private reconnectTimeout?;
|
|
24
24
|
private reconnectAttempts;
|
|
25
25
|
private redisPrefix?;
|
|
26
|
-
|
|
26
|
+
/** Session ID sent in the reconnect header for resumption — cleared once the ready op is received. */
|
|
27
|
+
private pendingResumeSessionId;
|
|
27
28
|
/**
|
|
28
29
|
* Creates an instance of Node.
|
|
29
30
|
* @param manager - The manager for the node.
|
|
@@ -149,7 +150,7 @@ export declare class Node {
|
|
|
149
150
|
* @emits {nodeRaw} - Emits a nodeRaw event with the raw message received from the WebSocket connection.
|
|
150
151
|
* @private
|
|
151
152
|
*/
|
|
152
|
-
protected message(
|
|
153
|
+
protected message(messagePayload: Buffer | string): Promise<void>;
|
|
153
154
|
/**
|
|
154
155
|
* Handles an event emitted from the Lavalink node.
|
|
155
156
|
* @param {PlayerEvent & PlayerEvents} payload The event emitted from the node.
|
package/dist/structures/Node.js
CHANGED
|
@@ -10,7 +10,7 @@ const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
|
10
10
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
11
11
|
const Enums_1 = require("./Enums");
|
|
12
12
|
const MagmastreamError_1 = require("./MagmastreamError");
|
|
13
|
-
const validSponsorBlocks = Object.values(Enums_1.SponsorBlockSegment).map((
|
|
13
|
+
const validSponsorBlocks = Object.values(Enums_1.SponsorBlockSegment).map((segment) => segment.toLowerCase());
|
|
14
14
|
class Node {
|
|
15
15
|
manager;
|
|
16
16
|
options;
|
|
@@ -29,9 +29,10 @@ class Node {
|
|
|
29
29
|
/** Whether the node is a NodeLink. */
|
|
30
30
|
isNodeLink = false;
|
|
31
31
|
reconnectTimeout;
|
|
32
|
-
reconnectAttempts =
|
|
32
|
+
reconnectAttempts = 0;
|
|
33
33
|
redisPrefix;
|
|
34
|
-
|
|
34
|
+
/** Session ID sent in the reconnect header for resumption — cleared once the ready op is received. */
|
|
35
|
+
pendingResumeSessionId = null;
|
|
35
36
|
/**
|
|
36
37
|
* Creates an instance of Node.
|
|
37
38
|
* @param manager - The manager for the node.
|
|
@@ -153,8 +154,6 @@ class Node {
|
|
|
153
154
|
try {
|
|
154
155
|
const raw = fs_1.default.readFileSync(filePath, "utf-8").trim();
|
|
155
156
|
this.sessionId = raw.length ? raw : null;
|
|
156
|
-
if (this.sessionId)
|
|
157
|
-
this.sessionIdsMap.set(this.getCompositeKey(), this.sessionId);
|
|
158
157
|
}
|
|
159
158
|
catch {
|
|
160
159
|
this.sessionId = null;
|
|
@@ -169,7 +168,6 @@ class Node {
|
|
|
169
168
|
const sid = await this.manager.redis.hget(key, compositeKey);
|
|
170
169
|
this.sessionId = sid ?? null;
|
|
171
170
|
if (this.sessionId) {
|
|
172
|
-
this.sessionIdsMap.set(compositeKey, this.sessionId);
|
|
173
171
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Restored sessionId for ${compositeKey}: ${this.sessionId}`);
|
|
174
172
|
}
|
|
175
173
|
}
|
|
@@ -210,7 +208,6 @@ class Node {
|
|
|
210
208
|
if (this.sessionId) {
|
|
211
209
|
fs_1.default.writeFileSync(tmpPath, this.sessionId, "utf-8");
|
|
212
210
|
fs_1.default.renameSync(tmpPath, filePath);
|
|
213
|
-
this.sessionIdsMap.set(this.getCompositeKey(), this.sessionId);
|
|
214
211
|
}
|
|
215
212
|
else {
|
|
216
213
|
try {
|
|
@@ -218,7 +215,6 @@ class Node {
|
|
|
218
215
|
fs_1.default.unlinkSync(filePath);
|
|
219
216
|
}
|
|
220
217
|
catch { }
|
|
221
|
-
this.sessionIdsMap.delete(this.getCompositeKey());
|
|
222
218
|
}
|
|
223
219
|
}
|
|
224
220
|
async updateSessionIdRedis() {
|
|
@@ -228,11 +224,9 @@ class Node {
|
|
|
228
224
|
try {
|
|
229
225
|
if (this.sessionId) {
|
|
230
226
|
await this.manager.redis.hset(key, compositeKey, this.sessionId);
|
|
231
|
-
this.sessionIdsMap.set(compositeKey, this.sessionId);
|
|
232
227
|
}
|
|
233
228
|
else {
|
|
234
229
|
await this.manager.redis.hdel(key, compositeKey);
|
|
235
|
-
this.sessionIdsMap.delete(compositeKey);
|
|
236
230
|
}
|
|
237
231
|
}
|
|
238
232
|
catch (err) {
|
|
@@ -262,8 +256,16 @@ class Node {
|
|
|
262
256
|
"User-Id": this.manager.options.clientId,
|
|
263
257
|
"Client-Name": this.manager.options.clientName,
|
|
264
258
|
};
|
|
265
|
-
|
|
266
|
-
|
|
259
|
+
// Capture resume session ID for the WS header, then clear this.sessionId.
|
|
260
|
+
// REST calls guard on this.sessionId being non-null — keeping the stale value
|
|
261
|
+
// would let updatePlayer/destroyPlayer fire with an invalid session during the
|
|
262
|
+
// reconnect window (between connect() and the 'ready' op).
|
|
263
|
+
// pendingResumeSessionId is kept so the 'ready' handler can still compute
|
|
264
|
+
// hadPreviousSession correctly. this.sessionId is re-set by 'ready'.
|
|
265
|
+
this.pendingResumeSessionId = this.sessionId;
|
|
266
|
+
this.sessionId = null;
|
|
267
|
+
if (typeof this.pendingResumeSessionId === "string" && this.pendingResumeSessionId.length > 0) {
|
|
268
|
+
headers["Session-Id"] = this.pendingResumeSessionId;
|
|
267
269
|
}
|
|
268
270
|
this.socket = new ws_1.default(`ws${this.options.useSSL ? "s" : ""}://${this.address}/v4/websocket`, { headers });
|
|
269
271
|
this.socket.on("open", this.open.bind(this));
|
|
@@ -274,7 +276,7 @@ class Node {
|
|
|
274
276
|
const debugInfo = {
|
|
275
277
|
connected: this.connected,
|
|
276
278
|
address: this.address,
|
|
277
|
-
|
|
279
|
+
pendingResumeSessionId: this.pendingResumeSessionId,
|
|
278
280
|
options: {
|
|
279
281
|
clientId: this.manager.options.clientId,
|
|
280
282
|
clientName: this.manager.options.clientName,
|
|
@@ -300,18 +302,18 @@ class Node {
|
|
|
300
302
|
identifier: this.options.identifier,
|
|
301
303
|
address: this.address,
|
|
302
304
|
sessionId: this.sessionId,
|
|
303
|
-
playerCount: this.manager.players.filter((
|
|
305
|
+
playerCount: this.manager.players.filter((player) => player.node == this).size,
|
|
304
306
|
};
|
|
305
307
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Destroying node: ${Utils_1.JSONUtils.safe(debugInfo, 2)}`);
|
|
306
308
|
// Automove all players connected to that node
|
|
307
|
-
const players = this.manager.players.filter((
|
|
309
|
+
const players = this.manager.players.filter((player) => player.node == this);
|
|
308
310
|
if (players.size) {
|
|
309
311
|
await Promise.all(Array.from(players.values(), (player) => player.autoMoveNode()));
|
|
310
312
|
}
|
|
311
313
|
// Always clear reconnect state regardless of connection status
|
|
312
314
|
clearTimeout(this.reconnectTimeout);
|
|
313
315
|
this.reconnectTimeout = undefined;
|
|
314
|
-
this.reconnectAttempts =
|
|
316
|
+
this.reconnectAttempts = 0;
|
|
315
317
|
// Only close the socket if it is actually open
|
|
316
318
|
if (this.connected) {
|
|
317
319
|
this.socket.close(1000, "destroy");
|
|
@@ -347,7 +349,8 @@ class Node {
|
|
|
347
349
|
};
|
|
348
350
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Reconnecting node: ${Utils_1.JSONUtils.safe(debugInfo, 2)}`);
|
|
349
351
|
this.reconnectTimeout = setTimeout(async () => {
|
|
350
|
-
|
|
352
|
+
this.reconnectAttempts++;
|
|
353
|
+
if (this.reconnectAttempts > this.options.maxRetryAttempts) {
|
|
351
354
|
const error = new MagmastreamError_1.MagmaStreamError({
|
|
352
355
|
code: Enums_1.MagmaStreamErrorCode.NODE_RECONNECT_FAILED,
|
|
353
356
|
message: `Unable to reconnect after ${this.options.maxRetryAttempts} attempts.`,
|
|
@@ -360,7 +363,6 @@ class Node {
|
|
|
360
363
|
this.socket = null;
|
|
361
364
|
this.manager.emit(Enums_1.ManagerEventTypes.NodeReconnect, this);
|
|
362
365
|
await this.connect();
|
|
363
|
-
this.reconnectAttempts++;
|
|
364
366
|
}, this.options.retryDelayMs);
|
|
365
367
|
}
|
|
366
368
|
/**
|
|
@@ -382,13 +384,15 @@ class Node {
|
|
|
382
384
|
open() {
|
|
383
385
|
if (this.reconnectTimeout)
|
|
384
386
|
clearTimeout(this.reconnectTimeout);
|
|
387
|
+
this.reconnectTimeout = undefined;
|
|
388
|
+
this.reconnectAttempts = 0;
|
|
385
389
|
const debugInfo = {
|
|
386
390
|
identifier: this.options.identifier,
|
|
387
391
|
connected: this.connected,
|
|
388
392
|
};
|
|
389
393
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Connected node: ${Utils_1.JSONUtils.safe(debugInfo, 2)}`);
|
|
390
394
|
this.manager.emit(Enums_1.ManagerEventTypes.NodeConnect, this);
|
|
391
|
-
const playersOnBackupNode = this.manager.players.filter((
|
|
395
|
+
const playersOnBackupNode = this.manager.players.filter((player) => player.node.options.isBackup);
|
|
392
396
|
if (playersOnBackupNode.size) {
|
|
393
397
|
Promise.all(Array.from(playersOnBackupNode.values(), (player) => player.moveNode(this.options.identifier)));
|
|
394
398
|
}
|
|
@@ -414,8 +418,12 @@ class Node {
|
|
|
414
418
|
};
|
|
415
419
|
this.manager.emit(Enums_1.ManagerEventTypes.NodeDisconnect, this, { code, reason });
|
|
416
420
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Disconnected node: ${Utils_1.JSONUtils.safe(debugInfo, 2)}`);
|
|
421
|
+
// Null the session ID immediately so REST calls during the reconnect delay
|
|
422
|
+
// window don't fire with a stale session. connect() reloads it from storage
|
|
423
|
+
// into pendingResumeSessionId for the WS Session-Id header.
|
|
424
|
+
this.sessionId = null;
|
|
417
425
|
if (this.manager.useableNode) {
|
|
418
|
-
const players = this.manager.players.filter((
|
|
426
|
+
const players = this.manager.players.filter((player) => player.node.options.identifier == this.options.identifier);
|
|
419
427
|
if (players.size) {
|
|
420
428
|
await Promise.all(Array.from(players.values(), (player) => player.autoMoveNode()));
|
|
421
429
|
}
|
|
@@ -457,12 +465,12 @@ class Node {
|
|
|
457
465
|
* @emits {nodeRaw} - Emits a nodeRaw event with the raw message received from the WebSocket connection.
|
|
458
466
|
* @private
|
|
459
467
|
*/
|
|
460
|
-
async message(
|
|
461
|
-
if (Array.isArray(
|
|
462
|
-
|
|
463
|
-
else if (
|
|
464
|
-
|
|
465
|
-
const payload = JSON.parse(
|
|
468
|
+
async message(messagePayload) {
|
|
469
|
+
if (Array.isArray(messagePayload))
|
|
470
|
+
messagePayload = Buffer.concat(messagePayload);
|
|
471
|
+
else if (messagePayload instanceof ArrayBuffer)
|
|
472
|
+
messagePayload = Buffer.from(messagePayload);
|
|
473
|
+
const payload = JSON.parse(messagePayload.toString());
|
|
466
474
|
if (!payload.op)
|
|
467
475
|
return;
|
|
468
476
|
this.manager.emit(Enums_1.ManagerEventTypes.NodeRaw, payload);
|
|
@@ -491,7 +499,11 @@ class Node {
|
|
|
491
499
|
case "ready":
|
|
492
500
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Node message: ${Utils_1.JSONUtils.safe(payload, 2)}`);
|
|
493
501
|
this.rest.setSessionId(payload.sessionId);
|
|
494
|
-
|
|
502
|
+
// pendingResumeSessionId holds what we sent in Session-Id header (if anything).
|
|
503
|
+
// Use it — not this.sessionId which was nulled in connect() — to detect whether
|
|
504
|
+
// we attempted resumption with a different session than what Lavalink gave back.
|
|
505
|
+
const hadPreviousSession = this.pendingResumeSessionId && this.pendingResumeSessionId !== payload.sessionId;
|
|
506
|
+
this.pendingResumeSessionId = null;
|
|
495
507
|
this.sessionId = payload.sessionId;
|
|
496
508
|
await this.updateSessionId();
|
|
497
509
|
this.info = await this.fetchInfo();
|
|
@@ -524,6 +536,11 @@ class Node {
|
|
|
524
536
|
return;
|
|
525
537
|
const track = await player.queue.getCurrent();
|
|
526
538
|
const type = payload.type;
|
|
539
|
+
const TRACK_EVENTS = ["TrackStartEvent", "TrackEndEvent", "TrackStuckEvent", "TrackExceptionEvent"];
|
|
540
|
+
if (!track && TRACK_EVENTS.includes(type)) {
|
|
541
|
+
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[Node] Received ${type} for guild ${payload.guildId} but queue has no current track — ignoring.`);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
527
544
|
let error;
|
|
528
545
|
switch (type) {
|
|
529
546
|
case "TrackStartEvent":
|
|
@@ -1121,15 +1138,15 @@ class Node {
|
|
|
1121
1138
|
context: { identifier: this.options.identifier, guildId: player.guildId },
|
|
1122
1139
|
});
|
|
1123
1140
|
}
|
|
1124
|
-
if (segments.some((
|
|
1141
|
+
if (segments.some((segment) => !validSponsorBlocks.includes(segment.toLowerCase()))) {
|
|
1125
1142
|
throw new MagmastreamError_1.MagmaStreamError({
|
|
1126
1143
|
code: Enums_1.MagmaStreamErrorCode.NODE_PROTOCOL_ERROR,
|
|
1127
|
-
message: `Invalid SponsorBlock segments provided. Valid ones are: ${validSponsorBlocks.map((
|
|
1144
|
+
message: `Invalid SponsorBlock segments provided. Valid ones are: ${validSponsorBlocks.map((segment) => `'${segment}'`).join(", ")}`,
|
|
1128
1145
|
context: { identifier: this.options.identifier, guildId: player.guildId, invalidSegments: segments },
|
|
1129
1146
|
});
|
|
1130
1147
|
}
|
|
1131
1148
|
try {
|
|
1132
|
-
await this.rest.put(`/v4/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, segments.map((
|
|
1149
|
+
await this.rest.put(`/v4/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, segments.map((segment) => segment.toLowerCase()));
|
|
1133
1150
|
}
|
|
1134
1151
|
catch (err) {
|
|
1135
1152
|
throw err instanceof MagmastreamError_1.MagmaStreamError
|
|
@@ -55,7 +55,7 @@ export declare class Player {
|
|
|
55
55
|
/** Should only be used when the node is a NodeLink */
|
|
56
56
|
protected voiceReceiverWsClient: WebSocket | null;
|
|
57
57
|
protected isConnectToVoiceReceiver: boolean;
|
|
58
|
-
protected voiceReceiverReconnectTimeout
|
|
58
|
+
protected voiceReceiverReconnectTimeout?: NodeJS.Timeout;
|
|
59
59
|
protected voiceReceiverAttempt: number;
|
|
60
60
|
protected voiceReceiverReconnectTries: number;
|
|
61
61
|
/**
|
|
@@ -263,7 +263,7 @@ class Player {
|
|
|
263
263
|
}
|
|
264
264
|
if (this.voiceReceiverReconnectTimeout) {
|
|
265
265
|
clearTimeout(this.voiceReceiverReconnectTimeout);
|
|
266
|
-
this.voiceReceiverReconnectTimeout =
|
|
266
|
+
this.voiceReceiverReconnectTimeout = undefined;
|
|
267
267
|
}
|
|
268
268
|
if (this.voiceReceiverWsClient) {
|
|
269
269
|
this.voiceReceiverWsClient.removeAllListeners();
|
|
@@ -1043,7 +1043,7 @@ class Player {
|
|
|
1043
1043
|
this.voiceReceiverWsClient = new ws_1.WebSocket(`${useSSL ? "wss" : "ws"}://${host}:${port}/connection/data`, { headers });
|
|
1044
1044
|
this.voiceReceiverWsClient.on("open", () => this.openVoiceReceiver());
|
|
1045
1045
|
this.voiceReceiverWsClient.on("error", (err) => this.onVoiceReceiverError(err));
|
|
1046
|
-
this.voiceReceiverWsClient.on("message", (
|
|
1046
|
+
this.voiceReceiverWsClient.on("message", (rawMessage) => this.onVoiceReceiverMessage(rawMessage.toString()));
|
|
1047
1047
|
this.voiceReceiverWsClient.on("close", (code, reason) => this.closeVoiceReceiver(code, reason.toString()));
|
|
1048
1048
|
}
|
|
1049
1049
|
/**
|
|
@@ -1118,7 +1118,7 @@ class Player {
|
|
|
1118
1118
|
async openVoiceReceiver() {
|
|
1119
1119
|
if (this.voiceReceiverReconnectTimeout)
|
|
1120
1120
|
clearTimeout(this.voiceReceiverReconnectTimeout);
|
|
1121
|
-
this.voiceReceiverReconnectTimeout =
|
|
1121
|
+
this.voiceReceiverReconnectTimeout = undefined;
|
|
1122
1122
|
this.isConnectToVoiceReceiver = true;
|
|
1123
1123
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[PLAYER] Opened voice receiver for player ${this.guildId}`);
|
|
1124
1124
|
this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverConnect, this);
|
|
@@ -1166,18 +1166,18 @@ class Player {
|
|
|
1166
1166
|
* @returns {Promise<void>} - A promise that resolves when the voice state is updated.
|
|
1167
1167
|
*/
|
|
1168
1168
|
async updateVoice() {
|
|
1169
|
-
const
|
|
1170
|
-
const
|
|
1171
|
-
if (!
|
|
1169
|
+
const voiceState = this.voiceState;
|
|
1170
|
+
const voiceEvent = voiceState?.event;
|
|
1171
|
+
if (!voiceState?.channelId || !voiceState?.sessionId || !voiceEvent?.token || !voiceEvent?.endpoint)
|
|
1172
1172
|
return;
|
|
1173
1173
|
await this.node.rest.updatePlayer({
|
|
1174
1174
|
guildId: this.options.guildId,
|
|
1175
1175
|
data: {
|
|
1176
1176
|
voice: {
|
|
1177
|
-
token:
|
|
1178
|
-
endpoint:
|
|
1179
|
-
sessionId:
|
|
1180
|
-
channelId:
|
|
1177
|
+
token: voiceEvent.token,
|
|
1178
|
+
endpoint: voiceEvent.endpoint,
|
|
1179
|
+
sessionId: voiceState.sessionId,
|
|
1180
|
+
channelId: voiceState.channelId,
|
|
1181
1181
|
},
|
|
1182
1182
|
},
|
|
1183
1183
|
});
|
|
@@ -497,6 +497,8 @@ export interface LavaPlayer {
|
|
|
497
497
|
export interface ErrorOrEmptySearchResult {
|
|
498
498
|
/** The load type of the result. */
|
|
499
499
|
loadType: LoadTypes.Empty | LoadTypes.Error;
|
|
500
|
+
/** Always an empty array for error/empty results. */
|
|
501
|
+
tracks: [];
|
|
500
502
|
}
|
|
501
503
|
/**
|
|
502
504
|
* Track Search Result
|
package/dist/structures/Utils.js
CHANGED
|
@@ -62,8 +62,8 @@ class TrackUtils {
|
|
|
62
62
|
static isTrack(track) {
|
|
63
63
|
if (typeof track !== "object" || track === null)
|
|
64
64
|
return false;
|
|
65
|
-
const
|
|
66
|
-
return REQUIRED_TRACK_KEYS.every((key) => typeof
|
|
65
|
+
const trackRecord = track;
|
|
66
|
+
return REQUIRED_TRACK_KEYS.every((key) => typeof trackRecord[key] === "string");
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
69
|
* Checks if the provided argument is a valid Track array.
|
|
@@ -295,7 +295,7 @@ class AutoPlayUtils {
|
|
|
295
295
|
const resolvedTracks = await this.resolveTracksFromQuery(`${randomTrack.artist.name} - ${randomTrack.name}`, this.manager.options.defaultSearchPlatform, track.requester);
|
|
296
296
|
if (!resolvedTracks.length)
|
|
297
297
|
return [];
|
|
298
|
-
const filteredTracks = resolvedTracks.filter((
|
|
298
|
+
const filteredTracks = resolvedTracks.filter((resolvedTrack) => resolvedTrack.uri !== track.uri);
|
|
299
299
|
if (!filteredTracks.length) {
|
|
300
300
|
return [];
|
|
301
301
|
}
|
|
@@ -392,10 +392,10 @@ class AutoPlayUtils {
|
|
|
392
392
|
const h2 = element.querySelector('h2[itemprop="name"]');
|
|
393
393
|
if (!h2)
|
|
394
394
|
return null;
|
|
395
|
-
const
|
|
396
|
-
if (!
|
|
395
|
+
const anchorElement = h2.querySelector('a[itemprop="url"]');
|
|
396
|
+
if (!anchorElement)
|
|
397
397
|
return null;
|
|
398
|
-
const href =
|
|
398
|
+
const href = anchorElement.getAttribute("href");
|
|
399
399
|
return href ? `https://soundcloud.com${href}` : null;
|
|
400
400
|
})
|
|
401
401
|
.filter(Boolean);
|
|
@@ -433,7 +433,7 @@ class AutoPlayUtils {
|
|
|
433
433
|
searchURI = `https://www.youtube.com/watch?v=${videoID}&list=RD${videoID}&index=${randomIndex}`;
|
|
434
434
|
} while (track.uri.includes(searchURI));
|
|
435
435
|
const resolvedTracks = await this.resolveTracksFromQuery(searchURI, Enums_1.SearchPlatform.YouTube, requester);
|
|
436
|
-
const filteredTracks = resolvedTracks.filter((
|
|
436
|
+
const filteredTracks = resolvedTracks.filter((resolvedTrack) => resolvedTrack.uri !== track.uri);
|
|
437
437
|
return filteredTracks;
|
|
438
438
|
}
|
|
439
439
|
case Enums_1.AutoPlayPlatform.Tidal: {
|
|
@@ -596,7 +596,7 @@ class AutoPlayUtils {
|
|
|
596
596
|
context: { recommendedResult },
|
|
597
597
|
});
|
|
598
598
|
}
|
|
599
|
-
return data.map((
|
|
599
|
+
return data.map((trackData) => TrackUtils.build(trackData, requester, true));
|
|
600
600
|
}
|
|
601
601
|
case Enums_1.LoadTypes.Album:
|
|
602
602
|
case Enums_1.LoadTypes.Artist:
|
|
@@ -606,7 +606,7 @@ class AutoPlayUtils {
|
|
|
606
606
|
case Enums_1.LoadTypes.Playlist: {
|
|
607
607
|
const data = recommendedResult.data;
|
|
608
608
|
if (this.isPlaylistRawData(data)) {
|
|
609
|
-
return data.tracks.map((
|
|
609
|
+
return data.tracks.map((trackData) => TrackUtils.build(trackData, requester, true));
|
|
610
610
|
}
|
|
611
611
|
throw new MagmastreamError_1.MagmaStreamError({
|
|
612
612
|
code: Enums_1.MagmaStreamErrorCode.UTILS_AUTOPLAY_BUILD_FAILED,
|
|
@@ -666,9 +666,9 @@ class PlayerUtils {
|
|
|
666
666
|
if (!obj || typeof obj !== "object")
|
|
667
667
|
return obj;
|
|
668
668
|
const result = {};
|
|
669
|
-
for (const [k,
|
|
670
|
-
if (!isNonSerializable(
|
|
671
|
-
result[k] =
|
|
669
|
+
for (const [k, entryValue] of Object.entries(obj)) {
|
|
670
|
+
if (!isNonSerializable(entryValue)) {
|
|
671
|
+
result[k] = entryValue;
|
|
672
672
|
}
|
|
673
673
|
}
|
|
674
674
|
return result;
|
|
@@ -690,8 +690,8 @@ class PlayerUtils {
|
|
|
690
690
|
}
|
|
691
691
|
};
|
|
692
692
|
const safeCurrent = current ? serializeTrack(current) : null;
|
|
693
|
-
const safeTracks = tracks.map(serializeTrack).filter((
|
|
694
|
-
const safePrevious = previous.map(serializeTrack).filter((
|
|
693
|
+
const safeTracks = tracks.map(serializeTrack).filter((serializedTrack) => serializedTrack !== null);
|
|
694
|
+
const safePrevious = previous.map(serializeTrack).filter((serializedTrack) => serializedTrack !== null);
|
|
695
695
|
let safeNode = null;
|
|
696
696
|
if (player.node) {
|
|
697
697
|
try {
|
|
@@ -721,6 +721,8 @@ class PlayerUtils {
|
|
|
721
721
|
paused: player.paused,
|
|
722
722
|
playing: player.playing,
|
|
723
723
|
position: player.position,
|
|
724
|
+
isAutoplay: player.isAutoplay,
|
|
725
|
+
autoplayTries: player.autoplayTries,
|
|
724
726
|
trackRepeat: player.trackRepeat,
|
|
725
727
|
queueRepeat: player.queueRepeat,
|
|
726
728
|
dynamicRepeat: player.dynamicRepeat,
|
|
@@ -63,12 +63,12 @@ class CloudstormManager extends Manager_1.Manager {
|
|
|
63
63
|
}
|
|
64
64
|
// CloudStorm has no user/guild cache — return minimal portable info only.
|
|
65
65
|
async resolveUser(user) {
|
|
66
|
-
const
|
|
67
|
-
const cached = this.getUserFromCache(
|
|
66
|
+
const userId = typeof user === "string" ? user : String(user.id);
|
|
67
|
+
const cached = this.getUserFromCache(userId);
|
|
68
68
|
if (cached)
|
|
69
69
|
return cached;
|
|
70
70
|
return {
|
|
71
|
-
id,
|
|
71
|
+
id: userId,
|
|
72
72
|
username: typeof user === "string" ? undefined : user.username,
|
|
73
73
|
};
|
|
74
74
|
}
|
|
@@ -50,16 +50,16 @@ class DiscordJSManager extends Manager_1.Manager {
|
|
|
50
50
|
guild.shard.send(packet);
|
|
51
51
|
}
|
|
52
52
|
async resolveUser(user) {
|
|
53
|
-
const
|
|
54
|
-
const cached = this.client.users.cache.get(
|
|
53
|
+
const userId = typeof user === "string" ? user : String(user.id);
|
|
54
|
+
const cached = this.client.users.cache.get(userId);
|
|
55
55
|
if (cached)
|
|
56
56
|
return cached;
|
|
57
57
|
try {
|
|
58
|
-
const fetched = await this.client.users.fetch(
|
|
58
|
+
const fetched = await this.client.users.fetch(userId);
|
|
59
59
|
return fetched;
|
|
60
60
|
}
|
|
61
61
|
catch {
|
|
62
|
-
return { id, username: typeof user === "string" ? undefined : user.username };
|
|
62
|
+
return { id: userId, username: typeof user === "string" ? undefined : user.username };
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
resolveGuild(guildId) {
|
|
@@ -57,14 +57,14 @@ class DiscordenoManager extends Manager_1.Manager {
|
|
|
57
57
|
* Uses user-provided cache getter if available, otherwise falls back to minimal info.
|
|
58
58
|
*/
|
|
59
59
|
async resolveUser(user) {
|
|
60
|
-
const
|
|
60
|
+
const userId = typeof user === "string" ? user : String(user.id);
|
|
61
61
|
// Try user-provided cache getter
|
|
62
|
-
const cached = this.getUserFromCache(
|
|
62
|
+
const cached = this.getUserFromCache(userId);
|
|
63
63
|
if (cached)
|
|
64
64
|
return cached;
|
|
65
65
|
// Fallback: return minimal info
|
|
66
66
|
return {
|
|
67
|
-
id,
|
|
67
|
+
id: userId,
|
|
68
68
|
username: typeof user === "string" ? undefined : user.username,
|
|
69
69
|
};
|
|
70
70
|
}
|
package/dist/wrappers/eris.js
CHANGED
|
@@ -35,12 +35,12 @@ class ErisManager extends Manager_1.Manager {
|
|
|
35
35
|
guild.shard.sendWS(packet.op, packet.d);
|
|
36
36
|
}
|
|
37
37
|
async resolveUser(user) {
|
|
38
|
-
const
|
|
39
|
-
const cached = this.client.users.get(
|
|
38
|
+
const userId = typeof user === "string" ? user : String(user.id);
|
|
39
|
+
const cached = this.client.users.get(userId);
|
|
40
40
|
if (cached)
|
|
41
41
|
return cached;
|
|
42
42
|
return {
|
|
43
|
-
id,
|
|
43
|
+
id: userId,
|
|
44
44
|
username: typeof user === "string" ? undefined : user.username,
|
|
45
45
|
};
|
|
46
46
|
}
|
package/dist/wrappers/oceanic.js
CHANGED
|
@@ -35,12 +35,12 @@ class OceanicManager extends Manager_1.Manager {
|
|
|
35
35
|
guild.shard.send(packet.op, packet.d);
|
|
36
36
|
}
|
|
37
37
|
async resolveUser(user) {
|
|
38
|
-
const
|
|
39
|
-
const cached = this.client.users.get(
|
|
38
|
+
const userId = typeof user === "string" ? user : String(user.id);
|
|
39
|
+
const cached = this.client.users.get(userId);
|
|
40
40
|
if (cached)
|
|
41
41
|
return cached;
|
|
42
42
|
return {
|
|
43
|
-
id,
|
|
43
|
+
id: userId,
|
|
44
44
|
username: typeof user === "string" ? undefined : user.username,
|
|
45
45
|
};
|
|
46
46
|
}
|
package/dist/wrappers/seyfert.js
CHANGED
|
@@ -63,15 +63,15 @@ class SeyfertManager extends Manager_1.Manager {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
async resolveUser(user) {
|
|
66
|
-
const
|
|
67
|
-
const cached = this.client.cache.users?.get(
|
|
66
|
+
const userId = typeof user === "string" ? user : String(user.id);
|
|
67
|
+
const cached = this.client.cache.users?.get(userId);
|
|
68
68
|
if (cached)
|
|
69
69
|
return cached;
|
|
70
70
|
try {
|
|
71
|
-
return await this.client.users.fetch(
|
|
71
|
+
return await this.client.users.fetch(userId);
|
|
72
72
|
}
|
|
73
73
|
catch {
|
|
74
|
-
return { id, username: typeof user === "string" ? undefined : user.username };
|
|
74
|
+
return { id: userId, username: typeof user === "string" ? undefined : user.username };
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
resolveGuild(guildId) {
|