Basic Client
Overview
This guide starts a complete discord.go bot, listens for READY, handles a direct message event, and registers both slash and prefix commands. It is the recommended first example because it shows the normal high-level path without requiring application code to decode Gateway payloads.
Prerequisites
- Go
1.26.4or newer. DISCORD_TOKENcontaining a bot token, supplied by the environment.- A test guild where the bot can view channels and send messages.
GuildsandGuildMessagesenabled for the application.MessageContentis also required by the prefix and direct content checks in this exact source example.- The Message Content privileged intent enabled in the Developer Portal.
Architecture
bot.New creates the Gateway and REST-backed bot. bot.WithIntents controls the Gateway subscription, bot.WithRouter attaches slash and prefix dispatch, and OnMessageCreate registers a direct handler. The router registers slash commands automatically when READY is received. Replies use context helpers that create bounded REST requests.
Quick Start
From the repository root:
export DISCORD_TOKEN='replace-with-a-bot-token'
go run ./docs/examples/code/pingTry /ping, !ping, or a message containing exactly ping in a channel visible to the bot.
Complete Runnable Example
examples/ping/main.go is the complete runnable program. It has imports and main, reads DISCORD_TOKEN, configures intents.Guilds|intents.GuildMessages|intents.MessageContent, creates a router, registers handlers, and ends with b.Run().
Do not copy only a handler from that file and call it a complete program. The token check, imports, bot options, and lifecycle call are required.
Explanation
The source uses router.Command("ping", ...) for a slash command and router.Prefix("ping", ...) for !ping. A MessageContext supplies Content and Reply; an InteractionContext supplies interaction-specific reply methods. OnMessageCreate is useful for event-driven behavior that is not a command. Bot-authored messages are filtered before this handler is called.
The example builds an embed with messages.NewEmbedBuilder and returns errors from all reply calls to the log. This is preferable to silently ignoring failed REST requests.
Basic Usage
- Replace the example handler with a small command that responds with
ctx.Reply. - Add a READY handler with
b.OnReadyfor startup logs and metrics. - Use
bot.WithPrefixto change!to an application-specific prefix. - Remove
MessageContentand the content handler if the bot only needs slash commands.
Intermediate Usage
- Add global or per-command middleware with
router.UseandCommand.Use. - Use
bot.WithGuildCommandSync(guildID)while developing commands in one guild. - Use
bot.RunContextin a service that already owns cancellation instead of relying on process signals. - Add
bot.WithMaxHandlerConcurrencywhen unbounded parallel handlers could exhaust downstream resources.
Advanced Usage
- Use
bot.Start(ctx),bot.WaitReady(ctx),bot.Done(), andbot.Stop(ctx)for explicit lifecycle control. - Use
bot.WithCacheorbot.WithStoreonly when the application has a defined cache or persistence policy. - Use
bot.WithShards(0)for automatic shard sizing when the application has grown beyond one Gateway session. - Use
bot.OnErrorandBot.Stats()to expose handler panics, event counts, command syncs, and lifecycle health.
Common Patterns
- Keep the token in
DISCORD_TOKEN,TOKEN, or a secret manager and pass it tobot.Newonly at startup. - Select the smallest intent set.
MessageContentis privileged and unnecessary for slash-only bots. - Return or log errors from
Reply,ReplyEmbed, andReplyComplex. - Use
ctx.Bot.Restfor APIs not exposed by a context helper, with acontext.WithTimeoutdeadline. - Let
b.Run()handle SIGINT/SIGTERM for a simple executable; useRunContextfor an orchestrated service.
Best Practices
- Keep test and production applications separate so command synchronization cannot overwrite the wrong command set.
- Do not log the token or embed untrusted input as raw administrative instructions.
- Configure Discord permissions separately from Gateway intents; intents do not grant channel permissions.
- Use structured logs for READY, disconnect, reconnect, and REST failures.
- Set a shutdown deadline and allow active handlers to finish before the process exits.
Common Mistakes with wrong/correct examples
Wrong
token := "YOUR_BOT_TOKEN"
b := bot.New(token)Correct
token := os.Getenv("DISCORD_TOKEN")
if token == "" {
log.Fatal("DISCORD_TOKEN is required")
}
b := bot.New(token, bot.WithIntents(intents.Guilds))Wrong
router.Command("Ping", "", handler)Correct
router.Command("ping", "Check whether the bot is online", handler)Command names must be lower-case and descriptions for chat-input commands must be non-empty. The corrected fragment still belongs inside a complete program.
Expected Result
The process logs a READY message, the bot appears online, /ping returns the Pong embed, !ping returns a prefix response, and a direct ping message receives a response. Ctrl+C causes b.Run() to stop the Gateway and wait for active handlers.