Presence And Latency
Overview
Presence is the status and activity Discord displays for the bot. discord.go supports an initial presence through WithPresence and runtime updates through SetPresence, SetStatus, and SetActivity. It also exposes gateway heartbeat latency and a lightweight REST latency measurement.
Architecture
PresenceUpdate is serialized into the gateway OP 3 payload. A single bot sends the payload through its gateway client; a sharded bot broadcasts it to every active shard. The bot remembers the latest successful presence and reapplies it after a fresh identify. GatewayLatency reads the heartbeat round-trip value, while APILatency measures a GetCurrentUser REST request.
Quick Start
This complete program configures an initial activity and changes the status after READY.
package main
import (
"context"
"log"
"os"
"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")
}
b := bot.New(token,
bot.WithIntents(intents.Guilds),
bot.WithPresence(bot.PresenceUpdate{
Status: "online",
Activities: []bot.Activity{{Name: "discord.go", Type: 0}},
}),
)
b.OnReady(func(ctx *bot.ReadyContext) {
if err := ctx.Bot.SetStatus(context.Background(), "online"); err != nil {
log.Printf("presence: %v", err)
}
log.Printf("ready as %s", ctx.User.Username)
})
if err := b.Run(); err != nil {
log.Fatal(err)
}
}Presence updates require a running gateway. The activity Type is the integer Discord activity type; 0 is the usual playing activity.
Creating/Configuration
bot.Activity contains Name, Type, and optional URL. bot.PresenceUpdate contains Since, Activities, Status, and AFK. Pass a value to WithPresence for automatic application after READY. Use SetPresence for multiple activities or to control all fields. Use SetStatus to preserve the current activity list and SetActivity to replace the activity list with one activity.
Using
Basic: set status
Call SetStatus(ctx, "idle"), "dnd", or another Discord-supported status after READY. The method preserves the remembered activities.
Intermediate: update an activity
Call SetActivity(ctx, bot.Activity{Name: "a queue", Type: 0}). This replaces the current activity slice with one item.
Advanced: monitor latency
GatewayLatency() is local and normally cheap; APILatency(ctx) performs a network request and returns both duration and error. Measure these separately when diagnosing gateway versus REST problems.
Common Patterns
- Configure a stable initial presence with
WithPresenceand change dynamic text only when it changes. - Reapply a presence from
OnReadyonly when the application needs a runtime value not known during construction. - Do not use REST latency as a proxy for gateway health.
- In sharded deployments, rely on
SetPresenceso every shard receives the update. - Treat a zero gateway latency as "not available yet", not as a real zero RTT.
Best Practices
Keep presence updates infrequent
Why: gateway status updates consume connection bandwidth and are visible UI.
Pros: less noise and fewer avoidable gateway operations.
Cons: highly dynamic counts may become slightly stale.
Use an honest status
Why: presence is user-facing operational information.
Pros: users can understand whether the service is available.
Cons: an activity can expose deployment or workload details, so avoid secrets and private identifiers.
Measure both paths
Why: gateway heartbeat and REST API requests fail independently.
Pros: dashboards distinguish Discord gateway issues from HTTP/rate-limit issues.
Cons: APILatency adds a real request and should not be sampled too often.
Common Mistakes
Incorrect: setting presence before the gateway starts.
_ = b.SetStatus(context.Background(), "online")
_ = b.Start(context.Background())Correct: configure it with WithPresence or wait for READY.
b := bot.New(token, bot.WithPresence(bot.PresenceUpdate{Status: "online"}))Incorrect: assuming SetActivity appends to existing activities.
_ = b.SetActivity(ctx, bot.Activity{Name: "new", Type: 0})Correct: use SetPresence when multiple activities must be preserved.
_ = b.SetPresence(ctx, bot.PresenceUpdate{
Status: "online",
Activities: []bot.Activity{
{Name: "one", Type: 0},
{Name: "two", Type: 0},
},
})API Walkthrough
ActivityhasName,Type, andURLfields.PresenceUpdatehasSince *int64,Activities []Activity,Status, andAFKfields.WithPresence(PresenceUpdate) bot.Optionstores an initial presence.SetPresence(context.Context, PresenceUpdate) errorsends and remembers the complete presence; it returns an error when the gateway is not running.SetStatus(context.Context, string) errorpreserves activities while changing status.SetActivity(context.Context, Activity) errorpreserves status while replacing activities with the supplied activity.GatewayLatency() time.Durationreturns the latest heartbeat ping or zero when no gateway heartbeater is available.APILatency(context.Context) (time.Duration, error)measures the current-user REST request.
Examples
Related APIs
lifecycle.mdfor READY and reconnect behavior.client.mdfor shard and gateway options.../low-level/gateway/heartbeat.mdfor heartbeat details.