aqualink 2.11.4 → 2.11.5
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/structures/Aqua.js +4 -2
- package/build/structures/Connection.js +178 -82
- package/build/structures/Node.js +133 -136
- package/build/structures/Player.js +196 -146
- package/build/structures/Rest.js +266 -149
- package/package.json +1 -1
package/build/structures/Aqua.js
CHANGED
|
@@ -619,12 +619,11 @@ class Aqua extends EventEmitter {
|
|
|
619
619
|
_handlePlayerDestroy(player) {
|
|
620
620
|
const node = player.nodes
|
|
621
621
|
node?.players?.delete?.(player)
|
|
622
|
-
|
|
623
622
|
if (this.players.get(player.guildId) === player) {
|
|
624
623
|
this.players.delete(player.guildId)
|
|
625
624
|
}
|
|
626
625
|
|
|
627
|
-
this.emit(
|
|
626
|
+
this.emit("playerDestroy", player)
|
|
628
627
|
}
|
|
629
628
|
|
|
630
629
|
async destroyPlayer(guildId) {
|
|
@@ -646,11 +645,14 @@ class Aqua extends EventEmitter {
|
|
|
646
645
|
if (!requestNode) throw new Error('No nodes available')
|
|
647
646
|
|
|
648
647
|
const formattedQuery = isProbablyUrl(query) ? query : `${source}${SEARCH_PREFIX}${query}`
|
|
648
|
+
const now = performance.now()
|
|
649
|
+
|
|
649
650
|
|
|
650
651
|
try {
|
|
651
652
|
const endpoint = `/${this.restVersion}/loadtracks?identifier=${encodeURIComponent(formattedQuery)}`
|
|
652
653
|
const response = await requestNode.rest.makeRequest('GET', endpoint)
|
|
653
654
|
|
|
655
|
+
|
|
654
656
|
if (!response || response.loadType === 'empty' || response.loadType === 'NO_MATCHES') {
|
|
655
657
|
return EMPTY_TRACKS_RESPONSE
|
|
656
658
|
}
|
|
@@ -1,47 +1,79 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const POOL_SIZE = 10
|
|
4
|
+
const LISTENER_CHECK_INTERVAL = 5000
|
|
5
|
+
const UPDATE_TIMEOUT = 5000
|
|
6
|
+
|
|
7
|
+
const ENDPOINT_PATTERN = /^([a-z-]+)/i
|
|
8
|
+
|
|
9
|
+
const STATE_FLAGS = {
|
|
10
|
+
CONNECTED: 1 << 0,
|
|
11
|
+
PAUSED: 1 << 1,
|
|
12
|
+
SELF_DEAF: 1 << 2,
|
|
13
|
+
SELF_MUTE: 1 << 3,
|
|
14
|
+
HAS_DEBUG_LISTENERS: 1 << 4,
|
|
15
|
+
HAS_MOVE_LISTENERS: 1 << 5,
|
|
16
|
+
UPDATE_SCHEDULED: 1 << 6
|
|
17
|
+
}
|
|
5
18
|
|
|
6
19
|
class UpdatePayloadPool {
|
|
7
20
|
constructor() {
|
|
8
|
-
this.pool =
|
|
21
|
+
this.pool = new Array(POOL_SIZE)
|
|
22
|
+
this.size = 0
|
|
23
|
+
|
|
24
|
+
for (let i = 0; i < POOL_SIZE; i++) {
|
|
25
|
+
this.pool[i] = this._createPayload()
|
|
26
|
+
}
|
|
27
|
+
this.size = POOL_SIZE
|
|
9
28
|
}
|
|
10
29
|
|
|
11
|
-
|
|
12
|
-
return
|
|
30
|
+
_createPayload() {
|
|
31
|
+
return {
|
|
13
32
|
guildId: null,
|
|
14
33
|
data: {
|
|
15
34
|
voice: {
|
|
16
35
|
token: null,
|
|
17
36
|
endpoint: null,
|
|
18
|
-
sessionId: null
|
|
37
|
+
sessionId: null,
|
|
38
|
+
resume: undefined,
|
|
39
|
+
sequence: undefined
|
|
19
40
|
},
|
|
20
41
|
volume: null
|
|
21
42
|
}
|
|
22
43
|
}
|
|
23
44
|
}
|
|
24
45
|
|
|
46
|
+
acquire() {
|
|
47
|
+
if (this.size > 0) {
|
|
48
|
+
return this.pool[--this.size]
|
|
49
|
+
}
|
|
50
|
+
return this._createPayload()
|
|
51
|
+
}
|
|
52
|
+
|
|
25
53
|
release(payload) {
|
|
54
|
+
if (!payload || this.size >= POOL_SIZE) return
|
|
55
|
+
|
|
26
56
|
payload.guildId = null
|
|
27
|
-
payload.data.voice
|
|
28
|
-
|
|
29
|
-
|
|
57
|
+
const voice = payload.data.voice
|
|
58
|
+
voice.token = null
|
|
59
|
+
voice.endpoint = null
|
|
60
|
+
voice.sessionId = null
|
|
61
|
+
voice.resume = undefined
|
|
62
|
+
voice.sequence = undefined
|
|
30
63
|
payload.data.volume = null
|
|
31
|
-
delete payload.data.voice.resume
|
|
32
|
-
delete payload.data.voice.sequence
|
|
33
64
|
|
|
34
|
-
|
|
35
|
-
this.pool.push(payload)
|
|
36
|
-
}
|
|
65
|
+
this.pool[this.size++] = payload
|
|
37
66
|
}
|
|
38
67
|
}
|
|
39
68
|
|
|
69
|
+
const sharedPayloadPool = new UpdatePayloadPool()
|
|
70
|
+
|
|
40
71
|
class Connection {
|
|
41
72
|
constructor(player) {
|
|
42
|
-
|
|
43
|
-
if (!player
|
|
44
|
-
|
|
73
|
+
// Validate once
|
|
74
|
+
if (!player?.aqua?.clientId || !player.nodes) {
|
|
75
|
+
throw new TypeError('Invalid player configuration')
|
|
76
|
+
}
|
|
45
77
|
|
|
46
78
|
this._player = player
|
|
47
79
|
this._aqua = player.aqua
|
|
@@ -49,62 +81,85 @@ class Connection {
|
|
|
49
81
|
this._guildId = player.guildId
|
|
50
82
|
this._clientId = player.aqua.clientId
|
|
51
83
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Object.assign(this, state)
|
|
64
|
-
|
|
65
|
-
this._hasDebugListeners = false
|
|
66
|
-
this._hasMoveListeners = false
|
|
84
|
+
this.voiceChannel = player.voiceChannel
|
|
85
|
+
this.sessionId = null
|
|
86
|
+
this.endpoint = null
|
|
87
|
+
this.token = null
|
|
88
|
+
this.region = null
|
|
89
|
+
this.sequence = 0
|
|
90
|
+
this._lastEndpoint = null
|
|
91
|
+
this._pendingUpdate = null
|
|
92
|
+
|
|
93
|
+
this._stateFlags = 0
|
|
67
94
|
this._lastListenerCheck = 0
|
|
68
|
-
this._listenerCheckInterval = 5000
|
|
69
95
|
|
|
70
|
-
this._payloadPool =
|
|
96
|
+
this._payloadPool = sharedPayloadPool
|
|
97
|
+
|
|
98
|
+
this._executeVoiceUpdate = this._executeVoiceUpdate.bind(this)
|
|
71
99
|
|
|
72
100
|
this._checkListeners()
|
|
73
101
|
}
|
|
74
102
|
|
|
75
103
|
_checkListeners() {
|
|
76
104
|
const now = Date.now()
|
|
77
|
-
if (now - this._lastListenerCheck <
|
|
105
|
+
if (now - this._lastListenerCheck < LISTENER_CHECK_INTERVAL) {
|
|
78
106
|
return
|
|
79
107
|
}
|
|
80
108
|
|
|
81
|
-
|
|
82
|
-
|
|
109
|
+
let flags = this._stateFlags
|
|
110
|
+
flags = this._aqua.listenerCount('debug') > 0
|
|
111
|
+
? flags | STATE_FLAGS.HAS_DEBUG_LISTENERS
|
|
112
|
+
: flags & ~STATE_FLAGS.HAS_DEBUG_LISTENERS
|
|
113
|
+
|
|
114
|
+
flags = this._aqua.listenerCount('playerMove') > 0
|
|
115
|
+
? flags | STATE_FLAGS.HAS_MOVE_LISTENERS
|
|
116
|
+
: flags & ~STATE_FLAGS.HAS_MOVE_LISTENERS
|
|
117
|
+
|
|
118
|
+
this._stateFlags = flags
|
|
83
119
|
this._lastListenerCheck = now
|
|
84
120
|
}
|
|
85
121
|
|
|
86
122
|
_extractRegion(endpoint) {
|
|
87
123
|
if (!endpoint || typeof endpoint !== 'string') return null
|
|
88
124
|
|
|
89
|
-
const
|
|
90
|
-
|
|
125
|
+
const dashIndex = endpoint.indexOf('-')
|
|
126
|
+
if (dashIndex > 0) {
|
|
127
|
+
const region = endpoint.substring(0, dashIndex)
|
|
128
|
+
let isValid = true
|
|
129
|
+
for (let i = 0; i < region.length; i++) {
|
|
130
|
+
const code = region.charCodeAt(i)
|
|
131
|
+
if (!((code >= 65 && code <= 90) || (code >= 97 && code <= 122))) {
|
|
132
|
+
isValid = false
|
|
133
|
+
break
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (isValid) return region
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const match = ENDPOINT_PATTERN.exec(endpoint)
|
|
140
|
+
return match?.[1] || null
|
|
91
141
|
}
|
|
92
142
|
|
|
93
143
|
setServerUpdate(data) {
|
|
94
|
-
if (!data ||
|
|
95
|
-
|
|
96
|
-
|
|
144
|
+
if (!data?.endpoint || !data.token ||
|
|
145
|
+
typeof data.endpoint !== 'string' ||
|
|
146
|
+
typeof data.token !== 'string') {
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const trimmedEndpoint = data.endpoint.trim()
|
|
97
151
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
152
|
+
if (this._lastEndpoint === trimmedEndpoint && this.token === data.token) {
|
|
153
|
+
return
|
|
154
|
+
}
|
|
101
155
|
|
|
102
156
|
const newRegion = this._extractRegion(trimmedEndpoint)
|
|
157
|
+
|
|
103
158
|
const hasRegionChange = this.region !== newRegion
|
|
104
159
|
const hasEndpointChange = this._lastEndpoint !== trimmedEndpoint
|
|
105
160
|
|
|
106
161
|
if (hasRegionChange || hasEndpointChange) {
|
|
107
|
-
if (hasRegionChange && this.
|
|
162
|
+
if (hasRegionChange && (this._stateFlags & STATE_FLAGS.HAS_DEBUG_LISTENERS)) {
|
|
108
163
|
this._aqua.emit('debug', `[Player ${this._guildId}] Region: ${this.region || 'none'} → ${newRegion}`)
|
|
109
164
|
}
|
|
110
165
|
|
|
@@ -127,7 +182,7 @@ class Connection {
|
|
|
127
182
|
}
|
|
128
183
|
|
|
129
184
|
resendVoiceUpdate({ resume = false } = {}) {
|
|
130
|
-
if (!this.sessionId
|
|
185
|
+
if (!(this.sessionId && this.endpoint && this.token)) {
|
|
131
186
|
return false
|
|
132
187
|
}
|
|
133
188
|
|
|
@@ -136,44 +191,46 @@ class Connection {
|
|
|
136
191
|
}
|
|
137
192
|
|
|
138
193
|
setStateUpdate(data) {
|
|
139
|
-
if (!data ||
|
|
140
|
-
typeof data !== 'object' ||
|
|
141
|
-
data.user_id !== this._clientId) {
|
|
194
|
+
if (!data || data.user_id !== this._clientId) {
|
|
142
195
|
return
|
|
143
196
|
}
|
|
144
197
|
|
|
145
198
|
const { session_id, channel_id, self_deaf, self_mute } = data
|
|
146
199
|
|
|
147
200
|
if (channel_id) {
|
|
201
|
+
let needsUpdate = false
|
|
202
|
+
|
|
148
203
|
if (this.voiceChannel !== channel_id) {
|
|
149
|
-
if (this.
|
|
204
|
+
if (this._stateFlags & STATE_FLAGS.HAS_MOVE_LISTENERS) {
|
|
150
205
|
this._aqua.emit('playerMove', this.voiceChannel, channel_id)
|
|
151
206
|
}
|
|
152
207
|
this.voiceChannel = channel_id
|
|
153
208
|
this._player.voiceChannel = channel_id
|
|
209
|
+
needsUpdate = true
|
|
154
210
|
}
|
|
155
211
|
|
|
156
212
|
if (this.sessionId !== session_id) {
|
|
157
213
|
this.sessionId = session_id
|
|
214
|
+
needsUpdate = true
|
|
158
215
|
}
|
|
159
216
|
|
|
160
|
-
const playerUpdates = Object.create(null)
|
|
161
|
-
playerUpdates.self_deaf = !!self_deaf
|
|
162
|
-
playerUpdates.self_mute = !!self_mute
|
|
163
|
-
playerUpdates.connected = true
|
|
164
217
|
|
|
165
|
-
|
|
218
|
+
this._player.self_deaf = !!self_deaf
|
|
219
|
+
this._player.self_mute = !!self_mute
|
|
220
|
+
this._player.connected = true
|
|
166
221
|
|
|
167
|
-
|
|
222
|
+
if (needsUpdate) {
|
|
223
|
+
this._scheduleVoiceUpdate()
|
|
224
|
+
}
|
|
168
225
|
} else {
|
|
169
226
|
this._handleDisconnect()
|
|
170
227
|
}
|
|
171
228
|
}
|
|
172
229
|
|
|
173
230
|
_handleDisconnect() {
|
|
174
|
-
if (!this._player
|
|
231
|
+
if (!this._player?.connected) return
|
|
175
232
|
|
|
176
|
-
if (this.
|
|
233
|
+
if (this._stateFlags & STATE_FLAGS.HAS_DEBUG_LISTENERS) {
|
|
177
234
|
this._aqua.emit('debug', `[Player ${this._guildId}] Disconnected`)
|
|
178
235
|
}
|
|
179
236
|
|
|
@@ -190,21 +247,47 @@ class Connection {
|
|
|
190
247
|
}
|
|
191
248
|
}
|
|
192
249
|
|
|
193
|
-
|
|
194
|
-
if (
|
|
195
|
-
|
|
250
|
+
async attemptResume() {
|
|
251
|
+
if (!(this.sessionId && this.endpoint && this.token)) {
|
|
252
|
+
throw new Error('Missing required voice state')
|
|
196
253
|
}
|
|
197
254
|
|
|
198
|
-
|
|
255
|
+
const payload = this._payloadPool.acquire()
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
payload.guildId = this._guildId
|
|
259
|
+
payload.data.voice.token = this.token
|
|
260
|
+
payload.data.voice.endpoint = this.endpoint
|
|
261
|
+
payload.data.voice.sessionId = this.sessionId
|
|
262
|
+
payload.data.voice.resume = true
|
|
263
|
+
payload.data.volume = this._player?.volume
|
|
264
|
+
|
|
265
|
+
if (this.sequence >= 0 && Number.isFinite(this.sequence)) {
|
|
266
|
+
payload.data.voice.sequence = this.sequence
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
await this._sendUpdate(payload)
|
|
270
|
+
return true
|
|
271
|
+
} catch (error) {
|
|
272
|
+
if (this._stateFlags & STATE_FLAGS.HAS_DEBUG_LISTENERS) {
|
|
273
|
+
this._aqua.emit('debug', `[Player ${this._guildId}] Resume update failed: ${error?.message}`)
|
|
274
|
+
}
|
|
275
|
+
return false
|
|
276
|
+
} finally {
|
|
277
|
+
this._payloadPool.release(payload)
|
|
278
|
+
}
|
|
199
279
|
}
|
|
200
280
|
|
|
201
|
-
|
|
202
|
-
if (
|
|
203
|
-
|
|
204
|
-
this._updateTimer = null
|
|
281
|
+
updateSequence(seq) {
|
|
282
|
+
if (typeof seq === 'number' && seq >= 0 && Number.isFinite(seq)) {
|
|
283
|
+
this.sequence = Math.max(seq, this.sequence)
|
|
205
284
|
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
_clearPendingUpdate() {
|
|
288
|
+
this._stateFlags &= ~STATE_FLAGS.UPDATE_SCHEDULED
|
|
206
289
|
|
|
207
|
-
if (this._pendingUpdate
|
|
290
|
+
if (this._pendingUpdate?.payload) {
|
|
208
291
|
this._payloadPool.release(this._pendingUpdate.payload)
|
|
209
292
|
}
|
|
210
293
|
|
|
@@ -212,22 +295,28 @@ class Connection {
|
|
|
212
295
|
}
|
|
213
296
|
|
|
214
297
|
_scheduleVoiceUpdate(isResume = false) {
|
|
215
|
-
if (!this.sessionId
|
|
298
|
+
if (!(this.sessionId && this.endpoint && this.token)) {
|
|
299
|
+
return
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (this._stateFlags & STATE_FLAGS.UPDATE_SCHEDULED) {
|
|
216
303
|
return
|
|
217
304
|
}
|
|
218
305
|
|
|
219
306
|
this._clearPendingUpdate()
|
|
220
307
|
|
|
221
308
|
const payload = this._payloadPool.acquire()
|
|
309
|
+
|
|
222
310
|
payload.guildId = this._guildId
|
|
223
|
-
payload.data.voice
|
|
224
|
-
|
|
225
|
-
|
|
311
|
+
const voice = payload.data.voice
|
|
312
|
+
voice.token = this.token
|
|
313
|
+
voice.endpoint = this.endpoint
|
|
314
|
+
voice.sessionId = this.sessionId
|
|
226
315
|
payload.data.volume = this._player.volume
|
|
227
316
|
|
|
228
317
|
if (isResume) {
|
|
229
|
-
|
|
230
|
-
|
|
318
|
+
voice.resume = true
|
|
319
|
+
voice.sequence = this.sequence
|
|
231
320
|
}
|
|
232
321
|
|
|
233
322
|
this._pendingUpdate = {
|
|
@@ -236,17 +325,18 @@ class Connection {
|
|
|
236
325
|
timestamp: Date.now()
|
|
237
326
|
}
|
|
238
327
|
|
|
239
|
-
this.
|
|
328
|
+
this._stateFlags |= STATE_FLAGS.UPDATE_SCHEDULED
|
|
329
|
+
|
|
330
|
+
queueMicrotask(this._executeVoiceUpdate)
|
|
240
331
|
}
|
|
241
332
|
|
|
242
333
|
_executeVoiceUpdate() {
|
|
334
|
+
this._stateFlags &= ~STATE_FLAGS.UPDATE_SCHEDULED
|
|
335
|
+
|
|
243
336
|
const pending = this._pendingUpdate
|
|
244
337
|
if (!pending) return
|
|
245
338
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const age = Date.now() - pending.timestamp
|
|
249
|
-
if (age > 5000) {
|
|
339
|
+
if (Date.now() - pending.timestamp > UPDATE_TIMEOUT) {
|
|
250
340
|
this._payloadPool.release(pending.payload)
|
|
251
341
|
this._pendingUpdate = null
|
|
252
342
|
return
|
|
@@ -255,6 +345,12 @@ class Connection {
|
|
|
255
345
|
const payload = pending.payload
|
|
256
346
|
this._pendingUpdate = null
|
|
257
347
|
|
|
348
|
+
// to avoid any delay. Uncomment if needed:
|
|
349
|
+
// if (pending.isResume) {
|
|
350
|
+
// this._sendUpdateSync(payload)
|
|
351
|
+
// return
|
|
352
|
+
// }
|
|
353
|
+
|
|
258
354
|
this._sendUpdate(payload)
|
|
259
355
|
.finally(() => {
|
|
260
356
|
this._payloadPool.release(payload)
|
|
@@ -262,7 +358,7 @@ class Connection {
|
|
|
262
358
|
}
|
|
263
359
|
|
|
264
360
|
async _sendUpdate(payload) {
|
|
265
|
-
if (!this._nodes
|
|
361
|
+
if (!this._nodes?.rest) {
|
|
266
362
|
throw new Error('Nodes or REST interface not available')
|
|
267
363
|
}
|
|
268
364
|
|
|
@@ -271,10 +367,9 @@ class Connection {
|
|
|
271
367
|
} catch (error) {
|
|
272
368
|
if (error.code !== 'ECONNREFUSED' &&
|
|
273
369
|
error.code !== 'ENOTFOUND' &&
|
|
274
|
-
this.
|
|
370
|
+
(this._stateFlags & STATE_FLAGS.HAS_DEBUG_LISTENERS)) {
|
|
275
371
|
this._aqua.emit('debug', `[Player ${this._guildId}] Update failed: ${error.message}`)
|
|
276
372
|
}
|
|
277
|
-
|
|
278
373
|
throw error
|
|
279
374
|
}
|
|
280
375
|
}
|
|
@@ -293,6 +388,7 @@ class Connection {
|
|
|
293
388
|
this.token = null
|
|
294
389
|
this.region = null
|
|
295
390
|
this._lastEndpoint = null
|
|
391
|
+
this._stateFlags = 0
|
|
296
392
|
}
|
|
297
393
|
}
|
|
298
394
|
|