@discordjs/voice 0.6.0 → 0.7.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/LICENSE +190 -21
- package/README.md +41 -18
- package/dist/index.d.ts +155 -89
- package/dist/index.js +10 -26
- package/dist/index.js.map +7 -1
- package/dist/index.mjs +10 -0
- package/dist/index.mjs.map +7 -0
- package/package.json +67 -96
- package/dist/DataStore.js +0 -151
- package/dist/DataStore.js.map +0 -1
- package/dist/VoiceConnection.js +0 -529
- package/dist/VoiceConnection.js.map +0 -1
- package/dist/audio/AudioPlayer.js +0 -449
- package/dist/audio/AudioPlayer.js.map +0 -1
- package/dist/audio/AudioPlayerError.js +0 -17
- package/dist/audio/AudioPlayerError.js.map +0 -1
- package/dist/audio/AudioResource.js +0 -162
- package/dist/audio/AudioResource.js.map +0 -1
- package/dist/audio/PlayerSubscription.js +0 -23
- package/dist/audio/PlayerSubscription.js.map +0 -1
- package/dist/audio/TransformerGraph.js +0 -233
- package/dist/audio/TransformerGraph.js.map +0 -1
- package/dist/audio/index.js +0 -18
- package/dist/audio/index.js.map +0 -1
- package/dist/joinVoiceChannel.js +0 -24
- package/dist/joinVoiceChannel.js.map +0 -1
- package/dist/networking/Networking.js +0 -457
- package/dist/networking/Networking.js.map +0 -1
- package/dist/networking/VoiceUDPSocket.js +0 -145
- package/dist/networking/VoiceUDPSocket.js.map +0 -1
- package/dist/networking/VoiceWebSocket.js +0 -129
- package/dist/networking/VoiceWebSocket.js.map +0 -1
- package/dist/networking/index.js +0 -16
- package/dist/networking/index.js.map +0 -1
- package/dist/receive/AudioReceiveStream.js +0 -64
- package/dist/receive/AudioReceiveStream.js.map +0 -1
- package/dist/receive/SSRCMap.js +0 -69
- package/dist/receive/SSRCMap.js.map +0 -1
- package/dist/receive/SpeakingMap.js +0 -38
- package/dist/receive/SpeakingMap.js.map +0 -1
- package/dist/receive/VoiceReceiver.js +0 -149
- package/dist/receive/VoiceReceiver.js.map +0 -1
- package/dist/receive/index.js +0 -16
- package/dist/receive/index.js.map +0 -1
- package/dist/util/Secretbox.js +0 -50
- package/dist/util/Secretbox.js.map +0 -1
- package/dist/util/abortAfter.js +0 -15
- package/dist/util/abortAfter.js.map +0 -1
- package/dist/util/adapter.js +0 -3
- package/dist/util/adapter.js.map +0 -1
- package/dist/util/demuxProbe.js +0 -90
- package/dist/util/demuxProbe.js.map +0 -1
- package/dist/util/entersState.js +0 -26
- package/dist/util/entersState.js.map +0 -1
- package/dist/util/generateDependencyReport.js +0 -82
- package/dist/util/generateDependencyReport.js.map +0 -1
- package/dist/util/index.js +0 -17
- package/dist/util/index.js.map +0 -1
- package/dist/util/util.js +0 -7
- package/dist/util/util.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VoiceUDPSocket.js","sourceRoot":"","sources":["../../src/networking/VoiceUDPSocket.ts"],"names":[],"mappings":"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","sourcesContent":["import { createSocket, Socket } from 'dgram';\nimport { isIPv4 } from 'net';\nimport { TypedEmitter } from 'tiny-typed-emitter';\nimport { Awaited } from '../util/util';\n\n/**\n * Stores an IP address and port. Used to store socket details for the local client as well as\n * for Discord.\n */\nexport interface SocketConfig {\n\tip: string;\n\tport: number;\n}\n\ninterface KeepAlive {\n\tvalue: number;\n\ttimestamp: number;\n}\n\nexport interface VoiceUDPSocketEvents {\n\terror: (error: Error) => Awaited<void>;\n\tclose: () => Awaited<void>;\n\tdebug: (message: string) => Awaited<void>;\n\tmessage: (message: Buffer) => Awaited<void>;\n}\n\n/**\n * The interval in milliseconds at which keep alive datagrams are sent\n */\nconst KEEP_ALIVE_INTERVAL = 5e3;\n\n/**\n * The maximum number of keep alive packets which can be missed\n */\nconst KEEP_ALIVE_LIMIT = 12;\n\n/**\n * The maximum value of the keep alive counter\n */\nconst MAX_COUNTER_VALUE = 2 ** 32 - 1;\n\n/**\n * Manages the UDP networking for a voice connection.\n */\nexport class VoiceUDPSocket extends TypedEmitter<VoiceUDPSocketEvents> {\n\t/**\n\t * The underlying network Socket for the VoiceUDPSocket.\n\t */\n\tprivate readonly socket: Socket;\n\n\t/**\n\t * The socket details for Discord (remote).\n\t */\n\tprivate readonly remote: SocketConfig;\n\n\t/**\n\t * A list of keep alives that are waiting to be acknowledged.\n\t */\n\tprivate readonly keepAlives: KeepAlive[];\n\n\t/**\n\t * The counter used in the keep alive mechanism\n\t */\n\tprivate keepAliveCounter = 0;\n\n\t/**\n\t * The buffer used to write the keep alive counter into\n\t */\n\tprivate readonly keepAliveBuffer: Buffer;\n\n\t/**\n\t * The Node.js interval for the keep-alive mechanism\n\t */\n\tprivate readonly keepAliveInterval: NodeJS.Timeout;\n\n\t/**\n\t * The time taken to receive a response to keep alive messages\n\t */\n\tpublic ping?: number;\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 VoiceUDPSocket.\n\t *\n\t * @param remote - Details of the remote socket\n\t */\n\tpublic constructor(remote: SocketConfig, debug = false) {\n\t\tsuper();\n\t\tthis.socket = createSocket('udp4');\n\t\tthis.socket.on('error', (error: Error) => this.emit('error', error));\n\t\tthis.socket.on('message', (buffer: Buffer) => this.onMessage(buffer));\n\t\tthis.socket.on('close', () => this.emit('close'));\n\t\tthis.remote = remote;\n\t\tthis.keepAlives = [];\n\t\tthis.keepAliveBuffer = Buffer.alloc(8);\n\t\tthis.keepAliveInterval = setInterval(() => this.keepAlive(), KEEP_ALIVE_INTERVAL);\n\t\tsetImmediate(() => this.keepAlive());\n\n\t\tthis.debug = debug ? (message: string) => this.emit('debug', message) : null;\n\t}\n\n\t/**\n\t * Called when a message is received on the UDP socket\n\t * @param buffer The received buffer\n\t */\n\tprivate onMessage(buffer: Buffer): void {\n\t\t// Handle keep alive message\n\t\tif (buffer.length === 8) {\n\t\t\tconst counter = buffer.readUInt32LE(0);\n\t\t\tconst index = this.keepAlives.findIndex(({ value }) => value === counter);\n\t\t\tif (index === -1) return;\n\t\t\tthis.ping = Date.now() - this.keepAlives[index].timestamp;\n\t\t\t// Delete all keep alives up to and including the received one\n\t\t\tthis.keepAlives.splice(0, index);\n\t\t}\n\t\t// Propagate the message\n\t\tthis.emit('message', buffer);\n\t}\n\n\t/**\n\t * Called at a regular interval to check whether we are still able to send datagrams to Discord\n\t */\n\tprivate keepAlive() {\n\t\tif (this.keepAlives.length >= KEEP_ALIVE_LIMIT) {\n\t\t\tthis.debug?.('UDP socket has not received enough responses from Discord - closing socket');\n\t\t\tthis.destroy();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.keepAliveBuffer.writeUInt32LE(this.keepAliveCounter, 0);\n\t\tthis.send(this.keepAliveBuffer);\n\t\tthis.keepAlives.push({\n\t\t\tvalue: this.keepAliveCounter,\n\t\t\ttimestamp: Date.now(),\n\t\t});\n\t\tthis.keepAliveCounter++;\n\t\tif (this.keepAliveCounter > MAX_COUNTER_VALUE) {\n\t\t\tthis.keepAliveCounter = 0;\n\t\t}\n\t}\n\n\t/**\n\t * Sends a buffer to Discord.\n\t *\n\t * @param buffer - The buffer to send\n\t */\n\tpublic send(buffer: Buffer) {\n\t\treturn this.socket.send(buffer, this.remote.port, this.remote.ip);\n\t}\n\n\t/**\n\t * Closes the socket, the instance will not be able to be reused.\n\t */\n\tpublic destroy() {\n\t\ttry {\n\t\t\tthis.socket.close();\n\t\t} catch {}\n\t\tclearInterval(this.keepAliveInterval);\n\t}\n\n\t/**\n\t * Performs IP discovery to discover the local address and port to be used for the voice connection.\n\t *\n\t * @param ssrc - The SSRC received from Discord\n\t */\n\tpublic performIPDiscovery(ssrc: number): Promise<SocketConfig> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst listener = (message: Buffer) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (message.readUInt16BE(0) !== 2) return;\n\t\t\t\t\tconst packet = parseLocalPacket(message);\n\t\t\t\t\tthis.socket.off('message', listener);\n\t\t\t\t\tresolve(packet);\n\t\t\t\t} catch {}\n\t\t\t};\n\n\t\t\tthis.socket.on('message', listener);\n\t\t\tthis.socket.once('close', () => reject(new Error('Cannot perform IP discovery - socket closed')));\n\n\t\t\tconst discoveryBuffer = Buffer.alloc(74);\n\n\t\t\tdiscoveryBuffer.writeUInt16BE(1, 0);\n\t\t\tdiscoveryBuffer.writeUInt16BE(70, 2);\n\t\t\tdiscoveryBuffer.writeUInt32BE(ssrc, 4);\n\t\t\tthis.send(discoveryBuffer);\n\t\t});\n\t}\n}\n\n/**\n * Parses the response from Discord to aid with local IP discovery.\n *\n * @param message - The received message\n */\nexport function parseLocalPacket(message: Buffer): SocketConfig {\n\tconst packet = Buffer.from(message);\n\n\tconst ip = packet.slice(8, packet.indexOf(0, 8)).toString('utf-8');\n\n\tif (!isIPv4(ip)) {\n\t\tthrow new Error('Malformed IP address');\n\t}\n\n\tconst port = packet.readUInt16BE(packet.length - 2);\n\n\treturn { ip, port };\n}\n"]}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.VoiceWebSocket = void 0;
|
|
7
|
-
const ws_1 = __importDefault(require("ws"));
|
|
8
|
-
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
|
|
9
|
-
/**
|
|
10
|
-
* An extension of the WebSocket class to provide helper functionality when interacting
|
|
11
|
-
* with the Discord Voice gateway.
|
|
12
|
-
*/
|
|
13
|
-
class VoiceWebSocket extends tiny_typed_emitter_1.TypedEmitter {
|
|
14
|
-
/**
|
|
15
|
-
* Creates a new VoiceWebSocket
|
|
16
|
-
*
|
|
17
|
-
* @param address - The address to connect to
|
|
18
|
-
*/
|
|
19
|
-
constructor(address, debug) {
|
|
20
|
-
super();
|
|
21
|
-
/**
|
|
22
|
-
* The number of consecutively missed heartbeats.
|
|
23
|
-
*/
|
|
24
|
-
this.missedHeartbeats = 0;
|
|
25
|
-
this.ws = new ws_1.default(address);
|
|
26
|
-
this.ws.onmessage = (e) => this.onMessage(e);
|
|
27
|
-
this.ws.onopen = (e) => this.emit('open', e);
|
|
28
|
-
this.ws.onerror = (e) => this.emit('error', e instanceof Error ? e : e.error);
|
|
29
|
-
this.ws.onclose = (e) => this.emit('close', e);
|
|
30
|
-
this.lastHeartbeatAck = 0;
|
|
31
|
-
this.lastHeatbeatSend = 0;
|
|
32
|
-
this.debug = debug ? (message) => this.emit('debug', message) : null;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Destroys the VoiceWebSocket. The heartbeat interval is cleared, and the connection is closed.
|
|
36
|
-
*/
|
|
37
|
-
destroy() {
|
|
38
|
-
var _a;
|
|
39
|
-
try {
|
|
40
|
-
(_a = this.debug) === null || _a === void 0 ? void 0 : _a.call(this, 'destroyed');
|
|
41
|
-
this.setHeartbeatInterval(-1);
|
|
42
|
-
this.ws.close(1000);
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
this.emit('error', error);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Handles message events on the WebSocket. Attempts to JSON parse the messages and emit them
|
|
50
|
-
* as packets.
|
|
51
|
-
*
|
|
52
|
-
* @param event - The message event
|
|
53
|
-
*/
|
|
54
|
-
onMessage(event) {
|
|
55
|
-
var _a;
|
|
56
|
-
if (typeof event.data !== 'string')
|
|
57
|
-
return;
|
|
58
|
-
(_a = this.debug) === null || _a === void 0 ? void 0 : _a.call(this, `<< ${event.data}`);
|
|
59
|
-
let packet;
|
|
60
|
-
try {
|
|
61
|
-
packet = JSON.parse(event.data);
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
this.emit('error', error);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
if (packet.op === 6 /* HeartbeatAck */) {
|
|
68
|
-
this.lastHeartbeatAck = Date.now();
|
|
69
|
-
this.missedHeartbeats = 0;
|
|
70
|
-
this.ping = this.lastHeartbeatAck - this.lastHeatbeatSend;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Packet event.
|
|
74
|
-
*
|
|
75
|
-
* @event VoiceWebSocket#packet
|
|
76
|
-
* @type {any}
|
|
77
|
-
*/
|
|
78
|
-
this.emit('packet', packet);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Sends a JSON-stringifiable packet over the WebSocket
|
|
82
|
-
*
|
|
83
|
-
* @param packet - The packet to send
|
|
84
|
-
*/
|
|
85
|
-
sendPacket(packet) {
|
|
86
|
-
var _a;
|
|
87
|
-
try {
|
|
88
|
-
const stringified = JSON.stringify(packet);
|
|
89
|
-
(_a = this.debug) === null || _a === void 0 ? void 0 : _a.call(this, `>> ${stringified}`);
|
|
90
|
-
return this.ws.send(stringified);
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
this.emit('error', error);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Sends a heartbeat over the WebSocket
|
|
98
|
-
*/
|
|
99
|
-
sendHeartbeat() {
|
|
100
|
-
this.lastHeatbeatSend = Date.now();
|
|
101
|
-
this.missedHeartbeats++;
|
|
102
|
-
const nonce = this.lastHeatbeatSend;
|
|
103
|
-
return this.sendPacket({
|
|
104
|
-
op: 3 /* Heartbeat */,
|
|
105
|
-
d: nonce,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Sets/clears an interval to send heartbeats over the WebSocket
|
|
110
|
-
*
|
|
111
|
-
* @param ms - The interval in milliseconds. If negative, the interval will be unset.
|
|
112
|
-
*/
|
|
113
|
-
setHeartbeatInterval(ms) {
|
|
114
|
-
if (typeof this.heartbeatInterval !== 'undefined')
|
|
115
|
-
clearInterval(this.heartbeatInterval);
|
|
116
|
-
if (ms > 0) {
|
|
117
|
-
this.heartbeatInterval = setInterval(() => {
|
|
118
|
-
if (this.lastHeatbeatSend !== 0 && this.missedHeartbeats >= 3) {
|
|
119
|
-
// Missed too many heartbeats - disconnect
|
|
120
|
-
this.ws.close();
|
|
121
|
-
this.setHeartbeatInterval(-1);
|
|
122
|
-
}
|
|
123
|
-
this.sendHeartbeat();
|
|
124
|
-
}, ms);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
exports.VoiceWebSocket = VoiceWebSocket;
|
|
129
|
-
//# sourceMappingURL=VoiceWebSocket.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VoiceWebSocket.js","sourceRoot":"","sources":["../../src/networking/VoiceWebSocket.ts"],"names":[],"mappings":"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","sourcesContent":["import { VoiceOpcodes } from 'discord-api-types/voice/v4';\nimport WebSocket, { MessageEvent } from 'ws';\nimport { TypedEmitter } from 'tiny-typed-emitter';\nimport { Awaited } from '../util/util';\n\n/**\n * Debug event for VoiceWebSocket.\n *\n * @event VoiceWebSocket#debug\n * @type {string}\n */\n\nexport interface VoiceWebSocketEvents {\n\terror: (error: Error) => Awaited<void>;\n\topen: (event: WebSocket.OpenEvent) => Awaited<void>;\n\tclose: (event: WebSocket.CloseEvent) => Awaited<void>;\n\tdebug: (message: string) => Awaited<void>;\n\tpacket: (packet: any) => Awaited<void>;\n}\n\n/**\n * An extension of the WebSocket class to provide helper functionality when interacting\n * with the Discord Voice gateway.\n */\nexport class VoiceWebSocket extends TypedEmitter<VoiceWebSocketEvents> {\n\t/**\n\t * The current heartbeat interval, if any\n\t */\n\tprivate heartbeatInterval?: NodeJS.Timeout;\n\n\t/**\n\t * The time (milliseconds since UNIX epoch) that the last heartbeat acknowledgement packet was received.\n\t * This is set to 0 if an acknowledgement packet hasn't been received yet.\n\t */\n\tprivate lastHeartbeatAck: number;\n\n\t/**\n\t * The time (milliseconds since UNIX epoch) that the last heartbeat was sent. This is set to 0 if a heartbeat\n\t * hasn't been sent yet.\n\t */\n\tprivate lastHeatbeatSend: number;\n\n\t/**\n\t * The number of consecutively missed heartbeats.\n\t */\n\tprivate missedHeartbeats = 0;\n\n\t/**\n\t * The last recorded ping.\n\t */\n\tpublic ping?: number;\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 * The underlying WebSocket of this wrapper\n\t */\n\tprivate readonly ws: WebSocket;\n\n\t/**\n\t * Creates a new VoiceWebSocket\n\t *\n\t * @param address - The address to connect to\n\t */\n\tpublic constructor(address: string, debug: boolean) {\n\t\tsuper();\n\t\tthis.ws = new WebSocket(address);\n\t\tthis.ws.onmessage = (e) => this.onMessage(e);\n\t\tthis.ws.onopen = (e) => this.emit('open', e);\n\t\tthis.ws.onerror = (e: Error | WebSocket.ErrorEvent) => this.emit('error', e instanceof Error ? e : e.error);\n\t\tthis.ws.onclose = (e) => this.emit('close', e);\n\n\t\tthis.lastHeartbeatAck = 0;\n\t\tthis.lastHeatbeatSend = 0;\n\n\t\tthis.debug = debug ? (message: string) => this.emit('debug', message) : null;\n\t}\n\n\t/**\n\t * Destroys the VoiceWebSocket. The heartbeat interval is cleared, and the connection is closed.\n\t */\n\tpublic destroy() {\n\t\ttry {\n\t\t\tthis.debug?.('destroyed');\n\t\t\tthis.setHeartbeatInterval(-1);\n\t\t\tthis.ws.close(1000);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t}\n\t}\n\n\t/**\n\t * Handles message events on the WebSocket. Attempts to JSON parse the messages and emit them\n\t * as packets.\n\t *\n\t * @param event - The message event\n\t */\n\tpublic onMessage(event: MessageEvent) {\n\t\tif (typeof event.data !== 'string') return;\n\n\t\tthis.debug?.(`<< ${event.data}`);\n\n\t\tlet packet: any;\n\t\ttry {\n\t\t\tpacket = JSON.parse(event.data);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t\treturn;\n\t\t}\n\n\t\tif (packet.op === VoiceOpcodes.HeartbeatAck) {\n\t\t\tthis.lastHeartbeatAck = Date.now();\n\t\t\tthis.missedHeartbeats = 0;\n\t\t\tthis.ping = this.lastHeartbeatAck - this.lastHeatbeatSend;\n\t\t}\n\n\t\t/**\n\t\t * Packet event.\n\t\t *\n\t\t * @event VoiceWebSocket#packet\n\t\t * @type {any}\n\t\t */\n\t\tthis.emit('packet', packet);\n\t}\n\n\t/**\n\t * Sends a JSON-stringifiable packet over the WebSocket\n\t *\n\t * @param packet - The packet to send\n\t */\n\tpublic sendPacket(packet: any) {\n\t\ttry {\n\t\t\tconst stringified = JSON.stringify(packet);\n\t\t\tthis.debug?.(`>> ${stringified}`);\n\t\t\treturn this.ws.send(stringified);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t}\n\t}\n\n\t/**\n\t * Sends a heartbeat over the WebSocket\n\t */\n\tprivate sendHeartbeat() {\n\t\tthis.lastHeatbeatSend = Date.now();\n\t\tthis.missedHeartbeats++;\n\t\tconst nonce = this.lastHeatbeatSend;\n\t\treturn this.sendPacket({\n\t\t\top: VoiceOpcodes.Heartbeat,\n\t\t\td: nonce,\n\t\t});\n\t}\n\n\t/**\n\t * Sets/clears an interval to send heartbeats over the WebSocket\n\t *\n\t * @param ms - The interval in milliseconds. If negative, the interval will be unset.\n\t */\n\tpublic setHeartbeatInterval(ms: number) {\n\t\tif (typeof this.heartbeatInterval !== 'undefined') clearInterval(this.heartbeatInterval);\n\t\tif (ms > 0) {\n\t\t\tthis.heartbeatInterval = setInterval(() => {\n\t\t\t\tif (this.lastHeatbeatSend !== 0 && this.missedHeartbeats >= 3) {\n\t\t\t\t\t// Missed too many heartbeats - disconnect\n\t\t\t\t\tthis.ws.close();\n\t\t\t\t\tthis.setHeartbeatInterval(-1);\n\t\t\t\t}\n\t\t\t\tthis.sendHeartbeat();\n\t\t\t}, ms);\n\t\t}\n\t}\n}\n"]}
|
package/dist/networking/index.js
DELETED
|
@@ -1,16 +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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
-
};
|
|
12
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
__exportStar(require("./Networking"), exports);
|
|
14
|
-
__exportStar(require("./VoiceUDPSocket"), exports);
|
|
15
|
-
__exportStar(require("./VoiceWebSocket"), exports);
|
|
16
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/networking/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,+CAA6B;AAC7B,mDAAiC;AACjC,mDAAiC","sourcesContent":["export * from './Networking';\nexport * from './VoiceUDPSocket';\nexport * from './VoiceWebSocket';\n"]}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AudioReceiveStream = exports.createDefaultAudioReceiveStreamOptions = exports.EndBehaviorType = void 0;
|
|
4
|
-
const stream_1 = require("stream");
|
|
5
|
-
const AudioPlayer_1 = require("../audio/AudioPlayer");
|
|
6
|
-
/**
|
|
7
|
-
* The different behaviors an audio receive stream can have for deciding when to end.
|
|
8
|
-
*/
|
|
9
|
-
var EndBehaviorType;
|
|
10
|
-
(function (EndBehaviorType) {
|
|
11
|
-
/**
|
|
12
|
-
* The stream will only end when manually destroyed.
|
|
13
|
-
*/
|
|
14
|
-
EndBehaviorType[EndBehaviorType["Manual"] = 0] = "Manual";
|
|
15
|
-
/**
|
|
16
|
-
* The stream will end after a given time period of silence/no audio packets.
|
|
17
|
-
*/
|
|
18
|
-
EndBehaviorType[EndBehaviorType["AfterSilence"] = 1] = "AfterSilence";
|
|
19
|
-
/**
|
|
20
|
-
* The stream will end after a given time period of no audio packets.
|
|
21
|
-
*/
|
|
22
|
-
EndBehaviorType[EndBehaviorType["AfterInactivity"] = 2] = "AfterInactivity";
|
|
23
|
-
})(EndBehaviorType = exports.EndBehaviorType || (exports.EndBehaviorType = {}));
|
|
24
|
-
function createDefaultAudioReceiveStreamOptions() {
|
|
25
|
-
return {
|
|
26
|
-
end: {
|
|
27
|
-
behavior: EndBehaviorType.Manual,
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
exports.createDefaultAudioReceiveStreamOptions = createDefaultAudioReceiveStreamOptions;
|
|
32
|
-
/**
|
|
33
|
-
* A readable stream of Opus packets received from a specific entity
|
|
34
|
-
* in a Discord voice connection.
|
|
35
|
-
*/
|
|
36
|
-
class AudioReceiveStream extends stream_1.Readable {
|
|
37
|
-
constructor({ end, ...options }) {
|
|
38
|
-
super({
|
|
39
|
-
...options,
|
|
40
|
-
objectMode: true,
|
|
41
|
-
});
|
|
42
|
-
this.end = end;
|
|
43
|
-
}
|
|
44
|
-
push(buffer) {
|
|
45
|
-
if (buffer) {
|
|
46
|
-
if (this.end.behavior === EndBehaviorType.AfterInactivity ||
|
|
47
|
-
(this.end.behavior === EndBehaviorType.AfterSilence &&
|
|
48
|
-
(buffer.compare(AudioPlayer_1.SILENCE_FRAME) !== 0 || typeof this.endTimeout === 'undefined'))) {
|
|
49
|
-
this.renewEndTimeout(this.end);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return super.push(buffer);
|
|
53
|
-
}
|
|
54
|
-
renewEndTimeout(end) {
|
|
55
|
-
if (this.endTimeout) {
|
|
56
|
-
clearTimeout(this.endTimeout);
|
|
57
|
-
}
|
|
58
|
-
this.endTimeout = setTimeout(() => this.push(null), end.duration);
|
|
59
|
-
}
|
|
60
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
61
|
-
_read() { }
|
|
62
|
-
}
|
|
63
|
-
exports.AudioReceiveStream = AudioReceiveStream;
|
|
64
|
-
//# sourceMappingURL=AudioReceiveStream.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AudioReceiveStream.js","sourceRoot":"","sources":["../../src/receive/AudioReceiveStream.ts"],"names":[],"mappings":";;;AAAA,mCAAmD;AACnD,sDAAqD;AAErD;;GAEG;AACH,IAAY,eAaX;AAbD,WAAY,eAAe;IAC1B;;OAEG;IACH,yDAAM,CAAA;IACN;;OAEG;IACH,qEAAY,CAAA;IACZ;;OAEG;IACH,2EAAe,CAAA;AAChB,CAAC,EAbW,eAAe,GAAf,uBAAe,KAAf,uBAAe,QAa1B;AAeD,SAAgB,sCAAsC;IACrD,OAAO;QACN,GAAG,EAAE;YACJ,QAAQ,EAAE,eAAe,CAAC,MAAM;SAChC;KACD,CAAC;AACH,CAAC;AAND,wFAMC;AAED;;;GAGG;AACH,MAAa,kBAAmB,SAAQ,iBAAQ;IAQ/C,YAAmB,EAAE,GAAG,EAAE,GAAG,OAAO,EAA6B;QAChE,KAAK,CAAC;YACL,GAAG,OAAO;YACV,UAAU,EAAE,IAAI;SAChB,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IAChB,CAAC;IAEM,IAAI,CAAC,MAAqB;QAChC,IAAI,MAAM,EAAE;YACX,IACC,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,eAAe,CAAC,eAAe;gBACrD,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,eAAe,CAAC,YAAY;oBAClD,CAAC,MAAM,CAAC,OAAO,CAAC,2BAAa,CAAC,KAAK,CAAC,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,WAAW,CAAC,CAAC,EAChF;gBACD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;aAC/B;SACD;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAEO,eAAe,CAAC,GAAuC;QAC9D,IAAI,IAAI,CAAC,UAAU,EAAE;YACpB,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;SAC9B;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnE,CAAC;IAED,gEAAgE;IACzD,KAAK,KAAI,CAAC;CACjB;AAxCD,gDAwCC","sourcesContent":["import { Readable, ReadableOptions } from 'stream';\nimport { SILENCE_FRAME } from '../audio/AudioPlayer';\n\n/**\n * The different behaviors an audio receive stream can have for deciding when to end.\n */\nexport enum EndBehaviorType {\n\t/**\n\t * The stream will only end when manually destroyed.\n\t */\n\tManual,\n\t/**\n\t * The stream will end after a given time period of silence/no audio packets.\n\t */\n\tAfterSilence,\n\t/**\n\t * The stream will end after a given time period of no audio packets.\n\t */\n\tAfterInactivity,\n}\n\nexport type EndBehavior =\n\t| {\n\t\t\tbehavior: EndBehaviorType.Manual;\n\t }\n\t| {\n\t\t\tbehavior: EndBehaviorType.AfterSilence | EndBehaviorType.AfterInactivity;\n\t\t\tduration: number;\n\t };\n\nexport interface AudioReceiveStreamOptions extends ReadableOptions {\n\tend: EndBehavior;\n}\n\nexport function createDefaultAudioReceiveStreamOptions(): AudioReceiveStreamOptions {\n\treturn {\n\t\tend: {\n\t\t\tbehavior: EndBehaviorType.Manual,\n\t\t},\n\t};\n}\n\n/**\n * A readable stream of Opus packets received from a specific entity\n * in a Discord voice connection.\n */\nexport class AudioReceiveStream extends Readable {\n\t/**\n\t * The end behavior of the receive stream.\n\t */\n\tpublic readonly end: EndBehavior;\n\n\tprivate endTimeout?: NodeJS.Timeout;\n\n\tpublic constructor({ end, ...options }: AudioReceiveStreamOptions) {\n\t\tsuper({\n\t\t\t...options,\n\t\t\tobjectMode: true,\n\t\t});\n\n\t\tthis.end = end;\n\t}\n\n\tpublic push(buffer: Buffer | null) {\n\t\tif (buffer) {\n\t\t\tif (\n\t\t\t\tthis.end.behavior === EndBehaviorType.AfterInactivity ||\n\t\t\t\t(this.end.behavior === EndBehaviorType.AfterSilence &&\n\t\t\t\t\t(buffer.compare(SILENCE_FRAME) !== 0 || typeof this.endTimeout === 'undefined'))\n\t\t\t) {\n\t\t\t\tthis.renewEndTimeout(this.end);\n\t\t\t}\n\t\t}\n\n\t\treturn super.push(buffer);\n\t}\n\n\tprivate renewEndTimeout(end: EndBehavior & { duration: number }) {\n\t\tif (this.endTimeout) {\n\t\t\tclearTimeout(this.endTimeout);\n\t\t}\n\t\tthis.endTimeout = setTimeout(() => this.push(null), end.duration);\n\t}\n\n\t// eslint-disable-next-line @typescript-eslint/no-empty-function\n\tpublic _read() {}\n}\n"]}
|
package/dist/receive/SSRCMap.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SSRCMap = void 0;
|
|
4
|
-
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
|
|
5
|
-
/**
|
|
6
|
-
* Maps audio SSRCs to data of users in voice connections.
|
|
7
|
-
*/
|
|
8
|
-
class SSRCMap extends tiny_typed_emitter_1.TypedEmitter {
|
|
9
|
-
constructor() {
|
|
10
|
-
super();
|
|
11
|
-
this.map = new Map();
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Updates the map with new user data
|
|
15
|
-
*
|
|
16
|
-
* @param data The data to update with
|
|
17
|
-
*/
|
|
18
|
-
update(data) {
|
|
19
|
-
const existing = this.map.get(data.audioSSRC);
|
|
20
|
-
const newValue = {
|
|
21
|
-
...this.map.get(data.audioSSRC),
|
|
22
|
-
...data,
|
|
23
|
-
};
|
|
24
|
-
this.map.set(data.audioSSRC, newValue);
|
|
25
|
-
if (!existing)
|
|
26
|
-
this.emit('create', newValue);
|
|
27
|
-
this.emit('update', existing, newValue);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Gets the stored voice data of a user.
|
|
31
|
-
*
|
|
32
|
-
* @param target The target, either their user ID or audio SSRC
|
|
33
|
-
*/
|
|
34
|
-
get(target) {
|
|
35
|
-
if (typeof target === 'number') {
|
|
36
|
-
return this.map.get(target);
|
|
37
|
-
}
|
|
38
|
-
for (const data of this.map.values()) {
|
|
39
|
-
if (data.userId === target) {
|
|
40
|
-
return data;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Deletes the stored voice data about a user.
|
|
46
|
-
*
|
|
47
|
-
* @param target The target of the delete operation, either their audio SSRC or user ID
|
|
48
|
-
* @returns The data that was deleted, if any
|
|
49
|
-
*/
|
|
50
|
-
delete(target) {
|
|
51
|
-
if (typeof target === 'number') {
|
|
52
|
-
const existing = this.map.get(target);
|
|
53
|
-
if (existing) {
|
|
54
|
-
this.map.delete(target);
|
|
55
|
-
this.emit('delete', existing);
|
|
56
|
-
}
|
|
57
|
-
return existing;
|
|
58
|
-
}
|
|
59
|
-
for (const [audioSSRC, data] of this.map.entries()) {
|
|
60
|
-
if (data.userId === target) {
|
|
61
|
-
this.map.delete(audioSSRC);
|
|
62
|
-
this.emit('delete', data);
|
|
63
|
-
return data;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
exports.SSRCMap = SSRCMap;
|
|
69
|
-
//# sourceMappingURL=SSRCMap.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SSRCMap.js","sourceRoot":"","sources":["../../src/receive/SSRCMap.ts"],"names":[],"mappings":";;;AAAA,2DAAkD;AA+BlD;;GAEG;AACH,MAAa,OAAQ,SAAQ,iCAA2B;IAMvD;QACC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,IAAmB;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAE9C,MAAM,QAAQ,GAAG;YAChB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;YAC/B,GAAG,IAAI;SACP,CAAC;QAEF,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,MAAuB;QACjC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;YAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;SAC5B;QACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE;YACrC,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE;gBAC3B,OAAO,IAAI,CAAC;aACZ;SACD;IACF,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,MAAuB;QACpC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;YAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,QAAQ,EAAE;gBACb,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACxB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;aAC9B;YACD,OAAO,QAAQ,CAAC;SAChB;QACD,KAAK,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE;YACnD,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE;gBAC3B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC3B,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;gBAC1B,OAAO,IAAI,CAAC;aACZ;SACD;IACF,CAAC;CACD;AApED,0BAoEC","sourcesContent":["import { TypedEmitter } from 'tiny-typed-emitter';\nimport { Awaited } from '../util/util';\n\n/**\n * The known data for a user in a Discord voice connection\n */\nexport interface VoiceUserData {\n\t/**\n\t * The SSRC of the user's audio stream\n\t */\n\taudioSSRC: number;\n\t/**\n\t * The SSRC of the user's video stream (if one exists).\n\t * Cannot be 0. If undefined, the user has no video stream.\n\t */\n\tvideoSSRC?: number;\n\t/**\n\t * The Discord user ID of the user\n\t */\n\tuserId: string;\n}\n\n/**\n * The events that an SSRCMap may emit.\n */\nexport interface SSRCMapEvents {\n\tcreate: (newData: VoiceUserData) => Awaited<void>;\n\tupdate: (oldData: VoiceUserData | undefined, newData: VoiceUserData) => Awaited<void>;\n\tdelete: (deletedData: VoiceUserData) => Awaited<void>;\n}\n\n/**\n * Maps audio SSRCs to data of users in voice connections.\n */\nexport class SSRCMap extends TypedEmitter<SSRCMapEvents> {\n\t/**\n\t * The underlying map\n\t */\n\tprivate readonly map: Map<number, VoiceUserData>;\n\n\tpublic constructor() {\n\t\tsuper();\n\t\tthis.map = new Map();\n\t}\n\n\t/**\n\t * Updates the map with new user data\n\t *\n\t * @param data The data to update with\n\t */\n\tpublic update(data: VoiceUserData) {\n\t\tconst existing = this.map.get(data.audioSSRC);\n\n\t\tconst newValue = {\n\t\t\t...this.map.get(data.audioSSRC),\n\t\t\t...data,\n\t\t};\n\n\t\tthis.map.set(data.audioSSRC, newValue);\n\t\tif (!existing) this.emit('create', newValue);\n\t\tthis.emit('update', existing, newValue);\n\t}\n\n\t/**\n\t * Gets the stored voice data of a user.\n\t *\n\t * @param target The target, either their user ID or audio SSRC\n\t */\n\tpublic get(target: number | string) {\n\t\tif (typeof target === 'number') {\n\t\t\treturn this.map.get(target);\n\t\t}\n\t\tfor (const data of this.map.values()) {\n\t\t\tif (data.userId === target) {\n\t\t\t\treturn data;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Deletes the stored voice data about a user.\n\t *\n\t * @param target The target of the delete operation, either their audio SSRC or user ID\n\t * @returns The data that was deleted, if any\n\t */\n\tpublic delete(target: number | string) {\n\t\tif (typeof target === 'number') {\n\t\t\tconst existing = this.map.get(target);\n\t\t\tif (existing) {\n\t\t\t\tthis.map.delete(target);\n\t\t\t\tthis.emit('delete', existing);\n\t\t\t}\n\t\t\treturn existing;\n\t\t}\n\t\tfor (const [audioSSRC, data] of this.map.entries()) {\n\t\t\tif (data.userId === target) {\n\t\t\t\tthis.map.delete(audioSSRC);\n\t\t\t\tthis.emit('delete', data);\n\t\t\t\treturn data;\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SpeakingMap = void 0;
|
|
4
|
-
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
|
|
5
|
-
/**
|
|
6
|
-
* Tracks the speaking states of users in a voice channel.
|
|
7
|
-
*/
|
|
8
|
-
class SpeakingMap extends tiny_typed_emitter_1.TypedEmitter {
|
|
9
|
-
constructor() {
|
|
10
|
-
super();
|
|
11
|
-
this.users = new Map();
|
|
12
|
-
this.speakingTimeouts = new Map();
|
|
13
|
-
}
|
|
14
|
-
onPacket(userId) {
|
|
15
|
-
const timeout = this.speakingTimeouts.get(userId);
|
|
16
|
-
if (timeout) {
|
|
17
|
-
clearTimeout(timeout);
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
this.users.set(userId, Date.now());
|
|
21
|
-
this.emit('start', userId);
|
|
22
|
-
}
|
|
23
|
-
this.startTimeout(userId);
|
|
24
|
-
}
|
|
25
|
-
startTimeout(userId) {
|
|
26
|
-
this.speakingTimeouts.set(userId, setTimeout(() => {
|
|
27
|
-
this.emit('end', userId);
|
|
28
|
-
this.speakingTimeouts.delete(userId);
|
|
29
|
-
this.users.delete(userId);
|
|
30
|
-
}, SpeakingMap.DELAY));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
exports.SpeakingMap = SpeakingMap;
|
|
34
|
-
/**
|
|
35
|
-
* The delay after a packet is received from a user until they're marked as not speaking anymore.
|
|
36
|
-
*/
|
|
37
|
-
SpeakingMap.DELAY = 100;
|
|
38
|
-
//# sourceMappingURL=SpeakingMap.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SpeakingMap.js","sourceRoot":"","sources":["../../src/receive/SpeakingMap.ts"],"names":[],"mappings":";;;AAAA,2DAAkD;AAiBlD;;GAEG;AACH,MAAa,WAAY,SAAQ,iCAA+B;IAY/D;QACC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,gBAAgB,GAAG,IAAI,GAAG,EAAE,CAAC;IACnC,CAAC;IAEM,QAAQ,CAAC,MAAc;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAClD,IAAI,OAAO,EAAE;YACZ,YAAY,CAAC,OAAO,CAAC,CAAC;SACtB;aAAM;YACN,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;SAC3B;QACD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAEO,YAAY,CAAC,MAAc;QAClC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CACxB,MAAM,EACN,UAAU,CAAC,GAAG,EAAE;YACf,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACzB,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,CACrB,CAAC;IACH,CAAC;;AAtCF,kCAuCC;AAtCA;;GAEG;AACoB,iBAAK,GAAG,GAAG,CAAC","sourcesContent":["import { TypedEmitter } from 'tiny-typed-emitter';\nimport { Awaited } from '../util/util';\n\n/**\n * The events that a SpeakingMap can emit\n */\nexport interface SpeakingMapEvents {\n\t/**\n\t * Emitted when a user starts speaking.\n\t */\n\tstart: (userId: string) => Awaited<void>;\n\t/**\n\t * Emitted when a user stops speaking.\n\t */\n\tend: (userId: string) => Awaited<void>;\n}\n\n/**\n * Tracks the speaking states of users in a voice channel.\n */\nexport class SpeakingMap extends TypedEmitter<SpeakingMapEvents> {\n\t/**\n\t * The delay after a packet is received from a user until they're marked as not speaking anymore.\n\t */\n\tpublic static readonly DELAY = 100;\n\t/**\n\t * The currently speaking users, mapped to the milliseconds since UNIX epoch at which they started speaking.\n\t */\n\tpublic readonly users: Map<string, number>;\n\n\tprivate readonly speakingTimeouts: Map<string, NodeJS.Timeout>;\n\n\tpublic constructor() {\n\t\tsuper();\n\t\tthis.users = new Map();\n\t\tthis.speakingTimeouts = new Map();\n\t}\n\n\tpublic onPacket(userId: string) {\n\t\tconst timeout = this.speakingTimeouts.get(userId);\n\t\tif (timeout) {\n\t\t\tclearTimeout(timeout);\n\t\t} else {\n\t\t\tthis.users.set(userId, Date.now());\n\t\t\tthis.emit('start', userId);\n\t\t}\n\t\tthis.startTimeout(userId);\n\t}\n\n\tprivate startTimeout(userId: string) {\n\t\tthis.speakingTimeouts.set(\n\t\t\tuserId,\n\t\t\tsetTimeout(() => {\n\t\t\t\tthis.emit('end', userId);\n\t\t\t\tthis.speakingTimeouts.delete(userId);\n\t\t\t\tthis.users.delete(userId);\n\t\t\t}, SpeakingMap.DELAY),\n\t\t);\n\t}\n}\n"]}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.VoiceReceiver = void 0;
|
|
4
|
-
const Secretbox_1 = require("../util/Secretbox");
|
|
5
|
-
const AudioReceiveStream_1 = require("./AudioReceiveStream");
|
|
6
|
-
const SpeakingMap_1 = require("./SpeakingMap");
|
|
7
|
-
const SSRCMap_1 = require("./SSRCMap");
|
|
8
|
-
/**
|
|
9
|
-
* Attaches to a VoiceConnection, allowing you to receive audio packets from other
|
|
10
|
-
* users that are speaking.
|
|
11
|
-
*
|
|
12
|
-
* @beta
|
|
13
|
-
*/
|
|
14
|
-
class VoiceReceiver {
|
|
15
|
-
constructor(voiceConnection) {
|
|
16
|
-
this.voiceConnection = voiceConnection;
|
|
17
|
-
this.ssrcMap = new SSRCMap_1.SSRCMap();
|
|
18
|
-
this.speaking = new SpeakingMap_1.SpeakingMap();
|
|
19
|
-
this.subscriptions = new Map();
|
|
20
|
-
this.connectionData = {};
|
|
21
|
-
this.onWsPacket = this.onWsPacket.bind(this);
|
|
22
|
-
this.onUdpMessage = this.onUdpMessage.bind(this);
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Called when a packet is received on the attached connection's WebSocket.
|
|
26
|
-
*
|
|
27
|
-
* @param packet The received packet
|
|
28
|
-
* @internal
|
|
29
|
-
*/
|
|
30
|
-
onWsPacket(packet) {
|
|
31
|
-
var _a, _b, _c, _d, _e;
|
|
32
|
-
if (packet.op === 13 /* ClientDisconnect */ && typeof ((_a = packet.d) === null || _a === void 0 ? void 0 : _a.user_id) === 'string') {
|
|
33
|
-
this.ssrcMap.delete(packet.d.user_id);
|
|
34
|
-
}
|
|
35
|
-
else if (packet.op === 5 /* Speaking */ &&
|
|
36
|
-
typeof ((_b = packet.d) === null || _b === void 0 ? void 0 : _b.user_id) === 'string' &&
|
|
37
|
-
typeof ((_c = packet.d) === null || _c === void 0 ? void 0 : _c.ssrc) === 'number') {
|
|
38
|
-
this.ssrcMap.update({ userId: packet.d.user_id, audioSSRC: packet.d.ssrc });
|
|
39
|
-
}
|
|
40
|
-
else if (packet.op === 12 /* ClientConnect */ &&
|
|
41
|
-
typeof ((_d = packet.d) === null || _d === void 0 ? void 0 : _d.user_id) === 'string' &&
|
|
42
|
-
typeof ((_e = packet.d) === null || _e === void 0 ? void 0 : _e.audio_ssrc) === 'number') {
|
|
43
|
-
this.ssrcMap.update({
|
|
44
|
-
userId: packet.d.user_id,
|
|
45
|
-
audioSSRC: packet.d.audio_ssrc,
|
|
46
|
-
videoSSRC: packet.d.video_ssrc === 0 ? undefined : packet.d.video_ssrc,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
decrypt(buffer, mode, nonce, secretKey) {
|
|
51
|
-
// Choose correct nonce depending on encryption
|
|
52
|
-
let end;
|
|
53
|
-
if (mode === 'xsalsa20_poly1305_lite') {
|
|
54
|
-
buffer.copy(nonce, 0, buffer.length - 4);
|
|
55
|
-
end = buffer.length - 4;
|
|
56
|
-
}
|
|
57
|
-
else if (mode === 'xsalsa20_poly1305_suffix') {
|
|
58
|
-
buffer.copy(nonce, 0, buffer.length - 24);
|
|
59
|
-
end = buffer.length - 24;
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
buffer.copy(nonce, 0, 0, 12);
|
|
63
|
-
}
|
|
64
|
-
// Open packet
|
|
65
|
-
const decrypted = Secretbox_1.methods.open(buffer.slice(12, end), nonce, secretKey);
|
|
66
|
-
if (!decrypted)
|
|
67
|
-
return;
|
|
68
|
-
return Buffer.from(decrypted);
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Parses an audio packet, decrypting it to yield an Opus packet.
|
|
72
|
-
*
|
|
73
|
-
* @param buffer The buffer to parse
|
|
74
|
-
* @param mode The encryption mode
|
|
75
|
-
* @param nonce The nonce buffer used by the connection for encryption
|
|
76
|
-
* @param secretKey The secret key used by the connection for encryption
|
|
77
|
-
* @returns The parsed Opus packet
|
|
78
|
-
*/
|
|
79
|
-
parsePacket(buffer, mode, nonce, secretKey) {
|
|
80
|
-
let packet = this.decrypt(buffer, mode, nonce, secretKey);
|
|
81
|
-
if (!packet)
|
|
82
|
-
return;
|
|
83
|
-
// Strip RTP Header Extensions (one-byte only)
|
|
84
|
-
if (packet[0] === 0xbe && packet[1] === 0xde && packet.length > 4) {
|
|
85
|
-
const headerExtensionLength = packet.readUInt16BE(2);
|
|
86
|
-
let offset = 4;
|
|
87
|
-
for (let i = 0; i < headerExtensionLength; i++) {
|
|
88
|
-
const byte = packet[offset];
|
|
89
|
-
offset++;
|
|
90
|
-
if (byte === 0)
|
|
91
|
-
continue;
|
|
92
|
-
offset += 1 + (byte >> 4);
|
|
93
|
-
}
|
|
94
|
-
// Skip over undocumented Discord byte (if present)
|
|
95
|
-
const byte = packet.readUInt8(offset);
|
|
96
|
-
if (byte === 0x00 || byte === 0x02)
|
|
97
|
-
offset++;
|
|
98
|
-
packet = packet.slice(offset);
|
|
99
|
-
}
|
|
100
|
-
return packet;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Called when the UDP socket of the attached connection receives a message.
|
|
104
|
-
*
|
|
105
|
-
* @param msg The received message
|
|
106
|
-
* @internal
|
|
107
|
-
*/
|
|
108
|
-
onUdpMessage(msg) {
|
|
109
|
-
if (msg.length <= 8)
|
|
110
|
-
return;
|
|
111
|
-
const ssrc = msg.readUInt32BE(8);
|
|
112
|
-
const userData = this.ssrcMap.get(ssrc);
|
|
113
|
-
if (!userData)
|
|
114
|
-
return;
|
|
115
|
-
this.speaking.onPacket(userData.userId);
|
|
116
|
-
const stream = this.subscriptions.get(userData.userId);
|
|
117
|
-
if (!stream)
|
|
118
|
-
return;
|
|
119
|
-
if (this.connectionData.encryptionMode && this.connectionData.nonceBuffer && this.connectionData.secretKey) {
|
|
120
|
-
const packet = this.parsePacket(msg, this.connectionData.encryptionMode, this.connectionData.nonceBuffer, this.connectionData.secretKey);
|
|
121
|
-
if (packet) {
|
|
122
|
-
stream.push(packet);
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
stream.destroy(new Error('Failed to parse packet'));
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Creates a subscription for the given user ID.
|
|
131
|
-
*
|
|
132
|
-
* @param target The ID of the user to subscribe to
|
|
133
|
-
* @returns A readable stream of Opus packets received from the target
|
|
134
|
-
*/
|
|
135
|
-
subscribe(userId, options) {
|
|
136
|
-
const existing = this.subscriptions.get(userId);
|
|
137
|
-
if (existing)
|
|
138
|
-
return existing;
|
|
139
|
-
const stream = new AudioReceiveStream_1.AudioReceiveStream({
|
|
140
|
-
...AudioReceiveStream_1.createDefaultAudioReceiveStreamOptions(),
|
|
141
|
-
...options,
|
|
142
|
-
});
|
|
143
|
-
stream.once('close', () => this.subscriptions.delete(userId));
|
|
144
|
-
this.subscriptions.set(userId, stream);
|
|
145
|
-
return stream;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
exports.VoiceReceiver = VoiceReceiver;
|
|
149
|
-
//# sourceMappingURL=VoiceReceiver.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VoiceReceiver.js","sourceRoot":"","sources":["../../src/receive/VoiceReceiver.ts"],"names":[],"mappings":"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","sourcesContent":["import { VoiceOpcodes } from 'discord-api-types/voice/v4';\nimport { ConnectionData } from '../networking/Networking';\nimport { methods } from '../util/Secretbox';\nimport type { VoiceConnection } from '../VoiceConnection';\nimport {\n\tAudioReceiveStream,\n\tAudioReceiveStreamOptions,\n\tcreateDefaultAudioReceiveStreamOptions,\n} from './AudioReceiveStream';\nimport { SpeakingMap } from './SpeakingMap';\nimport { SSRCMap } from './SSRCMap';\n\n/**\n * Attaches to a VoiceConnection, allowing you to receive audio packets from other\n * users that are speaking.\n *\n * @beta\n */\nexport class VoiceReceiver {\n\t/**\n\t * The attached connection of this receiver.\n\t */\n\tpublic readonly voiceConnection;\n\n\t/**\n\t * Maps SSRCs to Discord user IDs.\n\t */\n\tpublic readonly ssrcMap: SSRCMap;\n\n\t/**\n\t * The current audio subscriptions of this receiver.\n\t */\n\tpublic readonly subscriptions: Map<string, AudioReceiveStream>;\n\n\t/**\n\t * The connection data of the receiver.\n\t * @internal\n\t */\n\tpublic connectionData: Partial<ConnectionData>;\n\n\t/**\n\t * The speaking map of the receiver.\n\t */\n\tpublic readonly speaking: SpeakingMap;\n\n\tpublic constructor(voiceConnection: VoiceConnection) {\n\t\tthis.voiceConnection = voiceConnection;\n\t\tthis.ssrcMap = new SSRCMap();\n\t\tthis.speaking = new SpeakingMap();\n\t\tthis.subscriptions = new Map();\n\t\tthis.connectionData = {};\n\n\t\tthis.onWsPacket = this.onWsPacket.bind(this);\n\t\tthis.onUdpMessage = this.onUdpMessage.bind(this);\n\t}\n\n\t/**\n\t * Called when a packet is received on the attached connection's WebSocket.\n\t *\n\t * @param packet The received packet\n\t * @internal\n\t */\n\tpublic onWsPacket(packet: any) {\n\t\tif (packet.op === VoiceOpcodes.ClientDisconnect && typeof packet.d?.user_id === 'string') {\n\t\t\tthis.ssrcMap.delete(packet.d.user_id);\n\t\t} else if (\n\t\t\tpacket.op === VoiceOpcodes.Speaking &&\n\t\t\ttypeof packet.d?.user_id === 'string' &&\n\t\t\ttypeof packet.d?.ssrc === 'number'\n\t\t) {\n\t\t\tthis.ssrcMap.update({ userId: packet.d.user_id, audioSSRC: packet.d.ssrc });\n\t\t} else if (\n\t\t\tpacket.op === VoiceOpcodes.ClientConnect &&\n\t\t\ttypeof packet.d?.user_id === 'string' &&\n\t\t\ttypeof packet.d?.audio_ssrc === 'number'\n\t\t) {\n\t\t\tthis.ssrcMap.update({\n\t\t\t\tuserId: packet.d.user_id,\n\t\t\t\taudioSSRC: packet.d.audio_ssrc,\n\t\t\t\tvideoSSRC: packet.d.video_ssrc === 0 ? undefined : packet.d.video_ssrc,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate decrypt(buffer: Buffer, mode: string, nonce: Buffer, secretKey: Uint8Array) {\n\t\t// Choose correct nonce depending on encryption\n\t\tlet end;\n\t\tif (mode === 'xsalsa20_poly1305_lite') {\n\t\t\tbuffer.copy(nonce, 0, buffer.length - 4);\n\t\t\tend = buffer.length - 4;\n\t\t} else if (mode === 'xsalsa20_poly1305_suffix') {\n\t\t\tbuffer.copy(nonce, 0, buffer.length - 24);\n\t\t\tend = buffer.length - 24;\n\t\t} else {\n\t\t\tbuffer.copy(nonce, 0, 0, 12);\n\t\t}\n\n\t\t// Open packet\n\t\tconst decrypted = methods.open(buffer.slice(12, end), nonce, secretKey);\n\t\tif (!decrypted) return;\n\t\treturn Buffer.from(decrypted);\n\t}\n\n\t/**\n\t * Parses an audio packet, decrypting it to yield an Opus packet.\n\t *\n\t * @param buffer The buffer to parse\n\t * @param mode The encryption mode\n\t * @param nonce The nonce buffer used by the connection for encryption\n\t * @param secretKey The secret key used by the connection for encryption\n\t * @returns The parsed Opus packet\n\t */\n\tprivate parsePacket(buffer: Buffer, mode: string, nonce: Buffer, secretKey: Uint8Array) {\n\t\tlet packet = this.decrypt(buffer, mode, nonce, secretKey);\n\t\tif (!packet) return;\n\n\t\t// Strip RTP Header Extensions (one-byte only)\n\t\tif (packet[0] === 0xbe && packet[1] === 0xde && packet.length > 4) {\n\t\t\tconst headerExtensionLength = packet.readUInt16BE(2);\n\t\t\tlet offset = 4;\n\t\t\tfor (let i = 0; i < headerExtensionLength; i++) {\n\t\t\t\tconst byte = packet[offset];\n\t\t\t\toffset++;\n\t\t\t\tif (byte === 0) continue;\n\t\t\t\toffset += 1 + (byte >> 4);\n\t\t\t}\n\t\t\t// Skip over undocumented Discord byte (if present)\n\t\t\tconst byte = packet.readUInt8(offset);\n\t\t\tif (byte === 0x00 || byte === 0x02) offset++;\n\n\t\t\tpacket = packet.slice(offset);\n\t\t}\n\n\t\treturn packet;\n\t}\n\n\t/**\n\t * Called when the UDP socket of the attached connection receives a message.\n\t *\n\t * @param msg The received message\n\t * @internal\n\t */\n\tpublic onUdpMessage(msg: Buffer) {\n\t\tif (msg.length <= 8) return;\n\t\tconst ssrc = msg.readUInt32BE(8);\n\n\t\tconst userData = this.ssrcMap.get(ssrc);\n\t\tif (!userData) return;\n\n\t\tthis.speaking.onPacket(userData.userId);\n\n\t\tconst stream = this.subscriptions.get(userData.userId);\n\t\tif (!stream) return;\n\n\t\tif (this.connectionData.encryptionMode && this.connectionData.nonceBuffer && this.connectionData.secretKey) {\n\t\t\tconst packet = this.parsePacket(\n\t\t\t\tmsg,\n\t\t\t\tthis.connectionData.encryptionMode,\n\t\t\t\tthis.connectionData.nonceBuffer,\n\t\t\t\tthis.connectionData.secretKey,\n\t\t\t);\n\t\t\tif (packet) {\n\t\t\t\tstream.push(packet);\n\t\t\t} else {\n\t\t\t\tstream.destroy(new Error('Failed to parse packet'));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Creates a subscription for the given user ID.\n\t *\n\t * @param target The ID of the user to subscribe to\n\t * @returns A readable stream of Opus packets received from the target\n\t */\n\tpublic subscribe(userId: string, options?: Partial<AudioReceiveStreamOptions>) {\n\t\tconst existing = this.subscriptions.get(userId);\n\t\tif (existing) return existing;\n\n\t\tconst stream = new AudioReceiveStream({\n\t\t\t...createDefaultAudioReceiveStreamOptions(),\n\t\t\t...options,\n\t\t});\n\n\t\tstream.once('close', () => this.subscriptions.delete(userId));\n\t\tthis.subscriptions.set(userId, stream);\n\t\treturn stream;\n\t}\n}\n"]}
|
package/dist/receive/index.js
DELETED
|
@@ -1,16 +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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
-
};
|
|
12
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
__exportStar(require("./VoiceReceiver"), exports);
|
|
14
|
-
__exportStar(require("./SSRCMap"), exports);
|
|
15
|
-
__exportStar(require("./AudioReceiveStream"), exports);
|
|
16
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/receive/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,kDAAgC;AAChC,4CAA0B;AAC1B,uDAAqC","sourcesContent":["export * from './VoiceReceiver';\nexport * from './SSRCMap';\nexport * from './AudioReceiveStream';\n"]}
|
package/dist/util/Secretbox.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.methods = void 0;
|
|
4
|
-
const libs = {
|
|
5
|
-
sodium: (sodium) => ({
|
|
6
|
-
open: sodium.api.crypto_secretbox_open_easy,
|
|
7
|
-
close: sodium.api.crypto_secretbox_easy,
|
|
8
|
-
random: (n, buffer) => {
|
|
9
|
-
if (!buffer)
|
|
10
|
-
buffer = Buffer.allocUnsafe(n);
|
|
11
|
-
sodium.api.randombytes_buf(buffer);
|
|
12
|
-
return buffer;
|
|
13
|
-
},
|
|
14
|
-
}),
|
|
15
|
-
'libsodium-wrappers': (sodium) => ({
|
|
16
|
-
open: sodium.crypto_secretbox_open_easy,
|
|
17
|
-
close: sodium.crypto_secretbox_easy,
|
|
18
|
-
random: (n) => sodium.randombytes_buf(n),
|
|
19
|
-
}),
|
|
20
|
-
tweetnacl: (tweetnacl) => ({
|
|
21
|
-
open: tweetnacl.secretbox.open,
|
|
22
|
-
close: tweetnacl.secretbox,
|
|
23
|
-
random: (n) => tweetnacl.randomBytes(n),
|
|
24
|
-
}),
|
|
25
|
-
};
|
|
26
|
-
const fallbackError = () => {
|
|
27
|
-
throw new Error(`Cannot play audio as no valid encryption package is installed.
|
|
28
|
-
- Install sodium, libsodium-wrappers, or tweetnacl.
|
|
29
|
-
- Use the generateDependencyReport() function for more information.\n`);
|
|
30
|
-
};
|
|
31
|
-
const methods = {
|
|
32
|
-
open: fallbackError,
|
|
33
|
-
close: fallbackError,
|
|
34
|
-
random: fallbackError,
|
|
35
|
-
};
|
|
36
|
-
exports.methods = methods;
|
|
37
|
-
void (async () => {
|
|
38
|
-
for (const libName of Object.keys(libs)) {
|
|
39
|
-
try {
|
|
40
|
-
// eslint-disable-next-line
|
|
41
|
-
const lib = require(libName);
|
|
42
|
-
if (libName === 'libsodium-wrappers' && lib.ready)
|
|
43
|
-
await lib.ready; // eslint-disable-line no-await-in-loop
|
|
44
|
-
Object.assign(methods, libs[libName](lib));
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
catch { } // eslint-disable-line no-empty
|
|
48
|
-
}
|
|
49
|
-
})();
|
|
50
|
-
//# sourceMappingURL=Secretbox.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Secretbox.js","sourceRoot":"","sources":["../../src/util/Secretbox.ts"],"names":[],"mappings":";;;AAMA,MAAM,IAAI,GAAG;IACZ,MAAM,EAAE,CAAC,MAAW,EAAW,EAAE,CAAC,CAAC;QAClC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,0BAA0B;QAC3C,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,qBAAqB;QACvC,MAAM,EAAE,CAAC,CAAM,EAAE,MAAe,EAAE,EAAE;YACnC,IAAI,CAAC,MAAM;gBAAE,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAC5C,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YACnC,OAAO,MAAM,CAAC;QACf,CAAC;KACD,CAAC;IACF,oBAAoB,EAAE,CAAC,MAAW,EAAW,EAAE,CAAC,CAAC;QAChD,IAAI,EAAE,MAAM,CAAC,0BAA0B;QACvC,KAAK,EAAE,MAAM,CAAC,qBAAqB;QACnC,MAAM,EAAE,CAAC,CAAM,EAAE,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;KAC7C,CAAC;IACF,SAAS,EAAE,CAAC,SAAc,EAAW,EAAE,CAAC,CAAC;QACxC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,IAAI;QAC9B,KAAK,EAAE,SAAS,CAAC,SAAS;QAC1B,MAAM,EAAE,CAAC,CAAM,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;KAC5C,CAAC;CACO,CAAC;AAEX,MAAM,aAAa,GAAG,GAAG,EAAE;IAC1B,MAAM,IAAI,KAAK,CACd;;sEAEoE,CACpE,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,OAAO,GAAY;IACxB,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,aAAa;IACpB,MAAM,EAAE,aAAa;CACrB,CAAC;AAcO,0BAAO;AAZhB,KAAK,CAAC,KAAK,IAAI,EAAE;IAChB,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAA0B,EAAE;QACjE,IAAI;YACH,2BAA2B;YAC3B,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;YAC7B,IAAI,OAAO,KAAK,oBAAoB,IAAI,GAAG,CAAC,KAAK;gBAAE,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,uCAAuC;YAC3G,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3C,MAAM;SACN;QAAC,MAAM,GAAE,CAAC,+BAA+B;KAC1C;AACF,CAAC,CAAC,EAAE,CAAC","sourcesContent":["interface Methods {\n\topen(buffer: Buffer, nonce: Buffer, secretKey: Uint8Array): Buffer | null;\n\tclose(opusPacket: Buffer, nonce: Buffer, secretKey: Uint8Array): Buffer;\n\trandom(bytes: number, nonce: Buffer): Buffer;\n}\n\nconst libs = {\n\tsodium: (sodium: any): Methods => ({\n\t\topen: sodium.api.crypto_secretbox_open_easy,\n\t\tclose: sodium.api.crypto_secretbox_easy,\n\t\trandom: (n: any, buffer?: Buffer) => {\n\t\t\tif (!buffer) buffer = Buffer.allocUnsafe(n);\n\t\t\tsodium.api.randombytes_buf(buffer);\n\t\t\treturn buffer;\n\t\t},\n\t}),\n\t'libsodium-wrappers': (sodium: any): Methods => ({\n\t\topen: sodium.crypto_secretbox_open_easy,\n\t\tclose: sodium.crypto_secretbox_easy,\n\t\trandom: (n: any) => sodium.randombytes_buf(n),\n\t}),\n\ttweetnacl: (tweetnacl: any): Methods => ({\n\t\topen: tweetnacl.secretbox.open,\n\t\tclose: tweetnacl.secretbox,\n\t\trandom: (n: any) => tweetnacl.randomBytes(n),\n\t}),\n} as const;\n\nconst fallbackError = () => {\n\tthrow new Error(\n\t\t`Cannot play audio as no valid encryption package is installed.\n- Install sodium, libsodium-wrappers, or tweetnacl.\n- Use the generateDependencyReport() function for more information.\\n`,\n\t);\n};\n\nconst methods: Methods = {\n\topen: fallbackError,\n\tclose: fallbackError,\n\trandom: fallbackError,\n};\n\nvoid (async () => {\n\tfor (const libName of Object.keys(libs) as (keyof typeof libs)[]) {\n\t\ttry {\n\t\t\t// eslint-disable-next-line\n\t\t\tconst lib = require(libName);\n\t\t\tif (libName === 'libsodium-wrappers' && lib.ready) await lib.ready; // eslint-disable-line no-await-in-loop\n\t\t\tObject.assign(methods, libs[libName](lib));\n\t\t\tbreak;\n\t\t} catch {} // eslint-disable-line no-empty\n\t}\n})();\n\nexport { methods };\n"]}
|