Help us shape the future of Go! Take the 2026 Go Developer Survey

dgr

package module
v0.3.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 1, 2026 License: BSD-2-Clause Imports: 11 Imported by: 0

README

discord-go-router

discord-go-router is a small typed routing layer for discordgo. It lets you register Discord application commands with Go structs, receive parsed arguments in a typed context, and reply with messages, embeds, and simple buttons.

Features

  • Slash commands from struct fields and dgr tags
  • Slash command groups with subcommands and subcommand groups
  • Message and user context menu commands
  • Typed command arguments through Context[T]
  • Built-in interaction replies with ephemeral messages, embeds, and one button
  • Choice fields using struct members of type dgr.Choice
  • Discord resolved types for users, channels, roles, attachments, and mentionables

Install

go get github.com/minumarapid/discord-go-router

Quick Start

package main

import (
	"log"
	"os"

	dgr "github.com/minumarapid/discord-go-router"
	"github.com/bwmarrin/discordgo"
)

type PingArgs struct {
	Text string `dgr:"text" desc:"Text to echo" required:"true"`
}

func main() {
	bot, err := dgr.New(os.Getenv("DISCORD_TOKEN"))
	if err != nil {
		log.Fatal(err)
	}

	dgr.RegSlash(bot, "ping", "Reply with a pong", func(c *dgr.Context[PingArgs]) {
		embed := &discordgo.MessageEmbed{
			Title:       "Pong",
			Description: c.Args.Text,
			Color:       0x57F287,
		}

		if err := c.Reply("pong", dgr.WithEmbeds(embed)); err != nil {
			log.Println(err)
		}
	})

	if err := bot.Run(os.Getenv("DISCORD_GUILD_ID")); err != nil {
		log.Fatal(err)
	}
}

Run opens the Discord session, bulk-overwrites the registered application commands for the given guild, and blocks until Ctrl+C.

Slash Command Arguments

Define a struct for each slash command. Fields with a dgr tag become Discord options.

type Args struct {
	Name     string                         `dgr:"name" desc:"Display name" required:"true"`
	Count    int                            `dgr:"count" desc:"Repeat count"`
	Public   bool                           `dgr:"public" desc:"Show publicly"`
	User     *dgr.InteractionUser           `dgr:"user" desc:"Target user"`
	Channel  *discordgo.Channel             `dgr:"channel" desc:"Target channel"`
	Role     *discordgo.Role                `dgr:"role" desc:"Target role"`
	File     *discordgo.MessageAttachment   `dgr:"file" desc:"Attachment"`
	Mention  *dgr.Mentionable               `dgr:"mention" desc:"User or role"`
}

Supported field types:

Go type Discord option type
string String
int, int64 Integer
float32, float64 Number
bool Boolean
*dgr.InteractionUser User
*discordgo.Channel Channel
*discordgo.Role Role
*discordgo.MessageAttachment Attachment
*dgr.Mentionable Mentionable
struct containing dgr.Choice fields String choices

Use desc for the Discord option description and required:"true" for required options.

Choices

Choices are represented as a nested struct whose fields are dgr.Choice. The selected choice is set to true.

type ColorChoices struct {
	Red   dgr.Choice `name:"Red" value:"red"`
	Blue  dgr.Choice `name:"Blue" value:"blue"`
	Green dgr.Choice `dgr:"green"`
}

type PaintArgs struct {
	Color ColorChoices `dgr:"color" desc:"Paint color" required:"true"`
}

dgr.RegSlash(bot, "paint", "Pick a color", func(c *dgr.Context[PaintArgs]) {
	selected := dgr.SelectedChoiceOf(&c.Args.Color)
	if selected == nil {
		_ = c.Reply("No color selected", dgr.WithEphemeral())
		return
	}

	_ = c.Reply("Color selected: "+selected.Value, dgr.WithEphemeral())
})

Choice fields support these tags:

Tag Meaning
dgr:"value" Sets both Discord choice name and value
name:"Display name" Sets the Discord choice name
label:"Display name" Alias for name
value:"stored-value" Sets the Discord choice value

Without tags, the Go field name is used for both the Discord choice name and value. Selected returns the selected *Choice; SelectedChoiceOf returns the selected choice plus its configured Name and Value.

Subcommands

Use Group to create a slash command with subcommands.

