aqualink 2.19.1 → 2.20.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/build/handlers/autoplay.js +7 -3
- package/build/handlers/fetchImage.js +2 -4
- package/build/index.d.ts +145 -4
- package/build/index.js +48 -0
- package/build/structures/Aqua.js +249 -62
- package/build/structures/Connection.js +138 -10
- package/build/structures/Filters.js +1 -1
- package/build/structures/Node.js +41 -3
- package/build/structures/Player.js +145 -47
- package/build/structures/Plugins.js +2 -2
- package/build/structures/Queue.js +13 -18
- package/build/structures/Rest.js +146 -136
- package/build/structures/Track.js +1 -1
- package/package.json +3 -4
|
@@ -48,6 +48,17 @@ const _functions = {
|
|
|
48
48
|
const label = (dot === -1 ? endpoint : endpoint.slice(0, dot)).toLowerCase()
|
|
49
49
|
if (!label) return 'unknown'
|
|
50
50
|
|
|
51
|
+
// Discord voice hosts commonly look like: c-gru20-<hash>.discord.media
|
|
52
|
+
const cPrefix = /^c-([a-z]{3})(?:\d+)?(?:-|$)/
|
|
53
|
+
const m1 = cPrefix.exec(label)
|
|
54
|
+
if (m1?.[1]) return m1[1]
|
|
55
|
+
|
|
56
|
+
// Fallback for labels that still contain an iata-like token.
|
|
57
|
+
const token = /(?:^|-)([a-z]{3})(?:\d+)?(?:-|$)/
|
|
58
|
+
const m2 = token.exec(label)
|
|
59
|
+
if (m2?.[1]) return m2[1]
|
|
60
|
+
|
|
61
|
+
// Last fallback: strip trailing digits from first label.
|
|
51
62
|
let i = label.length - 1
|
|
52
63
|
while (i >= 0) {
|
|
53
64
|
const c = label.charCodeAt(i)
|
|
@@ -56,15 +67,13 @@ const _functions = {
|
|
|
56
67
|
}
|
|
57
68
|
return label.slice(0, i + 1) || 'unknown'
|
|
58
69
|
},
|
|
59
|
-
fillVoicePayload: (payload, guildId, conn, player
|
|
70
|
+
fillVoicePayload: (payload, guildId, conn, player) => {
|
|
60
71
|
payload.guildId = guildId
|
|
61
72
|
const v = payload.data.voice
|
|
62
73
|
v.token = conn.token
|
|
63
74
|
v.endpoint = conn.endpoint
|
|
64
75
|
v.sessionId = conn.sessionId
|
|
65
76
|
v.channelId = player.voiceChannel
|
|
66
|
-
v.resume = resume ? true : undefined
|
|
67
|
-
v.sequence = resume ? conn.sequence : undefined
|
|
68
77
|
payload.data.volume = player?.volume ?? 100
|
|
69
78
|
return payload
|
|
70
79
|
}
|
|
@@ -83,9 +92,7 @@ class PayloadPool {
|
|
|
83
92
|
voice: {
|
|
84
93
|
token: null,
|
|
85
94
|
endpoint: null,
|
|
86
|
-
sessionId: null
|
|
87
|
-
resume: undefined,
|
|
88
|
-
sequence: undefined
|
|
95
|
+
sessionId: null
|
|
89
96
|
},
|
|
90
97
|
volume: null
|
|
91
98
|
}
|
|
@@ -101,7 +108,6 @@ class PayloadPool {
|
|
|
101
108
|
payload.guildId = null
|
|
102
109
|
const v = payload.data.voice
|
|
103
110
|
v.token = v.endpoint = v.sessionId = null
|
|
104
|
-
v.resume = v.sequence = undefined
|
|
105
111
|
payload.data.volume = null
|
|
106
112
|
this._pool[this._size++] = payload
|
|
107
113
|
}
|
|
@@ -151,6 +157,7 @@ class Connection {
|
|
|
151
157
|
|
|
152
158
|
this._lastStateReqAt = 0
|
|
153
159
|
this._stateGeneration = 0
|
|
160
|
+
this._regionMigrationAttempted = false
|
|
154
161
|
}
|
|
155
162
|
|
|
156
163
|
_hasValidVoiceData() {
|
|
@@ -201,6 +208,7 @@ class Connection {
|
|
|
201
208
|
this._lastEndpoint = endpoint
|
|
202
209
|
this._reconnectAttempts = 0
|
|
203
210
|
this._consecutiveFailures = 0
|
|
211
|
+
this._regionMigrationAttempted = false
|
|
204
212
|
}
|
|
205
213
|
|
|
206
214
|
this.endpoint = endpoint
|
|
@@ -208,10 +216,71 @@ class Connection {
|
|
|
208
216
|
this.token = data.token
|
|
209
217
|
this.channelId = data.channel_id || this.channelId || this.voiceChannel
|
|
210
218
|
this._lastVoiceDataUpdate = Date.now()
|
|
219
|
+
this._aqua?._trace?.('connection.serverUpdate', {
|
|
220
|
+
guildId: this._guildId,
|
|
221
|
+
endpoint: this.endpoint,
|
|
222
|
+
region: this.region,
|
|
223
|
+
txId: data.txId || null
|
|
224
|
+
})
|
|
211
225
|
this._stateFlags &= ~STATE.VOICE_DATA_STALE
|
|
212
226
|
|
|
213
227
|
if (this._player?.paused) this._player.pause(false)
|
|
228
|
+
const migrated = this._checkRegionMigration()
|
|
229
|
+
if (migrated) return
|
|
214
230
|
this._scheduleVoiceUpdate()
|
|
231
|
+
this._player?._flushDeferredPlay?.()
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
_checkRegionMigration() {
|
|
235
|
+
if (this._destroyed || this._regionMigrationAttempted) return false
|
|
236
|
+
if (
|
|
237
|
+
!this._aqua?.autoRegionMigrate ||
|
|
238
|
+
!this.region ||
|
|
239
|
+
this.region === 'unknown'
|
|
240
|
+
)
|
|
241
|
+
return false
|
|
242
|
+
const player = this._player
|
|
243
|
+
if (!player || player.destroyed || player._resuming || player._reconnecting)
|
|
244
|
+
return false
|
|
245
|
+
|
|
246
|
+
const currentNode = player.nodes
|
|
247
|
+
if (!currentNode) return false
|
|
248
|
+
|
|
249
|
+
const currentRegions = Array.isArray(currentNode.regions)
|
|
250
|
+
? currentNode.regions
|
|
251
|
+
: []
|
|
252
|
+
const alreadyMatching = currentRegions.some((r) =>
|
|
253
|
+
this._aqua._regionMatches?.(r, this.region)
|
|
254
|
+
)
|
|
255
|
+
if (alreadyMatching) {
|
|
256
|
+
this._regionMigrationAttempted = true
|
|
257
|
+
return false
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const targetNode = this._aqua._findBestNodeForRegion?.(this.region)
|
|
261
|
+
if (!targetNode || targetNode === currentNode) return false
|
|
262
|
+
|
|
263
|
+
this._regionMigrationAttempted = true
|
|
264
|
+
this._aqua?._trace?.('connection.region.migrate', {
|
|
265
|
+
guildId: this._guildId,
|
|
266
|
+
region: this.region,
|
|
267
|
+
from: currentNode?.name || currentNode?.host,
|
|
268
|
+
to: targetNode?.name || targetNode?.host
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
queueMicrotask(() => {
|
|
272
|
+
this._aqua
|
|
273
|
+
.movePlayerToNode?.(this._guildId, targetNode, 'region')
|
|
274
|
+
.catch((err) => {
|
|
275
|
+
this._regionMigrationAttempted = false
|
|
276
|
+
this._aqua?._trace?.('connection.region.migrate.error', {
|
|
277
|
+
guildId: this._guildId,
|
|
278
|
+
region: this.region,
|
|
279
|
+
error: err?.message || String(err)
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
return true
|
|
215
284
|
}
|
|
216
285
|
|
|
217
286
|
resendVoiceUpdate(force = false) {
|
|
@@ -237,6 +306,10 @@ class Connection {
|
|
|
237
306
|
if (data.txId && data.txId < this.txId) return
|
|
238
307
|
|
|
239
308
|
if (!channelId) {
|
|
309
|
+
this._aqua?._trace?.('connection.stateUpdate.nullChannel', {
|
|
310
|
+
guildId: this._guildId,
|
|
311
|
+
txId: data.txId || null
|
|
312
|
+
})
|
|
240
313
|
this.isWaitingForDisconnect = true
|
|
241
314
|
if (!this._nullChannelTimer) {
|
|
242
315
|
this._nullChannelTimer = setTimeout(() => {
|
|
@@ -249,6 +322,12 @@ class Connection {
|
|
|
249
322
|
}
|
|
250
323
|
|
|
251
324
|
this.isWaitingForDisconnect = false
|
|
325
|
+
this._aqua?._trace?.('connection.stateUpdate', {
|
|
326
|
+
guildId: this._guildId,
|
|
327
|
+
channelId,
|
|
328
|
+
sessionId,
|
|
329
|
+
txId: data.txId || null
|
|
330
|
+
})
|
|
252
331
|
|
|
253
332
|
if (p && p.txId > this.txId) this.txId = p.txId
|
|
254
333
|
|
|
@@ -289,6 +368,9 @@ class Connection {
|
|
|
289
368
|
|
|
290
369
|
this._stateFlags =
|
|
291
370
|
(this._stateFlags | STATE.DISCONNECTING) & ~STATE.CONNECTED
|
|
371
|
+
this._aqua?._trace?.('connection.disconnect', {
|
|
372
|
+
guildId: this._guildId
|
|
373
|
+
})
|
|
292
374
|
this._clearNullChannelTimer()
|
|
293
375
|
this._clearPendingUpdate()
|
|
294
376
|
this._clearReconnectTimer()
|
|
@@ -338,6 +420,13 @@ class Connection {
|
|
|
338
420
|
|
|
339
421
|
async attemptResume() {
|
|
340
422
|
if (!this._canAttemptResumeCore()) return false
|
|
423
|
+
this._aqua?._trace?.('connection.resume.attempt', {
|
|
424
|
+
guildId: this._guildId,
|
|
425
|
+
reconnectAttempts: this._reconnectAttempts,
|
|
426
|
+
hasSessionId: !!this.sessionId,
|
|
427
|
+
hasEndpoint: !!this.endpoint,
|
|
428
|
+
hasToken: !!this.token
|
|
429
|
+
})
|
|
341
430
|
|
|
342
431
|
const currentGen = this._stateGeneration
|
|
343
432
|
|
|
@@ -382,6 +471,9 @@ class Connection {
|
|
|
382
471
|
}
|
|
383
472
|
|
|
384
473
|
await this._sendUpdate(payload)
|
|
474
|
+
this._aqua?._trace?.('connection.resume.success', {
|
|
475
|
+
guildId: this._guildId
|
|
476
|
+
})
|
|
385
477
|
|
|
386
478
|
this._reconnectAttempts = 0
|
|
387
479
|
this._consecutiveFailures = 0
|
|
@@ -399,6 +491,10 @@ class Connection {
|
|
|
399
491
|
AqualinkEvents.Debug,
|
|
400
492
|
`Resume failed for guild ${this._guildId}: ${e?.message || e}`
|
|
401
493
|
)
|
|
494
|
+
this._aqua?._trace?.('connection.resume.error', {
|
|
495
|
+
guildId: this._guildId,
|
|
496
|
+
error: e?.message || String(e)
|
|
497
|
+
})
|
|
402
498
|
|
|
403
499
|
if (
|
|
404
500
|
this._reconnectAttempts < MAX_RECONNECT_ATTEMPTS &&
|
|
@@ -459,8 +555,23 @@ class Connection {
|
|
|
459
555
|
}
|
|
460
556
|
|
|
461
557
|
_scheduleVoiceUpdate() {
|
|
462
|
-
if (this._destroyed)
|
|
463
|
-
|
|
558
|
+
if (this._destroyed) {
|
|
559
|
+
this._aqua?._trace?.('connection.update.skip', {
|
|
560
|
+
guildId: this._guildId,
|
|
561
|
+
reason: 'destroyed'
|
|
562
|
+
})
|
|
563
|
+
return
|
|
564
|
+
}
|
|
565
|
+
if (!this._hasValidVoiceData()) {
|
|
566
|
+
this._aqua?._trace?.('connection.update.skip', {
|
|
567
|
+
guildId: this._guildId,
|
|
568
|
+
reason: 'invalid_voice_data',
|
|
569
|
+
hasSessionId: !!this.sessionId,
|
|
570
|
+
hasEndpoint: !!this.endpoint,
|
|
571
|
+
hasToken: !!this.token
|
|
572
|
+
})
|
|
573
|
+
return
|
|
574
|
+
}
|
|
464
575
|
|
|
465
576
|
if (!this._pendingUpdate) {
|
|
466
577
|
const payload = sharedPool.acquire()
|
|
@@ -485,6 +596,9 @@ class Connection {
|
|
|
485
596
|
|
|
486
597
|
if (this._stateFlags & STATE.UPDATE_SCHEDULED) return
|
|
487
598
|
this._stateFlags |= STATE.UPDATE_SCHEDULED
|
|
599
|
+
this._aqua?._trace?.('connection.update.scheduled', {
|
|
600
|
+
guildId: this._guildId
|
|
601
|
+
})
|
|
488
602
|
|
|
489
603
|
this._voiceFlushTimer = setTimeout(
|
|
490
604
|
() => this._executeVoiceUpdate(),
|
|
@@ -524,8 +638,22 @@ class Connection {
|
|
|
524
638
|
if (!this._rest) throw new Error('REST interface unavailable')
|
|
525
639
|
|
|
526
640
|
try {
|
|
641
|
+
this._aqua?._trace?.('connection.update.send', {
|
|
642
|
+
guildId: this._guildId,
|
|
643
|
+
hasSessionId: !!this._rest?.sessionId,
|
|
644
|
+
hasVoice:
|
|
645
|
+
!!payload?.data?.voice?.sessionId && !!payload?.data?.voice?.endpoint
|
|
646
|
+
})
|
|
527
647
|
await this._rest.updatePlayer(payload)
|
|
648
|
+
this._aqua?._trace?.('connection.update.ok', {
|
|
649
|
+
guildId: this._guildId
|
|
650
|
+
})
|
|
528
651
|
} catch (e) {
|
|
652
|
+
this._aqua?._trace?.('connection.update.error', {
|
|
653
|
+
guildId: this._guildId,
|
|
654
|
+
statusCode: e?.statusCode || e?.response?.statusCode || null,
|
|
655
|
+
error: e?.message || String(e)
|
|
656
|
+
})
|
|
529
657
|
if (e.statusCode === 404 || e.response?.statusCode === 404) {
|
|
530
658
|
const isSessionError = e.body?.message?.includes('sessionId') || false
|
|
531
659
|
if (this._aqua) {
|
|
@@ -574,4 +702,4 @@ class Connection {
|
|
|
574
702
|
}
|
|
575
703
|
}
|
|
576
704
|
|
|
577
|
-
module.exports = Connection
|
|
705
|
+
module.exports = Connection
|
|
@@ -41,7 +41,7 @@ const EMPTY_ARRAY = Object.freeze([])
|
|
|
41
41
|
|
|
42
42
|
const FILTER_POOL_SIZE = 16
|
|
43
43
|
const filterPool = {
|
|
44
|
-
pools: Object.fromEntries(Object.keys(FILTER_DEFAULTS).map(k => [k, []])),
|
|
44
|
+
pools: Object.fromEntries(Object.keys(FILTER_DEFAULTS).map((k) => [k, []])),
|
|
45
45
|
|
|
46
46
|
acquire(type) {
|
|
47
47
|
const pool = this.pools[type]
|
package/build/structures/Node.js
CHANGED
|
@@ -187,6 +187,10 @@ class Node {
|
|
|
187
187
|
this._isConnecting = false
|
|
188
188
|
this.reconnectAttempted = 0
|
|
189
189
|
this._emitDebug('WebSocket connection established')
|
|
190
|
+
this.aqua?._trace?.('node.ws.open', {
|
|
191
|
+
node: this.name,
|
|
192
|
+
reconnectAttempted: this.reconnectAttempted
|
|
193
|
+
})
|
|
190
194
|
|
|
191
195
|
if (!this.aqua?.bypassChecks?.nodeFetchInfo && !this.info) {
|
|
192
196
|
const timeoutId = setTimeout(() => {
|
|
@@ -268,6 +272,12 @@ class Node {
|
|
|
268
272
|
code,
|
|
269
273
|
reason: _functions.reasonToString(reason)
|
|
270
274
|
})
|
|
275
|
+
this.aqua?._trace?.('node.ws.close', {
|
|
276
|
+
node: this.name,
|
|
277
|
+
code,
|
|
278
|
+
reason: _functions.reasonToString(reason),
|
|
279
|
+
hasSessionId: !!this.sessionId
|
|
280
|
+
})
|
|
271
281
|
|
|
272
282
|
if (this.isDestroyed) return
|
|
273
283
|
|
|
@@ -301,6 +311,11 @@ class Node {
|
|
|
301
311
|
this._clearReconnectTimeout()
|
|
302
312
|
|
|
303
313
|
const attempt = ++this.reconnectAttempted
|
|
314
|
+
this.aqua?._trace?.('node.ws.reconnect.scheduled', {
|
|
315
|
+
node: this.name,
|
|
316
|
+
attempt,
|
|
317
|
+
infinite: !!this.infiniteReconnects
|
|
318
|
+
})
|
|
304
319
|
|
|
305
320
|
if (this.infiniteReconnects) {
|
|
306
321
|
this.aqua.emit(AqualinkEvents.NodeReconnect, this, {
|
|
@@ -339,8 +354,7 @@ class Node {
|
|
|
339
354
|
|
|
340
355
|
_calcBackoff(attempt) {
|
|
341
356
|
const baseBackoff =
|
|
342
|
-
this.reconnectTimeout *
|
|
343
|
-
Node.BACKOFF_MULTIPLIER ** Math.min(attempt, 10)
|
|
357
|
+
this.reconnectTimeout * Node.BACKOFF_MULTIPLIER ** Math.min(attempt, 10)
|
|
344
358
|
const maxJitter = Math.min(
|
|
345
359
|
Node.JITTER_MAX,
|
|
346
360
|
baseBackoff * Node.JITTER_FACTOR
|
|
@@ -370,6 +384,10 @@ class Node {
|
|
|
370
384
|
this._isConnecting = true
|
|
371
385
|
this.state = NODE_STATE.CONNECTING
|
|
372
386
|
this._cleanup()
|
|
387
|
+
this.aqua?._trace?.('node.ws.connect', {
|
|
388
|
+
node: this.name,
|
|
389
|
+
url: this.wsUrl
|
|
390
|
+
})
|
|
373
391
|
|
|
374
392
|
try {
|
|
375
393
|
const h = this._boundHandlers
|
|
@@ -570,6 +588,12 @@ class Node {
|
|
|
570
588
|
oldSessionId && oldSessionId !== sessionId && !payload.resumed
|
|
571
589
|
|
|
572
590
|
this.sessionId = sessionId
|
|
591
|
+
this.aqua?._trace?.('node.ready.packet', {
|
|
592
|
+
node: this.name,
|
|
593
|
+
resumed: !!payload.resumed,
|
|
594
|
+
oldSessionId,
|
|
595
|
+
newSessionId: sessionId
|
|
596
|
+
})
|
|
573
597
|
this.rest.setSessionId(sessionId)
|
|
574
598
|
this._headers['Session-Id'] = sessionId
|
|
575
599
|
|
|
@@ -611,6 +635,11 @@ class Node {
|
|
|
611
635
|
|
|
612
636
|
async _resumePlayers() {
|
|
613
637
|
if (!this.sessionId) return
|
|
638
|
+
this.aqua?._trace?.('node.resume.begin', {
|
|
639
|
+
node: this.name,
|
|
640
|
+
sessionId: this.sessionId,
|
|
641
|
+
players: this.aqua?.players?.size || 0
|
|
642
|
+
})
|
|
614
643
|
|
|
615
644
|
try {
|
|
616
645
|
await this.rest.makeRequest('PATCH', `/v4/sessions/${this.sessionId}`, {
|
|
@@ -626,6 +655,11 @@ class Node {
|
|
|
626
655
|
) {
|
|
627
656
|
try {
|
|
628
657
|
this._emitDebug(`Rejoining voice for guild ${guildId} on resume`)
|
|
658
|
+
this.aqua?._trace?.('node.resume.rejoin', {
|
|
659
|
+
node: this.name,
|
|
660
|
+
guildId,
|
|
661
|
+
voiceChannel: player.voiceChannel
|
|
662
|
+
})
|
|
629
663
|
player.connect({
|
|
630
664
|
voiceChannel: player.voiceChannel,
|
|
631
665
|
deaf: player.deaf,
|
|
@@ -644,6 +678,10 @@ class Node {
|
|
|
644
678
|
await this.aqua.loadPlayers()
|
|
645
679
|
}
|
|
646
680
|
} catch (err) {
|
|
681
|
+
this.aqua?._trace?.('node.resume.error', {
|
|
682
|
+
node: this.name,
|
|
683
|
+
error: _functions.errMsg(err)
|
|
684
|
+
})
|
|
647
685
|
this._emitError(`Failed to resume session: ${_functions.errMsg(err)}`)
|
|
648
686
|
throw err
|
|
649
687
|
}
|
|
@@ -664,4 +702,4 @@ class Node {
|
|
|
664
702
|
}
|
|
665
703
|
}
|
|
666
704
|
|
|
667
|
-
module.exports = Node
|
|
705
|
+
module.exports = Node
|