Implementation of the Discord Voice API for Node.js with comprehensive audio playback, reception, and end-to-end encryption support
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Direct access to networking components for advanced use cases, including WebSocket and UDP socket management, protocol handling, connection state control, and low-level Discord voice protocol implementation.
Core networking class that manages the complete Discord voice connection lifecycle.
/**
* Manages Discord voice connection networking including WebSocket and UDP components
*/
class Networking extends EventEmitter {
/** Current networking state */
readonly state: NetworkingState;
/**
* Create a new networking instance
* @param connectionOptions - Discord voice connection configuration
* @param options - Additional networking options
*/
constructor(connectionOptions: ConnectionOptions, options: NetworkingOptions);
/** Destroy the networking instance and all components */
destroy(): void;
/**
* Prepare an Opus audio packet for transmission
* @param opusPacket - Raw Opus audio data
* @returns Prepared packet buffer or undefined if not ready
*/
prepareAudioPacket(opusPacket: Buffer): Buffer | undefined;
/**
* Dispatch previously prepared audio packet
* @returns true if packet was sent successfully
*/
dispatchAudio(): boolean;
/**
* Set speaking status for this connection
* @param speaking - Whether currently speaking/sending audio
*/
setSpeaking(speaking: boolean): void;
}
interface ConnectionOptions {
/** Voice channel ID */
channelId: string;
/** Discord voice server endpoint */
endpoint: string;
/** Voice server ID */
serverId: string;
/** Voice session ID from gateway */
sessionId: string;
/** Voice connection token from gateway */
token: string;
/** Bot user ID */
userId: string;
}
interface NetworkingOptions {
/** Enable DAVE end-to-end encryption (default: true) */
daveEncryption?: boolean;
/** Enable debug logging (default: false) */
debug?: boolean;
/** Decryption failure tolerance for DAVE (default: 24) */
decryptionFailureTolerance?: number;
}Usage Example:
import { Networking } from "@discordjs/voice";
// Create networking instance (typically done internally by VoiceConnection)
const networking = new Networking(
{
channelId: "123456789012345678",
endpoint: "discord-voice-server.discord.gg",
serverId: "server-id-here",
sessionId: "session-id-here",
token: "voice-token-here",
userId: "bot-user-id-here",
},
{
daveEncryption: true,
debug: true,
decryptionFailureTolerance: 24,
}
);
// Monitor networking state
networking.on("stateChange", (oldState, newState) => {
console.log(`Networking: ${oldState.code} -> ${newState.code}`);
});
// Prepare and send audio
const opusPacket = Buffer.from(/* opus audio data */);
const preparedPacket = networking.prepareAudioPacket(opusPacket);
if (preparedPacket) {
networking.dispatchAudio();
}Comprehensive state management for voice connection lifecycle.
enum NetworkingStatusCode {
/** Opening WebSocket connection to voice gateway */
OpeningWs,
/** Identifying with voice gateway */
Identifying,
/** Performing UDP handshake for audio transmission */
UdpHandshaking,
/** Selecting voice protocol and encryption */
SelectingProtocol,
/** Ready for audio transmission and reception */
Ready,
/** Resuming existing voice connection */
Resuming,
/** Connection closed */
Closed
}
type NetworkingState =
| NetworkingOpeningWsState
| NetworkingIdentifyingState
| NetworkingUdpHandshakingState
| NetworkingSelectingProtocolState
| NetworkingReadyState
| NetworkingResumingState
| NetworkingClosedState;
interface NetworkingOpeningWsState {
code: NetworkingStatusCode.OpeningWs;
ws: VoiceWebSocket;
connectionOptions: ConnectionOptions;
}
interface NetworkingIdentifyingState {
code: NetworkingStatusCode.Identifying;
ws: VoiceWebSocket;
connectionOptions: ConnectionOptions;
}
interface NetworkingUdpHandshakingState {
code: NetworkingStatusCode.UdpHandshaking;
ws: VoiceWebSocket;
udp: VoiceUDPSocket;
connectionData: ConnectionData;
connectionOptions: ConnectionOptions;
}
interface NetworkingSelectingProtocolState {
code: NetworkingStatusCode.SelectingProtocol;
ws: VoiceWebSocket;
udp: VoiceUDPSocket;
connectionData: ConnectionData;
connectionOptions: ConnectionOptions;
}
interface NetworkingReadyState {
code: NetworkingStatusCode.Ready;
ws: VoiceWebSocket;
udp: VoiceUDPSocket;
connectionData: ConnectionData;
connectionOptions: ConnectionOptions;
preparedPacket?: Buffer;
dave?: DAVESession;
}
interface NetworkingResumingState {
code: NetworkingStatusCode.Resuming;
ws: VoiceWebSocket;
udp: VoiceUDPSocket;
connectionData: ConnectionData;
connectionOptions: ConnectionOptions;
preparedPacket?: Buffer;
dave?: DAVESession;
}
interface NetworkingClosedState {
code: NetworkingStatusCode.Closed;
}WebSocket wrapper for Discord voice gateway communication.
/**
* WebSocket wrapper for Discord voice gateway
*/
class VoiceWebSocket extends EventEmitter {
/** Last recorded ping in milliseconds */
ping?: number;
/** Last acknowledged sequence number */
sequence: number;
/**
* Create voice WebSocket connection
* @param address - WebSocket address to connect to
* @param debug - Enable debug logging
*/
constructor(address: string, debug: boolean);
/** Destroy the WebSocket connection */
destroy(): void;
/**
* Handle incoming message from WebSocket
* @param event - WebSocket message event
*/
onMessage(event: MessageEvent): void;
/**
* Send JSON packet to voice gateway
* @param packet - Voice gateway payload to send
*/
sendPacket(packet: VoiceSendPayload): void;
/**
* Send binary message to voice gateway
* @param opcode - Voice opcode for the message
* @param payload - Binary payload data
*/
sendBinaryMessage(opcode: VoiceOpcodes, payload: Buffer): void;
/**
* Set or clear heartbeat interval
* @param ms - Heartbeat interval in milliseconds, or -1 to clear
*/
setHeartbeatInterval(ms: number): void;
}
// Voice WebSocket Events
interface VoiceWebSocket extends EventEmitter {
/** Emitted when connection encounters an error */
on(event: "error", listener: (error: Error) => void): this;
/** Emitted when WebSocket connection opens */
on(event: "open", listener: () => void): this;
/** Emitted when WebSocket connection closes */
on(event: "close", listener: (code: number, reason: string) => void): this;
/** Emitted for debug messages */
on(event: "debug", listener: (message: string) => void): this;
/** Emitted when JSON packet is received */
on(event: "packet", listener: (packet: VoiceReceivePayload) => void): this;
/** Emitted when binary message is received */
on(event: "binary", listener: (message: BinaryWebSocketMessage) => void): this;
}
interface BinaryWebSocketMessage {
op: VoiceOpcodes;
payload: Buffer;
seq: number;
}Usage Example:
import { VoiceWebSocket } from "@discordjs/voice";
const ws = new VoiceWebSocket("wss://voice-gateway.discord.gg", true);
ws.on("open", () => {
console.log("Voice WebSocket connected");
});
ws.on("packet", (packet) => {
console.log("Received packet:", packet.op);
});
ws.on("close", (code, reason) => {
console.log(`WebSocket closed: ${code} - ${reason}`);
});
// Send identify packet
ws.sendPacket({
op: VoiceOpcodes.Identify,
d: {
server_id: "server-id",
user_id: "user-id",
session_id: "session-id",
token: "voice-token",
},
});UDP socket manager for audio packet transmission.
/**
* UDP socket for Discord voice audio transmission
*/
class VoiceUDPSocket extends EventEmitter {
/** Deprecated ping measurement */
ping?: number;
/**
* Create UDP socket connection to Discord voice server
* @param remote - Remote socket configuration (IP and port)
*/
constructor(remote: SocketConfig);
/**
* Send buffer to Discord voice server
* @param buffer - Audio or control data to send
*/
send(buffer: Buffer): void;
/** Close the UDP socket */
destroy(): void;
/**
* Perform IP discovery to determine external IP and port
* @param ssrc - SSRC identifier for this connection
* @returns Promise resolving to discovered socket configuration
*/
performIPDiscovery(ssrc: number): Promise<SocketConfig>;
}
interface SocketConfig {
/** IP address */
ip: string;
/** Port number */
port: number;
}
/**
* Parse IP discovery response packet
* @param message - Response buffer from IP discovery
* @returns Parsed socket configuration
*/
function parseLocalPacket(message: Buffer): SocketConfig;
// Voice UDP Socket Events
interface VoiceUDPSocket extends EventEmitter {
/** Emitted when socket encounters an error */
on(event: "error", listener: (error: Error) => void): this;
/** Emitted when socket is closed */
on(event: "close", listener: () => void): this;
/** Emitted for debug messages */
on(event: "debug", listener: (message: string) => void): this;
/** Emitted when message is received */
on(event: "message", listener: (message: Buffer) => void): this;
}Usage Example:
import { VoiceUDPSocket, parseLocalPacket } from "@discordjs/voice";
const udp = new VoiceUDPSocket({ ip: "162.159.130.1", port: 50001 });
udp.on("message", (message) => {
console.log("Received UDP message:", message.length, "bytes");
});
// Perform IP discovery
const ssrc = 12345;
const localConfig = await udp.performIPDiscovery(ssrc);
console.log("Discovered local IP:", localConfig.ip, "port:", localConfig.port);
// Send audio packet
const audioPacket = Buffer.from(/* encrypted audio data */);
udp.send(audioPacket);Runtime connection information and state data.
interface ConnectionData {
/** Set of connected client user IDs */
connectedClients: Set<string>;
/** Voice encryption mode being used */
encryptionMode: string;
/** Current nonce for encryption */
nonce: number;
/** Nonce buffer for encryption operations */
nonceBuffer: Buffer;
/** Number of audio packets sent */
packetsPlayed: number;
/** Encryption secret key */
secretKey: Uint8Array;
/** RTP sequence number */
sequence: number;
/** Current speaking status */
speaking: boolean;
/** SSRC identifier for this connection */
ssrc: number;
/** RTP timestamp */
timestamp: number;
}
/** Supported voice encryption modes */
const SUPPORTED_ENCRYPTION_MODES: VoiceEncryptionMode[];For advanced use cases, you can implement custom networking behavior:
import { Networking, NetworkingStatusCode } from "@discordjs/voice";
class CustomNetworking extends Networking {
constructor(connectionOptions, options) {
super(connectionOptions, options);
// Override state change handling
this.on("stateChange", (oldState, newState) => {
if (newState.code === NetworkingStatusCode.Ready) {
console.log("Custom networking ready for audio");
this.setupCustomAudioProcessing();
}
});
}
prepareAudioPacket(opusPacket) {
// Custom packet preparation logic
const prepared = super.prepareAudioPacket(opusPacket);
if (prepared) {
// Add custom headers or processing
return this.addCustomHeaders(prepared);
}
return prepared;
}
private addCustomHeaders(packet) {
// Custom implementation
return packet;
}
private setupCustomAudioProcessing() {
// Custom audio processing setup
}
}For maximum control, work directly with WebSocket and UDP components:
import { VoiceWebSocket, VoiceUDPSocket, VoiceOpcodes } from "@discordjs/voice";
// Direct WebSocket control
const ws = new VoiceWebSocket("wss://voice-server.discord.gg", true);
const udp = new VoiceUDPSocket({ ip: "127.0.0.1", port: 50001 });
ws.on("packet", (packet) => {
switch (packet.op) {
case VoiceOpcodes.Ready:
console.log("Voice ready, starting UDP handshake");
break;
case VoiceOpcodes.SessionDescription:
console.log("Received session description");
break;
case VoiceOpcodes.Speaking:
console.log("Speaking update:", packet.d);
break;
}
});
// Custom packet handling
ws.sendPacket({
op: VoiceOpcodes.SelectProtocol,
d: {
protocol: "udp",
data: {
address: "192.168.1.1",
port: 1234,
mode: "aead_aes256_gcm_rtpsize",
},
},
});Handle networking errors appropriately:
networking.on("error", (error) => {
console.error("Networking error:", error);
});
networking.on("close", () => {
console.log("Networking connection closed");
});
ws.on("error", (error) => {
console.error("WebSocket error:", error);
});
udp.on("error", (error) => {
console.error("UDP error:", error);
});Install with Tessl CLI
npx tessl i tessl/npm-discordjs--voice