aqualink 2.9.13 → 2.10.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/README.md +94 -58
- package/build/handlers/autoplay.js +39 -44
- package/build/structures/Aqua.js +483 -493
- package/build/structures/Connection.js +91 -88
- package/build/structures/Filters.js +178 -167
- package/build/structures/Node.js +99 -96
- package/build/structures/Player.js +275 -296
- package/build/structures/Rest.js +265 -146
- package/build/structures/Track.js +51 -126
- package/package.json +17 -2
package/build/structures/Node.js
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
const WebSocket = require('ws')
|
|
4
4
|
const Rest = require('./Rest')
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const JSON_START_REGEX = /^\s*[{[]/
|
|
6
|
+
const JSON_START_CHAR = /^[\s{[]/
|
|
8
7
|
const WS_READY_STATES = Object.freeze({ OPEN: 1, CLOSED: 3 })
|
|
9
8
|
|
|
10
9
|
class Node {
|
|
@@ -16,6 +15,16 @@ class Node {
|
|
|
16
15
|
static JITTER_FACTOR = 0.2
|
|
17
16
|
static WS_CLOSE_NORMAL = 1000
|
|
18
17
|
|
|
18
|
+
stats = {
|
|
19
|
+
players: 0,
|
|
20
|
+
playingPlayers: 0,
|
|
21
|
+
uptime: 0,
|
|
22
|
+
memory: { free: 0, used: 0, allocated: 0, reservable: 0 },
|
|
23
|
+
cpu: { cores: 0, systemLoad: 0, lavalinkLoad: 0 },
|
|
24
|
+
frameStats: { sent: 0, nulled: 0, deficit: 0 },
|
|
25
|
+
ping: 0
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
constructor(aqua, connOptions, options = {}) {
|
|
20
29
|
this.aqua = aqua
|
|
21
30
|
|
|
@@ -35,7 +44,7 @@ class Node {
|
|
|
35
44
|
this.password = password
|
|
36
45
|
this.sessionId = sessionId
|
|
37
46
|
this.regions = regions
|
|
38
|
-
this.secure =
|
|
47
|
+
this.secure = !!secure
|
|
39
48
|
this.wsUrl = `ws${secure ? 's' : ''}://${host}:${port}/v4/websocket`
|
|
40
49
|
|
|
41
50
|
this.rest = new Rest(aqua, this)
|
|
@@ -61,52 +70,15 @@ class Node {
|
|
|
61
70
|
this.reconnectTimeoutId = null
|
|
62
71
|
this.isDestroyed = false
|
|
63
72
|
|
|
64
|
-
this._boundHandlers = this._createBoundHandlers()
|
|
65
73
|
this._headers = this._buildHeaders()
|
|
66
|
-
this._initStats()
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
_createBoundHandlers() {
|
|
70
|
-
return {
|
|
71
|
-
onOpen: () => this._handleOpen(),
|
|
72
|
-
onError: error => this._handleError(error),
|
|
73
|
-
onMessage: msg => this._handleMessage(msg),
|
|
74
|
-
onClose: (code, reason) => this._handleClose(code, reason)
|
|
75
|
-
}
|
|
76
74
|
}
|
|
77
75
|
|
|
78
|
-
|
|
79
|
-
this.stats = {
|
|
80
|
-
players: 0,
|
|
81
|
-
playingPlayers: 0,
|
|
82
|
-
uptime: 0,
|
|
83
|
-
memory: { free: 0, used: 0, allocated: 0, reservable: 0, freePercentage: 0, usedPercentage: 0 },
|
|
84
|
-
cpu: { cores: 0, systemLoad: 0, lavalinkLoad: 0, lavalinkLoadPercentage: 0 },
|
|
85
|
-
frameStats: { sent: 0, nulled: 0, deficit: 0 },
|
|
86
|
-
ping: 0
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
_buildHeaders() {
|
|
91
|
-
const headers = {
|
|
92
|
-
'Authorization': this.password,
|
|
93
|
-
'User-Id': this.aqua.clientId,
|
|
94
|
-
'Client-Name': `Aqua/${this.aqua.version} (https://github.com/ToddyTheNoobDud/AquaLink)`
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (this.sessionId) {
|
|
98
|
-
headers['Session-Id'] = this.sessionId
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return headers
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async _handleOpen() {
|
|
76
|
+
_handleOpen = async () => {
|
|
105
77
|
this.connected = true
|
|
106
78
|
this.reconnectAttempted = 0
|
|
107
79
|
this._emitDebug('WebSocket connection established')
|
|
108
80
|
|
|
109
|
-
if (this.aqua.bypassChecks?.nodeFetchInfo) return
|
|
81
|
+
if (this.aqua.bypassChecks?.nodeFetchInfo) return
|
|
110
82
|
|
|
111
83
|
try {
|
|
112
84
|
this.info = await this.rest.makeRequest('GET', '/v4/info')
|
|
@@ -121,14 +93,14 @@ class Node {
|
|
|
121
93
|
}
|
|
122
94
|
}
|
|
123
95
|
|
|
124
|
-
_handleError(error) {
|
|
96
|
+
_handleError = (error) => {
|
|
125
97
|
this.aqua.emit('nodeError', this, error)
|
|
126
98
|
}
|
|
127
99
|
|
|
128
|
-
_handleMessage(msg) {
|
|
129
|
-
if (!
|
|
100
|
+
_handleMessage = (msg) => {
|
|
101
|
+
if (!JSON_START_CHAR.test(msg)) {
|
|
130
102
|
this._emitDebug(`Invalid JSON format: ${msg.slice(0, 100)}...`)
|
|
131
|
-
return
|
|
103
|
+
return
|
|
132
104
|
}
|
|
133
105
|
|
|
134
106
|
let payload
|
|
@@ -136,29 +108,55 @@ class Node {
|
|
|
136
108
|
payload = JSON.parse(msg)
|
|
137
109
|
} catch {
|
|
138
110
|
this._emitDebug(`JSON parse failed: ${msg.slice(0, 100)}...`)
|
|
139
|
-
return
|
|
111
|
+
return
|
|
140
112
|
}
|
|
141
113
|
|
|
114
|
+
|
|
142
115
|
const { op, guildId } = payload
|
|
143
|
-
if (!op) return
|
|
116
|
+
if (!op) return
|
|
144
117
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
118
|
+
if (typeof op === 'number') {
|
|
119
|
+
this._handleNumericOp(op, guildId, payload);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (op === 'stats') {
|
|
124
|
+
this._updateStats(payload)
|
|
125
|
+
} else if (op === 'ready') {
|
|
126
|
+
this._handleReady(payload)
|
|
127
|
+
} else {
|
|
128
|
+
this._handleCustomOp(op, guildId, payload)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
_handleClose = (code, reason) => {
|
|
133
|
+
this.connected = false
|
|
134
|
+
const reasonStr = reason?.toString() || 'No reason provided'
|
|
135
|
+
|
|
136
|
+
this.aqua.emit('nodeDisconnect', this, { code, reason: reasonStr })
|
|
137
|
+
this.aqua.handleNodeFailover(this)
|
|
138
|
+
this._scheduleReconnect(code)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
_buildHeaders() {
|
|
142
|
+
const headers = {
|
|
143
|
+
Authorization: this.password,
|
|
144
|
+
'User-Id': this.aqua.clientId,
|
|
145
|
+
'Client-Name': `Aqua/${this.aqua.version} (https://github.com/ToddyTheNoobDud/AquaLink)`
|
|
154
146
|
}
|
|
147
|
+
|
|
148
|
+
if (this.sessionId) {
|
|
149
|
+
headers['Session-Id'] = this.sessionId
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return headers
|
|
155
153
|
}
|
|
156
154
|
|
|
157
155
|
_handleCustomOp(op, guildId, payload) {
|
|
158
|
-
if (
|
|
156
|
+
if (op.startsWith('Lyrics')) {
|
|
159
157
|
const player = guildId ? this.aqua.players.get(guildId) : null
|
|
160
158
|
this.aqua.emit(op, player, payload.track || null, payload)
|
|
161
|
-
return
|
|
159
|
+
return
|
|
162
160
|
}
|
|
163
161
|
|
|
164
162
|
if (guildId) {
|
|
@@ -167,13 +165,28 @@ class Node {
|
|
|
167
165
|
}
|
|
168
166
|
}
|
|
169
167
|
|
|
170
|
-
|
|
171
|
-
this.
|
|
172
|
-
const reasonStr = reason?.toString() || 'No reason provided'
|
|
168
|
+
_handleNumericOp(op, guildId, payload) {
|
|
169
|
+
const player = guildId ? this.aqua.players.get(guildId) : null;
|
|
173
170
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
171
|
+
switch (op) {
|
|
172
|
+
case 2:
|
|
173
|
+
if (player?.connection) {
|
|
174
|
+
player.connection.setServerUpdate(payload.d);
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
case 5:
|
|
178
|
+
if (player?.connection) {
|
|
179
|
+
player.connection.updateSequence(payload.d.sequence);
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case 9:
|
|
184
|
+
this.aqua.emit('debug', `[Player ${guildId}] Voice resumed successfully`);
|
|
185
|
+
break;
|
|
186
|
+
|
|
187
|
+
default:
|
|
188
|
+
this.aqua.emit('debug', `Unknown numeric op ${op} for guild ${guildId}`);
|
|
189
|
+
}
|
|
177
190
|
}
|
|
178
191
|
|
|
179
192
|
_scheduleReconnect(code) {
|
|
@@ -181,19 +194,19 @@ class Node {
|
|
|
181
194
|
|
|
182
195
|
if (code === Node.WS_CLOSE_NORMAL || this.isDestroyed) {
|
|
183
196
|
this._emitDebug('WebSocket closed normally, not reconnecting')
|
|
184
|
-
return
|
|
197
|
+
return
|
|
185
198
|
}
|
|
186
199
|
|
|
187
200
|
if (this.infiniteReconnects) {
|
|
188
201
|
this.aqua.emit('nodeReconnect', this, 'Infinite reconnects enabled, trying again in 10 seconds')
|
|
189
202
|
this.reconnectTimeoutId = setTimeout(() => this.connect(), 10000)
|
|
190
|
-
return
|
|
203
|
+
return
|
|
191
204
|
}
|
|
192
205
|
|
|
193
206
|
if (this.reconnectAttempted >= this.reconnectTries) {
|
|
194
207
|
this._emitError(new Error(`Max reconnection attempts reached (${this.reconnectTries})`))
|
|
195
208
|
this.destroy(true)
|
|
196
|
-
return
|
|
209
|
+
return
|
|
197
210
|
}
|
|
198
211
|
|
|
199
212
|
const backoffTime = this._calcBackoff()
|
|
@@ -221,12 +234,12 @@ class Node {
|
|
|
221
234
|
}
|
|
222
235
|
}
|
|
223
236
|
|
|
224
|
-
|
|
225
|
-
if (this.isDestroyed) return
|
|
237
|
+
connect() {
|
|
238
|
+
if (this.isDestroyed) return
|
|
226
239
|
|
|
227
240
|
if (this.ws?.readyState === WS_READY_STATES.OPEN) {
|
|
228
241
|
this._emitDebug('WebSocket already connected')
|
|
229
|
-
return
|
|
242
|
+
return
|
|
230
243
|
}
|
|
231
244
|
|
|
232
245
|
this._cleanup()
|
|
@@ -236,15 +249,14 @@ class Node {
|
|
|
236
249
|
perMessageDeflate: false
|
|
237
250
|
})
|
|
238
251
|
|
|
239
|
-
|
|
240
|
-
this.ws.once('
|
|
241
|
-
this.ws.
|
|
242
|
-
this.ws.
|
|
243
|
-
this.ws.once('close', handlers.onClose)
|
|
252
|
+
this.ws.once('open', this._handleOpen)
|
|
253
|
+
this.ws.once('error', this._handleError)
|
|
254
|
+
this.ws.on('message', this._handleMessage)
|
|
255
|
+
this.ws.once('close', this._handleClose)
|
|
244
256
|
}
|
|
245
257
|
|
|
246
258
|
_cleanup() {
|
|
247
|
-
if (!this.ws) return
|
|
259
|
+
if (!this.ws) return
|
|
248
260
|
|
|
249
261
|
this.ws.removeAllListeners()
|
|
250
262
|
|
|
@@ -275,13 +287,13 @@ class Node {
|
|
|
275
287
|
}
|
|
276
288
|
|
|
277
289
|
async getStats() {
|
|
278
|
-
if (this.connected
|
|
290
|
+
if (this.connected) {
|
|
279
291
|
return this.stats
|
|
280
292
|
}
|
|
281
293
|
|
|
282
294
|
try {
|
|
283
295
|
const newStats = await this.rest.getStats()
|
|
284
|
-
if (newStats
|
|
296
|
+
if (newStats) {
|
|
285
297
|
this._mergeStats(newStats)
|
|
286
298
|
}
|
|
287
299
|
return this.stats
|
|
@@ -313,26 +325,16 @@ class Node {
|
|
|
313
325
|
}
|
|
314
326
|
|
|
315
327
|
_updateStats(payload) {
|
|
316
|
-
if (!payload) return
|
|
328
|
+
if (!payload) return
|
|
317
329
|
|
|
318
330
|
this.stats.players = payload.players
|
|
319
331
|
this.stats.playingPlayers = payload.playingPlayers
|
|
320
332
|
this.stats.uptime = payload.uptime
|
|
321
333
|
this.stats.ping = payload.ping
|
|
322
334
|
|
|
323
|
-
if (payload.memory)
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (payload.cpu) {
|
|
329
|
-
Object.assign(this.stats.cpu, payload.cpu)
|
|
330
|
-
this._calcCpuPercentages()
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if (payload.frameStats) {
|
|
334
|
-
Object.assign(this.stats.frameStats, payload.frameStats)
|
|
335
|
-
}
|
|
335
|
+
if (payload.memory) Object.assign(this.stats.memory, payload.memory)
|
|
336
|
+
if (payload.cpu) Object.assign(this.stats.cpu, payload.cpu)
|
|
337
|
+
if (payload.frameStats) Object.assign(this.stats.frameStats, payload.frameStats)
|
|
336
338
|
}
|
|
337
339
|
|
|
338
340
|
_calcMemoryPercentages() {
|
|
@@ -354,12 +356,13 @@ class Node {
|
|
|
354
356
|
_handleReady(payload) {
|
|
355
357
|
if (!payload.sessionId) {
|
|
356
358
|
this._emitError('Ready payload missing sessionId')
|
|
357
|
-
return
|
|
359
|
+
return
|
|
358
360
|
}
|
|
359
361
|
|
|
360
362
|
this.sessionId = payload.sessionId
|
|
361
363
|
this.rest.setSessionId(payload.sessionId)
|
|
362
|
-
this._headers =
|
|
364
|
+
this._headers['Session-Id'] = payload.sessionId
|
|
365
|
+
|
|
363
366
|
this.aqua.emit('nodeConnect', this)
|
|
364
367
|
}
|
|
365
368
|
|
|
@@ -367,7 +370,7 @@ class Node {
|
|
|
367
370
|
try {
|
|
368
371
|
await this.rest.makeRequest('PATCH', `/v4/sessions/${this.sessionId}`, {
|
|
369
372
|
resuming: true,
|
|
370
|
-
timeout:
|
|
373
|
+
timeout: this.resumeTimeout
|
|
371
374
|
})
|
|
372
375
|
await this.aqua.loadPlayers()
|
|
373
376
|
this._emitDebug('Session resumed successfully')
|