type BanArgs struct {
	User   *dgr.InteractionUser `dgr:"user" desc:"User to ban" required:"true"`
	Reason string               `dgr:"reason" desc:"Reason"`
}

admin := dgr.Group(bot, "admin", "Admin commands")
dgr.RegSlash(admin, "ban", "Ban a user", func(c *dgr.Context[BanArgs]) {
	_ = c.Reply("Banned", dgr.WithEphemeral())
})

Use SubGroup when you need /admin users ban style nesting.

users := dgr.SubGroup(admin, "users", "User commands")
dgr.RegSlash(users, "ban", "Ban a user", func(c *dgr.Context[BanArgs]) {
	_ = c.Reply("Banned", dgr.WithEphemeral())
})

Replies

Use Context.Reply to respond to the interaction.

err := c.Reply("hello")

Signature:

func (c *Context[T]) Reply(
	content string,
	opts ...ReplyOption,
) error

Examples:

_ = c.Reply("Only you can see this", dgr.WithEphemeral())

embed := &discordgo.MessageEmbed{
	Title:       "Result",
	Description: "Done",
}
_ = c.Reply("", dgr.WithEmbeds(embed))

Buttons

Create buttons from the current context, then pass them to Reply. The button handler receives a new Context[T] with the same Args.

dgr.RegSlash(bot, "confirm", "Show a confirm button", func(c *dgr.Context[struct{}]) {
	yes, err := c.NewButton("Confirm", discordgo.SuccessButton, func(c *dgr.Context[struct{}]) {
		_ = c.Reply("Confirmed", dgr.WithEphemeral())
	})
	if err != nil {
		_ = c.Reply("Failed to create button", dgr.WithEphemeral())
		return
	}

	no, err := c.NewButton("Cancel", discordgo.DangerButton, func(c *dgr.Context[struct{}]) {
		_ = c.Reply("Cancelled", dgr.WithEphemeral())
	})
	if err != nil {
		_ = c.Reply("Failed to create button", dgr.WithEphemeral())
		return
	}

	row, err := c.NewButtonRow(yes, no)
	if err != nil {
		_ = c.Reply("Failed to create buttons", dgr.WithEphemeral())
		return
	}

	_ = c.Reply("Continue?", dgr.WithEphemeral(), dgr.WithButtons(row))
})

Context Menu Commands

Message context menu command:

dgr.RegMessageCtx(bot, "Inspect message", func(c *dgr.Context[discordgo.Message]) {
	_ = c.Reply(c.Args.Content, dgr.WithEphemeral())
})

User context menu command:

dgr.RegUserCtx(bot, "Inspect user", func(c *dgr.Context[discordgo.User]) {
	_ = c.Reply(c.Args.Username, dgr.WithEphemeral())
})

Error-Returning APIs

The RegSlash, RegMessageCtx, and RegUserCtx helpers are convenience wrappers. Use the E variants when you want registration errors instead of a panic or ignored error:

err := dgr.RegSlashE(bot, "ping", "Reply with pong", handler)
err = dgr.RegMessageCtxE(bot, "Inspect message", messageHandler)
err = dgr.RegUserCtxE(bot, "Inspect user", userHandler)
group, err := dgr.GroupE(bot, "admin", "Admin commands")
err = dgr.RegSlashE(group, "ban", "Ban a user", banHandler)
users, err := dgr.SubGroupE(group, "users", "User commands")
err = dgr.RegSlashE(users, "ban", "Ban a user", banHandler)

More Documentation

Documentation

Overview

Package dgr provides a typed routing layer for discordgo interactions.

It registers Discord application commands from Go structs, parses interaction options into typed Context values, and provides helpers for replies and simple button components.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RegMessageCtx

func RegMessageCtx(d *Dgr, name string, handler func(c *Context[discordgo.Message]))

func RegMessageCtxE

func RegMessageCtxE(d *Dgr, name string, handler func(c *Context[discordgo.Message])) error

func RegMsgCreate

func RegMsgCreate(d *Dgr, channelIDs []string, handler func(c *MsgCreateCtx))

func RegMsgCreateE

func RegMsgCreateE(d *Dgr, channelIDs []string, handler func(c *MsgCreateCtx)) error

func RegSlash

func RegSlash[T any](target SlashTarget, name string, description string, handler func(c *Context[T]))

