@discordjs/voice 0.17.1-dev.1731672303-3669d5e11 → 0.18.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/README.md +5 -1
- package/dist/index.js +137 -80
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +140 -81
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -51,9 +51,13 @@ try installing another.
|
|
|
51
51
|
|
|
52
52
|
**Encryption Libraries (npm install):**
|
|
53
53
|
|
|
54
|
+
> [!NOTE]
|
|
55
|
+
> You only need to install one of these libraries if your system does not support `aes-256-gcm` (verify by running `require('node:crypto').getCiphers().includes('aes-256-gcm')`).
|
|
56
|
+
|
|
54
57
|
- `sodium-native`: ^3.3.0
|
|
55
58
|
- `sodium`: ^3.0.2
|
|
56
|
-
-
|
|
59
|
+
- `@stablelib/xchacha20poly1305`: ^2.0.0
|
|
60
|
+
- `@noble/ciphers`: ^1.0.0
|
|
57
61
|
- `libsodium-wrappers`: ^0.7.9
|
|
58
62
|
|
|
59
63
|
**Opus Libraries (npm install):**
|
package/dist/index.js
CHANGED
|
@@ -162,6 +162,7 @@ __name(deleteAudioPlayer, "deleteAudioPlayer");
|
|
|
162
162
|
|
|
163
163
|
// src/networking/Networking.ts
|
|
164
164
|
var import_node_buffer3 = require("buffer");
|
|
165
|
+
var import_node_crypto = __toESM(require("crypto"));
|
|
165
166
|
var import_node_events3 = require("events");
|
|
166
167
|
var import_v42 = require("discord-api-types/voice/v4");
|
|
167
168
|
|
|
@@ -169,66 +170,84 @@ var import_v42 = require("discord-api-types/voice/v4");
|
|
|
169
170
|
var import_node_buffer = require("buffer");
|
|
170
171
|
var libs = {
|
|
171
172
|
"sodium-native": /* @__PURE__ */ __name((sodium) => ({
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return output;
|
|
183
|
-
}, "close"),
|
|
184
|
-
random: /* @__PURE__ */ __name((num, buffer = import_node_buffer.Buffer.allocUnsafe(num)) => {
|
|
185
|
-
sodium.randombytes_buf(buffer);
|
|
186
|
-
return buffer;
|
|
187
|
-
}, "random")
|
|
173
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt: /* @__PURE__ */ __name((cipherText, additionalData, nonce2, key) => {
|
|
174
|
+
const message = import_node_buffer.Buffer.alloc(cipherText.length - sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES);
|
|
175
|
+
sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(message, null, cipherText, additionalData, nonce2, key);
|
|
176
|
+
return message;
|
|
177
|
+
}, "crypto_aead_xchacha20poly1305_ietf_decrypt"),
|
|
178
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt: /* @__PURE__ */ __name((plaintext, additionalData, nonce2, key) => {
|
|
179
|
+
const cipherText = import_node_buffer.Buffer.alloc(plaintext.length + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES);
|
|
180
|
+
sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(cipherText, plaintext, additionalData, null, nonce2, key);
|
|
181
|
+
return cipherText;
|
|
182
|
+
}, "crypto_aead_xchacha20poly1305_ietf_encrypt")
|
|
188
183
|
}), "sodium-native"),
|
|
189
184
|
sodium: /* @__PURE__ */ __name((sodium) => ({
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return
|
|
195
|
-
}, "
|
|
185
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt: /* @__PURE__ */ __name((cipherText, additionalData, nonce2, key) => {
|
|
186
|
+
return sodium.api.crypto_aead_xchacha20poly1305_ietf_decrypt(cipherText, additionalData, null, nonce2, key);
|
|
187
|
+
}, "crypto_aead_xchacha20poly1305_ietf_decrypt"),
|
|
188
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt: /* @__PURE__ */ __name((plaintext, additionalData, nonce2, key) => {
|
|
189
|
+
return sodium.api.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, null, nonce2, key);
|
|
190
|
+
}, "crypto_aead_xchacha20poly1305_ietf_encrypt")
|
|
196
191
|
}), "sodium"),
|
|
197
192
|
"libsodium-wrappers": /* @__PURE__ */ __name((sodium) => ({
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
193
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt: /* @__PURE__ */ __name((cipherText, additionalData, nonce2, key) => {
|
|
194
|
+
return sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null, cipherText, additionalData, nonce2, key);
|
|
195
|
+
}, "crypto_aead_xchacha20poly1305_ietf_decrypt"),
|
|
196
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt: /* @__PURE__ */ __name((plaintext, additionalData, nonce2, key) => {
|
|
197
|
+
return sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, null, nonce2, key);
|
|
198
|
+
}, "crypto_aead_xchacha20poly1305_ietf_encrypt")
|
|
201
199
|
}), "libsodium-wrappers"),
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
200
|
+
"@stablelib/xchacha20poly1305": /* @__PURE__ */ __name((stablelib) => ({
|
|
201
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt(plaintext, additionalData, nonce2, key) {
|
|
202
|
+
const crypto3 = new stablelib.XChaCha20Poly1305(key);
|
|
203
|
+
return crypto3.open(nonce2, plaintext, additionalData);
|
|
204
|
+
},
|
|
205
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt(cipherText, additionalData, nonce2, key) {
|
|
206
|
+
const crypto3 = new stablelib.XChaCha20Poly1305(key);
|
|
207
|
+
return crypto3.seal(nonce2, cipherText, additionalData);
|
|
208
|
+
}
|
|
209
|
+
}), "@stablelib/xchacha20poly1305"),
|
|
210
|
+
"@noble/ciphers/chacha": /* @__PURE__ */ __name((noble) => ({
|
|
211
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt(cipherText, additionalData, nonce2, key) {
|
|
212
|
+
const chacha = noble.xchacha20poly1305(key, nonce2, additionalData);
|
|
213
|
+
return chacha.decrypt(cipherText);
|
|
214
|
+
},
|
|
215
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, nonce2, key) {
|
|
216
|
+
const chacha = noble.xchacha20poly1305(key, nonce2, additionalData);
|
|
217
|
+
return chacha.encrypt(plaintext);
|
|
218
|
+
}
|
|
219
|
+
}), "@noble/ciphers/chacha")
|
|
207
220
|
};
|
|
208
221
|
var fallbackError = /* @__PURE__ */ __name(() => {
|
|
209
222
|
throw new Error(
|
|
210
223
|
`Cannot play audio as no valid encryption package is installed.
|
|
211
|
-
- Install
|
|
224
|
+
- Install one of:
|
|
225
|
+
- sodium
|
|
226
|
+
- libsodium-wrappers
|
|
227
|
+
- @stablelib/xchacha20poly1305
|
|
228
|
+
- @noble/ciphers.
|
|
212
229
|
- Use the generateDependencyReport() function for more information.
|
|
213
230
|
`
|
|
214
231
|
);
|
|
215
232
|
}, "fallbackError");
|
|
216
233
|
var methods = {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
random: fallbackError
|
|
234
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt: fallbackError,
|
|
235
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt: fallbackError
|
|
220
236
|
};
|
|
221
|
-
|
|
237
|
+
var secretboxLoadPromise = new Promise(async (resolve2) => {
|
|
222
238
|
for (const libName of Object.keys(libs)) {
|
|
223
239
|
try {
|
|
224
|
-
const lib =
|
|
225
|
-
if (libName === "libsodium-wrappers" && lib.ready)
|
|
240
|
+
const lib = await import(libName);
|
|
241
|
+
if (libName === "libsodium-wrappers" && lib.ready) {
|
|
242
|
+
await lib.ready;
|
|
243
|
+
}
|
|
226
244
|
Object.assign(methods, libs[libName](lib));
|
|
227
245
|
break;
|
|
228
246
|
} catch {
|
|
229
247
|
}
|
|
230
248
|
}
|
|
231
|
-
|
|
249
|
+
resolve2();
|
|
250
|
+
});
|
|
232
251
|
|
|
233
252
|
// src/util/util.ts
|
|
234
253
|
var noop = /* @__PURE__ */ __name(() => {
|
|
@@ -503,7 +522,10 @@ var VoiceWebSocket = class extends import_node_events2.EventEmitter {
|
|
|
503
522
|
var CHANNELS = 2;
|
|
504
523
|
var TIMESTAMP_INC = 48e3 / 100 * CHANNELS;
|
|
505
524
|
var MAX_NONCE_SIZE = 2 ** 32 - 1;
|
|
506
|
-
var SUPPORTED_ENCRYPTION_MODES = ["
|
|
525
|
+
var SUPPORTED_ENCRYPTION_MODES = ["aead_xchacha20_poly1305_rtpsize"];
|
|
526
|
+
if (import_node_crypto.default.getCiphers().includes("aes-256-gcm")) {
|
|
527
|
+
SUPPORTED_ENCRYPTION_MODES.unshift("aead_aes256_gcm_rtpsize");
|
|
528
|
+
}
|
|
507
529
|
var nonce = import_node_buffer3.Buffer.alloc(24);
|
|
508
530
|
function stringifyState(state) {
|
|
509
531
|
return JSON.stringify({
|
|
@@ -736,7 +758,7 @@ to ${stringifyState(newState)}`);
|
|
|
736
758
|
sequence: randomNBit(16),
|
|
737
759
|
timestamp: randomNBit(32),
|
|
738
760
|
nonce: 0,
|
|
739
|
-
nonceBuffer: import_node_buffer3.Buffer.alloc(24),
|
|
761
|
+
nonceBuffer: encryptionMode === "aead_aes256_gcm_rtpsize" ? import_node_buffer3.Buffer.alloc(12) : import_node_buffer3.Buffer.alloc(24),
|
|
740
762
|
speaking: false,
|
|
741
763
|
packetsPlayed: 0
|
|
742
764
|
}
|
|
@@ -840,15 +862,15 @@ to ${stringifyState(newState)}`);
|
|
|
840
862
|
* @param connectionData - The current connection data of the instance
|
|
841
863
|
*/
|
|
842
864
|
createAudioPacket(opusPacket, connectionData) {
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
865
|
+
const rtpHeader = import_node_buffer3.Buffer.alloc(12);
|
|
866
|
+
rtpHeader[0] = 128;
|
|
867
|
+
rtpHeader[1] = 120;
|
|
846
868
|
const { sequence, timestamp, ssrc } = connectionData;
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
return import_node_buffer3.Buffer.concat([
|
|
869
|
+
rtpHeader.writeUIntBE(sequence, 2, 2);
|
|
870
|
+
rtpHeader.writeUIntBE(timestamp, 4, 4);
|
|
871
|
+
rtpHeader.writeUIntBE(ssrc, 8, 4);
|
|
872
|
+
rtpHeader.copy(nonce, 0, 0, 12);
|
|
873
|
+
return import_node_buffer3.Buffer.concat([rtpHeader, ...this.encryptOpusPacket(opusPacket, connectionData, rtpHeader)]);
|
|
852
874
|
}
|
|
853
875
|
/**
|
|
854
876
|
* Encrypts an Opus packet using the format agreed upon by the instance and Discord.
|
|
@@ -856,26 +878,39 @@ to ${stringifyState(newState)}`);
|
|
|
856
878
|
* @param opusPacket - The Opus packet to encrypt
|
|
857
879
|
* @param connectionData - The current connection data of the instance
|
|
858
880
|
*/
|
|
859
|
-
encryptOpusPacket(opusPacket, connectionData) {
|
|
881
|
+
encryptOpusPacket(opusPacket, connectionData, additionalData) {
|
|
860
882
|
const { secretKey, encryptionMode } = connectionData;
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
883
|
+
connectionData.nonce++;
|
|
884
|
+
if (connectionData.nonce > MAX_NONCE_SIZE) connectionData.nonce = 0;
|
|
885
|
+
connectionData.nonceBuffer.writeUInt32BE(connectionData.nonce, 0);
|
|
886
|
+
const noncePadding = connectionData.nonceBuffer.subarray(0, 4);
|
|
887
|
+
let encrypted;
|
|
888
|
+
switch (encryptionMode) {
|
|
889
|
+
case "aead_aes256_gcm_rtpsize": {
|
|
890
|
+
const cipher = import_node_crypto.default.createCipheriv("aes-256-gcm", secretKey, connectionData.nonceBuffer);
|
|
891
|
+
cipher.setAAD(additionalData);
|
|
892
|
+
encrypted = import_node_buffer3.Buffer.concat([cipher.update(opusPacket), cipher.final(), cipher.getAuthTag()]);
|
|
893
|
+
return [encrypted, noncePadding];
|
|
894
|
+
}
|
|
895
|
+
case "aead_xchacha20_poly1305_rtpsize": {
|
|
896
|
+
encrypted = methods.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
|
897
|
+
opusPacket,
|
|
898
|
+
additionalData,
|
|
899
|
+
connectionData.nonceBuffer,
|
|
900
|
+
secretKey
|
|
901
|
+
);
|
|
902
|
+
return [encrypted, noncePadding];
|
|
903
|
+
}
|
|
904
|
+
default: {
|
|
905
|
+
throw new RangeError(`Unsupported encryption method: ${encryptionMode}`);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
874
908
|
}
|
|
875
909
|
};
|
|
876
910
|
|
|
877
911
|
// src/receive/VoiceReceiver.ts
|
|
878
912
|
var import_node_buffer5 = require("buffer");
|
|
913
|
+
var import_node_crypto2 = __toESM(require("crypto"));
|
|
879
914
|
var import_v43 = require("discord-api-types/voice/v4");
|
|
880
915
|
|
|
881
916
|
// src/receive/AudioReceiveStream.ts
|
|
@@ -1466,6 +1501,9 @@ var SpeakingMap = class _SpeakingMap extends import_node_events6.EventEmitter {
|
|
|
1466
1501
|
};
|
|
1467
1502
|
|
|
1468
1503
|
// src/receive/VoiceReceiver.ts
|
|
1504
|
+
var HEADER_EXTENSION_BYTE = import_node_buffer5.Buffer.from([190, 222]);
|
|
1505
|
+
var UNPADDED_NONCE_LENGTH = 4;
|
|
1506
|
+
var AUTH_TAG_LENGTH = 16;
|
|
1469
1507
|
var VoiceReceiver = class {
|
|
1470
1508
|
static {
|
|
1471
1509
|
__name(this, "VoiceReceiver");
|
|
@@ -1521,19 +1559,37 @@ var VoiceReceiver = class {
|
|
|
1521
1559
|
}
|
|
1522
1560
|
}
|
|
1523
1561
|
decrypt(buffer, mode, nonce2, secretKey) {
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
buffer.
|
|
1562
|
+
buffer.copy(nonce2, 0, buffer.length - UNPADDED_NONCE_LENGTH);
|
|
1563
|
+
let headerSize = 12;
|
|
1564
|
+
const first = buffer.readUint8();
|
|
1565
|
+
if (first >> 4 & 1) headerSize += 4;
|
|
1566
|
+
const header = buffer.subarray(0, headerSize);
|
|
1567
|
+
const encrypted = buffer.subarray(headerSize, buffer.length - AUTH_TAG_LENGTH - UNPADDED_NONCE_LENGTH);
|
|
1568
|
+
const authTag = buffer.subarray(
|
|
1569
|
+
buffer.length - AUTH_TAG_LENGTH - UNPADDED_NONCE_LENGTH,
|
|
1570
|
+
buffer.length - UNPADDED_NONCE_LENGTH
|
|
1571
|
+
);
|
|
1572
|
+
switch (mode) {
|
|
1573
|
+
case "aead_aes256_gcm_rtpsize": {
|
|
1574
|
+
const decipheriv = import_node_crypto2.default.createDecipheriv("aes-256-gcm", secretKey, nonce2);
|
|
1575
|
+
decipheriv.setAAD(header);
|
|
1576
|
+
decipheriv.setAuthTag(authTag);
|
|
1577
|
+
return import_node_buffer5.Buffer.concat([decipheriv.update(encrypted), decipheriv.final()]);
|
|
1578
|
+
}
|
|
1579
|
+
case "aead_xchacha20_poly1305_rtpsize": {
|
|
1580
|
+
return import_node_buffer5.Buffer.from(
|
|
1581
|
+
methods.crypto_aead_xchacha20poly1305_ietf_decrypt(
|
|
1582
|
+
import_node_buffer5.Buffer.concat([encrypted, authTag]),
|
|
1583
|
+
header,
|
|
1584
|
+
nonce2,
|
|
1585
|
+
secretKey
|
|
1586
|
+
)
|
|
1587
|
+
);
|
|
1588
|
+
}
|
|
1589
|
+
default: {
|
|
1590
|
+
throw new RangeError(`Unsupported decryption method: ${mode}`);
|
|
1591
|
+
}
|
|
1533
1592
|
}
|
|
1534
|
-
const decrypted = methods.open(buffer.slice(12, end), nonce2, secretKey);
|
|
1535
|
-
if (!decrypted) return;
|
|
1536
|
-
return import_node_buffer5.Buffer.from(decrypted);
|
|
1537
1593
|
}
|
|
1538
1594
|
/**
|
|
1539
1595
|
* Parses an audio packet, decrypting it to yield an Opus packet.
|
|
@@ -1547,9 +1603,9 @@ var VoiceReceiver = class {
|
|
|
1547
1603
|
parsePacket(buffer, mode, nonce2, secretKey) {
|
|
1548
1604
|
let packet = this.decrypt(buffer, mode, nonce2, secretKey);
|
|
1549
1605
|
if (!packet) return;
|
|
1550
|
-
if (
|
|
1551
|
-
const headerExtensionLength =
|
|
1552
|
-
packet = packet.subarray(4
|
|
1606
|
+
if (buffer.subarray(12, 14).compare(HEADER_EXTENSION_BYTE) === 0) {
|
|
1607
|
+
const headerExtensionLength = buffer.subarray(14).readUInt16BE();
|
|
1608
|
+
packet = packet.subarray(4 * headerExtensionLength);
|
|
1553
1609
|
}
|
|
1554
1610
|
return packet;
|
|
1555
1611
|
}
|
|
@@ -2432,7 +2488,7 @@ __name(findPackageJSON, "findPackageJSON");
|
|
|
2432
2488
|
function version(name) {
|
|
2433
2489
|
try {
|
|
2434
2490
|
if (name === "@discordjs/voice") {
|
|
2435
|
-
return "0.
|
|
2491
|
+
return "0.18.0";
|
|
2436
2492
|
}
|
|
2437
2493
|
const pkg = findPackageJSON((0, import_node_path.dirname)(require.resolve(name)), name, 3);
|
|
2438
2494
|
return pkg?.version ?? "not found";
|
|
@@ -2456,7 +2512,8 @@ function generateDependencyReport() {
|
|
|
2456
2512
|
addVersion("sodium-native");
|
|
2457
2513
|
addVersion("sodium");
|
|
2458
2514
|
addVersion("libsodium-wrappers");
|
|
2459
|
-
addVersion("
|
|
2515
|
+
addVersion("@stablelib/xchacha20poly1305");
|
|
2516
|
+
addVersion("@noble/ciphers");
|
|
2460
2517
|
report.push("");
|
|
2461
2518
|
report.push("FFmpeg");
|
|
2462
2519
|
try {
|
|
@@ -2575,7 +2632,7 @@ async function demuxProbe(stream, probeSize = 1024, validator = validateDiscordO
|
|
|
2575
2632
|
__name(demuxProbe, "demuxProbe");
|
|
2576
2633
|
|
|
2577
2634
|
// src/index.ts
|
|
2578
|
-
var version2 = "0.
|
|
2635
|
+
var version2 = "0.18.0";
|
|
2579
2636
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2580
2637
|
0 && (module.exports = {
|
|
2581
2638
|
AudioPlayer,
|