Skip to content

Partials And Cache ​

Overview ​

Discord events can contain optional objects or only the IDs needed to identify them. In discord.go, these shapes appear as pointers such as Interaction.User, Interaction.Member, Interaction.Channel, and Interaction.Guild; there is no separate Discord.js-style partials manager. Treat absent pointers as normal, then use the optional cache and REST fetch helpers when a complete resource is needed.

Tutorial: Cache-Aside Fetch ​

  1. Create a bounded cache.MemoryCache with a TTL.
  2. Attach it using bot.WithCache.
  3. Try CachedUser, CachedMember, or another typed lookup.
  4. On a miss, call FetchUser, FetchMember, or a matching method with a timeout.
  5. Treat the returned object as a snapshot and invalidate or refetch after mutations.

The built-in cache implements guild, channel, user, role, message, and member typed interfaces. A custom cache can implement only the interfaces its application needs.

Complete Runnable Example ​

Copy to examples/partials-cache/main.go, set DISCORD_TOKEN, and run it. Invoke /user-cache with a user option twice.

go
package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/discord-go/discord.go/bot"
	"github.com/discord-go/discord.go/cache"
	"github.com/discord-go/discord.go/intents"
	"github.com/discord-go/discord.go/interactions"
)

func main() {
	token := os.Getenv("DISCORD_TOKEN")
	if token == "" {
		log.Fatal("DISCORD_TOKEN is required")
	}

	router := bot.NewRouter()
	router.Command("user-cache", "Inspect a cached user", func(ctx *bot.InteractionContext) {
		userID := ctx.OptionUser("user")
		if userID == 0 {
			_ = ctx.ReplyEphemeral("A user is required.")
			return
		}
		if user, ok := ctx.Bot.CachedUser(userID); ok {
			_ = ctx.Reply(fmt.Sprintf("Cache hit: %s", user.Username))
			return
		}
		requestCtx, cancel := context.WithTimeout(ctx.Context(), 5*time.Second)
		defer cancel()
		user, err := ctx.Bot.FetchUser(requestCtx, userID)
		if err != nil {
			log.Printf("fetch user: %v", err)
			_ = ctx.ReplyEphemeral("The user could not be fetched.")
			return
		}
		_ = ctx.Reply(fmt.Sprintf("Cache miss; fetched %s", user.Username))
	}, interactions.ApplicationCommandOption{
		Type:        interactions.ApplicationCommandOptionTypeUser,
		Name:        "user",
		Description: "The user to fetch",
		Required:    true,
	})

	store := cache.NewMemoryCache(cache.WithTTL(10*time.Minute), cache.WithMaxSize(10000))
	b := bot.New(token, bot.WithIntents(intents.Guilds), bot.WithCache(store), bot.WithRouter(router))
	if err := b.Run(); err != nil {
		log.Fatal(err)
	}
}

Optional Data Rules ​

Always nil-check Member, User, Channel, and Guild pointers. A DM has no guild or member. A cache hit is not authoritative permission data, and a cache miss does not prove that a resource does not exist. Use FetchMember or another REST call when the operation requires fresh data.

Common Mistakes ​

  • Dereferencing an optional event pointer without checking it.
  • Configuring a cache but ignoring the hit boolean.
  • Using an unlimited cache for a large, long-running bot.
  • Assuming Gateway hydration covers data for unrequested intents.
  • Using stale cached permissions for a destructive action.

Expected Result ​

The first /user-cache call fetches and stores the selected user. A later call can use the typed cache hit until the TTL expires.

Released under the Apache License 2.0.