discord.js-selfbot-v13/src/util/Voice.js

2061 lines
66 KiB
JavaScript
Raw Normal View History

2022-12-14 11:50:07 +00:00
"use strict";
2022-05-21 14:02:00 +00:00
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
2022-12-14 11:50:07 +00:00
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
2022-08-01 06:02:58 +00:00
var __export = (target, all) => {
2022-12-14 11:50:07 +00:00
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
2022-05-21 14:02:00 +00:00
};
2022-08-01 06:02:58 +00:00
var __copyProps = (to, from, except, desc) => {
2022-12-14 11:50:07 +00:00
if (from && typeof from === "object" || typeof from === "function") {
2022-06-26 02:40:04 +00:00
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
2022-12-14 11:50:07 +00:00
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
2022-05-21 14:02:00 +00:00
}
return to;
};
2022-12-14 11:50:07 +00:00
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
2022-08-01 06:02:58 +00:00
var __publicField = (obj, key, value) => {
2022-12-14 11:50:07 +00:00
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
2022-06-26 02:40:04 +00:00
return value;
};
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/index.ts
2022-05-21 14:02:00 +00:00
var src_exports = {};
__export(src_exports, {
AudioPlayer: () => AudioPlayer,
AudioPlayerError: () => AudioPlayerError,
AudioPlayerStatus: () => AudioPlayerStatus,
AudioReceiveStream: () => AudioReceiveStream,
AudioResource: () => AudioResource,
EndBehaviorType: () => EndBehaviorType,
NoSubscriberBehavior: () => NoSubscriberBehavior,
PlayerSubscription: () => PlayerSubscription,
SSRCMap: () => SSRCMap,
SpeakingMap: () => SpeakingMap,
StreamType: () => StreamType,
VoiceConnection: () => VoiceConnection,
2022-05-21 14:02:00 +00:00
VoiceConnectionDisconnectReason: () => VoiceConnectionDisconnectReason,
VoiceConnectionStatus: () => VoiceConnectionStatus,
VoiceReceiver: () => VoiceReceiver,
createAudioPlayer: () => createAudioPlayer,
createAudioResource: () => createAudioResource,
createDefaultAudioReceiveStreamOptions: () => createDefaultAudioReceiveStreamOptions,
demuxProbe: () => demuxProbe,
entersState: () => entersState,
generateDependencyReport: () => generateDependencyReport,
getGroups: () => getGroups,
getVoiceConnection: () => getVoiceConnection,
getVoiceConnections: () => getVoiceConnections,
joinVoiceChannel: () => joinVoiceChannel,
2022-09-28 15:09:00 +00:00
validateDiscordOpusHead: () => validateDiscordOpusHead,
2022-12-14 11:50:07 +00:00
version: () => version2
2022-05-21 14:02:00 +00:00
});
module.exports = __toCommonJS(src_exports);
2022-06-26 02:40:04 +00:00
// src/VoiceConnection.ts
2022-12-14 11:50:07 +00:00
var import_node_events7 = require("events");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/DataStore.ts
2022-12-14 11:50:07 +00:00
var import_v10 = require("discord-api-types/v10");
2022-08-01 06:02:58 +00:00
function createJoinVoiceChannelPayload(config) {
2022-05-21 14:02:00 +00:00
return {
op: import_v10.GatewayOpcodes.VoiceStateUpdate,
d: {
guild_id: config.guildId,
channel_id: config.channelId,
self_deaf: config.selfDeaf,
2022-12-14 11:50:07 +00:00
self_mute: config.selfMute
}
2022-05-21 14:02:00 +00:00
};
}
2022-12-14 11:50:07 +00:00
__name(createJoinVoiceChannelPayload, "createJoinVoiceChannelPayload");
2022-05-21 14:02:00 +00:00
var groups = /* @__PURE__ */ new Map();
2022-12-14 11:50:07 +00:00
groups.set("default", /* @__PURE__ */ new Map());
2022-08-01 06:02:58 +00:00
function getOrCreateGroup(group) {
2022-05-21 14:02:00 +00:00
const existing = groups.get(group);
2022-12-14 11:50:07 +00:00
if (existing)
return existing;
2022-05-21 14:02:00 +00:00
const map = /* @__PURE__ */ new Map();
groups.set(group, map);
return map;
}
2022-12-14 11:50:07 +00:00
__name(getOrCreateGroup, "getOrCreateGroup");
2022-08-01 06:02:58 +00:00
function getGroups() {
2022-05-21 14:02:00 +00:00
return groups;
}
2022-12-14 11:50:07 +00:00
__name(getGroups, "getGroups");
function getVoiceConnections(group = "default") {
2022-05-21 14:02:00 +00:00
return groups.get(group);
}
2022-12-14 11:50:07 +00:00
__name(getVoiceConnections, "getVoiceConnections");
function getVoiceConnection(guildId, group = "default") {
2022-05-21 14:02:00 +00:00
return getVoiceConnections(group)?.get(guildId);
}
2022-12-14 11:50:07 +00:00
__name(getVoiceConnection, "getVoiceConnection");
2022-08-01 06:02:58 +00:00
function untrackVoiceConnection(voiceConnection) {
2022-05-21 14:02:00 +00:00
return getVoiceConnections(voiceConnection.joinConfig.group)?.delete(voiceConnection.joinConfig.guildId);
}
2022-12-14 11:50:07 +00:00
__name(untrackVoiceConnection, "untrackVoiceConnection");
2022-08-01 06:02:58 +00:00
function trackVoiceConnection(voiceConnection) {
2022-05-21 14:02:00 +00:00
return getOrCreateGroup(voiceConnection.joinConfig.group).set(voiceConnection.joinConfig.guildId, voiceConnection);
}
2022-12-14 11:50:07 +00:00
__name(trackVoiceConnection, "trackVoiceConnection");
2022-05-21 14:02:00 +00:00
var FRAME_LENGTH = 20;
var audioCycleInterval;
var nextTime = -1;
var audioPlayers = [];
2022-08-01 06:02:58 +00:00
function audioCycleStep() {
2022-12-14 11:50:07 +00:00
if (nextTime === -1)
return;
2022-05-21 14:02:00 +00:00
nextTime += FRAME_LENGTH;
2022-12-14 11:50:07 +00:00
const available = audioPlayers.filter((player) => player.checkPlayable());
for (const player of available) {
2022-12-14 11:50:07 +00:00
player["_stepDispatch"]();
}
2022-05-21 14:02:00 +00:00
prepareNextAudioFrame(available);
}
2022-12-14 11:50:07 +00:00
__name(audioCycleStep, "audioCycleStep");
2022-08-01 06:02:58 +00:00
function prepareNextAudioFrame(players) {
2022-05-21 14:02:00 +00:00
const nextPlayer = players.shift();
2022-08-01 06:02:58 +00:00
if (!nextPlayer) {
if (nextTime !== -1) {
2022-05-21 14:02:00 +00:00
audioCycleInterval = setTimeout(() => audioCycleStep(), nextTime - Date.now());
}
return;
}
2022-12-14 11:50:07 +00:00
nextPlayer["_stepPrepare"]();
2022-05-21 14:02:00 +00:00
setImmediate(() => prepareNextAudioFrame(players));
}
2022-12-14 11:50:07 +00:00
__name(prepareNextAudioFrame, "prepareNextAudioFrame");
2022-08-01 06:02:58 +00:00
function hasAudioPlayer(target) {
2022-05-21 14:02:00 +00:00
return audioPlayers.includes(target);
}
2022-12-14 11:50:07 +00:00
__name(hasAudioPlayer, "hasAudioPlayer");
2022-08-01 06:02:58 +00:00
function addAudioPlayer(player) {
2022-12-14 11:50:07 +00:00
if (hasAudioPlayer(player))
return player;
2022-05-21 14:02:00 +00:00
audioPlayers.push(player);
2022-08-01 06:02:58 +00:00
if (audioPlayers.length === 1) {
2022-05-21 14:02:00 +00:00
nextTime = Date.now();
setImmediate(() => audioCycleStep());
}
return player;
}
2022-12-14 11:50:07 +00:00
__name(addAudioPlayer, "addAudioPlayer");
2022-08-01 06:02:58 +00:00
function deleteAudioPlayer(player) {
2022-05-21 14:02:00 +00:00
const index = audioPlayers.indexOf(player);
2022-12-14 11:50:07 +00:00
if (index === -1)
return;
2022-05-21 14:02:00 +00:00
audioPlayers.splice(index, 1);
2022-08-01 06:02:58 +00:00
if (audioPlayers.length === 0) {
2022-05-21 14:02:00 +00:00
nextTime = -1;
2022-12-14 11:50:07 +00:00
if (typeof audioCycleInterval !== "undefined")
clearTimeout(audioCycleInterval);
2022-05-21 14:02:00 +00:00
}
}
2022-12-14 11:50:07 +00:00
__name(deleteAudioPlayer, "deleteAudioPlayer");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/networking/Networking.ts
2022-12-14 11:50:07 +00:00
var import_node_buffer3 = require("buffer");
var import_node_events3 = require("events");
var import_v42 = require("discord-api-types/voice/v4");
// src/util/Secretbox.ts
2022-12-14 11:50:07 +00:00
var import_node_buffer = require("buffer");
var libs = {
2022-12-14 11:50:07 +00:00
"sodium-native": (sodium) => ({
open: (buffer, nonce2, secretKey) => {
if (buffer) {
const output = import_node_buffer.Buffer.allocUnsafe(buffer.length - sodium.crypto_box_MACBYTES);
2022-12-14 11:50:07 +00:00
if (sodium.crypto_secretbox_open_easy(output, buffer, nonce2, secretKey))
return output;
}
return null;
},
close: (opusPacket, nonce2, secretKey) => {
const output = import_node_buffer.Buffer.allocUnsafe(opusPacket.length + sodium.crypto_box_MACBYTES);
sodium.crypto_secretbox_easy(output, opusPacket, nonce2, secretKey);
return output;
},
random: (num, buffer = import_node_buffer.Buffer.allocUnsafe(num)) => {
sodium.randombytes_buf(buffer);
return buffer;
2022-12-14 11:50:07 +00:00
}
}),
2022-12-14 11:50:07 +00:00
sodium: (sodium) => ({
open: sodium.api.crypto_secretbox_open_easy,
close: sodium.api.crypto_secretbox_easy,
random: (num, buffer = import_node_buffer.Buffer.allocUnsafe(num)) => {
sodium.api.randombytes_buf(buffer);
return buffer;
2022-12-14 11:50:07 +00:00
}
}),
2022-12-14 11:50:07 +00:00
"libsodium-wrappers": (sodium) => ({
open: sodium.crypto_secretbox_open_easy,
close: sodium.crypto_secretbox_easy,
2022-12-14 11:50:07 +00:00
random: sodium.randombytes_buf
}),
2022-12-14 11:50:07 +00:00
tweetnacl: (tweetnacl) => ({
open: tweetnacl.secretbox.open,
close: tweetnacl.secretbox,
2022-12-14 11:50:07 +00:00
random: tweetnacl.randomBytes
})
};
var fallbackError = /* @__PURE__ */ __name(() => {
throw new Error(
`Cannot play audio as no valid encryption package is installed.
- Install sodium, libsodium-wrappers, or tweetnacl.
- Use the generateDependencyReport() function for more information.
2022-12-14 11:50:07 +00:00
`
);
2022-12-14 11:50:07 +00:00
}, "fallbackError");
var methods = {
open: fallbackError,
close: fallbackError,
2022-12-14 11:50:07 +00:00
random: fallbackError
};
void (async () => {
for (const libName of Object.keys(libs)) {
try {
const lib = require(libName);
2022-12-14 11:50:07 +00:00
if (libName === "libsodium-wrappers" && lib.ready)
await lib.ready;
Object.assign(methods, libs[libName](lib));
break;
2022-12-14 11:50:07 +00:00
} catch {
}
}
})();
// src/util/util.ts
2022-12-14 11:50:07 +00:00
var noop = /* @__PURE__ */ __name(() => {
}, "noop");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/networking/VoiceUDPSocket.ts
2022-12-14 11:50:07 +00:00
var import_node_buffer2 = require("buffer");
var import_node_dgram = require("dgram");
var import_node_events = require("events");
var import_node_net = require("net");
2022-08-01 06:02:58 +00:00
function parseLocalPacket(message) {
const packet = import_node_buffer2.Buffer.from(message);
2022-12-14 11:50:07 +00:00
const ip = packet.slice(8, packet.indexOf(0, 8)).toString("utf8");
2022-08-01 06:02:58 +00:00
if (!(0, import_node_net.isIPv4)(ip)) {
2022-12-14 11:50:07 +00:00
throw new Error("Malformed IP address");
2022-05-21 14:02:00 +00:00
}
const port = packet.readUInt16BE(packet.length - 2);
return { ip, port };
}
2022-12-14 11:50:07 +00:00
__name(parseLocalPacket, "parseLocalPacket");
2022-05-21 14:02:00 +00:00
var KEEP_ALIVE_INTERVAL = 5e3;
var KEEP_ALIVE_LIMIT = 12;
var MAX_COUNTER_VALUE = 2 ** 32 - 1;
2022-08-01 06:02:58 +00:00
var VoiceUDPSocket = class extends import_node_events.EventEmitter {
socket;
remote;
keepAlives;
keepAliveCounter = 0;
keepAliveBuffer;
keepAliveInterval;
ping;
debug;
2022-08-01 06:02:58 +00:00
constructor(remote, debug = false) {
2022-05-21 14:02:00 +00:00
super();
2022-12-14 11:50:07 +00:00
this.socket = (0, import_node_dgram.createSocket)("udp4");
this.socket.on("error", (error) => this.emit("error", error));
this.socket.on("message", (buffer) => this.onMessage(buffer));
this.socket.on("close", () => this.emit("close"));
2022-05-21 14:02:00 +00:00
this.remote = remote;
this.keepAlives = [];
this.keepAliveBuffer = import_node_buffer2.Buffer.alloc(8);
2022-05-21 14:02:00 +00:00
this.keepAliveInterval = setInterval(() => this.keepAlive(), KEEP_ALIVE_INTERVAL);
setImmediate(() => this.keepAlive());
2022-12-14 11:50:07 +00:00
this.debug = debug ? (message) => this.emit("debug", message) : null;
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
onMessage(buffer) {
if (buffer.length === 8) {
2022-05-21 14:02:00 +00:00
const counter = buffer.readUInt32LE(0);
const index = this.keepAlives.findIndex(({ value }) => value === counter);
2022-12-14 11:50:07 +00:00
if (index === -1)
return;
2022-05-21 14:02:00 +00:00
this.ping = Date.now() - this.keepAlives[index].timestamp;
this.keepAlives.splice(0, index);
}
2022-12-14 11:50:07 +00:00
this.emit("message", buffer);
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
keepAlive() {
if (this.keepAlives.length >= KEEP_ALIVE_LIMIT) {
2022-12-14 11:50:07 +00:00
this.debug?.("UDP socket has not received enough responses from Discord - closing socket");
2022-05-21 14:02:00 +00:00
this.destroy();
return;
}
this.keepAliveBuffer.writeUInt32LE(this.keepAliveCounter, 0);
this.send(this.keepAliveBuffer);
this.keepAlives.push({
value: this.keepAliveCounter,
2022-12-14 11:50:07 +00:00
timestamp: Date.now()
2022-05-21 14:02:00 +00:00
});
this.keepAliveCounter++;
2022-08-01 06:02:58 +00:00
if (this.keepAliveCounter > MAX_COUNTER_VALUE) {
2022-05-21 14:02:00 +00:00
this.keepAliveCounter = 0;
}
}
2022-08-01 06:02:58 +00:00
send(buffer) {
this.socket.send(buffer, this.remote.port, this.remote.ip);
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
destroy() {
try {
2022-05-21 14:02:00 +00:00
this.socket.close();
2022-12-14 11:50:07 +00:00
} catch {
}
2022-05-21 14:02:00 +00:00
clearInterval(this.keepAliveInterval);
}
async performIPDiscovery(ssrc) {
2022-08-01 06:02:58 +00:00
return new Promise((resolve2, reject) => {
2022-12-14 11:50:07 +00:00
const listener = /* @__PURE__ */ __name((message) => {
2022-08-01 06:02:58 +00:00
try {
2022-12-14 11:50:07 +00:00
if (message.readUInt16BE(0) !== 2)
return;
2022-05-21 14:02:00 +00:00
const packet = parseLocalPacket(message);
2022-12-14 11:50:07 +00:00
this.socket.off("message", listener);
2022-05-21 14:02:00 +00:00
resolve2(packet);
2022-12-14 11:50:07 +00:00
} catch {
}
}, "listener");
this.socket.on("message", listener);
this.socket.once("close", () => reject(new Error("Cannot perform IP discovery - socket closed")));
const discoveryBuffer = import_node_buffer2.Buffer.alloc(74);
2022-05-21 14:02:00 +00:00
discoveryBuffer.writeUInt16BE(1, 0);
discoveryBuffer.writeUInt16BE(70, 2);
discoveryBuffer.writeUInt32BE(ssrc, 4);
this.send(discoveryBuffer);
});
}
};
2022-12-14 11:50:07 +00:00
__name(VoiceUDPSocket, "VoiceUDPSocket");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/networking/VoiceWebSocket.ts
2022-12-14 11:50:07 +00:00
var import_node_events2 = require("events");
var import_v4 = require("discord-api-types/voice/v4");
var import_ws = __toESM(require("ws"));
2022-08-01 06:02:58 +00:00
var VoiceWebSocket = class extends import_node_events2.EventEmitter {
heartbeatInterval;
lastHeartbeatAck;
lastHeartbeatSend;
missedHeartbeats = 0;
ping;
debug;
ws;
2022-08-01 06:02:58 +00:00
constructor(address, debug) {
2022-05-21 14:02:00 +00:00
super();
this.ws = new import_ws.default(address);
2022-12-14 11:50:07 +00:00
this.ws.onmessage = (err) => this.onMessage(err);
this.ws.onopen = (err) => this.emit("open", err);
this.ws.onerror = (err) => this.emit("error", err instanceof Error ? err : err.error);
this.ws.onclose = (err) => this.emit("close", err);
2022-05-21 14:02:00 +00:00
this.lastHeartbeatAck = 0;
this.lastHeartbeatSend = 0;
2022-12-14 11:50:07 +00:00
this.debug = debug ? (message) => this.emit("debug", message) : null;
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
destroy() {
try {
2022-12-14 11:50:07 +00:00
this.debug?.("destroyed");
2022-05-21 14:02:00 +00:00
this.setHeartbeatInterval(-1);
this.ws.close(1e3);
2022-08-01 06:02:58 +00:00
} catch (error) {
const err = error;
2022-12-14 11:50:07 +00:00
this.emit("error", err);
2022-05-21 14:02:00 +00:00
}
}
2022-08-01 06:02:58 +00:00
onMessage(event) {
2022-12-14 11:50:07 +00:00
if (typeof event.data !== "string")
return;
2022-05-21 14:02:00 +00:00
this.debug?.(`<< ${event.data}`);
let packet;
2022-08-01 06:02:58 +00:00
try {
2022-05-21 14:02:00 +00:00
packet = JSON.parse(event.data);
2022-08-01 06:02:58 +00:00
} catch (error) {
const err = error;
2022-12-14 11:50:07 +00:00
this.emit("error", err);
2022-05-21 14:02:00 +00:00
return;
}
2022-08-01 06:02:58 +00:00
if (packet.op === import_v4.VoiceOpcodes.HeartbeatAck) {
2022-05-21 14:02:00 +00:00
this.lastHeartbeatAck = Date.now();
this.missedHeartbeats = 0;
this.ping = this.lastHeartbeatAck - this.lastHeartbeatSend;
}
2022-12-14 11:50:07 +00:00
this.emit("packet", packet);
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
sendPacket(packet) {
try {
2022-05-21 14:02:00 +00:00
const stringified = JSON.stringify(packet);
this.debug?.(`>> ${stringified}`);
this.ws.send(stringified);
return;
2022-08-01 06:02:58 +00:00
} catch (error) {
const err = error;
2022-12-14 11:50:07 +00:00
this.emit("error", err);
2022-05-21 14:02:00 +00:00
}
}
2022-08-01 06:02:58 +00:00
sendHeartbeat() {
2022-05-21 14:02:00 +00:00
this.lastHeartbeatSend = Date.now();
this.missedHeartbeats++;
const nonce2 = this.lastHeartbeatSend;
this.sendPacket({
2022-05-21 14:02:00 +00:00
op: import_v4.VoiceOpcodes.Heartbeat,
2022-12-14 11:50:07 +00:00
d: nonce2
2022-05-21 14:02:00 +00:00
});
}
2022-08-01 06:02:58 +00:00
setHeartbeatInterval(ms) {
2022-12-14 11:50:07 +00:00
if (typeof this.heartbeatInterval !== "undefined")
clearInterval(this.heartbeatInterval);
2022-08-01 06:02:58 +00:00
if (ms > 0) {
this.heartbeatInterval = setInterval(() => {
if (this.lastHeartbeatSend !== 0 && this.missedHeartbeats >= 3) {
2022-05-21 14:02:00 +00:00
this.ws.close();
this.setHeartbeatInterval(-1);
}
this.sendHeartbeat();
}, ms);
}
}
};
2022-12-14 11:50:07 +00:00
__name(VoiceWebSocket, "VoiceWebSocket");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/networking/Networking.ts
2022-05-21 14:02:00 +00:00
var CHANNELS = 2;
2022-12-14 11:50:07 +00:00
var TIMESTAMP_INC = 48e3 / 100 * CHANNELS;
2022-05-21 14:02:00 +00:00
var MAX_NONCE_SIZE = 2 ** 32 - 1;
2022-12-14 11:50:07 +00:00
var SUPPORTED_ENCRYPTION_MODES = ["xsalsa20_poly1305_lite", "xsalsa20_poly1305_suffix", "xsalsa20_poly1305"];
var nonce = import_node_buffer3.Buffer.alloc(24);
2022-08-01 06:02:58 +00:00
function stringifyState(state) {
2022-05-21 14:02:00 +00:00
return JSON.stringify({
...state,
2022-12-14 11:50:07 +00:00
ws: Reflect.has(state, "ws"),
udp: Reflect.has(state, "udp")
2022-05-21 14:02:00 +00:00
});
}
2022-12-14 11:50:07 +00:00
__name(stringifyState, "stringifyState");
2022-08-01 06:02:58 +00:00
function chooseEncryptionMode(options) {
2022-12-14 11:50:07 +00:00
const option = options.find((option2) => SUPPORTED_ENCRYPTION_MODES.includes(option2));
2022-08-01 06:02:58 +00:00
if (!option) {
2022-12-14 11:50:07 +00:00
throw new Error(`No compatible encryption modes. Available include: ${options.join(", ")}`);
2022-05-21 14:02:00 +00:00
}
return option;
}
2022-12-14 11:50:07 +00:00
__name(chooseEncryptionMode, "chooseEncryptionMode");
function randomNBit(numberOfBits) {
return Math.floor(Math.random() * 2 ** numberOfBits);
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
__name(randomNBit, "randomNBit");
2022-08-01 06:02:58 +00:00
var Networking = class extends import_node_events3.EventEmitter {
_state;
debug;
2022-08-01 06:02:58 +00:00
constructor(options, debug) {
2022-05-21 14:02:00 +00:00
super();
this.onWsOpen = this.onWsOpen.bind(this);
this.onChildError = this.onChildError.bind(this);
this.onWsPacket = this.onWsPacket.bind(this);
this.onWsClose = this.onWsClose.bind(this);
this.onWsDebug = this.onWsDebug.bind(this);
this.onUdpDebug = this.onUdpDebug.bind(this);
this.onUdpClose = this.onUdpClose.bind(this);
2022-12-14 11:50:07 +00:00
this.debug = debug ? (message) => this.emit("debug", message) : null;
2022-05-21 14:02:00 +00:00
this._state = {
code: 0 /* OpeningWs */,
ws: this.createWebSocket(options.endpoint),
2022-12-14 11:50:07 +00:00
connectionOptions: options
2022-05-21 14:02:00 +00:00
};
}
2022-08-01 06:02:58 +00:00
destroy() {
2022-05-21 14:02:00 +00:00
this.state = {
2022-12-14 11:50:07 +00:00
code: 6 /* Closed */
2022-05-21 14:02:00 +00:00
};
}
2022-08-01 06:02:58 +00:00
get state() {
2022-05-21 14:02:00 +00:00
return this._state;
}
2022-08-01 06:02:58 +00:00
set state(newState) {
2022-12-14 11:50:07 +00:00
const oldWs = Reflect.get(this._state, "ws");
const newWs = Reflect.get(newState, "ws");
2022-08-01 06:02:58 +00:00
if (oldWs && oldWs !== newWs) {
2022-12-14 11:50:07 +00:00
oldWs.off("debug", this.onWsDebug);
oldWs.on("error", noop);
oldWs.off("error", this.onChildError);
oldWs.off("open", this.onWsOpen);
oldWs.off("packet", this.onWsPacket);
oldWs.off("close", this.onWsClose);
2022-05-21 14:02:00 +00:00
oldWs.destroy();
}
2022-12-14 11:50:07 +00:00
const oldUdp = Reflect.get(this._state, "udp");
const newUdp = Reflect.get(newState, "udp");
2022-08-01 06:02:58 +00:00
if (oldUdp && oldUdp !== newUdp) {
2022-12-14 11:50:07 +00:00
oldUdp.on("error", noop);
oldUdp.off("error", this.onChildError);
oldUdp.off("close", this.onUdpClose);
oldUdp.off("debug", this.onUdpDebug);
2022-05-21 14:02:00 +00:00
oldUdp.destroy();
}
const oldState = this._state;
this._state = newState;
2022-12-14 11:50:07 +00:00
this.emit("stateChange", oldState, newState);
2022-05-21 14:02:00 +00:00
this.debug?.(`state change:
from ${stringifyState(oldState)}
to ${stringifyState(newState)}`);
}
2022-08-01 06:02:58 +00:00
createWebSocket(endpoint) {
2022-05-21 14:02:00 +00:00
const ws = new VoiceWebSocket(`wss://${endpoint}?v=4`, Boolean(this.debug));
2022-12-14 11:50:07 +00:00
ws.on("error", this.onChildError);
ws.once("open", this.onWsOpen);
ws.on("packet", this.onWsPacket);
ws.once("close", this.onWsClose);
ws.on("debug", this.onWsDebug);
2022-05-21 14:02:00 +00:00
return ws;
}
2022-08-01 06:02:58 +00:00
onChildError(error) {
2022-12-14 11:50:07 +00:00
this.emit("error", error);
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
onWsOpen() {
if (this.state.code === 0 /* OpeningWs */) {
2022-05-21 14:02:00 +00:00
const packet = {
op: import_v42.VoiceOpcodes.Identify,
d: {
server_id: this.state.connectionOptions.serverId,
user_id: this.state.connectionOptions.userId,
session_id: this.state.connectionOptions.sessionId,
2022-12-14 11:50:07 +00:00
token: this.state.connectionOptions.token
}
2022-05-21 14:02:00 +00:00
};
this.state.ws.sendPacket(packet);
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
code: 1 /* Identifying */
2022-05-21 14:02:00 +00:00
};
2022-08-01 06:02:58 +00:00
} else if (this.state.code === 5 /* Resuming */) {
2022-05-21 14:02:00 +00:00
const packet = {
op: import_v42.VoiceOpcodes.Resume,
d: {
server_id: this.state.connectionOptions.serverId,
session_id: this.state.connectionOptions.sessionId,
2022-12-14 11:50:07 +00:00
token: this.state.connectionOptions.token
}
2022-05-21 14:02:00 +00:00
};
this.state.ws.sendPacket(packet);
}
}
2022-08-01 06:02:58 +00:00
onWsClose({ code }) {
2022-05-21 14:02:00 +00:00
const canResume = code === 4015 || code < 4e3;
2022-08-01 06:02:58 +00:00
if (canResume && this.state.code === 4 /* Ready */) {
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
code: 5 /* Resuming */,
2022-12-14 11:50:07 +00:00
ws: this.createWebSocket(this.state.connectionOptions.endpoint)
2022-05-21 14:02:00 +00:00
};
2022-08-01 06:02:58 +00:00
} else if (this.state.code !== 6 /* Closed */) {
2022-05-21 14:02:00 +00:00
this.destroy();
2022-12-14 11:50:07 +00:00
this.emit("close", code);
2022-05-21 14:02:00 +00:00
}
}
2022-08-01 06:02:58 +00:00
onUdpClose() {
if (this.state.code === 4 /* Ready */) {
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
code: 5 /* Resuming */,
2022-12-14 11:50:07 +00:00
ws: this.createWebSocket(this.state.connectionOptions.endpoint)
2022-05-21 14:02:00 +00:00
};
}
}
2022-08-01 06:02:58 +00:00
onWsPacket(packet) {
if (packet.op === import_v42.VoiceOpcodes.Hello && this.state.code !== 6 /* Closed */) {
2022-05-21 14:02:00 +00:00
this.state.ws.setHeartbeatInterval(packet.d.heartbeat_interval);
2022-08-01 06:02:58 +00:00
} else if (packet.op === import_v42.VoiceOpcodes.Ready && this.state.code === 1 /* Identifying */) {
2022-05-21 14:02:00 +00:00
const { ip, port, ssrc, modes } = packet.d;
const udp = new VoiceUDPSocket({ ip, port });
2022-12-14 11:50:07 +00:00
udp.on("error", this.onChildError);
udp.on("debug", this.onUdpDebug);
udp.once("close", this.onUdpClose);
udp.performIPDiscovery(ssrc).then((localConfig) => {
if (this.state.code !== 2 /* UdpHandshaking */)
return;
this.state.ws.sendPacket({
op: import_v42.VoiceOpcodes.SelectProtocol,
d: {
protocol: "udp",
data: {
address: localConfig.ip,
port: localConfig.port,
mode: chooseEncryptionMode(modes)
}
}
});
this.state = {
...this.state,
code: 3 /* SelectingProtocol */
};
}).catch((error) => this.emit("error", error));
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
code: 2 /* UdpHandshaking */,
udp,
connectionData: {
2022-12-14 11:50:07 +00:00
ssrc
}
2022-05-21 14:02:00 +00:00
};
2022-12-14 11:50:07 +00:00
} else if (packet.op === import_v42.VoiceOpcodes.SessionDescription && this.state.code === 3 /* SelectingProtocol */) {
2022-05-21 14:02:00 +00:00
const { mode: encryptionMode, secret_key: secretKey } = packet.d;
this.state = {
...this.state,
code: 4 /* Ready */,
connectionData: {
...this.state.connectionData,
encryptionMode,
secretKey: new Uint8Array(secretKey),
sequence: randomNBit(16),
timestamp: randomNBit(32),
nonce: 0,
nonceBuffer: import_node_buffer3.Buffer.alloc(24),
2022-05-21 14:02:00 +00:00
speaking: false,
2022-12-14 11:50:07 +00:00
packetsPlayed: 0
}
2022-05-21 14:02:00 +00:00
};
2022-08-01 06:02:58 +00:00
} else if (packet.op === import_v42.VoiceOpcodes.Resumed && this.state.code === 5 /* Resuming */) {
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
code: 4 /* Ready */
2022-05-21 14:02:00 +00:00
};
this.state.connectionData.speaking = false;
}
}
2022-08-01 06:02:58 +00:00
onWsDebug(message) {
2022-05-21 14:02:00 +00:00
this.debug?.(`[WS] ${message}`);
}
2022-08-01 06:02:58 +00:00
onUdpDebug(message) {
2022-05-21 14:02:00 +00:00
this.debug?.(`[UDP] ${message}`);
}
2022-08-01 06:02:58 +00:00
prepareAudioPacket(opusPacket) {
2022-05-21 14:02:00 +00:00
const state = this.state;
2022-12-14 11:50:07 +00:00
if (state.code !== 4 /* Ready */)
return;
2022-05-21 14:02:00 +00:00
state.preparedPacket = this.createAudioPacket(opusPacket, state.connectionData);
return state.preparedPacket;
}
2022-08-01 06:02:58 +00:00
dispatchAudio() {
2022-05-21 14:02:00 +00:00
const state = this.state;
2022-12-14 11:50:07 +00:00
if (state.code !== 4 /* Ready */)
return false;
if (typeof state.preparedPacket !== "undefined") {
2022-05-21 14:02:00 +00:00
this.playAudioPacket(state.preparedPacket);
state.preparedPacket = void 0;
return true;
}
return false;
}
2022-08-01 06:02:58 +00:00
playAudioPacket(audioPacket) {
2022-05-21 14:02:00 +00:00
const state = this.state;
2022-12-14 11:50:07 +00:00
if (state.code !== 4 /* Ready */)
return;
2022-05-21 14:02:00 +00:00
const { connectionData } = state;
connectionData.packetsPlayed++;
connectionData.sequence++;
connectionData.timestamp += TIMESTAMP_INC;
2022-12-14 11:50:07 +00:00
if (connectionData.sequence >= 2 ** 16)
connectionData.sequence = 0;
if (connectionData.timestamp >= 2 ** 32)
connectionData.timestamp = 0;
2022-05-21 14:02:00 +00:00
this.setSpeaking(true);
state.udp.send(audioPacket);
}
2022-08-01 06:02:58 +00:00
setSpeaking(speaking) {
2022-05-21 14:02:00 +00:00
const state = this.state;
2022-12-14 11:50:07 +00:00
if (state.code !== 4 /* Ready */)
return;
if (state.connectionData.speaking === speaking)
return;
2022-05-21 14:02:00 +00:00
state.connectionData.speaking = speaking;
state.ws.sendPacket({
op: import_v42.VoiceOpcodes.Speaking,
d: {
speaking: speaking ? 1 : 0,
delay: 0,
2022-12-14 11:50:07 +00:00
ssrc: state.connectionData.ssrc
}
2022-05-21 14:02:00 +00:00
});
}
2022-08-01 06:02:58 +00:00
createAudioPacket(opusPacket, connectionData) {
const packetBuffer = import_node_buffer3.Buffer.alloc(12);
2022-05-21 14:02:00 +00:00
packetBuffer[0] = 128;
packetBuffer[1] = 120;
const { sequence, timestamp, ssrc } = connectionData;
packetBuffer.writeUIntBE(sequence, 2, 2);
packetBuffer.writeUIntBE(timestamp, 4, 4);
packetBuffer.writeUIntBE(ssrc, 8, 4);
packetBuffer.copy(nonce, 0, 0, 12);
return import_node_buffer3.Buffer.concat([packetBuffer, ...this.encryptOpusPacket(opusPacket, connectionData)]);
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
encryptOpusPacket(opusPacket, connectionData) {
2022-05-21 14:02:00 +00:00
const { secretKey, encryptionMode } = connectionData;
2022-12-14 11:50:07 +00:00
if (encryptionMode === "xsalsa20_poly1305_lite") {
2022-05-21 14:02:00 +00:00
connectionData.nonce++;
2022-12-14 11:50:07 +00:00
if (connectionData.nonce > MAX_NONCE_SIZE)
connectionData.nonce = 0;
2022-05-21 14:02:00 +00:00
connectionData.nonceBuffer.writeUInt32BE(connectionData.nonce, 0);
2022-12-14 11:50:07 +00:00
return [
methods.close(opusPacket, connectionData.nonceBuffer, secretKey),
connectionData.nonceBuffer.slice(0, 4)
];
} else if (encryptionMode === "xsalsa20_poly1305_suffix") {
2022-05-21 14:02:00 +00:00
const random = methods.random(24, connectionData.nonceBuffer);
return [methods.close(opusPacket, random, secretKey), random];
}
return [methods.close(opusPacket, nonce, secretKey)];
}
};
2022-12-14 11:50:07 +00:00
__name(Networking, "Networking");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/receive/VoiceReceiver.ts
2022-12-14 11:50:07 +00:00
var import_node_buffer5 = require("buffer");
var import_v43 = require("discord-api-types/voice/v4");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/receive/AudioReceiveStream.ts
2022-12-14 11:50:07 +00:00
var import_node_stream = require("stream");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/audio/AudioPlayer.ts
2022-12-14 11:50:07 +00:00
var import_node_buffer4 = require("buffer");
var import_node_events4 = require("events");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/audio/AudioPlayerError.ts
2022-08-01 06:02:58 +00:00
var AudioPlayerError = class extends Error {
resource;
2022-08-01 06:02:58 +00:00
constructor(error, resource) {
2022-05-21 14:02:00 +00:00
super(error.message);
this.resource = resource;
this.name = error.name;
this.stack = error.stack;
}
};
2022-12-14 11:50:07 +00:00
__name(AudioPlayerError, "AudioPlayerError");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/audio/PlayerSubscription.ts
2022-08-01 06:02:58 +00:00
var PlayerSubscription = class {
connection;
player;
2022-08-01 06:02:58 +00:00
constructor(connection, player) {
2022-05-21 14:02:00 +00:00
this.connection = connection;
this.player = player;
}
2022-08-01 06:02:58 +00:00
unsubscribe() {
2022-12-14 11:50:07 +00:00
this.connection["onSubscriptionRemoved"](this);
this.player["unsubscribe"](this);
2022-05-21 14:02:00 +00:00
}
};
2022-12-14 11:50:07 +00:00
__name(PlayerSubscription, "PlayerSubscription");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/audio/AudioPlayer.ts
var SILENCE_FRAME = import_node_buffer4.Buffer.from([248, 255, 254]);
2022-12-14 11:50:07 +00:00
var NoSubscriberBehavior = /* @__PURE__ */ ((NoSubscriberBehavior2) => {
NoSubscriberBehavior2["Pause"] = "pause";
NoSubscriberBehavior2["Play"] = "play";
NoSubscriberBehavior2["Stop"] = "stop";
2022-05-21 14:02:00 +00:00
return NoSubscriberBehavior2;
})(NoSubscriberBehavior || {});
2022-12-14 11:50:07 +00:00
var AudioPlayerStatus = /* @__PURE__ */ ((AudioPlayerStatus2) => {
AudioPlayerStatus2["AutoPaused"] = "autopaused";
AudioPlayerStatus2["Buffering"] = "buffering";
AudioPlayerStatus2["Idle"] = "idle";
AudioPlayerStatus2["Paused"] = "paused";
AudioPlayerStatus2["Playing"] = "playing";
2022-05-21 14:02:00 +00:00
return AudioPlayerStatus2;
})(AudioPlayerStatus || {});
2022-08-01 06:02:58 +00:00
function stringifyState2(state) {
2022-05-21 14:02:00 +00:00
return JSON.stringify({
...state,
2022-12-14 11:50:07 +00:00
resource: Reflect.has(state, "resource"),
stepTimeout: Reflect.has(state, "stepTimeout")
2022-05-21 14:02:00 +00:00
});
}
2022-12-14 11:50:07 +00:00
__name(stringifyState2, "stringifyState");
var AudioPlayer = class extends import_node_events4.EventEmitter {
_state;
subscribers = [];
behaviors;
debug;
2022-08-01 06:02:58 +00:00
constructor(options = {}) {
2022-05-21 14:02:00 +00:00
super();
2022-12-14 11:50:07 +00:00
this._state = { status: "idle" /* Idle */ };
2022-05-21 14:02:00 +00:00
this.behaviors = {
2022-12-14 11:50:07 +00:00
noSubscriber: "pause" /* Pause */,
2022-05-21 14:02:00 +00:00
maxMissedFrames: 5,
2022-12-14 11:50:07 +00:00
...options.behaviors
2022-05-21 14:02:00 +00:00
};
2022-12-14 11:50:07 +00:00
this.debug = options.debug === false ? null : (message) => this.emit("debug", message);
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
get playable() {
2022-12-14 11:50:07 +00:00
return this.subscribers.filter(({ connection }) => connection.state.status === "ready" /* Ready */).map(({ connection }) => connection);
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
subscribe(connection) {
2022-12-14 11:50:07 +00:00
const existingSubscription = this.subscribers.find((subscription) => subscription.connection === connection);
2022-08-01 06:02:58 +00:00
if (!existingSubscription) {
2022-05-21 14:02:00 +00:00
const subscription = new PlayerSubscription(connection, this);
this.subscribers.push(subscription);
2022-12-14 11:50:07 +00:00
setImmediate(() => this.emit("subscribe", subscription));
2022-05-21 14:02:00 +00:00
return subscription;
}
return existingSubscription;
}
2022-08-01 06:02:58 +00:00
unsubscribe(subscription) {
2022-05-21 14:02:00 +00:00
const index = this.subscribers.indexOf(subscription);
const exists = index !== -1;
2022-08-01 06:02:58 +00:00
if (exists) {
2022-05-21 14:02:00 +00:00
this.subscribers.splice(index, 1);
subscription.connection.setSpeaking(false);
2022-12-14 11:50:07 +00:00
this.emit("unsubscribe", subscription);
2022-05-21 14:02:00 +00:00
}
return exists;
}
2022-08-01 06:02:58 +00:00
get state() {
2022-05-21 14:02:00 +00:00
return this._state;
}
2022-08-01 06:02:58 +00:00
set state(newState) {
2022-05-21 14:02:00 +00:00
const oldState = this._state;
2022-12-14 11:50:07 +00:00
const newResource = Reflect.get(newState, "resource");
if (oldState.status !== "idle" /* Idle */ && oldState.resource !== newResource) {
oldState.resource.playStream.on("error", noop);
oldState.resource.playStream.off("error", oldState.onStreamError);
2022-05-21 14:02:00 +00:00
oldState.resource.audioPlayer = void 0;
oldState.resource.playStream.destroy();
oldState.resource.playStream.read();
}
2022-12-14 11:50:07 +00:00
if (oldState.status === "buffering" /* Buffering */ && (newState.status !== "buffering" /* Buffering */ || newState.resource !== oldState.resource)) {
oldState.resource.playStream.off("end", oldState.onFailureCallback);
oldState.resource.playStream.off("close", oldState.onFailureCallback);
oldState.resource.playStream.off("finish", oldState.onFailureCallback);
oldState.resource.playStream.off("readable", oldState.onReadableCallback);
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
if (newState.status === "idle" /* Idle */) {
2022-05-21 14:02:00 +00:00
this._signalStopSpeaking();
deleteAudioPlayer(this);
}
2022-08-01 06:02:58 +00:00
if (newResource) {
2022-05-21 14:02:00 +00:00
addAudioPlayer(this);
}
2022-12-14 11:50:07 +00:00
const didChangeResources = oldState.status !== "idle" /* Idle */ && newState.status === "playing" /* Playing */ && oldState.resource !== newState.resource;
2022-05-21 14:02:00 +00:00
this._state = newState;
2022-12-14 11:50:07 +00:00
this.emit("stateChange", oldState, this._state);
2022-08-01 06:02:58 +00:00
if (oldState.status !== newState.status || didChangeResources) {
2022-05-21 14:02:00 +00:00
this.emit(newState.status, oldState, this._state);
}
this.debug?.(`state change:
from ${stringifyState2(oldState)}
to ${stringifyState2(newState)}`);
}
2022-08-01 06:02:58 +00:00
play(resource) {
if (resource.ended) {
2022-12-14 11:50:07 +00:00
throw new Error("Cannot play a resource that has already ended.");
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
if (resource.audioPlayer) {
if (resource.audioPlayer === this) {
2022-05-21 14:02:00 +00:00
return;
}
2022-12-14 11:50:07 +00:00
throw new Error("Resource is already being played by another audio player.");
2022-05-21 14:02:00 +00:00
}
resource.audioPlayer = this;
2022-12-14 11:50:07 +00:00
const onStreamError = /* @__PURE__ */ __name((error) => {
if (this.state.status !== "idle" /* Idle */) {
this.emit("error", new AudioPlayerError(error, this.state.resource));
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
if (this.state.status !== "idle" /* Idle */ && this.state.resource === resource) {
2022-05-21 14:02:00 +00:00
this.state = {
2022-12-14 11:50:07 +00:00
status: "idle" /* Idle */
2022-05-21 14:02:00 +00:00
};
}
2022-12-14 11:50:07 +00:00
}, "onStreamError");
resource.playStream.once("error", onStreamError);
2022-08-01 06:02:58 +00:00
if (resource.started) {
2022-05-21 14:02:00 +00:00
this.state = {
2022-12-14 11:50:07 +00:00
status: "playing" /* Playing */,
2022-05-21 14:02:00 +00:00
missedFrames: 0,
playbackDuration: 0,
resource,
2022-12-14 11:50:07 +00:00
onStreamError
2022-05-21 14:02:00 +00:00
};
2022-08-01 06:02:58 +00:00
} else {
const onReadableCallback = /* @__PURE__ */ __name(() => {
2022-12-14 11:50:07 +00:00
if (this.state.status === "buffering" /* Buffering */ && this.state.resource === resource) {
2022-05-21 14:02:00 +00:00
this.state = {
2022-12-14 11:50:07 +00:00
status: "playing" /* Playing */,
2022-05-21 14:02:00 +00:00
missedFrames: 0,
playbackDuration: 0,
resource,
2022-12-14 11:50:07 +00:00
onStreamError
2022-05-21 14:02:00 +00:00
};
}
2022-12-14 11:50:07 +00:00
}, "onReadableCallback");
2022-08-01 06:02:58 +00:00
const onFailureCallback = /* @__PURE__ */ __name(() => {
2022-12-14 11:50:07 +00:00
if (this.state.status === "buffering" /* Buffering */ && this.state.resource === resource) {
2022-05-21 14:02:00 +00:00
this.state = {
2022-12-14 11:50:07 +00:00
status: "idle" /* Idle */
2022-05-21 14:02:00 +00:00
};
}
2022-12-14 11:50:07 +00:00
}, "onFailureCallback");
resource.playStream.once("readable", onReadableCallback);
resource.playStream.once("end", onFailureCallback);
resource.playStream.once("close", onFailureCallback);
resource.playStream.once("finish", onFailureCallback);
2022-05-21 14:02:00 +00:00
this.state = {
2022-12-14 11:50:07 +00:00
status: "buffering" /* Buffering */,
2022-05-21 14:02:00 +00:00
resource,
onReadableCallback,
onFailureCallback,
2022-12-14 11:50:07 +00:00
onStreamError
2022-05-21 14:02:00 +00:00
};
}
}
2022-08-01 06:02:58 +00:00
pause(interpolateSilence = true) {
2022-12-14 11:50:07 +00:00
if (this.state.status !== "playing" /* Playing */)
return false;
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
status: "paused" /* Paused */,
silencePacketsRemaining: interpolateSilence ? 5 : 0
2022-05-21 14:02:00 +00:00
};
return true;
}
2022-08-01 06:02:58 +00:00
unpause() {
2022-12-14 11:50:07 +00:00
if (this.state.status !== "paused" /* Paused */)
return false;
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
status: "playing" /* Playing */,
missedFrames: 0
2022-05-21 14:02:00 +00:00
};
return true;
}
2022-08-01 06:02:58 +00:00
stop(force = false) {
2022-12-14 11:50:07 +00:00
if (this.state.status === "idle" /* Idle */)
return false;
2022-08-01 06:02:58 +00:00
if (force || this.state.resource.silencePaddingFrames === 0) {
2022-05-21 14:02:00 +00:00
this.state = {
2022-12-14 11:50:07 +00:00
status: "idle" /* Idle */
2022-05-21 14:02:00 +00:00
};
2022-08-01 06:02:58 +00:00
} else if (this.state.resource.silenceRemaining === -1) {
2022-05-21 14:02:00 +00:00
this.state.resource.silenceRemaining = this.state.resource.silencePaddingFrames;
}
return true;
}
2022-08-01 06:02:58 +00:00
checkPlayable() {
2022-05-21 14:02:00 +00:00
const state = this._state;
2022-12-14 11:50:07 +00:00
if (state.status === "idle" /* Idle */ || state.status === "buffering" /* Buffering */)
return false;
2022-08-01 06:02:58 +00:00
if (!state.resource.readable) {
2022-05-21 14:02:00 +00:00
this.state = {
2022-12-14 11:50:07 +00:00
status: "idle" /* Idle */
2022-05-21 14:02:00 +00:00
};
return false;
}
return true;
}
2022-08-01 06:02:58 +00:00
_stepDispatch() {
2022-05-21 14:02:00 +00:00
const state = this._state;
2022-12-14 11:50:07 +00:00
if (state.status === "idle" /* Idle */ || state.status === "buffering" /* Buffering */)
return;
for (const connection of this.playable) {
connection.dispatchAudio();
}
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
_stepPrepare() {
2022-05-21 14:02:00 +00:00
const state = this._state;
2022-12-14 11:50:07 +00:00
if (state.status === "idle" /* Idle */ || state.status === "buffering" /* Buffering */)
return;
2022-05-21 14:02:00 +00:00
const playable = this.playable;
2022-12-14 11:50:07 +00:00
if (state.status === "autopaused" /* AutoPaused */ && playable.length > 0) {
2022-05-21 14:02:00 +00:00
this.state = {
...state,
2022-12-14 11:50:07 +00:00
status: "playing" /* Playing */,
missedFrames: 0
2022-05-21 14:02:00 +00:00
};
}
2022-12-14 11:50:07 +00:00
if (state.status === "paused" /* Paused */ || state.status === "autopaused" /* AutoPaused */) {
2022-08-01 06:02:58 +00:00
if (state.silencePacketsRemaining > 0) {
2022-05-21 14:02:00 +00:00
state.silencePacketsRemaining--;
this._preparePacket(SILENCE_FRAME, playable, state);
2022-08-01 06:02:58 +00:00
if (state.silencePacketsRemaining === 0) {
2022-05-21 14:02:00 +00:00
this._signalStopSpeaking();
}
}
return;
}
2022-08-01 06:02:58 +00:00
if (playable.length === 0) {
2022-12-14 11:50:07 +00:00
if (this.behaviors.noSubscriber === "pause" /* Pause */) {
2022-05-21 14:02:00 +00:00
this.state = {
...state,
2022-12-14 11:50:07 +00:00
status: "autopaused" /* AutoPaused */,
silencePacketsRemaining: 5
2022-05-21 14:02:00 +00:00
};
return;
2022-12-14 11:50:07 +00:00
} else if (this.behaviors.noSubscriber === "stop" /* Stop */) {
2022-05-21 14:02:00 +00:00
this.stop(true);
}
}
const packet = state.resource.read();
2022-12-14 11:50:07 +00:00
if (state.status === "playing" /* Playing */) {
2022-08-01 06:02:58 +00:00
if (packet) {
2022-05-21 14:02:00 +00:00
this._preparePacket(packet, playable, state);
state.missedFrames = 0;
2022-08-01 06:02:58 +00:00
} else {
2022-05-21 14:02:00 +00:00
this._preparePacket(SILENCE_FRAME, playable, state);
state.missedFrames++;
2022-08-01 06:02:58 +00:00
if (state.missedFrames >= this.behaviors.maxMissedFrames) {
2022-05-21 14:02:00 +00:00
this.stop();
}
}
}
}
2022-08-01 06:02:58 +00:00
_signalStopSpeaking() {
for (const { connection } of this.subscribers) {
connection.setSpeaking(false);
}
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
_preparePacket(packet, receivers, state) {
2022-05-21 14:02:00 +00:00
state.playbackDuration += 20;
for (const connection of receivers) {
connection.prepareAudioPacket(packet);
}
2022-05-21 14:02:00 +00:00
}
};
2022-12-14 11:50:07 +00:00
__name(AudioPlayer, "AudioPlayer");
2022-08-01 06:02:58 +00:00
function createAudioPlayer(options) {
2022-05-21 14:02:00 +00:00
return new AudioPlayer(options);
}
2022-12-14 11:50:07 +00:00
__name(createAudioPlayer, "createAudioPlayer");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/receive/AudioReceiveStream.ts
2022-12-14 11:50:07 +00:00
var EndBehaviorType = /* @__PURE__ */ ((EndBehaviorType2) => {
EndBehaviorType2[EndBehaviorType2["Manual"] = 0] = "Manual";
EndBehaviorType2[EndBehaviorType2["AfterSilence"] = 1] = "AfterSilence";
EndBehaviorType2[EndBehaviorType2["AfterInactivity"] = 2] = "AfterInactivity";
2022-05-21 14:02:00 +00:00
return EndBehaviorType2;
})(EndBehaviorType || {});
2022-08-01 06:02:58 +00:00
function createDefaultAudioReceiveStreamOptions() {
2022-05-21 14:02:00 +00:00
return {
end: {
2022-12-14 11:50:07 +00:00
behavior: 0 /* Manual */
}
2022-05-21 14:02:00 +00:00
};
}
2022-12-14 11:50:07 +00:00
__name(createDefaultAudioReceiveStreamOptions, "createDefaultAudioReceiveStreamOptions");
2022-08-01 06:02:58 +00:00
var AudioReceiveStream = class extends import_node_stream.Readable {
end;
endTimeout;
2022-08-01 06:02:58 +00:00
constructor({ end, ...options }) {
2022-05-21 14:02:00 +00:00
super({
...options,
2022-12-14 11:50:07 +00:00
objectMode: true
2022-05-21 14:02:00 +00:00
});
this.end = end;
}
2022-08-01 06:02:58 +00:00
push(buffer) {
2022-12-14 11:50:07 +00:00
if (buffer && (this.end.behavior === 2 /* AfterInactivity */ || this.end.behavior === 1 /* AfterSilence */ && (buffer.compare(SILENCE_FRAME) !== 0 || typeof this.endTimeout === "undefined"))) {
this.renewEndTimeout(this.end);
2022-05-21 14:02:00 +00:00
}
return super.push(buffer);
}
2022-08-01 06:02:58 +00:00
renewEndTimeout(end) {
if (this.endTimeout) {
2022-05-21 14:02:00 +00:00
clearTimeout(this.endTimeout);
}
this.endTimeout = setTimeout(() => this.push(null), end.duration);
}
2022-12-14 11:50:07 +00:00
_read() {
}
2022-05-21 14:02:00 +00:00
};
2022-12-14 11:50:07 +00:00
__name(AudioReceiveStream, "AudioReceiveStream");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/receive/SSRCMap.ts
2022-12-14 11:50:07 +00:00
var import_node_events5 = require("events");
2022-08-01 06:02:58 +00:00
var SSRCMap = class extends import_node_events5.EventEmitter {
map;
2022-08-01 06:02:58 +00:00
constructor() {
2022-05-21 14:02:00 +00:00
super();
this.map = /* @__PURE__ */ new Map();
}
2022-08-01 06:02:58 +00:00
update(data) {
2022-05-21 14:02:00 +00:00
const existing = this.map.get(data.audioSSRC);
const newValue = {
...this.map.get(data.audioSSRC),
2022-12-14 11:50:07 +00:00
...data
2022-05-21 14:02:00 +00:00
};
this.map.set(data.audioSSRC, newValue);
2022-12-14 11:50:07 +00:00
if (!existing)
this.emit("create", newValue);
this.emit("update", existing, newValue);
2022-08-01 06:02:58 +00:00
}
get(target) {
2022-12-14 11:50:07 +00:00
if (typeof target === "number") {
2022-05-21 14:02:00 +00:00
return this.map.get(target);
}
2022-08-01 06:02:58 +00:00
for (const data of this.map.values()) {
if (data.userId === target) {
2022-05-21 14:02:00 +00:00
return data;
}
}
return void 0;
}
2022-08-01 06:02:58 +00:00
delete(target) {
2022-12-14 11:50:07 +00:00
if (typeof target === "number") {
2022-05-21 14:02:00 +00:00
const existing = this.map.get(target);
2022-08-01 06:02:58 +00:00
if (existing) {
2022-05-21 14:02:00 +00:00
this.map.delete(target);
2022-12-14 11:50:07 +00:00
this.emit("delete", existing);
2022-05-21 14:02:00 +00:00
}
return existing;
}
2022-08-01 06:02:58 +00:00
for (const [audioSSRC, data] of this.map.entries()) {
if (data.userId === target) {
2022-05-21 14:02:00 +00:00
this.map.delete(audioSSRC);
2022-12-14 11:50:07 +00:00
this.emit("delete", data);
2022-05-21 14:02:00 +00:00
return data;
}
}
return void 0;
}
};
2022-12-14 11:50:07 +00:00
__name(SSRCMap, "SSRCMap");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/receive/SpeakingMap.ts
2022-12-14 11:50:07 +00:00
var import_node_events6 = require("events");
2022-08-01 06:02:58 +00:00
var _SpeakingMap = class extends import_node_events6.EventEmitter {
users;
speakingTimeouts;
2022-08-01 06:02:58 +00:00
constructor() {
2022-05-21 14:02:00 +00:00
super();
this.users = /* @__PURE__ */ new Map();
this.speakingTimeouts = /* @__PURE__ */ new Map();
}
2022-08-01 06:02:58 +00:00
onPacket(userId) {
2022-05-21 14:02:00 +00:00
const timeout = this.speakingTimeouts.get(userId);
2022-08-01 06:02:58 +00:00
if (timeout) {
2022-05-21 14:02:00 +00:00
clearTimeout(timeout);
2022-08-01 06:02:58 +00:00
} else {
2022-05-21 14:02:00 +00:00
this.users.set(userId, Date.now());
2022-12-14 11:50:07 +00:00
this.emit("start", userId);
2022-05-21 14:02:00 +00:00
}
this.startTimeout(userId);
}
2022-08-01 06:02:58 +00:00
startTimeout(userId) {
this.speakingTimeouts.set(
userId,
setTimeout(() => {
2022-12-14 11:50:07 +00:00
this.emit("end", userId);
2022-08-01 06:02:58 +00:00
this.speakingTimeouts.delete(userId);
this.users.delete(userId);
2022-12-14 11:50:07 +00:00
}, _SpeakingMap.DELAY)
2022-08-01 06:02:58 +00:00
);
2022-05-21 14:02:00 +00:00
}
};
var SpeakingMap = _SpeakingMap;
2022-12-14 11:50:07 +00:00
__name(SpeakingMap, "SpeakingMap");
__publicField(SpeakingMap, "DELAY", 100);
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/receive/VoiceReceiver.ts
2022-08-01 06:02:58 +00:00
var VoiceReceiver = class {
voiceConnection;
ssrcMap;
subscriptions;
connectionData;
speaking;
2022-08-01 06:02:58 +00:00
constructor(voiceConnection) {
2022-05-21 14:02:00 +00:00
this.voiceConnection = voiceConnection;
this.ssrcMap = new SSRCMap();
this.speaking = new SpeakingMap();
this.subscriptions = /* @__PURE__ */ new Map();
this.connectionData = {};
this.onWsPacket = this.onWsPacket.bind(this);
this.onUdpMessage = this.onUdpMessage.bind(this);
}
2022-08-01 06:02:58 +00:00
onWsPacket(packet) {
2022-12-14 11:50:07 +00:00
if (packet.op === import_v43.VoiceOpcodes.ClientDisconnect && typeof packet.d?.user_id === "string") {
2022-05-21 14:02:00 +00:00
this.ssrcMap.delete(packet.d.user_id);
2022-12-14 11:50:07 +00:00
} else if (packet.op === import_v43.VoiceOpcodes.Speaking && typeof packet.d?.user_id === "string" && typeof packet.d?.ssrc === "number") {
2022-05-21 14:02:00 +00:00
this.ssrcMap.update({ userId: packet.d.user_id, audioSSRC: packet.d.ssrc });
2022-12-14 11:50:07 +00:00
} else if (packet.op === import_v43.VoiceOpcodes.ClientConnect && typeof packet.d?.user_id === "string" && typeof packet.d?.audio_ssrc === "number") {
2022-05-21 14:02:00 +00:00
this.ssrcMap.update({
userId: packet.d.user_id,
audioSSRC: packet.d.audio_ssrc,
2022-12-14 11:50:07 +00:00
videoSSRC: packet.d.video_ssrc === 0 ? void 0 : packet.d.video_ssrc
2022-05-21 14:02:00 +00:00
});
}
}
2022-08-01 06:02:58 +00:00
decrypt(buffer, mode, nonce2, secretKey) {
2022-05-21 14:02:00 +00:00
let end;
2022-12-14 11:50:07 +00:00
if (mode === "xsalsa20_poly1305_lite") {
2022-05-21 14:02:00 +00:00
buffer.copy(nonce2, 0, buffer.length - 4);
end = buffer.length - 4;
2022-12-14 11:50:07 +00:00
} else if (mode === "xsalsa20_poly1305_suffix") {
2022-05-21 14:02:00 +00:00
buffer.copy(nonce2, 0, buffer.length - 24);
end = buffer.length - 24;
2022-08-01 06:02:58 +00:00
} else {
2022-05-21 14:02:00 +00:00
buffer.copy(nonce2, 0, 0, 12);
}
const decrypted = methods.open(buffer.slice(12, end), nonce2, secretKey);
2022-12-14 11:50:07 +00:00
if (!decrypted)
return;
return import_node_buffer5.Buffer.from(decrypted);
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
parsePacket(buffer, mode, nonce2, secretKey) {
2022-05-21 14:02:00 +00:00
let packet = this.decrypt(buffer, mode, nonce2, secretKey);
2022-12-14 11:50:07 +00:00
if (!packet)
return;
2022-08-01 06:02:58 +00:00
if (packet[0] === 190 && packet[1] === 222) {
2022-05-21 14:02:00 +00:00
const headerExtensionLength = packet.readUInt16BE(2);
packet = packet.subarray(4 + 4 * headerExtensionLength);
2022-05-21 14:02:00 +00:00
}
return packet;
}
2022-08-01 06:02:58 +00:00
onUdpMessage(msg) {
2022-12-14 11:50:07 +00:00
if (msg.length <= 8)
return;
2022-05-21 14:02:00 +00:00
const ssrc = msg.readUInt32BE(8);
const userData = this.ssrcMap.get(ssrc);
2022-12-14 11:50:07 +00:00
if (!userData)
return;
2022-05-21 14:02:00 +00:00
this.speaking.onPacket(userData.userId);
const stream = this.subscriptions.get(userData.userId);
2022-12-14 11:50:07 +00:00
if (!stream)
return;
2022-08-01 06:02:58 +00:00
if (this.connectionData.encryptionMode && this.connectionData.nonceBuffer && this.connectionData.secretKey) {
const packet = this.parsePacket(
msg,
this.connectionData.encryptionMode,
this.connectionData.nonceBuffer,
2022-12-14 11:50:07 +00:00
this.connectionData.secretKey
2022-08-01 06:02:58 +00:00
);
if (packet) {
2022-05-21 14:02:00 +00:00
stream.push(packet);
2022-08-01 06:02:58 +00:00
} else {
2022-12-14 11:50:07 +00:00
stream.destroy(new Error("Failed to parse packet"));
2022-05-21 14:02:00 +00:00
}
}
}
2022-08-01 06:02:58 +00:00
subscribe(userId, options) {
2022-05-21 14:02:00 +00:00
const existing = this.subscriptions.get(userId);
2022-12-14 11:50:07 +00:00
if (existing)
return existing;
2022-05-21 14:02:00 +00:00
const stream = new AudioReceiveStream({
...createDefaultAudioReceiveStreamOptions(),
2022-12-14 11:50:07 +00:00
...options
2022-05-21 14:02:00 +00:00
});
2022-12-14 11:50:07 +00:00
stream.once("close", () => this.subscriptions.delete(userId));
2022-05-21 14:02:00 +00:00
this.subscriptions.set(userId, stream);
return stream;
}
};
2022-12-14 11:50:07 +00:00
__name(VoiceReceiver, "VoiceReceiver");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/VoiceConnection.ts
2022-12-14 11:50:07 +00:00
var VoiceConnectionStatus = /* @__PURE__ */ ((VoiceConnectionStatus2) => {
VoiceConnectionStatus2["Connecting"] = "connecting";
VoiceConnectionStatus2["Destroyed"] = "destroyed";
VoiceConnectionStatus2["Disconnected"] = "disconnected";
VoiceConnectionStatus2["Ready"] = "ready";
VoiceConnectionStatus2["Signalling"] = "signalling";
2022-05-21 14:02:00 +00:00
return VoiceConnectionStatus2;
})(VoiceConnectionStatus || {});
2022-12-14 11:50:07 +00:00
var VoiceConnectionDisconnectReason = /* @__PURE__ */ ((VoiceConnectionDisconnectReason2) => {
VoiceConnectionDisconnectReason2[VoiceConnectionDisconnectReason2["WebSocketClose"] = 0] = "WebSocketClose";
VoiceConnectionDisconnectReason2[VoiceConnectionDisconnectReason2["AdapterUnavailable"] = 1] = "AdapterUnavailable";
VoiceConnectionDisconnectReason2[VoiceConnectionDisconnectReason2["EndpointRemoved"] = 2] = "EndpointRemoved";
VoiceConnectionDisconnectReason2[VoiceConnectionDisconnectReason2["Manual"] = 3] = "Manual";
2022-05-21 14:02:00 +00:00
return VoiceConnectionDisconnectReason2;
})(VoiceConnectionDisconnectReason || {});
var VoiceConnection = class extends import_node_events7.EventEmitter {
rejoinAttempts;
_state;
joinConfig;
packets;
receiver;
debug;
constructor(joinConfig, options) {
2022-05-21 14:02:00 +00:00
super();
2022-12-14 11:50:07 +00:00
this.debug = options.debug ? (message) => this.emit("debug", message) : null;
2022-05-21 14:02:00 +00:00
this.rejoinAttempts = 0;
this.receiver = new VoiceReceiver(this);
this.onNetworkingClose = this.onNetworkingClose.bind(this);
this.onNetworkingStateChange = this.onNetworkingStateChange.bind(this);
this.onNetworkingError = this.onNetworkingError.bind(this);
this.onNetworkingDebug = this.onNetworkingDebug.bind(this);
const adapter = options.adapterCreator({
2022-12-14 11:50:07 +00:00
onVoiceServerUpdate: (data) => this.addServerPacket(data),
onVoiceStateUpdate: (data) => this.addStatePacket(data),
destroy: () => this.destroy(false)
2022-05-21 14:02:00 +00:00
});
2022-12-14 11:50:07 +00:00
this._state = { status: "signalling" /* Signalling */, adapter };
2022-05-21 14:02:00 +00:00
this.packets = {
server: void 0,
2022-12-14 11:50:07 +00:00
state: void 0
2022-05-21 14:02:00 +00:00
};
this.joinConfig = joinConfig;
}
2022-08-01 06:02:58 +00:00
get state() {
2022-05-21 14:02:00 +00:00
return this._state;
}
2022-08-01 06:02:58 +00:00
set state(newState) {
2022-05-21 14:02:00 +00:00
const oldState = this._state;
2022-12-14 11:50:07 +00:00
const oldNetworking = Reflect.get(oldState, "networking");
const newNetworking = Reflect.get(newState, "networking");
const oldSubscription = Reflect.get(oldState, "subscription");
const newSubscription = Reflect.get(newState, "subscription");
2022-08-01 06:02:58 +00:00
if (oldNetworking !== newNetworking) {
if (oldNetworking) {
2022-12-14 11:50:07 +00:00
oldNetworking.on("error", noop);
oldNetworking.off("debug", this.onNetworkingDebug);
oldNetworking.off("error", this.onNetworkingError);
oldNetworking.off("close", this.onNetworkingClose);
oldNetworking.off("stateChange", this.onNetworkingStateChange);
2022-05-21 14:02:00 +00:00
oldNetworking.destroy();
}
2022-12-14 11:50:07 +00:00
if (newNetworking)
this.updateReceiveBindings(newNetworking.state, oldNetworking?.state);
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
if (newState.status === "ready" /* Ready */) {
2022-05-21 14:02:00 +00:00
this.rejoinAttempts = 0;
2022-12-14 11:50:07 +00:00
} else if (newState.status === "destroyed" /* Destroyed */) {
2022-08-01 06:02:58 +00:00
for (const stream of this.receiver.subscriptions.values()) {
2022-12-14 11:50:07 +00:00
if (!stream.destroyed)
stream.destroy();
2022-05-21 14:02:00 +00:00
}
}
2022-12-14 11:50:07 +00:00
if (oldState.status !== "destroyed" /* Destroyed */ && newState.status === "destroyed" /* Destroyed */) {
2022-05-21 14:02:00 +00:00
oldState.adapter.destroy();
}
this._state = newState;
2022-08-01 06:02:58 +00:00
if (oldSubscription && oldSubscription !== newSubscription) {
2022-05-21 14:02:00 +00:00
oldSubscription.unsubscribe();
}
2022-12-14 11:50:07 +00:00
this.emit("stateChange", oldState, newState);
2022-08-01 06:02:58 +00:00
if (oldState.status !== newState.status) {
2022-05-21 14:02:00 +00:00
this.emit(newState.status, oldState, newState);
}
}
2022-08-01 06:02:58 +00:00
addServerPacket(packet) {
2022-05-21 14:02:00 +00:00
this.packets.server = packet;
2022-08-01 06:02:58 +00:00
if (packet.endpoint) {
2022-05-21 14:02:00 +00:00
this.configureNetworking();
2022-12-14 11:50:07 +00:00
} else if (this.state.status !== "destroyed" /* Destroyed */) {
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
status: "disconnected" /* Disconnected */,
reason: 2 /* EndpointRemoved */
2022-05-21 14:02:00 +00:00
};
}
}
2022-08-01 06:02:58 +00:00
addStatePacket(packet) {
2022-05-21 14:02:00 +00:00
this.packets.state = packet;
2022-12-14 11:50:07 +00:00
if (typeof packet.self_deaf !== "undefined")
this.joinConfig.selfDeaf = packet.self_deaf;
if (typeof packet.self_mute !== "undefined")
this.joinConfig.selfMute = packet.self_mute;
if (packet.channel_id)
this.joinConfig.channelId = packet.channel_id;
2022-08-01 06:02:58 +00:00
}
updateReceiveBindings(newState, oldState) {
2022-12-14 11:50:07 +00:00
const oldWs = Reflect.get(oldState ?? {}, "ws");
const newWs = Reflect.get(newState, "ws");
const oldUdp = Reflect.get(oldState ?? {}, "udp");
const newUdp = Reflect.get(newState, "udp");
2022-08-01 06:02:58 +00:00
if (oldWs !== newWs) {
2022-12-14 11:50:07 +00:00
oldWs?.off("packet", this.receiver.onWsPacket);
newWs?.on("packet", this.receiver.onWsPacket);
2022-08-01 06:02:58 +00:00
}
if (oldUdp !== newUdp) {
2022-12-14 11:50:07 +00:00
oldUdp?.off("message", this.receiver.onUdpMessage);
newUdp?.on("message", this.receiver.onUdpMessage);
2022-08-01 06:02:58 +00:00
}
2022-12-14 11:50:07 +00:00
this.receiver.connectionData = Reflect.get(newState, "connectionData") ?? {};
2022-08-01 06:02:58 +00:00
}
configureNetworking() {
2022-05-21 14:02:00 +00:00
const { server, state } = this.packets;
2022-12-14 11:50:07 +00:00
if (!server || !state || this.state.status === "destroyed" /* Destroyed */ || !server.endpoint)
return;
2022-08-01 06:02:58 +00:00
const networking = new Networking(
{
endpoint: server.endpoint,
serverId: server.guild_id ?? server.channel_id,
2022-08-01 06:02:58 +00:00
token: server.token,
sessionId: state.session_id,
2022-12-14 11:50:07 +00:00
userId: state.user_id
2022-08-01 06:02:58 +00:00
},
2022-12-14 11:50:07 +00:00
Boolean(this.debug)
2022-08-01 06:02:58 +00:00
);
2022-12-14 11:50:07 +00:00
networking.once("close", this.onNetworkingClose);
networking.on("stateChange", this.onNetworkingStateChange);
networking.on("error", this.onNetworkingError);
networking.on("debug", this.onNetworkingDebug);
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
status: "connecting" /* Connecting */,
networking
2022-05-21 14:02:00 +00:00
};
}
2022-08-01 06:02:58 +00:00
onNetworkingClose(code) {
2022-12-14 11:50:07 +00:00
if (this.state.status === "destroyed" /* Destroyed */)
return;
2022-08-01 06:02:58 +00:00
if (code === 4014) {
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
status: "disconnected" /* Disconnected */,
2022-05-21 14:02:00 +00:00
reason: 0 /* WebSocketClose */,
2022-12-14 11:50:07 +00:00
closeCode: code
2022-05-21 14:02:00 +00:00
};
2022-08-01 06:02:58 +00:00
} else {
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
status: "signalling" /* Signalling */
2022-05-21 14:02:00 +00:00
};
this.rejoinAttempts++;
2022-08-01 06:02:58 +00:00
if (!this.state.adapter.sendPayload(createJoinVoiceChannelPayload(this.joinConfig))) {
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
status: "disconnected" /* Disconnected */,
reason: 1 /* AdapterUnavailable */
2022-05-21 14:02:00 +00:00
};
}
}
}
2022-08-01 06:02:58 +00:00
onNetworkingStateChange(oldState, newState) {
2022-05-21 14:02:00 +00:00
this.updateReceiveBindings(newState, oldState);
2022-12-14 11:50:07 +00:00
if (oldState.code === newState.code)
return;
if (this.state.status !== "connecting" /* Connecting */ && this.state.status !== "ready" /* Ready */)
return;
2022-08-01 06:02:58 +00:00
if (newState.code === 4 /* Ready */) {
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
status: "ready" /* Ready */
2022-05-21 14:02:00 +00:00
};
2022-08-01 06:02:58 +00:00
} else if (newState.code !== 6 /* Closed */) {
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
status: "connecting" /* Connecting */
2022-05-21 14:02:00 +00:00
};
}
}
2022-08-01 06:02:58 +00:00
onNetworkingError(error) {
2022-12-14 11:50:07 +00:00
this.emit("error", error);
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
onNetworkingDebug(message) {
2022-05-21 14:02:00 +00:00
this.debug?.(`[NW] ${message}`);
}
2022-08-01 06:02:58 +00:00
prepareAudioPacket(buffer) {
2022-05-21 14:02:00 +00:00
const state = this.state;
2022-12-14 11:50:07 +00:00
if (state.status !== "ready" /* Ready */)
return;
2022-05-21 14:02:00 +00:00
return state.networking.prepareAudioPacket(buffer);
}
2022-08-01 06:02:58 +00:00
dispatchAudio() {
2022-05-21 14:02:00 +00:00
const state = this.state;
2022-12-14 11:50:07 +00:00
if (state.status !== "ready" /* Ready */)
return;
2022-05-21 14:02:00 +00:00
return state.networking.dispatchAudio();
}
2022-08-01 06:02:58 +00:00
playOpusPacket(buffer) {
2022-05-21 14:02:00 +00:00
const state = this.state;
2022-12-14 11:50:07 +00:00
if (state.status !== "ready" /* Ready */)
return;
2022-05-21 14:02:00 +00:00
state.networking.prepareAudioPacket(buffer);
return state.networking.dispatchAudio();
}
2022-08-01 06:02:58 +00:00
destroy(adapterAvailable = true) {
2022-12-14 11:50:07 +00:00
if (this.state.status === "destroyed" /* Destroyed */) {
throw new Error("Cannot destroy VoiceConnection - it has already been destroyed");
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
if (getVoiceConnection(this.joinConfig.guildId, this.joinConfig.group) === this) {
2022-05-21 14:02:00 +00:00
untrackVoiceConnection(this);
}
2022-08-01 06:02:58 +00:00
if (adapterAvailable) {
2022-05-21 14:02:00 +00:00
this.state.adapter.sendPayload(createJoinVoiceChannelPayload({ ...this.joinConfig, channelId: null }));
}
this.state = {
2022-12-14 11:50:07 +00:00
status: "destroyed" /* Destroyed */
2022-05-21 14:02:00 +00:00
};
}
2022-08-01 06:02:58 +00:00
disconnect() {
2022-12-14 11:50:07 +00:00
if (this.state.status === "destroyed" /* Destroyed */ || this.state.status === "signalling" /* Signalling */) {
2022-05-21 14:02:00 +00:00
return false;
}
this.joinConfig.channelId = null;
2022-08-01 06:02:58 +00:00
if (!this.state.adapter.sendPayload(createJoinVoiceChannelPayload(this.joinConfig))) {
2022-05-21 14:02:00 +00:00
this.state = {
adapter: this.state.adapter,
subscription: this.state.subscription,
2022-12-14 11:50:07 +00:00
status: "disconnected" /* Disconnected */,
reason: 1 /* AdapterUnavailable */
2022-05-21 14:02:00 +00:00
};
return false;
}
this.state = {
adapter: this.state.adapter,
reason: 3 /* Manual */,
2022-12-14 11:50:07 +00:00
status: "disconnected" /* Disconnected */
2022-05-21 14:02:00 +00:00
};
return true;
}
2022-08-01 06:02:58 +00:00
rejoin(joinConfig) {
2022-12-14 11:50:07 +00:00
if (this.state.status === "destroyed" /* Destroyed */) {
2022-05-21 14:02:00 +00:00
return false;
}
2022-12-14 11:50:07 +00:00
const notReady = this.state.status !== "ready" /* Ready */;
if (notReady)
this.rejoinAttempts++;
2022-05-21 14:02:00 +00:00
Object.assign(this.joinConfig, joinConfig);
2022-08-01 06:02:58 +00:00
if (this.state.adapter.sendPayload(createJoinVoiceChannelPayload(this.joinConfig))) {
if (notReady) {
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
status: "signalling" /* Signalling */
2022-05-21 14:02:00 +00:00
};
}
return true;
}
this.state = {
adapter: this.state.adapter,
subscription: this.state.subscription,
2022-12-14 11:50:07 +00:00
status: "disconnected" /* Disconnected */,
reason: 1 /* AdapterUnavailable */
2022-05-21 14:02:00 +00:00
};
return false;
}
2022-08-01 06:02:58 +00:00
setSpeaking(enabled) {
2022-12-14 11:50:07 +00:00
if (this.state.status !== "ready" /* Ready */)
return false;
2022-05-21 14:02:00 +00:00
return this.state.networking.setSpeaking(enabled);
}
2022-08-01 06:02:58 +00:00
subscribe(player) {
2022-12-14 11:50:07 +00:00
if (this.state.status === "destroyed" /* Destroyed */)
return;
const subscription = player["subscribe"](this);
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
subscription
2022-05-21 14:02:00 +00:00
};
return subscription;
}
2022-08-01 06:02:58 +00:00
get ping() {
2022-12-14 11:50:07 +00:00
if (this.state.status === "ready" /* Ready */ && this.state.networking.state.code === 4 /* Ready */) {
2022-05-21 14:02:00 +00:00
return {
ws: this.state.networking.state.ws.ping,
2022-12-14 11:50:07 +00:00
udp: this.state.networking.state.udp.ping
2022-05-21 14:02:00 +00:00
};
}
return {
ws: void 0,
2022-12-14 11:50:07 +00:00
udp: void 0
2022-05-21 14:02:00 +00:00
};
}
2022-08-01 06:02:58 +00:00
onSubscriptionRemoved(subscription) {
2022-12-14 11:50:07 +00:00
if (this.state.status !== "destroyed" /* Destroyed */ && this.state.subscription === subscription) {
2022-05-21 14:02:00 +00:00
this.state = {
...this.state,
2022-12-14 11:50:07 +00:00
subscription: void 0
2022-05-21 14:02:00 +00:00
};
}
}
};
2022-12-14 11:50:07 +00:00
__name(VoiceConnection, "VoiceConnection");
2022-08-01 06:02:58 +00:00
function createVoiceConnection(joinConfig, options) {
2022-05-21 14:02:00 +00:00
const payload = createJoinVoiceChannelPayload(joinConfig);
const existing = getVoiceConnection(joinConfig.guildId, joinConfig.group);
2022-12-14 11:50:07 +00:00
if (existing && existing.state.status !== "destroyed" /* Destroyed */) {
if (existing.state.status === "disconnected" /* Disconnected */) {
2022-05-21 14:02:00 +00:00
existing.rejoin({
channelId: joinConfig.channelId,
selfDeaf: joinConfig.selfDeaf,
2022-12-14 11:50:07 +00:00
selfMute: joinConfig.selfMute
2022-05-21 14:02:00 +00:00
});
2022-08-01 06:02:58 +00:00
} else if (!existing.state.adapter.sendPayload(payload)) {
2022-05-21 14:02:00 +00:00
existing.state = {
...existing.state,
2022-12-14 11:50:07 +00:00
status: "disconnected" /* Disconnected */,
reason: 1 /* AdapterUnavailable */
2022-05-21 14:02:00 +00:00
};
}
return existing;
}
const voiceConnection = new VoiceConnection(joinConfig, options);
2022-05-21 14:02:00 +00:00
trackVoiceConnection(voiceConnection);
2022-12-14 11:50:07 +00:00
if (voiceConnection.state.status !== "destroyed" /* Destroyed */ && !voiceConnection.state.adapter.sendPayload(payload)) {
voiceConnection.state = {
...voiceConnection.state,
2022-12-14 11:50:07 +00:00
status: "disconnected" /* Disconnected */,
reason: 1 /* AdapterUnavailable */
};
2022-05-21 14:02:00 +00:00
}
return voiceConnection;
}
2022-12-14 11:50:07 +00:00
__name(createVoiceConnection, "createVoiceConnection");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/joinVoiceChannel.ts
2022-08-01 06:02:58 +00:00
function joinVoiceChannel(options) {
2022-05-21 14:02:00 +00:00
const joinConfig = {
selfDeaf: true,
selfMute: false,
2022-12-14 11:50:07 +00:00
group: "default",
...options
2022-05-21 14:02:00 +00:00
};
return createVoiceConnection(joinConfig, {
adapterCreator: options.adapterCreator,
2022-12-14 11:50:07 +00:00
debug: options.debug
2022-05-21 14:02:00 +00:00
});
}
2022-12-14 11:50:07 +00:00
__name(joinVoiceChannel, "joinVoiceChannel");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/audio/AudioResource.ts
2022-12-14 11:50:07 +00:00
var import_node_stream2 = require("stream");
var import_prism_media2 = __toESM(require("prism-media"));
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/audio/TransformerGraph.ts
2022-12-14 11:50:07 +00:00
var import_prism_media = __toESM(require("prism-media"));
var FFMPEG_PCM_ARGUMENTS = ["-analyzeduration", "0", "-loglevel", "0", "-f", "s16le", "-ar", "48000", "-ac", "2"];
2022-05-21 14:02:00 +00:00
var FFMPEG_OPUS_ARGUMENTS = [
2022-12-14 11:50:07 +00:00
"-analyzeduration",
"0",
"-loglevel",
"0",
"-acodec",
"libopus",
"-f",
"opus",
"-ar",
"48000",
"-ac",
"2"
2022-05-21 14:02:00 +00:00
];
2022-12-14 11:50:07 +00:00
var StreamType = /* @__PURE__ */ ((StreamType2) => {
StreamType2["Arbitrary"] = "arbitrary";
StreamType2["OggOpus"] = "ogg/opus";
StreamType2["Opus"] = "opus";
StreamType2["Raw"] = "raw";
StreamType2["WebmOpus"] = "webm/opus";
2022-05-21 14:02:00 +00:00
return StreamType2;
})(StreamType || {});
2022-08-01 06:02:58 +00:00
var Node = class {
edges = [];
type;
2022-08-01 06:02:58 +00:00
constructor(type) {
2022-05-21 14:02:00 +00:00
this.type = type;
}
2022-08-01 06:02:58 +00:00
addEdge(edge) {
2022-05-21 14:02:00 +00:00
this.edges.push({ ...edge, from: this });
}
};
2022-12-14 11:50:07 +00:00
__name(Node, "Node");
2022-05-21 14:02:00 +00:00
var NODES = /* @__PURE__ */ new Map();
2022-08-01 06:02:58 +00:00
for (const streamType of Object.values(StreamType)) {
2022-05-21 14:02:00 +00:00
NODES.set(streamType, new Node(streamType));
}
2022-08-01 06:02:58 +00:00
function getNode(type) {
2022-05-21 14:02:00 +00:00
const node = NODES.get(type);
2022-12-14 11:50:07 +00:00
if (!node)
throw new Error(`Node type '${type}' does not exist!`);
2022-05-21 14:02:00 +00:00
return node;
}
2022-12-14 11:50:07 +00:00
__name(getNode, "getNode");
getNode("raw" /* Raw */).addEdge({
type: "opus encoder" /* OpusEncoder */,
to: getNode("opus" /* Opus */),
2022-05-21 14:02:00 +00:00
cost: 1.5,
2022-12-14 11:50:07 +00:00
transformer: () => new import_prism_media.default.opus.Encoder({ rate: 48e3, channels: 2, frameSize: 960 })
2022-05-21 14:02:00 +00:00
});
2022-12-14 11:50:07 +00:00
getNode("opus" /* Opus */).addEdge({
type: "opus decoder" /* OpusDecoder */,
to: getNode("raw" /* Raw */),
2022-05-21 14:02:00 +00:00
cost: 1.5,
2022-12-14 11:50:07 +00:00
transformer: () => new import_prism_media.default.opus.Decoder({ rate: 48e3, channels: 2, frameSize: 960 })
2022-05-21 14:02:00 +00:00
});
2022-12-14 11:50:07 +00:00
getNode("ogg/opus" /* OggOpus */).addEdge({
type: "ogg/opus demuxer" /* OggOpusDemuxer */,
to: getNode("opus" /* Opus */),
2022-05-21 14:02:00 +00:00
cost: 1,
2022-12-14 11:50:07 +00:00
transformer: () => new import_prism_media.default.opus.OggDemuxer()
2022-05-21 14:02:00 +00:00
});
2022-12-14 11:50:07 +00:00
getNode("webm/opus" /* WebmOpus */).addEdge({
type: "webm/opus demuxer" /* WebmOpusDemuxer */,
to: getNode("opus" /* Opus */),
2022-05-21 14:02:00 +00:00
cost: 1,
2022-12-14 11:50:07 +00:00
transformer: () => new import_prism_media.default.opus.WebmDemuxer()
2022-05-21 14:02:00 +00:00
});
var FFMPEG_PCM_EDGE = {
2022-12-14 11:50:07 +00:00
type: "ffmpeg pcm" /* FFmpegPCM */,
to: getNode("raw" /* Raw */),
2022-05-21 14:02:00 +00:00
cost: 2,
2022-12-14 11:50:07 +00:00
transformer: (input) => new import_prism_media.default.FFmpeg({
args: typeof input === "string" ? ["-i", input, ...FFMPEG_PCM_ARGUMENTS] : FFMPEG_PCM_ARGUMENTS
})
2022-05-21 14:02:00 +00:00
};
2022-12-14 11:50:07 +00:00
getNode("arbitrary" /* Arbitrary */).addEdge(FFMPEG_PCM_EDGE);
getNode("ogg/opus" /* OggOpus */).addEdge(FFMPEG_PCM_EDGE);
getNode("webm/opus" /* WebmOpus */).addEdge(FFMPEG_PCM_EDGE);
getNode("raw" /* Raw */).addEdge({
type: "volume transformer" /* InlineVolume */,
to: getNode("raw" /* Raw */),
2022-05-21 14:02:00 +00:00
cost: 0.5,
2022-12-14 11:50:07 +00:00
transformer: () => new import_prism_media.default.VolumeTransformer({ type: "s16le" })
2022-05-21 14:02:00 +00:00
});
2022-08-01 06:02:58 +00:00
function canEnableFFmpegOptimizations() {
try {
2022-12-14 11:50:07 +00:00
return import_prism_media.default.FFmpeg.getInfo().output.includes("--enable-libopus");
} catch {
}
2022-05-21 14:02:00 +00:00
return false;
}
2022-12-14 11:50:07 +00:00
__name(canEnableFFmpegOptimizations, "canEnableFFmpegOptimizations");
2022-08-01 06:02:58 +00:00
if (canEnableFFmpegOptimizations()) {
2022-05-21 14:02:00 +00:00
const FFMPEG_OGG_EDGE = {
2022-12-14 11:50:07 +00:00
type: "ffmpeg ogg" /* FFmpegOgg */,
to: getNode("ogg/opus" /* OggOpus */),
2022-05-21 14:02:00 +00:00
cost: 2,
2022-12-14 11:50:07 +00:00
transformer: (input) => new import_prism_media.default.FFmpeg({
args: typeof input === "string" ? ["-i", input, ...FFMPEG_OPUS_ARGUMENTS] : FFMPEG_OPUS_ARGUMENTS
})
2022-05-21 14:02:00 +00:00
};
2022-12-14 11:50:07 +00:00
getNode("arbitrary" /* Arbitrary */).addEdge(FFMPEG_OGG_EDGE);
getNode("ogg/opus" /* OggOpus */).addEdge(FFMPEG_OGG_EDGE);
getNode("webm/opus" /* WebmOpus */).addEdge(FFMPEG_OGG_EDGE);
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
function findPath(from, constraints, goal = getNode("opus" /* Opus */), path = [], depth = 5) {
2022-08-01 06:02:58 +00:00
if (from === goal && constraints(path)) {
2022-05-21 14:02:00 +00:00
return { cost: 0 };
2022-08-01 06:02:58 +00:00
} else if (depth === 0) {
return { cost: Number.POSITIVE_INFINITY };
2022-05-21 14:02:00 +00:00
}
let currentBest;
2022-08-01 06:02:58 +00:00
for (const edge of from.edges) {
2022-12-14 11:50:07 +00:00
if (currentBest && edge.cost > currentBest.cost)
continue;
2022-05-21 14:02:00 +00:00
const next = findPath(edge.to, constraints, goal, [...path, edge], depth - 1);
const cost = edge.cost + next.cost;
2022-08-01 06:02:58 +00:00
if (!currentBest || cost < currentBest.cost) {
2022-05-21 14:02:00 +00:00
currentBest = { cost, edge, next };
}
}
return currentBest ?? { cost: Number.POSITIVE_INFINITY };
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
__name(findPath, "findPath");
2022-08-01 06:02:58 +00:00
function constructPipeline(step) {
2022-05-21 14:02:00 +00:00
const edges = [];
let current = step;
2022-08-01 06:02:58 +00:00
while (current?.edge) {
2022-05-21 14:02:00 +00:00
edges.push(current.edge);
current = current.next;
}
return edges;
}
2022-12-14 11:50:07 +00:00
__name(constructPipeline, "constructPipeline");
2022-08-01 06:02:58 +00:00
function findPipeline(from, constraint) {
2022-05-21 14:02:00 +00:00
return constructPipeline(findPath(getNode(from), constraint));
}
2022-12-14 11:50:07 +00:00
__name(findPipeline, "findPipeline");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/audio/AudioResource.ts
2022-08-01 06:02:58 +00:00
var AudioResource = class {
playStream;
edges;
metadata;
volume;
encoder;
audioPlayer;
playbackDuration = 0;
started = false;
silencePaddingFrames;
silenceRemaining = -1;
2022-08-01 06:02:58 +00:00
constructor(edges, streams, metadata, silencePaddingFrames) {
2022-05-21 14:02:00 +00:00
this.edges = edges;
this.playStream = streams.length > 1 ? (0, import_node_stream2.pipeline)(streams, noop) : streams[0];
this.metadata = metadata;
this.silencePaddingFrames = silencePaddingFrames;
2022-08-01 06:02:58 +00:00
for (const stream of streams) {
if (stream instanceof import_prism_media2.default.VolumeTransformer) {
2022-05-21 14:02:00 +00:00
this.volume = stream;
2022-08-01 06:02:58 +00:00
} else if (stream instanceof import_prism_media2.default.opus.Encoder) {
2022-05-21 14:02:00 +00:00
this.encoder = stream;
}
}
2022-12-14 11:50:07 +00:00
this.playStream.once("readable", () => this.started = true);
2022-05-21 14:02:00 +00:00
}
2022-08-01 06:02:58 +00:00
get readable() {
2022-12-14 11:50:07 +00:00
if (this.silenceRemaining === 0)
return false;
2022-05-21 14:02:00 +00:00
const real = this.playStream.readable;
2022-08-01 06:02:58 +00:00
if (!real) {
2022-12-14 11:50:07 +00:00
if (this.silenceRemaining === -1)
this.silenceRemaining = this.silencePaddingFrames;
2022-05-21 14:02:00 +00:00
return this.silenceRemaining !== 0;
}
return real;
}
2022-08-01 06:02:58 +00:00
get ended() {
2022-05-21 14:02:00 +00:00
return this.playStream.readableEnded || this.playStream.destroyed || this.silenceRemaining === 0;
}
2022-08-01 06:02:58 +00:00
read() {
if (this.silenceRemaining === 0) {
2022-05-21 14:02:00 +00:00
return null;
2022-08-01 06:02:58 +00:00
} else if (this.silenceRemaining > 0) {
2022-05-21 14:02:00 +00:00
this.silenceRemaining--;
return SILENCE_FRAME;
}
const packet = this.playStream.read();
2022-08-01 06:02:58 +00:00
if (packet) {
2022-05-21 14:02:00 +00:00
this.playbackDuration += 20;
}
return packet;
}
};
2022-12-14 11:50:07 +00:00
__name(AudioResource, "AudioResource");
var VOLUME_CONSTRAINT = /* @__PURE__ */ __name((path) => path.some((edge) => edge.type === "volume transformer" /* InlineVolume */), "VOLUME_CONSTRAINT");
var NO_CONSTRAINT = /* @__PURE__ */ __name(() => true, "NO_CONSTRAINT");
2022-08-01 06:02:58 +00:00
function inferStreamType(stream) {
if (stream instanceof import_prism_media2.default.opus.Encoder) {
2022-12-14 11:50:07 +00:00
return { streamType: "opus" /* Opus */, hasVolume: false };
2022-08-01 06:02:58 +00:00
} else if (stream instanceof import_prism_media2.default.opus.Decoder) {
2022-12-14 11:50:07 +00:00
return { streamType: "raw" /* Raw */, hasVolume: false };
2022-08-01 06:02:58 +00:00
} else if (stream instanceof import_prism_media2.default.VolumeTransformer) {
2022-12-14 11:50:07 +00:00
return { streamType: "raw" /* Raw */, hasVolume: true };
2022-08-01 06:02:58 +00:00
} else if (stream instanceof import_prism_media2.default.opus.OggDemuxer) {
2022-12-14 11:50:07 +00:00
return { streamType: "opus" /* Opus */, hasVolume: false };
2022-08-01 06:02:58 +00:00
} else if (stream instanceof import_prism_media2.default.opus.WebmDemuxer) {
2022-12-14 11:50:07 +00:00
return { streamType: "opus" /* Opus */, hasVolume: false };
2022-08-01 06:02:58 +00:00
}
2022-12-14 11:50:07 +00:00
return { streamType: "arbitrary" /* Arbitrary */, hasVolume: false };
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
__name(inferStreamType, "inferStreamType");
2022-08-01 06:02:58 +00:00
function createAudioResource(input, options = {}) {
2022-05-21 14:02:00 +00:00
let inputType = options.inputType;
let needsInlineVolume = Boolean(options.inlineVolume);
2022-12-14 11:50:07 +00:00
if (typeof input === "string") {
inputType = "arbitrary" /* Arbitrary */;
} else if (typeof inputType === "undefined") {
2022-05-21 14:02:00 +00:00
const analysis = inferStreamType(input);
inputType = analysis.streamType;
needsInlineVolume = needsInlineVolume && !analysis.hasVolume;
}
const transformerPipeline = findPipeline(inputType, needsInlineVolume ? VOLUME_CONSTRAINT : NO_CONSTRAINT);
2022-08-01 06:02:58 +00:00
if (transformerPipeline.length === 0) {
2022-12-14 11:50:07 +00:00
if (typeof input === "string")
throw new Error(`Invalid pipeline constructed for string resource '${input}'`);
2022-05-21 14:02:00 +00:00
return new AudioResource([], [input], options.metadata ?? null, options.silencePaddingFrames ?? 5);
}
2022-12-14 11:50:07 +00:00
const streams = transformerPipeline.map((edge) => edge.transformer(input));
if (typeof input !== "string")
streams.unshift(input);
return new AudioResource(
transformerPipeline,
streams,
options.metadata ?? null,
options.silencePaddingFrames ?? 5
);
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
__name(createAudioResource, "createAudioResource");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/util/generateDependencyReport.ts
2022-12-14 11:50:07 +00:00
var import_node_path = require("path");
var import_prism_media3 = __toESM(require("prism-media"));
2022-08-01 06:02:58 +00:00
function findPackageJSON(dir, packageName, depth) {
2022-12-14 11:50:07 +00:00
if (depth === 0)
return void 0;
const attemptedPath = (0, import_node_path.resolve)(dir, "./package.json");
2022-08-01 06:02:58 +00:00
try {
2022-05-21 14:02:00 +00:00
const pkg = require(attemptedPath);
2022-12-14 11:50:07 +00:00
if (pkg.name !== packageName)
throw new Error("package.json does not match");
2022-05-21 14:02:00 +00:00
return pkg;
} catch {
2022-12-14 11:50:07 +00:00
return findPackageJSON((0, import_node_path.resolve)(dir, ".."), packageName, depth - 1);
2022-05-21 14:02:00 +00:00
}
}
2022-12-14 11:50:07 +00:00
__name(findPackageJSON, "findPackageJSON");
2022-08-01 06:02:58 +00:00
function version(name) {
try {
2022-12-14 11:50:07 +00:00
if (name === "@discordjs/voice") {
return "0.14.0";
}
const pkg = findPackageJSON((0, import_node_path.dirname)(require.resolve(name)), name, 3);
2022-12-14 11:50:07 +00:00
return pkg?.version ?? "not found";
} catch {
2022-12-14 11:50:07 +00:00
return "not found";
2022-05-21 14:02:00 +00:00
}
}
2022-12-14 11:50:07 +00:00
__name(version, "version");
2022-08-01 06:02:58 +00:00
function generateDependencyReport() {
2022-05-21 14:02:00 +00:00
const report = [];
2022-12-14 11:50:07 +00:00
const addVersion = /* @__PURE__ */ __name((name) => report.push(`- ${name}: ${version(name)}`), "addVersion");
report.push("Core Dependencies");
addVersion("@discordjs/voice");
addVersion("prism-media");
report.push("");
report.push("Opus Libraries");
addVersion("@discordjs/opus");
addVersion("opusscript");
report.push("");
report.push("Encryption Libraries");
addVersion("sodium-native");
addVersion("sodium");
addVersion("libsodium-wrappers");
addVersion("tweetnacl");
report.push("");
report.push("FFmpeg");
2022-08-01 06:02:58 +00:00
try {
2022-05-21 14:02:00 +00:00
const info = import_prism_media3.default.FFmpeg.getInfo();
report.push(`- version: ${info.version}`);
2022-12-14 11:50:07 +00:00
report.push(`- libopus: ${info.output.includes("--enable-libopus") ? "yes" : "no"}`);
} catch {
2022-12-14 11:50:07 +00:00
report.push("- not found");
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
return ["-".repeat(50), ...report, "-".repeat(50)].join("\n");
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
__name(generateDependencyReport, "generateDependencyReport");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/util/entersState.ts
2022-12-14 11:50:07 +00:00
var import_node_events8 = require("events");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/util/abortAfter.ts
2022-08-01 06:02:58 +00:00
function abortAfter(delay) {
2022-05-21 14:02:00 +00:00
const ac = new AbortController();
const timeout = setTimeout(() => ac.abort(), delay);
2022-12-14 11:50:07 +00:00
ac.signal.addEventListener("abort", () => clearTimeout(timeout));
2022-05-21 14:02:00 +00:00
return [ac, ac.signal];
}
2022-12-14 11:50:07 +00:00
__name(abortAfter, "abortAfter");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/util/entersState.ts
2022-08-01 06:02:58 +00:00
async function entersState(target, status, timeoutOrSignal) {
if (target.state.status !== status) {
2022-12-14 11:50:07 +00:00
const [ac, signal] = typeof timeoutOrSignal === "number" ? abortAfter(timeoutOrSignal) : [void 0, timeoutOrSignal];
2022-08-01 06:02:58 +00:00
try {
await (0, import_node_events8.once)(target, status, { signal });
2022-08-01 06:02:58 +00:00
} finally {
2022-05-21 14:02:00 +00:00
ac?.abort();
}
}
return target;
}
2022-12-14 11:50:07 +00:00
__name(entersState, "entersState");
2022-05-21 14:02:00 +00:00
2022-06-26 02:40:04 +00:00
// src/util/demuxProbe.ts
2022-12-14 11:50:07 +00:00
var import_node_buffer6 = require("buffer");
var import_node_process = __toESM(require("process"));
var import_node_stream3 = require("stream");
var import_prism_media4 = __toESM(require("prism-media"));
2022-08-01 06:02:58 +00:00
function validateDiscordOpusHead(opusHead) {
2022-05-21 14:02:00 +00:00
const channels = opusHead.readUInt8(9);
const sampleRate = opusHead.readUInt32LE(12);
return channels === 2 && sampleRate === 48e3;
}
2022-12-14 11:50:07 +00:00
__name(validateDiscordOpusHead, "validateDiscordOpusHead");
async function demuxProbe(stream, probeSize = 1024, validator = validateDiscordOpusHead) {
2022-08-01 06:02:58 +00:00
return new Promise((resolve2, reject) => {
if (stream.readableObjectMode) {
2022-12-14 11:50:07 +00:00
reject(new Error("Cannot probe a readable stream in object mode"));
return;
}
if (stream.readableEnded) {
2022-12-14 11:50:07 +00:00
reject(new Error("Cannot probe a stream that has ended"));
return;
}
let readBuffer = import_node_buffer6.Buffer.alloc(0);
let resolved;
2022-12-14 11:50:07 +00:00
const finish = /* @__PURE__ */ __name((type) => {
stream.off("data", onData);
stream.off("close", onClose);
stream.off("end", onClose);
2022-05-21 14:02:00 +00:00
stream.pause();
resolved = type;
2022-08-01 06:02:58 +00:00
if (stream.readableEnded) {
2022-05-21 14:02:00 +00:00
resolve2({
stream: import_node_stream3.Readable.from(readBuffer),
2022-12-14 11:50:07 +00:00
type
2022-05-21 14:02:00 +00:00
});
2022-08-01 06:02:58 +00:00
} else {
if (readBuffer.length > 0) {
2022-05-21 14:02:00 +00:00
stream.push(readBuffer);
}
resolve2({
stream,
2022-12-14 11:50:07 +00:00
type
2022-05-21 14:02:00 +00:00
});
}
2022-12-14 11:50:07 +00:00
}, "finish");
const foundHead = /* @__PURE__ */ __name((type) => (head) => {
if (validator(head)) {
finish(type);
}
}, "foundHead");
2022-05-21 14:02:00 +00:00
const webm = new import_prism_media4.default.opus.WebmDemuxer();
2022-12-14 11:50:07 +00:00
webm.once("error", noop);
webm.on("head", foundHead("webm/opus" /* WebmOpus */));
2022-05-21 14:02:00 +00:00
const ogg = new import_prism_media4.default.opus.OggDemuxer();
2022-12-14 11:50:07 +00:00
ogg.once("error", noop);
ogg.on("head", foundHead("ogg/opus" /* OggOpus */));
2022-08-01 06:02:58 +00:00
const onClose = /* @__PURE__ */ __name(() => {
if (!resolved) {
2022-12-14 11:50:07 +00:00
finish("arbitrary" /* Arbitrary */);
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
}, "onClose");
const onData = /* @__PURE__ */ __name((buffer) => {
readBuffer = import_node_buffer6.Buffer.concat([readBuffer, buffer]);
2022-05-21 14:02:00 +00:00
webm.write(buffer);
ogg.write(buffer);
2022-08-01 06:02:58 +00:00
if (readBuffer.length >= probeSize) {
2022-12-14 11:50:07 +00:00
stream.off("data", onData);
2022-05-21 14:02:00 +00:00
stream.pause();
import_node_process.default.nextTick(onClose);
2022-05-21 14:02:00 +00:00
}
2022-12-14 11:50:07 +00:00
}, "onData");
stream.once("error", reject);
stream.on("data", onData);
stream.once("close", onClose);
stream.once("end", onClose);
2022-05-21 14:02:00 +00:00
});
}
2022-12-14 11:50:07 +00:00
__name(demuxProbe, "demuxProbe");
// src/index.ts
2022-12-14 11:50:07 +00:00
var version2 = "0.14.0";
2022-05-21 14:02:00 +00:00
// Annotate the CommonJS export names for ESM import in node:
2022-12-14 11:50:07 +00:00
0 && (module.exports = {
AudioPlayer,
AudioPlayerError,
AudioPlayerStatus,
AudioReceiveStream,
AudioResource,
EndBehaviorType,
NoSubscriberBehavior,
PlayerSubscription,
SSRCMap,
SpeakingMap,
StreamType,
VoiceConnection,
VoiceConnectionDisconnectReason,
VoiceConnectionStatus,
VoiceReceiver,
createAudioPlayer,
createAudioResource,
createDefaultAudioReceiveStreamOptions,
demuxProbe,
entersState,
generateDependencyReport,
getGroups,
getVoiceConnection,
getVoiceConnections,
joinVoiceChannel,
validateDiscordOpusHead,
version
});
//# sourceMappingURL=index.js.map