@discordjs/voice 0.5.6 → 0.7.2

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 (56) hide show
  1. package/LICENSE +190 -21
  2. package/README.md +41 -18
  3. package/dist/index.d.ts +356 -215
  4. package/dist/index.js +10 -26
  5. package/dist/index.js.map +7 -1
  6. package/dist/index.mjs +10 -0
  7. package/dist/index.mjs.map +7 -0
  8. package/package.json +67 -93
  9. package/dist/DataStore.js +0 -151
  10. package/dist/DataStore.js.map +0 -1
  11. package/dist/VoiceConnection.js +0 -494
  12. package/dist/VoiceConnection.js.map +0 -1
  13. package/dist/audio/AudioPlayer.js +0 -449
  14. package/dist/audio/AudioPlayer.js.map +0 -1
  15. package/dist/audio/AudioPlayerError.js +0 -17
  16. package/dist/audio/AudioPlayerError.js.map +0 -1
  17. package/dist/audio/AudioResource.js +0 -164
  18. package/dist/audio/AudioResource.js.map +0 -1
  19. package/dist/audio/PlayerSubscription.js +0 -23
  20. package/dist/audio/PlayerSubscription.js.map +0 -1
  21. package/dist/audio/TransformerGraph.js +0 -233
  22. package/dist/audio/TransformerGraph.js.map +0 -1
  23. package/dist/audio/index.js +0 -18
  24. package/dist/audio/index.js.map +0 -1
  25. package/dist/joinVoiceChannel.js +0 -24
  26. package/dist/joinVoiceChannel.js.map +0 -1
  27. package/dist/networking/Networking.js +0 -457
  28. package/dist/networking/Networking.js.map +0 -1
  29. package/dist/networking/VoiceUDPSocket.js +0 -145
  30. package/dist/networking/VoiceUDPSocket.js.map +0 -1
  31. package/dist/networking/VoiceWebSocket.js +0 -129
  32. package/dist/networking/VoiceWebSocket.js.map +0 -1
  33. package/dist/networking/index.js +0 -16
  34. package/dist/networking/index.js.map +0 -1
  35. package/dist/receive/AudioReceiveStream.js +0 -20
  36. package/dist/receive/AudioReceiveStream.js.map +0 -1
  37. package/dist/receive/SSRCMap.js +0 -67
  38. package/dist/receive/SSRCMap.js.map +0 -1
  39. package/dist/receive/VoiceReceiver.js +0 -215
  40. package/dist/receive/VoiceReceiver.js.map +0 -1
  41. package/dist/receive/index.js +0 -16
  42. package/dist/receive/index.js.map +0 -1
  43. package/dist/util/Secretbox.js +0 -50
  44. package/dist/util/Secretbox.js.map +0 -1
  45. package/dist/util/adapter.js +0 -3
  46. package/dist/util/adapter.js.map +0 -1
  47. package/dist/util/demuxProbe.js +0 -90
  48. package/dist/util/demuxProbe.js.map +0 -1
  49. package/dist/util/entersState.js +0 -30
  50. package/dist/util/entersState.js.map +0 -1
  51. package/dist/util/generateDependencyReport.js +0 -82
  52. package/dist/util/generateDependencyReport.js.map +0 -1
  53. package/dist/util/index.js +0 -17
  54. package/dist/util/index.js.map +0 -1
  55. package/dist/util/util.js +0 -7
  56. package/dist/util/util.js.map +0 -1
