Documentation Index Fetch the complete documentation index at: https://mintlify.com/disgoorg/disgo/llms.txt
Use this file to discover all available pages before exploring further.
DisGo provides a comprehensive voice system for connecting to Discord voice channels, sending and receiving audio, and integrating with the DAVE (Discord Audio Visual Experience) protocol for end-to-end encryption.
Setting up voice
To use voice features, you need to enable the voice manager when creating your bot client and request the required gateway intents.
import (
" github.com/disgoorg/disgo "
" github.com/disgoorg/disgo/bot "
" github.com/disgoorg/disgo/gateway "
" github.com/disgoorg/disgo/voice "
)
client , err := disgo . New ( token ,
bot . WithGatewayConfigOpts (
gateway . WithIntents ( gateway . IntentGuildVoiceStates ),
),
bot . WithVoiceManagerConfigOpts (
voice . WithLogger ( logger ),
),
)
The IntentGuildVoiceStates intent is required to receive voice state updates from Discord.
Connecting to a voice channel
To connect to a voice channel, you create a voice connection through the voice manager and open it with a channel ID.
Create a voice connection
Use the voice manager to create a connection for a specific guild: conn := client . VoiceManager . CreateConn ( guildID )
Open the connection
Open the connection to a voice channel with options for muting and deafening: ctx , cancel := context . WithTimeout ( context . Background (), 10 * time . Second )
defer cancel ()
err := conn . Open ( ctx , channelID , false , false )
if err != nil {
// Handle error
}
The parameters are:
channelID - The voice channel to join
selfMute - Whether the bot should be muted
selfDeaf - Whether the bot should be deafened
Close when done
Always close the connection when you’re finished: defer func () {
closeCtx , cancel := context . WithTimeout ( context . Background (), 5 * time . Second )
defer cancel ()
conn . Close ( closeCtx )
}()
Sending audio
DisGo supports sending Opus-encoded audio frames to Discord. You can either write raw Opus frames or use the built-in frame provider system.
Sending raw Opus frames
The UDP connection implements io.Writer, allowing you to write Opus frames directly:
// Set speaking flag before sending audio
if err := conn . SetSpeaking ( ctx , voice . SpeakingFlagMicrophone ); err != nil {
panic ( err )
}
// Write Opus frames to the connection
opusFrame := [] byte { ... } // Your Opus-encoded audio
_ , err := conn . UDP (). Write ( opusFrame )
Using OpusReader
For reading from a file or stream, use the OpusReader to provide frames automatically:
file , err := os . Open ( "audio.dca" )
if err != nil {
panic ( err )
}
defer file . Close ()
reader := voice . NewOpusReader ( file )
conn . SetOpusFrameProvider ( reader )
The DCA (Discord Channel Audio) format is a simple container for Opus frames. Each frame is prefixed with a 4-byte little-endian length.
Custom frame providers
You can implement custom audio sources by implementing the OpusFrameProvider interface:
type OpusFrameProvider interface {
ProvideOpusFrame () ([] byte , error )
Close ()
}
type CustomAudioSource struct {
// Your implementation
}
func ( s * CustomAudioSource ) ProvideOpusFrame () ([] byte , error ) {
// Return next Opus frame
return frame , nil
}
func ( s * CustomAudioSource ) Close () {
// Cleanup
}
// Use it
conn . SetOpusFrameProvider ( & CustomAudioSource {})
Receiving audio
You can receive audio from other users in the voice channel by reading packets from the UDP connection.
Reading raw packets
go func () {
for {
packet , err := conn . UDP (). ReadPacket ()
if err != nil {
slog . Error ( "error reading packet" , slog . Any ( "err" , err ))
return
}
// packet.Opus contains the Opus-encoded audio
// packet.SSRC identifies the audio source
userID := conn . UserIDBySSRC ( packet . SSRC )
// Process audio...
}
}()
Using OpusWriter
To write received audio to a file or stream:
file , err := os . Create ( "recording.dca" )
if err != nil {
panic ( err )
}
defer file . Close ()
// Optional: filter specific users
userFilter := func ( userID snowflake . ID ) bool {
return userID == targetUserID
}
writer := voice . NewOpusWriter ( file , userFilter )
conn . SetOpusFrameReceiver ( writer )
Custom frame receivers
type OpusFrameReceiver interface {
ReceiveOpusFrame ( userID snowflake . ID , packet * Packet ) error
CleanupUser ( userID snowflake . ID )
Close ()
}
Voice gateway events
You can listen to voice gateway events by setting an event handler:
conn . SetEventHandlerFunc ( func ( gateway voice . Gateway , op voice . Opcode , seq int , data voice . GatewayMessageData ) {
switch d := data .( type ) {
case voice . GatewayMessageDataSpeaking :
slog . Info ( "user started speaking" , slog . Any ( "user_id" , d . UserID ))
case voice . GatewayMessageDataClientDisconnect :
slog . Info ( "user disconnected" , slog . Any ( "user_id" , d . UserID ))
}
})
DAVE protocol integration
DisGo supports the DAVE (Discord Audio Visual Experience) protocol for end-to-end encrypted voice. To enable DAVE, provide a session create function:
import " github.com/disgoorg/godave/golibdave "
client , err := disgo . New ( token ,
bot . WithVoiceManagerConfigOpts (
voice . WithDaveSessionCreateFunc ( golibdave . NewSession ),
),
)
DAVE support requires the godave library and native dependencies. See the godave repository for installation instructions.
Complete example
Here’s a complete example that joins a voice channel and plays audio:
package main
import (
" context "
" log/slog "
" os "
" time "
" github.com/disgoorg/disgo "
" github.com/disgoorg/disgo/bot "
" github.com/disgoorg/disgo/events "
" github.com/disgoorg/disgo/gateway "
" github.com/disgoorg/disgo/voice "
" github.com/disgoorg/snowflake/v2 "
)
func main () {
client , err := disgo . New ( os . Getenv ( "TOKEN" ),
bot . WithGatewayConfigOpts (
gateway . WithIntents ( gateway . IntentGuildVoiceStates ),
),
bot . WithEventListenerFunc ( func ( e * events . Ready ) {
go playAudio ( e . Client ())
}),
)
if err != nil {
panic ( err )
}
defer client . Close ( context . TODO ())
if err = client . OpenGateway ( context . TODO ()); err != nil {
panic ( err )
}
// Keep alive
select {}
}
func playAudio ( client * bot . Client ) {
guildID := snowflake . MustParse ( os . Getenv ( "GUILD_ID" ))
channelID := snowflake . MustParse ( os . Getenv ( "CHANNEL_ID" ))
conn := client . VoiceManager . CreateConn ( guildID )
ctx , cancel := context . WithTimeout ( context . Background (), 10 * time . Second )
defer cancel ()
if err := conn . Open ( ctx , channelID , false , false ); err != nil {
slog . Error ( "failed to open connection" , slog . Any ( "err" , err ))
return
}
defer conn . Close ( context . Background ())
if err := conn . SetSpeaking ( ctx , voice . SpeakingFlagMicrophone ); err != nil {
slog . Error ( "failed to set speaking" , slog . Any ( "err" , err ))
return
}
file , err := os . Open ( "audio.dca" )
if err != nil {
slog . Error ( "failed to open audio file" , slog . Any ( "err" , err ))
return
}
defer file . Close ()
reader := voice . NewOpusReader ( file )
conn . SetOpusFrameProvider ( reader )
}
Configuration options
Voice manager options
WithLogger(*slog.Logger) - Set custom logger
WithConnCreateFunc(ConnCreateFunc) - Custom connection factory
WithConnConfigOpts(...ConnConfigOpt) - Configure all connections
WithDaveSessionCreateFunc(godave.SessionCreateFunc) - Enable DAVE encryption
WithDaveSessionLogger(*slog.Logger) - Logger for DAVE sessions
Connection options
WithConnLogger(*slog.Logger) - Connection-specific logger
WithConnGatewayConfigOpts(...GatewayConfigOpt) - Configure voice gateway
WithUDPConnConfigOpts(...UDPConnConfigOpt) - Configure UDP connection
WithConnEventHandlerFunc(EventHandlerFunc) - Handle voice gateway events
WithConnAudioSenderCreateFunc(AudioSenderCreateFunc) - Custom audio sender
WithConnAudioReceiverCreateFunc(AudioReceiverCreateFunc) - Custom audio receiver
Best practices
Voice operations can hang if Discord doesn’t respond. Always use context timeouts: ctx , cancel := context . WithTimeout ( context . Background (), 10 * time . Second )
defer cancel ()
Handle disconnections gracefully
Users can disconnect or network issues can occur. Always handle errors when reading packets: packet , err := conn . UDP (). ReadPacket ()
if err != nil {
if errors . Is ( err , io . EOF ) {
// Connection closed
return
}
// Handle other errors
}
Set speaking flags appropriately
Discord uses speaking flags to optimize audio transmission. Set them before sending audio: conn . SetSpeaking ( ctx , voice . SpeakingFlagMicrophone )
Always close voice connections when done to free resources: defer func () {
ctx , cancel := context . WithTimeout ( context . Background (), 5 * time . Second )
defer cancel ()
conn . Close ( ctx )
}()