Voice Receive and Events
Enable Voice Receiveโ
The server must enable voiceReceive.enabled and choose a supported format such
as opus or pcm_s16le. The player must be connected to the intended Discord
voice channel before receiving speaker audio.
import { ManagerEventTypes } from "magmastream";
if (!player.node.isNodeLink) {
throw new Error("Voice receive requires NodeLink.");
}
manager.on(ManagerEventTypes.VoiceReceiverData, (eventPlayer, frame) => {
if (eventPlayer.guildId !== player.guildId) return;
// frame.data is binary audio in frame.type, not a text/base64 payload.
console.log(frame.userId, frame.type, frame.data.length);
});
manager.on(ManagerEventTypes.VoiceReceiverError, (eventPlayer, error) => {
console.error(eventPlayer.guildId, error);
});
await player.setupVoiceReceiver();
The receiver uses the node's authenticated
/v4/websocket/voice/<guildId> socket. Binary start/audio/stop frames are parsed
by the client. Legacy JSON speaking payloads remain accepted.
Receiver Eventsโ
| Event | Arguments |
|---|---|
voiceReceiverConnect | player |
voiceReceiverDisconnect | player |
voiceReceiverError | player, error |
voiceReceiverStartSpeaking | player, data |
voiceReceiverData | player, frame: VoiceReceiverData |
voiceReceiverEndSpeaking | player, data |
Speaking refers to the received speaker, not the bot beginning playback.
Live frames contain guildId, userId, data: Buffer, format, SSRC, and
timestamp. Binary stop events include accumulated audio as a Buffer. The public
start/end callback tuples remain typed as unknown, so validate them before use.
Opus packets are not automatically a playable Ogg/WAV file. PCM needs the server's sample-rate/channel configuration. Do not blindly concatenate either format and label it as a different codec.
The client buffers speaking chunks until a stop event. Process live frames promptly, apply your own recording limits, and remove the receiver when it is no longer needed:
await player.removeVoiceReceiver();
Destroying a player also clears receiver resources. Handle reconnect/error events and obtain appropriate consent before recording Discord users.
Native Gateway Eventsโ
The dedicated manager callbacks cover mixer, connection diagnostics, worker failure, volume/filter/seek/pause updates, player lifecycle, Eternal Box, and stream metadata. See the exact argument tuples in ManagerEvents.
import { ManagerEventTypes } from "magmastream";
manager.on(ManagerEventTypes.NodeLinkVolumeChanged, (eventPlayer, payload) => {
console.log(eventPlayer.guildId, payload);
});
manager.on(ManagerEventTypes.NodeLinkEvent, (node, payload) => {
console.log(node.options.identifier, payload.type);
});
Recognized volume, pause, seek, filter, and lifecycle events synchronize local player state. Global events without a guild ID are supported where applicable.
nodeLinkEvent forwards native events and unknown/plugin event payloads.
Unknown NodeLink event names are not reported as ordinary unknown-event
nodeError failures. Use dedicated callbacks for typed handling; the generic
callback does not replace every standard track/lyrics callback.
Standard Player Updatesโ
playerUpdate is emitted for both Lavalink and NodeLink:
import { ManagerEventTypes } from "magmastream";
manager.on(ManagerEventTypes.PlayerUpdate, (eventPlayer, message) => {
console.log(eventPlayer.guildId, message.state.position, message.state.connected);
});
message.state.connected is the server's voice-transport observation. Since
2.10.4, these periodic packets update position but do not overwrite the library's
player.state lifecycle. Explicit connection operations, voice leave/socket
closure, and native NodeLink lifecycle events retain their state handling.
Track-start callbacks do not force a connection or guarantee that Discord is
already receiving audio. Reconnect through the normal connection flow or native
lifecycle event rather than relying on periodic telemetry to change state.
Lyrics, chapter, and SponsorBlock callbacks may have no usable current track.
Check for null; do not assume those events always accompany active playback.