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’s event system allows you to respond to events from Discord’s Gateway API. Events are dispatched through the EventManager and can be consumed through multiple listener types.
Event flow
Gateway receives event
Discord sends an event through the WebSocket connection
Gateway handler processes
The gateway event handler converts the raw payload to a typed event
EventManager dispatches
The event is dispatched to all registered listeners
Listeners execute
Your event handler functions are called with the event data
Event listener types
DisGo provides three ways to listen for events:
Function listeners
The simplest approach is using typed functions:
import "github.com/disgoorg/disgo/events"
func handleMessage(event *events.MessageCreate) {
log.Printf("Message from %s: %s",
event.Message.Author.Username,
event.Message.Content,
)
}
client, err := disgo.New(token,
bot.WithDefaultGateway(),
bot.WithEventListenerFunc(handleMessage),
)
Channel listeners
For concurrent event processing:
messageChan := make(chan *events.MessageCreate, 100)
go func() {
for event := range messageChan {
// Process event
processMessage(event)
}
}()
client, err := disgo.New(token,
bot.WithDefaultGateway(),
bot.WithEventListenerChan(messageChan),
)
Adapter listeners
For handling multiple event types in one structure:
import "github.com/disgoorg/disgo/events"
listener := &events.ListenerAdapter{
OnMessageCreate: func(e *events.MessageCreate) {
log.Println("Message created")
},
OnMessageUpdate: func(e *events.MessageUpdate) {
log.Println("Message updated")
},
OnMessageDelete: func(e *events.MessageDelete) {
log.Println("Message deleted")
},
}
client, err := disgo.New(token,
bot.WithDefaultGateway(),
bot.WithEventListeners(listener),
)
Common event types
Message events
MessageCreate
MessageUpdate
MessageDelete
func handleMessageCreate(e *events.MessageCreate) {
// Access message data
content := e.Message.Content
author := e.Message.Author
channelID := e.ChannelID
// Get cached guild (if available)
if e.GuildID != nil {
guild, ok := e.Client().Caches.Guild(*e.GuildID)
}
}
func handleMessageUpdate(e *events.MessageUpdate) {
// Compare old and new messages
if e.OldMessage.Content != e.Message.Content {
log.Printf("Content changed: %s -> %s",
e.OldMessage.Content,
e.Message.Content,
)
}
}
func handleMessageDelete(e *events.MessageDelete) {
log.Printf("Message %s deleted from %s",
e.MessageID,
e.ChannelID,
)
}
Guild events
OnGuildJoin: func(e *events.GuildJoin) {
log.Printf("Joined guild: %s (%s)", e.Guild.Name, e.Guild.ID)
},
OnGuildUpdate: func(e *events.GuildUpdate) {
log.Printf("Guild %s updated", e.Guild.ID)
},
OnGuildLeave: func(e *events.GuildLeave) {
log.Printf("Left guild: %s", e.GuildID)
},
OnGuildReady: func(e *events.GuildReady) {
log.Printf("Guild %s ready", e.GuildID)
},
Member events
OnGuildMemberJoin: func(e *events.GuildMemberJoin) {
log.Printf("%s joined %s",
e.Member.User.Username,
e.GuildID,
)
},
OnGuildMemberUpdate: func(e *events.GuildMemberUpdate) {
// Check role changes
addedRoles := difference(e.Member.RoleIDs, e.OldMember.RoleIDs)
removedRoles := difference(e.OldMember.RoleIDs, e.Member.RoleIDs)
},
OnGuildMemberLeave: func(e *events.GuildMemberLeave) {
log.Printf("%s left %s", e.User.Username, e.GuildID)
},
Interaction events
OnInteraction: func(e *events.InteractionCreate) {
switch data := e.Interaction.Data.(type) {
case discord.SlashCommandInteractionData:
// Handle slash command
case discord.ComponentInteractionData:
// Handle button/select menu
case discord.ModalSubmitInteractionData:
// Handle modal submission
}
},
OnApplicationCommandInteraction: func(e *events.ApplicationCommandInteractionCreate) {
// Specifically for slash commands
},
OnComponentInteraction: func(e *events.ComponentInteractionCreate) {
// Specifically for buttons and select menus
},
Event interface
All events implement the Event interface:
type Event interface {
Client() *Client
SequenceNumber() int
}
This gives you access to:
func handler(e *events.MessageCreate) {
// Get the client instance
client := e.Client()
// Make REST API calls
client.Rest.CreateMessage(...)
// Access caches
guild, _ := client.Caches.Guild(e.GuildID)
// Get sequence number (for debugging)
seq := e.SequenceNumber()
}
Dynamic listener management
You can add and remove listeners at runtime:
// Create a listener
listener := bot.NewListenerFunc(func(e *events.MessageCreate) {
log.Println("Message:", e.Message.Content)
})
// Add it
client.AddEventListeners(listener)
// Remove it later
client.RemoveEventListeners(listener)
Custom event listener
Implement the EventListener interface for custom behavior:
type MyListener struct {
messageCount int
}
func (l *MyListener) OnEvent(event bot.Event) {
switch e := event.(type) {
case *events.MessageCreate:
l.messageCount++
if l.messageCount%100 == 0 {
log.Printf("Processed %d messages", l.messageCount)
}
case *events.Ready:
log.Println("Bot is ready!")
}
}
// Use it
client.AddEventListeners(&MyListener{})
Async event handling
By default, events are processed synchronously. Enable async processing:
import "github.com/disgoorg/disgo/bot"
client, err := disgo.New(token,
bot.WithEventManagerConfigOpts(
bot.WithAsyncEventsEnabled(true),
),
)
With async events enabled, your handlers run in goroutines. Ensure proper synchronization when accessing shared state.
Gateway lifecycle events
OnReady: func(e *events.Ready) {
log.Printf("Logged in as %s", e.User.Username)
log.Printf("Session ID: %s", e.SessionID)
},
OnResumed: func(e *events.Resumed) {
log.Println("Session resumed")
},
OnGuildsReady: func(e *events.GuildsReady) {
log.Printf("All guilds ready on shard %d", e.ShardID)
},
Raw events
Access raw gateway payloads:
import "github.com/disgoorg/disgo/gateway"
client, err := disgo.New(token,
bot.WithGatewayConfigOpts(
gateway.WithEnableRawEvents(true),
),
bot.WithEventListenerFunc(func(e *events.Raw) {
log.Printf("Raw event: %s", e.EventType)
}),
)
Complete example
package main
import (
"log"
"strings"
"github.com/disgoorg/disgo"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/disgo/gateway"
)
type Bot struct {
client *bot.Client
}
func (b *Bot) onReady(e *events.Ready) {
log.Printf("Bot ready: %s", e.User.Username)
}
func (b *Bot) onMessageCreate(e *events.MessageCreate) {
// Ignore bots
if e.Message.Author.Bot {
return
}
// Handle commands
if strings.HasPrefix(e.Message.Content, "!") {
b.handleCommand(e)
}
}
func (b *Bot) handleCommand(e *events.MessageCreate) {
args := strings.Fields(e.Message.Content)
if len(args) == 0 {
return
}
switch args[0] {
case "!ping":
_, _ = e.Client().Rest.CreateMessage(
e.ChannelID,
discord.MessageCreate{Content: "Pong!"},
)
case "!info":
if e.GuildID != nil {
guild, ok := e.Client().Caches.Guild(*e.GuildID)
if ok {
_, _ = e.Client().Rest.CreateMessage(
e.ChannelID,
discord.MessageCreate{
Content: "Guild: " + guild.Name,
},
)
}
}
}
}
func main() {
b := &Bot{}
client, err := disgo.New("TOKEN",
bot.WithDefaultGateway(),
bot.WithGatewayConfigOpts(
gateway.WithIntents(
gateway.IntentGuilds,
gateway.IntentGuildMessages,
gateway.IntentMessageContent,
),
),
bot.WithEventListeners(&events.ListenerAdapter{
OnReady: b.onReady,
OnMessageCreate: b.onMessageCreate,
}),
)
if err != nil {
log.Fatal(err)
}
b.client = client
// Connect and run
// ...
}
Event handlers should be fast. For long-running operations, spawn a goroutine to avoid blocking the event loop.