Permissions And Middleware
Overview
The high-level middleware package provides reusable authorization, validation, guild, role, owner, and cooldown guards. It works with slash, context-menu, and prefix commands. The permissions.Permission type is a 64-bit bitfield; combine flags with permissions.Build or permissions.NewBuilder.
Architecture
Slash-command middleware receives *bot.InteractionContext. Member permissions come from the interaction's Member.Permissions, while bot permissions come from the interaction's AppPermissions string. Prefix middleware receives a *bot.MessageContext; RequirePrefixPermissions looks up the author through the configured cache.
Middleware wraps a handler. Router middleware runs for all slash commands, command middleware runs for one command, and prefix middleware applies to one prefix command. Authorization middleware responds ephemerally when it rejects a slash interaction.
Quick Start
This complete program protects /audit with member and bot permissions.
package main
import (
"log"
"os"
"github.com/discord-go/discord.go/bot"
"github.com/discord-go/discord.go/intents"
"github.com/discord-go/discord.go/permissions"
)
func main() {
token := os.Getenv("DISCORD_TOKEN")
if token == "" {
log.Fatal("DISCORD_TOKEN is required")
}
router := bot.NewRouter()
router.Command("audit", "Show an audit entry", func(ctx *bot.InteractionContext) {
if err := ctx.Reply("Authorized"); err != nil {
log.Printf("reply: %v", err)
}
}).
Use(bot.GuildOnly()).
RequirePermissions(permissions.ViewAuditLog).
RequireBotPermissions(permissions.ViewAuditLog)
b := bot.New(token, bot.WithIntents(intents.Guilds), bot.WithRouter(router))
if err := b.Run(); err != nil {
log.Fatal(err)
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Grant the bot the required permission in the test guild and remember that Discord's application-command visibility and channel overwrites still apply.
Creating/Configuration
Build a permission set with permissions.Build(permissions.ViewAuditLog, permissions.SendMessages) or permissions.NewBuilder(...).Add(...).Build(). Pass it to RequirePermissions, RequireAnyPermissions, or RequireBotPermissions.
Use RequireRole and RequireAnyRole with snowflake.ID values, and RequireOwners with an application-owned allowlist. OwnerOnly checks the current guild owner. GuildOnly rejects DMs. Validate turns a returned error into an ephemeral response. Prefix permission checks need WithCache and a cache implementing MemberCache.
Using
Basic: all required permissions
RequirePermissions(perms) calls HasAll, so a member must contain every bit. Use RequireAnyPermissions when any one of several bits is sufficient.
Intermediate: layered policy
A moderation command often uses GuildOnly, member permission checks, bot permission checks, RequireRole, and Cooldown. The first rejecting middleware stops the chain.
Advanced: prefix authorization and custom checks
Use PrefixGuildOnly and RequirePrefixPermissions for text commands. Use Validate(func(*InteractionContext) error) for resource-specific checks such as "the target is below the moderator"; return a user-safe message.
Common Patterns
- Check both invoker and bot permissions for actions the bot must perform.
- Use
Administratorsparingly; exact permissions communicate intent better. - Put global logging or tracing middleware on
Router.Useand authorization on the command that needs it. - Keep owner IDs in configuration, not source code, when deployments vary.
- Use
Permission.Hasfor any-bit checks andHasAllfor all-bit checks.
Best Practices
Check the bot separately
Why: a user can be authorized while the bot lacks the channel permission.
Pros: failures are explained before a REST request and avoid predictable 403s.
Cons: AppPermissions is interaction-provided and cannot replace handling permission changes that happen after invocation.
Prefer least privilege
Why: broad permissions increase blast radius.
Pros: safer installations and clearer audits.
Cons: more middleware and REST error paths must be handled when guild policy changes.
Treat prefix cache checks as fallible
Why: a missing member cache entry is not proof that a user lacks permission.
Pros: rejecting on cache miss avoids accidental authorization.
Cons: a cache miss can deny a valid user; fetch the member explicitly when the operation justifies the network request.
Common Mistakes
Incorrect: using Has when all permissions are required.
if perms.Has(permissions.BanMembers | permissions.KickMembers) {
// This allows only one of the two bits.
}2
3
Correct:
if perms.HasAll(permissions.BanMembers | permissions.KickMembers) {
// Both bits are present.
}2
3
Incorrect: checking only the user's permission for a ban command.
router.Command("ban", "Ban a member", handleBan).
RequirePermissions(permissions.BanMembers)2
Correct: check the bot too.
router.Command("ban", "Ban a member", handleBan).
RequirePermissions(permissions.BanMembers).
RequireBotPermissions(permissions.BanMembers)2
3
API Walkthrough
permissions.Permissionis auint64bitfield. Constants includeAdministrator, channel send/read/manage flags, moderation flags, voice flags, thread flags, poll flags, and application-command flags.Permission.AddandRemovemutate a bitfield;Haschecks any matching bit andHasAllchecks every requested bit.permissions.Build(...Permission) Permissioncombines flags.NewBuilder(initial ...Permission) *Builder,Builder.Add,Remove, andBuildprovide fluent composition.permissions.OverwritecontainsID,Type,Allow, andDeny(allPermissiontyped).channels.Overwritealso usespermissions.PermissionforAllowandDeny, so the two types are compatible without manual string conversion.permissions.Calculate(memberID, guildID, guildOwnerID, baseRolePermissions, memberRoleIDs, memberRolePermissions, overwrites) Permissionapplies owner, administrator, everyone, role, and member overwrite precedence.RequirePermissions,RequireAnyPermissions,RequireBotPermissions,RequireRole,RequireAnyRole,RequireOwners,GuildOnly,OwnerOnly, andValidatereturn slash-commandMiddleware.PrefixGuildOnly,RequirePrefixPermissions,PrefixCooldown, and the prefix command configuration methods operate on prefix handlers.Cooldown(time.Duration)returns per-user interaction middleware. The cooldown key includes user, guild, and command name.InteractionContext.MemberPermissions()andBotPermissions()expose the permissions used by custom policy code.
Examples
Related APIs
commands.mdfor middleware attachment.caching.mdfor prefix member lookups.../low-level/permissions/README.mdfor bitfields.