func RegSlashE

func RegSlashE[T any](target SlashTarget, name string, description string, handler func(c *Context[T])) error

func RegUserCtx

func RegUserCtx(d *Dgr, name string, handler func(c *Context[discordgo.User]))

func RegUserCtxE

func RegUserCtxE(d *Dgr, name string, handler func(c *Context[discordgo.User])) error

Types

type Button

type Button[T any] struct {
	Text     string
	CustomID string
	Style    discordgo.ButtonStyle
	Handler  func(c *Context[T])
	Args     T
	// contains filtered or unexported fields
}

func (*Button[T]) Invoke

func (b *Button[T]) Invoke(s *discordgo.Session, i *discordgo.Interaction)

func (*Button[T]) ToButtonComponent

func (b *Button[T]) ToButtonComponent() discordgo.Button

func (*Button[T]) ToComponent

func (b *Button[T]) ToComponent() discordgo.MessageComponent

type ButtonComponent

type ButtonComponent interface {
	ToButtonComponent() discordgo.Button
}

type ButtonInvoker

type ButtonInvoker interface {
	Invoke(s *discordgo.Session, i *discordgo.Interaction)
}

type ButtonRow

type ButtonRow struct {
	Buttons []ButtonComponent
}

func (*ButtonRow) ToComponent

func (r *ButtonRow) ToComponent() discordgo.MessageComponent

type Choice

type Choice bool

func Selected

func Selected[T any](structPtr *T) *Choice

type CommandGroup

type CommandGroup struct {
	// contains filtered or unexported fields
}

func Group

func Group(d *Dgr, name string, description string) *CommandGroup

func GroupE

func GroupE(d *Dgr, name string, description string) (*CommandGroup, error)

type Context

type Context[T any] struct {
	Session     *discordgo.Session
	Interaction *discordgo.Interaction
	Args        T
	// contains filtered or unexported fields
}

func (*Context[T]) NewButton

func (c *Context[T]) NewButton(text string, style discordgo.ButtonStyle, handler func(c *Context[T])) (*Button[T], error)

func (*Context[T]) NewButtonRow

func (c *Context[T]) NewButtonRow(buttons ...ButtonComponent) (*ButtonRow, error)

func (*Context[T]) Reply

func (c *Context[T]) Reply(content string, opts ...ReplyOption) error

type Dgr

type Dgr struct {
	Session *discordgo.Session
	// contains filtered or unexported fields
}

func New

func New(token string) (*Dgr, error)

func (*Dgr) Run

func (d *Dgr) Run(guildID string) error

func (*Dgr) Stop

func (d *Dgr) Stop() error

func (*Dgr) SyncCommands

func (d *Dgr) SyncCommands(guildID string) error

type InteractionUser

type InteractionUser struct {
	*discordgo.User
	*discordgo.Member
}

type Mentionable

type Mentionable struct {
	User *InteractionUser
	Role *discordgo.Role
	Type MentionableType
}

type MentionableType

type MentionableType string
const (
	MentionableTypeUser MentionableType = "USER"
	MentionableTypeRole MentionableType = "ROLE"
)

type MsgCreateCtx

type MsgCreateCtx struct {
	Session *discordgo.Session
	Args    *discordgo.Message
	// contains filtered or unexported fields
}

type ReplyOption

type ReplyOption func(*discordgo.InteractionResponseData)

func WithButton

func WithButton[T any](button *Button[T]) ReplyOption

func WithButtons

func WithButtons(row *ButtonRow) ReplyOption

func WithEmbeds

func WithEmbeds(embeds ...*discordgo.MessageEmbed) ReplyOption

func WithEphemeral

func WithEphemeral() ReplyOption

type SelectedChoice

type SelectedChoice struct {
	Choice *Choice
	Name   string
	Value  string
}

func SelectedChoiceOf

func SelectedChoiceOf[T any](structPtr *T) *SelectedChoice

type SlashTarget

type SlashTarget interface {
	// contains filtered or unexported methods
}

type SubCommandGroup

type SubCommandGroup struct {
	// contains filtered or unexported fields
}

func SubGroup

func SubGroup(group *CommandGroup, name string, description string) *SubCommandGroup

func SubGroupE

func SubGroupE(group *CommandGroup, name string, description string) (*SubCommandGroup, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL