DisGo is the recommended Discord library for use with DisGoLink. Both libraries are built by the same team and share the same snowflake package, making integration seamless.
Why DisGo?
Native compatibility : Uses github.com/disgoorg/snowflake/v2 (same as DisGoLink)
Type-safe : No conversions needed between library types
Built together : Designed to work seamlessly with DisGoLink
Modern API : Clean, idiomatic Go code
Installation
go get github.com/disgoorg/disgo
go get github.com/disgoorg/disgolink/v3
Basic Setup
Here’s a complete example showing how to integrate DisGoLink with DisGo:
package main
import (
" context "
" log/slog "
" os "
" time "
" github.com/disgoorg/disgo "
" github.com/disgoorg/disgo/bot "
" github.com/disgoorg/disgo/cache "
" github.com/disgoorg/disgo/gateway "
" github.com/disgoorg/snowflake/v2 "
" github.com/disgoorg/disgolink/v3/disgolink "
)
type Bot struct {
Client bot . Client
Lavalink disgolink . Client
}
func main () {
b := & Bot {}
// Create DisGo client with required intents and cache
client , err := disgo . New ( os . Getenv ( "TOKEN" ),
bot . WithGatewayConfigOpts (
gateway . WithIntents ( gateway . IntentGuilds , gateway . IntentGuildVoiceStates ),
),
bot . WithCacheConfigOpts (
cache . WithCaches ( cache . FlagVoiceStates ),
),
bot . WithEventListenerFunc ( b . onVoiceStateUpdate ),
bot . WithEventListenerFunc ( b . onVoiceServerUpdate ),
)
if err != nil {
slog . Error ( "error while building disgo client" , slog . Any ( "err" , err ))
os . Exit ( 1 )
}
b . Client = client
// Create DisGoLink client
b . Lavalink = disgolink . New ( client . ApplicationID ())
// Open gateway connection
ctx , cancel := context . WithTimeout ( context . Background (), 10 * time . Second )
defer cancel ()
if err = client . OpenGateway ( ctx ); err != nil {
slog . Error ( "failed to open gateway" , slog . Any ( "err" , err ))
os . Exit ( 1 )
}
defer client . Close ( context . TODO ())
// Add Lavalink node
node , err := b . Lavalink . AddNode ( ctx , disgolink . NodeConfig {
Name : "main-node" ,
Address : "localhost:2333" ,
Password : "youshallnotpass" ,
Secure : false ,
})
if err != nil {
slog . Error ( "failed to add node" , slog . Any ( "err" , err ))
os . Exit ( 1 )
}
slog . Info ( "Bot is ready!" , slog . String ( "session_id" , node . SessionID ()))
}
Voice Event Forwarding
Critical : You must forward voice state and server updates to DisGoLink for audio to work.
DisGoLink needs to receive voice events from Discord to establish and maintain voice connections. Register these event handlers:
OnVoiceStateUpdate
This event fires when the bot joins, leaves, or moves between voice channels.
func ( b * Bot ) onVoiceStateUpdate ( event * events . GuildVoiceStateUpdate ) {
// Only handle events for our bot
if event . VoiceState . UserID != b . Client . ApplicationID () {
return
}
// Forward to DisGoLink
b . Lavalink . OnVoiceStateUpdate (
context . TODO (),
event . VoiceState . GuildID ,
event . VoiceState . ChannelID , // *snowflake.ID (nil when disconnected)
event . VoiceState . SessionID ,
)
// Optional: Clean up when bot leaves voice
if event . VoiceState . ChannelID == nil {
// Bot disconnected from voice
// Clean up queues, players, etc.
}
}
OnVoiceServerUpdate
This event provides the voice server endpoint and token needed for the connection.
func ( b * Bot ) onVoiceServerUpdate ( event * events . VoiceServerUpdate ) {
// Forward to DisGoLink
b . Lavalink . OnVoiceServerUpdate (
context . TODO (),
event . GuildID ,
event . Token ,
* event . Endpoint , // String containing voice server URL
)
}
Player Management
Creating and Using Players
func ( b * Bot ) playTrack ( guildID snowflake . ID , channelID * snowflake . ID ) error {
// Connect to voice channel
if err := b . Client . UpdateVoiceState ( context . TODO (), guildID , channelID , false , false ); err != nil {
return err
}
// Get or create player
player := b . Lavalink . Player ( guildID )
// Load and play track
ctx , cancel := context . WithTimeout ( context . Background (), 10 * time . Second )
defer cancel ()
return b . Lavalink . BestNode (). LoadTracksHandler ( ctx , "ytsearch:rick astley never gonna give you up" ,
disgolink . NewResultHandler (
func ( track lavalink . Track ) {
// Play the track
player . Update ( context . TODO (), lavalink . WithTrack ( track ))
},
func ( playlist lavalink . Playlist ) {
// Play first track from playlist
player . Update ( context . TODO (), lavalink . WithTrack ( playlist . Tracks [ 0 ]))
},
func ( tracks [] lavalink . Track ) {
// Play first search result
player . Update ( context . TODO (), lavalink . WithTrack ( tracks [ 0 ]))
},
func () {
// No matches found
},
func ( err error ) {
// Error loading track
},
),
)
}
Listening to Player Events
Register event listeners when creating the DisGoLink client:
b . Lavalink = disgolink . New ( client . ApplicationID (),
disgolink . WithListenerFunc ( b . onTrackStart ),
disgolink . WithListenerFunc ( b . onTrackEnd ),
disgolink . WithListenerFunc ( b . onTrackException ),
disgolink . WithListenerFunc ( b . onTrackStuck ),
disgolink . WithListenerFunc ( b . onPlayerPause ),
disgolink . WithListenerFunc ( b . onPlayerResume ),
disgolink . WithListenerFunc ( b . onWebSocketClosed ),
)
Implement the event handlers:
func ( b * Bot ) onTrackStart ( player disgolink . Player , event lavalink . TrackStartEvent ) {
slog . Info ( "track started" ,
slog . String ( "title" , event . Track . Info . Title ),
slog . String ( "guild" , event . GuildID (). String ()),
)
}
func ( b * Bot ) onTrackEnd ( player disgolink . Player , event lavalink . TrackEndEvent ) {
// Check if we should play next track
if ! event . Reason . MayStartNext () {
return
}
// Load next track from queue
// Implementation depends on your queue system
}
func ( b * Bot ) onTrackException ( player disgolink . Player , event lavalink . TrackExceptionEvent ) {
slog . Error ( "track exception" ,
slog . String ( "message" , event . Exception . Message ),
slog . String ( "severity" , string ( event . Exception . Severity )),
)
}
func ( b * Bot ) onWebSocketClosed ( player disgolink . Player , event lavalink . WebSocketClosedEvent ) {
slog . Warn ( "voice websocket closed" ,
slog . Int ( "code" , event . Code ),
slog . String ( "reason" , event . Reason ),
slog . Bool ( "by_remote" , event . ByRemote ),
)
}
Player Controls
// Pause/Resume
player . Update ( context . TODO (), lavalink . WithPaused ( true ))
// Change volume (0-1000)
player . Update ( context . TODO (), lavalink . WithVolume ( 80 ))
// Seek to position
player . Update ( context . TODO (), lavalink . WithPosition ( lavalink . Duration ( 30000 ))) // 30 seconds
// Stop playback
player . Update ( context . TODO (), lavalink . WithNullTrack ())
// Apply filters
filters := player . Filters ()
filters . Volume = lavalink . Ptr ( 0.5 )
player . Update ( context . TODO (), lavalink . WithFilters ( filters ))
Complete Example
Check out the full working example with commands, queue management, and all features:
View DisGo Example on GitHub
Next Steps
Player Guide Learn about player management and controls
Track Loading Load tracks from various sources