Main File
Overview
The main file is the executable composition root. It should load configuration, create the router, register commands and observers, configure intents, and start the bot. This page adapts the Discord.js Main File topic to a Go lifecycle with explicit context support.
Architecture
bot.RunContext starts the Gateway and runs until its context is canceled or the Gateway stops. Command synchronization occurs as part of the bot's READY handling. OnReady is for logging and startup observations; definitions should already exist. signal.NotifyContext translates process shutdown into the context passed to the bot.
Prerequisites
- A Go module and
discord.godependency. DISCORD_TOKENset in the environment.- An installed application with at least
View ChannelandSend Messages. - Familiarity with
context.Contextand process signals.
Quick Start
Save this complete program as main.go:
```go
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"github.com/discord-go/discord.go/bot"
"github.com/discord-go/discord.go/intents"
)
func main() {
token := os.Getenv("DISCORD_TOKEN")
if token == "" {
log.Fatal("DISCORD_TOKEN is required")
}
router := bot.NewRouter()
router.Command("status", "Show bot status", func(ctx *bot.InteractionContext) {
if err := ctx.Reply("Online."); err != nil {
log.Printf("status reply: %v", err)
}
})
b := bot.New(token,
bot.WithIntents(intents.Guilds),
bot.WithRouter(router),
)
b.OnReady(func(ctx *bot.ReadyContext) {
log.Printf("ready as %s", ctx.User.Username)
})
b.OnDisconnect(func() {
log.Println("gateway disconnected")
})
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
if err := b.RunContext(ctx); err != nil {
log.Printf("bot stopped: %v", err)
}
}Creating/Using
The program builds all state before starting the Gateway. In a service manager, the parent process can own the context and call RunContext directly. In a simple executable, Run is shorter and already listens for SIGINT and SIGTERM. Choose one lifecycle owner; do not call both Run and Start for the same bot.
Common Patterns
- Read and validate environment configuration at the top of
main. - Create one router and attach it once.
- Register
OnError,OnReady, andOnDisconnectbefore starting. - Use
RunContextwhen an HTTP server or service supervisor owns cancellation. - Use
Start,Done, andStopwhen startup and shutdown need separate phases.
Best Practices
- Keep
mainshort and move feature registration into functions or packages. - Give shutdown a deadline when active handlers can perform external work.
- Do not perform blocking initialization from
OnReadywithout a bounded context. - Treat reconnects as normal and make lifecycle observers idempotent.
- Return a non-zero process status for configuration and startup failures.
Common Mistakes
Incorrect
b.Run()
b.RunContext(ctx)Correct
if err := b.RunContext(ctx); err != nil {
log.Printf("bot stopped: %v", err)
}Incorrect
b.OnReady(func(ctx *bot.ReadyContext) {
registerCommands(bot.NewRouter())
})Correct
router := bot.NewRouter()
registerCommands(router)
b := bot.New(token, bot.WithRouter(router))The router must be attached before the bot receives READY.
API Walkthrough
bot.Runowns OS signal handling for a simple executable.bot.RunContextruns until a supplied context is canceled.bot.Startstarts asynchronously and returns after the connection is created.bot.Donecloses when the current run terminates.bot.Stopcancels the Gateway and waits for active handlers.bot.OnDisconnectobserves a terminated Gateway loop.
Examples
- Project Setup shows feature registration.
- Gateway uses explicit subscriptions and shutdown.
- Full Template demonstrates production-oriented wiring.