@@ -1,457 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
- Object.defineProperty(o, "default", { enumerable: true, value: v });
11
- }) : function(o, v) {
12
- o["default"] = v;
13
- });
14
- var __importStar = (this && this.__importStar) || function (mod) {
15
- if (mod && mod.__esModule) return mod;
16
- var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
- __setModuleDefault(result, mod);
19
- return result;
20
- };
21
- Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.Networking = exports.NetworkingStatusCode = exports.SUPPORTED_ENCRYPTION_MODES = void 0;
23
- const VoiceUDPSocket_1 = require("./VoiceUDPSocket");
24
- const VoiceWebSocket_1 = require("./VoiceWebSocket");
25
- const secretbox = __importStar(require("../util/Secretbox"));
26
- const util_1 = require("../util/util");
27
- const tiny_typed_emitter_1 = require("tiny-typed-emitter");
28
- // The number of audio channels required by Discord
29
- const CHANNELS = 2;
30
- const TIMESTAMP_INC = (48000 / 100) * CHANNELS;
31
- const MAX_NONCE_SIZE = 2 ** 32 - 1;
32
- exports.SUPPORTED_ENCRYPTION_MODES = ['xsalsa20_poly1305_lite', 'xsalsa20_poly1305_suffix', 'xsalsa20_poly1305'];
33
- /**
34
- * The different statuses that a networking instance can hold. The order
35
- * of the states between OpeningWs and Ready is chronological (first the
36
- * instance enters OpeningWs, then it enters Identifying etc.)
37
- */
38
- var NetworkingStatusCode;
39
- (function (NetworkingStatusCode) {
40
- NetworkingStatusCode[NetworkingStatusCode["OpeningWs"] = 0] = "OpeningWs";
41
- NetworkingStatusCode[NetworkingStatusCode["Identifying"] = 1] = "Identifying";
42
- NetworkingStatusCode[NetworkingStatusCode["UdpHandshaking"] = 2] = "UdpHandshaking";
43
- NetworkingStatusCode[NetworkingStatusCode["SelectingProtocol"] = 3] = "SelectingProtocol";
44
- NetworkingStatusCode[NetworkingStatusCode["Ready"] = 4] = "Ready";
45
- NetworkingStatusCode[NetworkingStatusCode["Resuming"] = 5] = "Resuming";
46
- NetworkingStatusCode[NetworkingStatusCode["Closed"] = 6] = "Closed";
47
- })(NetworkingStatusCode = exports.NetworkingStatusCode || (exports.NetworkingStatusCode = {}));
48
- /**
49
- * An empty buffer that is reused in packet encryption by many different networking instances.
50
- */
51
- const nonce = Buffer.alloc(24);
52
- /**
53
- * Manages the networking required to maintain a voice connection and dispatch audio packets
54
- */
55
- class Networking extends tiny_typed_emitter_1.TypedEmitter {
56
- /**
57
- * Creates a new Networking instance.
58
- */
59
- constructor(options, debug) {
60
- super();
61
- this.onWsOpen = this.onWsOpen.bind(this);
62
- this.onChildError = this.onChildError.bind(this);
63
- this.onWsPacket = this.onWsPacket.bind(this);
64
- this.onWsClose = this.onWsClose.bind(this);
65
- this.onWsDebug = this.onWsDebug.bind(this);
66
- this.onUdpDebug = this.onUdpDebug.bind(this);
67
- this.onUdpClose = this.onUdpClose.bind(this);
68
- this.debug = debug ? (message) => this.emit('debug', message) : null;
69
- this._state = {
70
- code: NetworkingStatusCode.OpeningWs,
71
- ws: this.createWebSocket(options.endpoint),
72
- connectionOptions: options,
73
- };
74
- }
75
- /**
76
- * Destroys the Networking instance, transitioning it into the Closed state.
77
- */
78
- destroy() {
79
- this.state = {
80
- code: NetworkingStatusCode.Closed,
81
- };
82
- }
83
- /**
84
- * The current state of the networking instance.
85
- */
86
- get state() {
87
- return this._state;
88
- }
89
- /**
90
- * Sets a new state for the networking instance, performing clean-up operations where necessary.
91
- */
92
- set state(newState) {
93
- var _a;
94
- const oldWs = Reflect.get(this._state, 'ws');
95
- const newWs = Reflect.get(newState, 'ws');
96
- if (oldWs && oldWs !== newWs) {
97
- // The old WebSocket is being freed - remove all handlers from it
98
- oldWs.off('debug', this.onWsDebug);
99
- oldWs.on('error', util_1.noop);
100
- oldWs.off('error', this.onChildError);
101
- oldWs.off('open', this.onWsOpen);
102
- oldWs.off('packet', this.onWsPacket);
103
- oldWs.off('close', this.onWsClose);
104
- oldWs.destroy();
105
- }
106
- const oldUdp = Reflect.get(this._state, 'udp');
107
- const newUdp = Reflect.get(newState, 'udp');
108
- if (oldUdp && oldUdp !== newUdp) {
109
- oldUdp.on('error', util_1.noop);
110
- oldUdp.off('error', this.onChildError);
111
- oldUdp.off('close', this.onUdpClose);
112
- oldUdp.off('debug', this.onUdpDebug);
113
- oldUdp.destroy();
114
- }
115
- const oldState = this._state;
116
- this._state = newState;
117
- this.emit('stateChange', oldState, newState);
118
- /**
119
- * Debug event for Networking.
120
- *
121
- * @event Networking#debug
122
- * @type {string}
123
- */
124
- (_a = this.debug) === null || _a === void 0 ? void 0 : _a.call(this, `state change:\nfrom ${stringifyState(oldState)}\nto ${stringifyState(newState)}`);
125
- }
126
- /**
127
- * Creates a new WebSocket to a Discord Voice gateway.
128
- *
129
- * @param endpoint - The endpoint to connect to
130
- * @param debug - Whether to enable debug logging
131
- */
132
- createWebSocket(endpoint) {
133
- const ws = new VoiceWebSocket_1.VoiceWebSocket(`wss://${endpoint}?v=4`, Boolean(this.debug));
134
- ws.on('error', this.onChildError);
135
- ws.once('open', this.onWsOpen);
136
- ws.on('packet', this.onWsPacket);
137
- ws.once('close', this.onWsClose);
138
- ws.on('debug', this.onWsDebug);
139
- return ws;
140
- }
141
- /**
142
- * Propagates errors from the children VoiceWebSocket and VoiceUDPSocket.
143
- *
144
- * @param error - The error that was emitted by a child
145
- */
146
- onChildError(error) {
147
- this.emit('error', error);
148
- }
149
- /**
150
- * Called when the WebSocket opens. Depending on the state that the instance is in,
151
- * it will either identify with a new session, or it will attempt to resume an existing session.
152
- */
153
- onWsOpen() {
154
- if (this.state.code === NetworkingStatusCode.OpeningWs) {
155
- const packet = {
156
- op: 0 /* Identify */,
157
- d: {
158
- server_id: this.state.connectionOptions.serverId,
159
- user_id: this.state.connectionOptions.userId,
160
- session_id: this.state.connectionOptions.sessionId,
161
- token: this.state.connectionOptions.token,
162
- },
163
- };
164
- this.state.ws.sendPacket(packet);
165
- this.state = {
166
- ...this.state,
167
- code: NetworkingStatusCode.Identifying,
168
- };
169
- }
170
- else if (this.state.code === NetworkingStatusCode.Resuming) {
171
- const packet = {
172
- op: 7 /* Resume */,
173
- d: {
174
- server_id: this.state.connectionOptions.serverId,
175
- session_id: this.state.connectionOptions.sessionId,
176
- token: this.state.connectionOptions.token,
177
- },
178
- };
179
- this.state.ws.sendPacket(packet);
180
- }
181
- }
182
- /**
183
- * Called when the WebSocket closes. Based on the reason for closing (given by the code parameter),
184
- * the instance will either attempt to resume, or enter the closed state and emit a 'close' event
185
- * with the close code, allowing the user to decide whether or not they would like to reconnect.
186
- *
187
- * @param code - The close code
188
- */
189
- onWsClose({ code }) {
190
- const canResume = code === 4015 || code < 4000;
191
- if (canResume && this.state.code === NetworkingStatusCode.Ready) {
192
- this.state = {
193
- ...this.state,
194
- code: NetworkingStatusCode.Resuming,
195
- ws: this.createWebSocket(this.state.connectionOptions.endpoint),
196
- };
197
- }
198
- else if (this.state.code !== NetworkingStatusCode.Closed) {
199
- this.destroy();
200
- this.emit('close', code);
201
- }
202
- }
203
- /**
204
- * Called when the UDP socket has closed itself if it has stopped receiving replies from Discord
205
- */
206
- onUdpClose() {
207
- if (this.state.code === NetworkingStatusCode.Ready) {
208
- this.state = {
209
- ...this.state,
210
- code: NetworkingStatusCode.Resuming,
211
- ws: this.createWebSocket(this.state.connectionOptions.endpoint),
212
- };
213
- }
214
- }
215
- /**
216
- * Called when a packet is received on the connection's WebSocket
217
- * @param packet - The received packet
218
- */
219
- onWsPacket(packet) {
220
- if (packet.op === 8 /* Hello */ && this.state.code !== NetworkingStatusCode.Closed) {
221
- this.state.ws.setHeartbeatInterval(packet.d.heartbeat_interval);
222
- }
223
- else if (packet.op === 2 /* Ready */ && this.state.code === NetworkingStatusCode.Identifying) {
224
- const { ip, port, ssrc, modes } = packet.d;
225
- const udp = new VoiceUDPSocket_1.VoiceUDPSocket({ ip, port });
226
- udp.on('error', this.onChildError);
227
- udp.on('debug', this.onUdpDebug);
228
- udp.once('close', this.onUdpClose);
229
- udp
230
- .performIPDiscovery(ssrc)
231
- .then((localConfig) => {
232
- if (this.state.code !== NetworkingStatusCode.UdpHandshaking)
233
- return;
234
- this.state.ws.sendPacket({
235
- op: 1 /* SelectProtocol */,
236
- d: {
237
- protocol: 'udp',
238
- data: {
239
- address: localConfig.ip,
240
- port: localConfig.port,
241
- mode: chooseEncryptionMode(modes),
242
- },
243
- },
244
- });
245
- this.state = {
246
- ...this.state,
247
- code: NetworkingStatusCode.SelectingProtocol,
248
- };
249
- })
250
- .catch((error) => this.emit('error', error));
251
- this.state = {
252
- ...this.state,
253
- code: NetworkingStatusCode.UdpHandshaking,
254
- udp,
255
- connectionData: {
256
- ssrc,
257
- },
258
- };
259
- }
260
- else if (packet.op === 4 /* SessionDescription */ &&
261
- this.state.code === NetworkingStatusCode.SelectingProtocol) {
262
- const { mode: encryptionMode, secret_key: secretKey } = packet.d;
263
- this.state = {
264
- ...this.state,
265
- code: NetworkingStatusCode.Ready,
266
- connectionData: {
267
- ...this.state.connectionData,
268
- encryptionMode,
269
- secretKey: new Uint8Array(secretKey),
270
- sequence: randomNBit(16),
271
- timestamp: randomNBit(32),
272
- nonce: 0,
273
- nonceBuffer: Buffer.alloc(24),
274
- speaking: false,
275
- packetsPlayed: 0,
276
- },
277
- };
278
- }
279
- else if (packet.op === 9 /* Resumed */ && this.state.code === NetworkingStatusCode.Resuming) {
280
- this.state = {
281
- ...this.state,
282
- code: NetworkingStatusCode.Ready,
283
- };
284
- this.state.connectionData.speaking = false;
285
- }
286
- }
287
- /**
288
- * Propagates debug messages from the child WebSocket.
289
- *
290
- * @param message - The emitted debug message
291
- */
292
- onWsDebug(message) {
293
- var _a;
294
- (_a = this.debug) === null || _a === void 0 ? void 0 : _a.call(this, `[WS] ${message}`);
295
- }
296
- /**
297
- * Propagates debug messages from the child UDPSocket.
298
- *
299
- * @param message - The emitted debug message
300
- */
301
- onUdpDebug(message) {
302
- var _a;
303
- (_a = this.debug) === null || _a === void 0 ? void 0 : _a.call(this, `[UDP] ${message}`);
304
- }
305
- /**
306
- * Prepares an Opus packet for playback. This includes attaching metadata to it and encrypting it.
307
- * It will be stored within the instance, and can be played by dispatchAudio().
308
- *
309
- * @remarks
310
- * Calling this method while there is already a prepared audio packet that has not yet been dispatched
311
- * will overwrite the existing audio packet. This should be avoided.
312
- *
313
- * @param opusPacket - The Opus packet to encrypt
314
- *
315
- * @returns The audio packet that was prepared.
316
- */
317
- prepareAudioPacket(opusPacket) {
318
- const state = this.state;
319
- if (state.code !== NetworkingStatusCode.Ready)
320
- return;
321
- state.preparedPacket = this.createAudioPacket(opusPacket, state.connectionData);
322
- return state.preparedPacket;
323
- }
324
- /**
325
- * Dispatches the audio packet previously prepared by prepareAudioPacket(opusPacket). The audio packet
326
- * is consumed and cannot be dispatched again.
327
- */
328
- dispatchAudio() {
329
- const state = this.state;
330
- if (state.code !== NetworkingStatusCode.Ready)
331
- return false;
332
- if (typeof state.preparedPacket !== 'undefined') {
333
- this.playAudioPacket(state.preparedPacket);
334
- state.preparedPacket = undefined;
335
- return true;
336
- }
337
- return false;
338
- }
339
- /**
340
- * Plays an audio packet, updating timing metadata used for playback.
341
- *
342
- * @param audioPacket - The audio packet to play
343
- */
344
- playAudioPacket(audioPacket) {
345
- const state = this.state;
346
- if (state.code !== NetworkingStatusCode.Ready)
347
- return;
348
- const { connectionData } = state;
349
- connectionData.packetsPlayed++;
350
- connectionData.sequence++;
351
- connectionData.timestamp += TIMESTAMP_INC;
352
- if (connectionData.sequence >= 2 ** 16)
353
- connectionData.sequence = 0;
354
- if (connectionData.timestamp >= 2 ** 32)
355
- connectionData.timestamp = 0;
356
- this.setSpeaking(true);
357
- state.udp.send(audioPacket);
358
- }
359
- /**
360
- * Sends a packet to the voice gateway indicating that the client has start/stopped sending
361
- * audio.
362
- *
363
- * @param speaking - Whether or not the client should be shown as speaking
364
- */
365
- setSpeaking(speaking) {
366
- const state = this.state;
367
- if (state.code !== NetworkingStatusCode.Ready)
368
- return;
369
- if (state.connectionData.speaking === speaking)
370
- return;
371
- state.connectionData.speaking = speaking;
372
- state.ws.sendPacket({
373
- op: 5 /* Speaking */,
374
- d: {
375
- speaking: speaking ? 1 : 0,
376
- delay: 0,
377
- ssrc: state.connectionData.ssrc,
378
- },
379
- });
380
- }
381
- /**
382
- * Creates a new audio packet from an Opus packet. This involves encrypting the packet,
383
- * then prepending a header that includes metadata.
384
- *
385
- * @param opusPacket - The Opus packet to prepare
386
- * @param connectionData - The current connection data of the instance
387
- */
388
- createAudioPacket(opusPacket, connectionData) {
389
- const packetBuffer = Buffer.alloc(12);
390
- packetBuffer[0] = 0x80;
391
- packetBuffer[1] = 0x78;
392
- const { sequence, timestamp, ssrc } = connectionData;
393
- packetBuffer.writeUIntBE(sequence, 2, 2);
394
- packetBuffer.writeUIntBE(timestamp, 4, 4);
395
- packetBuffer.writeUIntBE(ssrc, 8, 4);
396
- packetBuffer.copy(nonce, 0, 0, 12);
397
- return Buffer.concat([packetBuffer, ...this.encryptOpusPacket(opusPacket, connectionData)]);
398
- }
399
- /**
400
- * Encrypts an Opus packet using the format agreed upon by the instance and Discord.
401
- *
402
- * @param opusPacket - The Opus packet to encrypt
403
- * @param connectionData - The current connection data of the instance
404
- */
405
- encryptOpusPacket(opusPacket, connectionData) {
406
- const { secretKey, encryptionMode } = connectionData;
407
- if (encryptionMode === 'xsalsa20_poly1305_lite') {
408
- connectionData.nonce++;
409
- if (connectionData.nonce > MAX_NONCE_SIZE)
410
- connectionData.nonce = 0;
411
- connectionData.nonceBuffer.writeUInt32BE(connectionData.nonce, 0);
412
- return [
413
- secretbox.methods.close(opusPacket, connectionData.nonceBuffer, secretKey),
414
- connectionData.nonceBuffer.slice(0, 4),
415
- ];
416
- }
417
- else if (encryptionMode === 'xsalsa20_poly1305_suffix') {
418
- const random = secretbox.methods.random(24, connectionData.nonceBuffer);
419
- return [secretbox.methods.close(opusPacket, random, secretKey), random];
420
- }
421
- return [secretbox.methods.close(opusPacket, nonce, secretKey)];
422
- }
423
- }
424
- exports.Networking = Networking;
425
- /**
426
- * Returns a random number that is in the range of n bits.
427
- *
428
- * @param n - The number of bits
429
- */
430
- function randomNBit(n) {
431
- return Math.floor(Math.random() * 2 ** n);
432
- }
433
- /**
434
- * Stringifies a NetworkingState
435
- *
436
- * @param state - The state to stringify
437
- */
438
- function stringifyState(state) {
439
- return JSON.stringify({
440
- ...state,
441
- ws: Reflect.has(state, 'ws'),
442
- udp: Reflect.has(state, 'udp'),
443
- });
444
- }
445
- /**
446
- * Chooses an encryption mode from a list of given options. Chooses the most preferred option.
447
- *
448
- * @param options - The available encryption options
449
- */
450
- function chooseEncryptionMode(options) {
451
- const option = options.find((option) => exports.SUPPORTED_ENCRYPTION_MODES.includes(option));
452
- if (!option) {
453
- throw new Error(`No compatible encryption modes. Available include: ${options.join(', ')}`);
454
- }
455
- return option;
456
- }
457
- //# sourceMappingURL=Networking.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Networking.js","sourceRoot":"","sources":["../../src/networking/Networking.ts"],"names":[],"mappings":"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","sourcesContent":["import { VoiceOpcodes } from 'discord-api-types/voice/v4';\nimport { VoiceUDPSocket } from './VoiceUDPSocket';\nimport { VoiceWebSocket } from './VoiceWebSocket';\nimport * as secretbox from '../util/Secretbox';\nimport { Awaited, noop } from '../util/util';\nimport { CloseEvent } from 'ws';\nimport { TypedEmitter } from 'tiny-typed-emitter';\n\n// The number of audio channels required by Discord\nconst CHANNELS = 2;\nconst TIMESTAMP_INC = (48000 / 100) * CHANNELS;\nconst MAX_NONCE_SIZE = 2 ** 32 - 1;\n\nexport const SUPPORTED_ENCRYPTION_MODES = ['xsalsa20_poly1305_lite', 'xsalsa20_poly1305_suffix', 'xsalsa20_poly1305'];\n\n/**\n * The different statuses that a networking instance can hold. The order\n * of the states between OpeningWs and Ready is chronological (first the\n * instance enters OpeningWs, then it enters Identifying etc.)\n */\nexport enum NetworkingStatusCode {\n\tOpeningWs,\n\tIdentifying,\n\tUdpHandshaking,\n\tSelectingProtocol,\n\tReady,\n\tResuming,\n\tClosed,\n}\n\n/**\n * The initial Networking state. Instances will be in this state when a WebSocket connection to a Discord\n * voice gateway is being opened.\n */\nexport interface NetworkingOpeningWsState {\n\tcode: NetworkingStatusCode.OpeningWs;\n\tws: VoiceWebSocket;\n\tconnectionOptions: ConnectionOptions;\n}\n\n/**\n * The state that a Networking instance will be in when it is attempting to authorize itself.\n */\nexport interface NetworkingIdentifyingState {\n\tcode: NetworkingStatusCode.Identifying;\n\tws: VoiceWebSocket;\n\tconnectionOptions: ConnectionOptions;\n}\n\n/**\n * The state that a Networking instance will be in when opening a UDP connection to the IP and port provided\n * by Discord, as well as performing IP discovery.\n */\nexport interface NetworkingUdpHandshakingState {\n\tcode: NetworkingStatusCode.UdpHandshaking;\n\tws: VoiceWebSocket;\n\tudp: VoiceUDPSocket;\n\tconnectionOptions: ConnectionOptions;\n\tconnectionData: Pick<ConnectionData, 'ssrc'>;\n}\n\n/**\n * The state that a Networking instance will be in when selecting an encryption protocol for audio packets.\n */\nexport interface NetworkingSelectingProtocolState {\n\tcode: NetworkingStatusCode.SelectingProtocol;\n\tws: VoiceWebSocket;\n\tudp: VoiceUDPSocket;\n\tconnectionOptions: ConnectionOptions;\n\tconnectionData: Pick<ConnectionData, 'ssrc'>;\n}\n\n/**\n * The state that a Networking instance will be in when it has a fully established connection to a Discord\n * voice server.\n */\nexport interface NetworkingReadyState {\n\tcode: NetworkingStatusCode.Ready;\n\tws: VoiceWebSocket;\n\tudp: VoiceUDPSocket;\n\tconnectionOptions: ConnectionOptions;\n\tconnectionData: ConnectionData;\n\tpreparedPacket?: Buffer;\n}\n\n/**\n * The state that a Networking instance will be in when its connection has been dropped unexpectedly, and it\n * is attempting to resume an existing session.\n */\nexport interface NetworkingResumingState {\n\tcode: NetworkingStatusCode.Resuming;\n\tws: VoiceWebSocket;\n\tudp: VoiceUDPSocket;\n\tconnectionOptions: ConnectionOptions;\n\tconnectionData: ConnectionData;\n\tpreparedPacket?: Buffer;\n}\n\n/**\n * The state that a Networking instance will be in when it has been destroyed. It cannot be recovered from this\n * state.\n */\nexport interface NetworkingClosedState {\n\tcode: NetworkingStatusCode.Closed;\n}\n\n/**\n * The various states that a networking instance can be in.\n */\nexport type NetworkingState =\n\t| NetworkingOpeningWsState\n\t| NetworkingIdentifyingState\n\t| NetworkingUdpHandshakingState\n\t| NetworkingSelectingProtocolState\n\t| NetworkingReadyState\n\t| NetworkingResumingState\n\t| NetworkingClosedState;\n\n/**\n * Details required to connect to the Discord voice gateway. These details\n * are first received on the main bot gateway, in the form of VOICE_SERVER_UPDATE\n * and VOICE_STATE_UPDATE packets.\n */\ninterface ConnectionOptions {\n\tserverId: string;\n\tuserId: string;\n\tsessionId: string;\n\ttoken: string;\n\tendpoint: string;\n}\n\n/**\n * Information about the current connection, e.g. which encryption mode is to be used on\n * the connection, timing information for playback of streams.\n */\nexport interface ConnectionData {\n\tssrc: number;\n\tencryptionMode: string;\n\tsecretKey: Uint8Array;\n\tsequence: number;\n\ttimestamp: number;\n\tpacketsPlayed: number;\n\tnonce: number;\n\tnonceBuffer: Buffer;\n\tspeaking: boolean;\n}\n\n/**\n * An empty buffer that is reused in packet encryption by many different networking instances.\n */\nconst nonce = Buffer.alloc(24);\n\nexport interface NetworkingEvents {\n\tdebug: (message: string) => Awaited<void>;\n\terror: (error: Error) => Awaited<void>;\n\tstateChange: (oldState: NetworkingState, newState: NetworkingState) => Awaited<void>;\n\tclose: (code: number) => Awaited<void>;\n}\n\n/**\n * Manages the networking required to maintain a voice connection and dispatch audio packets\n */\nexport class Networking extends TypedEmitter<NetworkingEvents> {\n\tprivate _state: NetworkingState;\n\n\t/**\n\t * The debug logger function, if debugging is enabled.\n\t */\n\tprivate readonly debug: null | ((message: string) => void);\n\n\t/**\n\t * Creates a new Networking instance.\n\t */\n\tpublic constructor(options: ConnectionOptions, debug: boolean) {\n\t\tsuper();\n\n\t\tthis.onWsOpen = this.onWsOpen.bind(this);\n\t\tthis.onChildError = this.onChildError.bind(this);\n\t\tthis.onWsPacket = this.onWsPacket.bind(this);\n\t\tthis.onWsClose = this.onWsClose.bind(this);\n\t\tthis.onWsDebug = this.onWsDebug.bind(this);\n\t\tthis.onUdpDebug = this.onUdpDebug.bind(this);\n\t\tthis.onUdpClose = this.onUdpClose.bind(this);\n\n\t\tthis.debug = debug ? (message: string) => this.emit('debug', message) : null;\n\n\t\tthis._state = {\n\t\t\tcode: NetworkingStatusCode.OpeningWs,\n\t\t\tws: this.createWebSocket(options.endpoint),\n\t\t\tconnectionOptions: options,\n\t\t};\n\t}\n\n\t/**\n\t * Destroys the Networking instance, transitioning it into the Closed state.\n\t */\n\tpublic destroy() {\n\t\tthis.state = {\n\t\t\tcode: NetworkingStatusCode.Closed,\n\t\t};\n\t}\n\n\t/**\n\t * The current state of the networking instance.\n\t */\n\tpublic get state(): NetworkingState {\n\t\treturn this._state;\n\t}\n\n\t/**\n\t * Sets a new state for the networking instance, performing clean-up operations where necessary.\n\t */\n\tpublic set state(newState: NetworkingState) {\n\t\tconst oldWs = Reflect.get(this._state, 'ws') as VoiceWebSocket | undefined;\n\t\tconst newWs = Reflect.get(newState, 'ws') as VoiceWebSocket | undefined;\n\t\tif (oldWs && oldWs !== newWs) {\n\t\t\t// The old WebSocket is being freed - remove all handlers from it\n\t\t\toldWs.off('debug', this.onWsDebug);\n\t\t\toldWs.on('error', noop);\n\t\t\toldWs.off('error', this.onChildError);\n\t\t\toldWs.off('open', this.onWsOpen);\n\t\t\toldWs.off('packet', this.onWsPacket);\n\t\t\toldWs.off('close', this.onWsClose);\n\t\t\toldWs.destroy();\n\t\t}\n\n\t\tconst oldUdp = Reflect.get(this._state, 'udp') as VoiceUDPSocket | undefined;\n\t\tconst newUdp = Reflect.get(newState, 'udp') as VoiceUDPSocket | undefined;\n\n\t\tif (oldUdp && oldUdp !== newUdp) {\n\t\t\toldUdp.on('error', noop);\n\t\t\toldUdp.off('error', this.onChildError);\n\t\t\toldUdp.off('close', this.onUdpClose);\n\t\t\toldUdp.off('debug', this.onUdpDebug);\n\t\t\toldUdp.destroy();\n\t\t}\n\n\t\tconst oldState = this._state;\n\t\tthis._state = newState;\n\t\tthis.emit('stateChange', oldState, newState);\n\n\t\t/**\n\t\t * Debug event for Networking.\n\t\t *\n\t\t * @event Networking#debug\n\t\t * @type {string}\n\t\t */\n\t\tthis.debug?.(`state change:\\nfrom ${stringifyState(oldState)}\\nto ${stringifyState(newState)}`);\n\t}\n\n\t/**\n\t * Creates a new WebSocket to a Discord Voice gateway.\n\t *\n\t * @param endpoint - The endpoint to connect to\n\t * @param debug - Whether to enable debug logging\n\t */\n\tprivate createWebSocket(endpoint: string) {\n\t\tconst ws = new VoiceWebSocket(`wss://${endpoint}?v=4`, Boolean(this.debug));\n\n\t\tws.on('error', this.onChildError);\n\t\tws.once('open', this.onWsOpen);\n\t\tws.on('packet', this.onWsPacket);\n\t\tws.once('close', this.onWsClose);\n\t\tws.on('debug', this.onWsDebug);\n\n\t\treturn ws;\n\t}\n\n\t/**\n\t * Propagates errors from the children VoiceWebSocket and VoiceUDPSocket.\n\t *\n\t * @param error - The error that was emitted by a child\n\t */\n\tprivate onChildError(error: Error) {\n\t\tthis.emit('error', error);\n\t}\n\n\t/**\n\t * Called when the WebSocket opens. Depending on the state that the instance is in,\n\t * it will either identify with a new session, or it will attempt to resume an existing session.\n\t */\n\tprivate onWsOpen() {\n\t\tif (this.state.code === NetworkingStatusCode.OpeningWs) {\n\t\t\tconst packet = {\n\t\t\t\top: VoiceOpcodes.Identify,\n\t\t\t\td: {\n\t\t\t\t\tserver_id: this.state.connectionOptions.serverId,\n\t\t\t\t\tuser_id: this.state.connectionOptions.userId,\n\t\t\t\t\tsession_id: this.state.connectionOptions.sessionId,\n\t\t\t\t\ttoken: this.state.connectionOptions.token,\n\t\t\t\t},\n\t\t\t};\n\t\t\tthis.state.ws.sendPacket(packet);\n\t\t\tthis.state = {\n\t\t\t\t...this.state,\n\t\t\t\tcode: NetworkingStatusCode.Identifying,\n\t\t\t};\n\t\t} else if (this.state.code === NetworkingStatusCode.Resuming) {\n\t\t\tconst packet = {\n\t\t\t\top: VoiceOpcodes.Resume,\n\t\t\t\td: {\n\t\t\t\t\tserver_id: this.state.connectionOptions.serverId,\n\t\t\t\t\tsession_id: this.state.connectionOptions.sessionId,\n\t\t\t\t\ttoken: this.state.connectionOptions.token,\n\t\t\t\t},\n\t\t\t};\n\t\t\tthis.state.ws.sendPacket(packet);\n\t\t}\n\t}\n\n\t/**\n\t * Called when the WebSocket closes. Based on the reason for closing (given by the code parameter),\n\t * the instance will either attempt to resume, or enter the closed state and emit a 'close' event\n\t * with the close code, allowing the user to decide whether or not they would like to reconnect.\n\t *\n\t * @param code - The close code\n\t */\n\tprivate onWsClose({ code }: CloseEvent) {\n\t\tconst canResume = code === 4015 || code < 4000;\n\t\tif (canResume && this.state.code === NetworkingStatusCode.Ready) {\n\t\t\tthis.state = {\n\t\t\t\t...this.state,\n\t\t\t\tcode: NetworkingStatusCode.Resuming,\n\t\t\t\tws: this.createWebSocket(this.state.connectionOptions.endpoint),\n\t\t\t};\n\t\t} else if (this.state.code !== NetworkingStatusCode.Closed) {\n\t\t\tthis.destroy();\n\t\t\tthis.emit('close', code);\n\t\t}\n\t}\n\n\t/**\n\t * Called when the UDP socket has closed itself if it has stopped receiving replies from Discord\n\t */\n\tprivate onUdpClose() {\n\t\tif (this.state.code === NetworkingStatusCode.Ready) {\n\t\t\tthis.state = {\n\t\t\t\t...this.state,\n\t\t\t\tcode: NetworkingStatusCode.Resuming,\n\t\t\t\tws: this.createWebSocket(this.state.connectionOptions.endpoint),\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Called when a packet is received on the connection's WebSocket\n\t * @param packet - The received packet\n\t */\n\tprivate onWsPacket(packet: any) {\n\t\tif (packet.op === VoiceOpcodes.Hello && this.state.code !== NetworkingStatusCode.Closed) {\n\t\t\tthis.state.ws.setHeartbeatInterval(packet.d.heartbeat_interval);\n\t\t} else if (packet.op === VoiceOpcodes.Ready && this.state.code === NetworkingStatusCode.Identifying) {\n\t\t\tconst { ip, port, ssrc, modes } = packet.d;\n\n\t\t\tconst udp = new VoiceUDPSocket({ ip, port });\n\t\t\tudp.on('error', this.onChildError);\n\t\t\tudp.on('debug', this.onUdpDebug);\n\t\t\tudp.once('close', this.onUdpClose);\n\t\t\tudp\n\t\t\t\t.performIPDiscovery(ssrc)\n\t\t\t\t.then((localConfig) => {\n\t\t\t\t\tif (this.state.code !== NetworkingStatusCode.UdpHandshaking) return;\n\t\t\t\t\tthis.state.ws.sendPacket({\n\t\t\t\t\t\top: VoiceOpcodes.SelectProtocol,\n\t\t\t\t\t\td: {\n\t\t\t\t\t\t\tprotocol: 'udp',\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\taddress: localConfig.ip,\n\t\t\t\t\t\t\t\tport: localConfig.port,\n\t\t\t\t\t\t\t\tmode: chooseEncryptionMode(modes),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\tthis.state = {\n\t\t\t\t\t\t...this.state,\n\t\t\t\t\t\tcode: NetworkingStatusCode.SelectingProtocol,\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.catch((error) => this.emit('error', error));\n\n\t\t\tthis.state = {\n\t\t\t\t...this.state,\n\t\t\t\tcode: NetworkingStatusCode.UdpHandshaking,\n\t\t\t\tudp,\n\t\t\t\tconnectionData: {\n\t\t\t\t\tssrc,\n\t\t\t\t},\n\t\t\t};\n\t\t} else if (\n\t\t\tpacket.op === VoiceOpcodes.SessionDescription &&\n\t\t\tthis.state.code === NetworkingStatusCode.SelectingProtocol\n\t\t) {\n\t\t\tconst { mode: encryptionMode, secret_key: secretKey } = packet.d;\n\t\t\tthis.state = {\n\t\t\t\t...this.state,\n\t\t\t\tcode: NetworkingStatusCode.Ready,\n\t\t\t\tconnectionData: {\n\t\t\t\t\t...this.state.connectionData,\n\t\t\t\t\tencryptionMode,\n\t\t\t\t\tsecretKey: new Uint8Array(secretKey),\n\t\t\t\t\tsequence: randomNBit(16),\n\t\t\t\t\ttimestamp: randomNBit(32),\n\t\t\t\t\tnonce: 0,\n\t\t\t\t\tnonceBuffer: Buffer.alloc(24),\n\t\t\t\t\tspeaking: false,\n\t\t\t\t\tpacketsPlayed: 0,\n\t\t\t\t},\n\t\t\t};\n\t\t} else if (packet.op === VoiceOpcodes.Resumed && this.state.code === NetworkingStatusCode.Resuming) {\n\t\t\tthis.state = {\n\t\t\t\t...this.state,\n\t\t\t\tcode: NetworkingStatusCode.Ready,\n\t\t\t};\n\t\t\tthis.state.connectionData.speaking = false;\n\t\t}\n\t}\n\n\t/**\n\t * Propagates debug messages from the child WebSocket.\n\t *\n\t * @param message - The emitted debug message\n\t */\n\tprivate onWsDebug(message: string) {\n\t\tthis.debug?.(`[WS] ${message}`);\n\t}\n\n\t/**\n\t * Propagates debug messages from the child UDPSocket.\n\t *\n\t * @param message - The emitted debug message\n\t */\n\tprivate onUdpDebug(message: string) {\n\t\tthis.debug?.(`[UDP] ${message}`);\n\t}\n\n\t/**\n\t * Prepares an Opus packet for playback. This includes attaching metadata to it and encrypting it.\n\t * It will be stored within the instance, and can be played by dispatchAudio().\n\t *\n\t * @remarks\n\t * Calling this method while there is already a prepared audio packet that has not yet been dispatched\n\t * will overwrite the existing audio packet. This should be avoided.\n\t *\n\t * @param opusPacket - The Opus packet to encrypt\n\t *\n\t * @returns The audio packet that was prepared.\n\t */\n\tpublic prepareAudioPacket(opusPacket: Buffer) {\n\t\tconst state = this.state;\n\t\tif (state.code !== NetworkingStatusCode.Ready) return;\n\t\tstate.preparedPacket = this.createAudioPacket(opusPacket, state.connectionData);\n\t\treturn state.preparedPacket;\n\t}\n\n\t/**\n\t * Dispatches the audio packet previously prepared by prepareAudioPacket(opusPacket). The audio packet\n\t * is consumed and cannot be dispatched again.\n\t */\n\tpublic dispatchAudio() {\n\t\tconst state = this.state;\n\t\tif (state.code !== NetworkingStatusCode.Ready) return false;\n\t\tif (typeof state.preparedPacket !== 'undefined') {\n\t\t\tthis.playAudioPacket(state.preparedPacket);\n\t\t\tstate.preparedPacket = undefined;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Plays an audio packet, updating timing metadata used for playback.\n\t *\n\t * @param audioPacket - The audio packet to play\n\t */\n\tprivate playAudioPacket(audioPacket: Buffer) {\n\t\tconst state = this.state;\n\t\tif (state.code !== NetworkingStatusCode.Ready) return;\n\t\tconst { connectionData } = state;\n\t\tconnectionData.packetsPlayed++;\n\t\tconnectionData.sequence++;\n\t\tconnectionData.timestamp += TIMESTAMP_INC;\n\t\tif (connectionData.sequence >= 2 ** 16) connectionData.sequence = 0;\n\t\tif (connectionData.timestamp >= 2 ** 32) connectionData.timestamp = 0;\n\t\tthis.setSpeaking(true);\n\t\tstate.udp.send(audioPacket);\n\t}\n\n\t/**\n\t * Sends a packet to the voice gateway indicating that the client has start/stopped sending\n\t * audio.\n\t *\n\t * @param speaking - Whether or not the client should be shown as speaking\n\t */\n\tpublic setSpeaking(speaking: boolean) {\n\t\tconst state = this.state;\n\t\tif (state.code !== NetworkingStatusCode.Ready) return;\n\t\tif (state.connectionData.speaking === speaking) return;\n\t\tstate.connectionData.speaking = speaking;\n\t\tstate.ws.sendPacket({\n\t\t\top: VoiceOpcodes.Speaking,\n\t\t\td: {\n\t\t\t\tspeaking: speaking ? 1 : 0,\n\t\t\t\tdelay: 0,\n\t\t\t\tssrc: state.connectionData.ssrc,\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Creates a new audio packet from an Opus packet. This involves encrypting the packet,\n\t * then prepending a header that includes metadata.\n\t *\n\t * @param opusPacket - The Opus packet to prepare\n\t * @param connectionData - The current connection data of the instance\n\t */\n\tprivate createAudioPacket(opusPacket: Buffer, connectionData: ConnectionData) {\n\t\tconst packetBuffer = Buffer.alloc(12);\n\t\tpacketBuffer[0] = 0x80;\n\t\tpacketBuffer[1] = 0x78;\n\n\t\tconst { sequence, timestamp, ssrc } = connectionData;\n\n\t\tpacketBuffer.writeUIntBE(sequence, 2, 2);\n\t\tpacketBuffer.writeUIntBE(timestamp, 4, 4);\n\t\tpacketBuffer.writeUIntBE(ssrc, 8, 4);\n\n\t\tpacketBuffer.copy(nonce, 0, 0, 12);\n\t\treturn Buffer.concat([packetBuffer, ...this.encryptOpusPacket(opusPacket, connectionData)]);\n\t}\n\n\t/**\n\t * Encrypts an Opus packet using the format agreed upon by the instance and Discord.\n\t *\n\t * @param opusPacket - The Opus packet to encrypt\n\t * @param connectionData - The current connection data of the instance\n\t */\n\tprivate encryptOpusPacket(opusPacket: Buffer, connectionData: ConnectionData) {\n\t\tconst { secretKey, encryptionMode } = connectionData;\n\n\t\tif (encryptionMode === 'xsalsa20_poly1305_lite') {\n\t\t\tconnectionData.nonce++;\n\t\t\tif (connectionData.nonce > MAX_NONCE_SIZE) connectionData.nonce = 0;\n\t\t\tconnectionData.nonceBuffer.writeUInt32BE(connectionData.nonce, 0);\n\t\t\treturn [\n\t\t\t\tsecretbox.methods.close(opusPacket, connectionData.nonceBuffer, secretKey),\n\t\t\t\tconnectionData.nonceBuffer.slice(0, 4),\n\t\t\t];\n\t\t} else if (encryptionMode === 'xsalsa20_poly1305_suffix') {\n\t\t\tconst random = secretbox.methods.random(24, connectionData.nonceBuffer);\n\t\t\treturn [secretbox.methods.close(opusPacket, random, secretKey), random];\n\t\t}\n\t\treturn [secretbox.methods.close(opusPacket, nonce, secretKey)];\n\t}\n}\n\n/**\n * Returns a random number that is in the range of n bits.\n *\n * @param n - The number of bits\n */\nfunction randomNBit(n: number) {\n\treturn Math.floor(Math.random() * 2 ** n);\n}\n\n/**\n * Stringifies a NetworkingState\n *\n * @param state - The state to stringify\n */\nfunction stringifyState(state: NetworkingState) {\n\treturn JSON.stringify({\n\t\t...state,\n\t\tws: Reflect.has(state, 'ws'),\n\t\tudp: Reflect.has(state, 'udp'),\n\t});\n}\n\n/**\n * Chooses an encryption mode from a list of given options. Chooses the most preferred option.\n *\n * @param options - The available encryption options\n */\nfunction chooseEncryptionMode(options: string[]): string {\n\tconst option = options.find((option) => SUPPORTED_ENCRYPTION_MODES.includes(option));\n\tif (!option) {\n\t\tthrow new Error(`No compatible encryption modes. Available include: ${options.join(', ')}`);\n\t}\n\treturn option;\n}\n"]}
@@ -1,145 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseLocalPacket = exports.VoiceUDPSocket = void 0;
4
- const dgram_1 = require("dgram");
5
- const net_1 = require("net");
6
- const tiny_typed_emitter_1 = require("tiny-typed-emitter");
7
- /**
8
- * The interval in milliseconds at which keep alive datagrams are sent
9
- */
10
- const KEEP_ALIVE_INTERVAL = 5e3;
11
- /**
12
- * The maximum number of keep alive packets which can be missed
13
- */
14
- const KEEP_ALIVE_LIMIT = 12;
15
- /**
16
- * The maximum value of the keep alive counter
17
- */
18
- const MAX_COUNTER_VALUE = 2 ** 32 - 1;
19
- /**
20
- * Manages the UDP networking for a voice connection.
21
- */
22
- class VoiceUDPSocket extends tiny_typed_emitter_1.TypedEmitter {
23
- /**
24
- * Creates a new VoiceUDPSocket.
25
- *
26
- * @param remote - Details of the remote socket
27
- */
28
- constructor(remote, debug = false) {
29
- super();
30
- /**
31
- * The counter used in the keep alive mechanism
32
- */
33
- this.keepAliveCounter = 0;
34
- this.socket = dgram_1.createSocket('udp4');
35
- this.socket.on('error', (error) => this.emit('error', error));
36
- this.socket.on('message', (buffer) => this.onMessage(buffer));
37
- this.socket.on('close', () => this.emit('close'));
38
- this.remote = remote;
39
- this.keepAlives = [];
40
- this.keepAliveBuffer = Buffer.alloc(8);
41
- this.keepAliveInterval = setInterval(() => this.keepAlive(), KEEP_ALIVE_INTERVAL);
42
- setImmediate(() => this.keepAlive());
43
- this.debug = debug ? (message) => this.emit('debug', message) : null;
44
- }
45
- /**
46
- * Called when a message is received on the UDP socket
47
- * @param buffer The received buffer
48
- */
49
- onMessage(buffer) {
50
- // Handle keep alive message
51
- if (buffer.length === 8) {
52
- const counter = buffer.readUInt32LE(0);
53
- const index = this.keepAlives.findIndex(({ value }) => value === counter);
54
- if (index === -1)
55
- return;
56
- this.ping = Date.now() - this.keepAlives[index].timestamp;
57
- // Delete all keep alives up to and including the received one
58
- this.keepAlives.splice(0, index);
59
- }
60
- // Propagate the message
61
- this.emit('message', buffer);
62
- }
63
- /**
64
- * Called at a regular interval to check whether we are still able to send datagrams to Discord
65
- */
66
- keepAlive() {
67
- var _a;
68
- if (this.keepAlives.length >= KEEP_ALIVE_LIMIT) {
69
- (_a = this.debug) === null || _a === void 0 ? void 0 : _a.call(this, 'UDP socket has not received enough responses from Discord - closing socket');
70
- this.destroy();
71
- return;
72
- }
73
- this.keepAliveBuffer.writeUInt32LE(this.keepAliveCounter, 0);
74
- this.send(this.keepAliveBuffer);
75
- this.keepAlives.push({
76
- value: this.keepAliveCounter,
77
- timestamp: Date.now(),
78
- });
79
- this.keepAliveCounter++;
80
- if (this.keepAliveCounter > MAX_COUNTER_VALUE) {
81
- this.keepAliveCounter = 0;
82
- }
83
- }
84
- /**
85
- * Sends a buffer to Discord.
86
- *
87
- * @param buffer - The buffer to send
88
- */
89
- send(buffer) {
90
- return this.socket.send(buffer, this.remote.port, this.remote.ip);
91
- }
92
- /**
93
- * Closes the socket, the instance will not be able to be reused.
94
- */
95
- destroy() {
96
- try {
97
- this.socket.close();
98
- }
99
- catch { }
100
- clearInterval(this.keepAliveInterval);
101
- }
102
- /**
103
- * Performs IP discovery to discover the local address and port to be used for the voice connection.
104
- *
105
- * @param ssrc - The SSRC received from Discord
106
- */
107
- performIPDiscovery(ssrc) {
108
- return new Promise((resolve, reject) => {
109
- const listener = (message) => {
110
- try {
111
- if (message.readUInt16BE(0) !== 2)
112
- return;
113
- const packet = parseLocalPacket(message);
114
- this.socket.off('message', listener);
115
- resolve(packet);
116
- }
117
- catch { }
118
- };
119
- this.socket.on('message', listener);
120
- this.socket.once('close', () => reject(new Error('Cannot perform IP discovery - socket closed')));
121
- const discoveryBuffer = Buffer.alloc(74);
122
- discoveryBuffer.writeUInt16BE(1, 0);
123
- discoveryBuffer.writeUInt16BE(70, 2);
124
- discoveryBuffer.writeUInt32BE(ssrc, 4);
125
- this.send(discoveryBuffer);
126
- });
127
- }
128
- }
129
- exports.VoiceUDPSocket = VoiceUDPSocket;
130
- /**
131
- * Parses the response from Discord to aid with local IP discovery.
132
- *
133
- * @param message - The received message
134
- */
135
- function parseLocalPacket(message) {
136
- const packet = Buffer.from(message);
137
- const ip = packet.slice(8, packet.indexOf(0, 8)).toString('utf-8');
138
- if (!net_1.isIPv4(ip)) {
139
- throw new Error('Malformed IP address');
140
- }
141
- const port = packet.readUInt16BE(packet.length - 2);
142
- return { ip, port };
143
- }
144
- exports.parseLocalPacket = parseLocalPacket;
145
- //# sourceMappingURL=VoiceUDPSocket.js.map