dispatch

package module
v1.2.2 Latest Latest
Warning

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

Go to latest
Published: May 14, 2026 License: MIT Imports: 6 Imported by: 13

README

Dispatch

A lightweight Discord bot command framework for Go. Dispatch simplifies building command-driven Discord bots with support for argument parsing, permission checking, prefix configuration, and command aliases.

Features

  • Easy Command Registration: Register commands with a simple, declarative API
  • Automatic Message Routing: Built-in Discord message event handling and command dispatch
  • Flexible Argument Parsing: Support for multiple argument types (strings, integers, users, members, durations, custom types)
  • Permission Management: Enforce user and bot permission requirements per command
  • Customizable Prefixes: Dynamic prefix configuration on a per-guild basis
  • Command Aliases: Support up to 3 aliases per command for flexible invocation
  • Argument Combinations: Define flexible argument requirement patterns

Installation

go get github.com/RhykerWells/dispatch

Quick Start

1. Create a CommandHandler
package main

import (
	"github.com/RhykerWells/dispatch"
	"github.com/bwmarrin/discordgo"
)

func main() {
	// Create a new command handler
	handler := dispatch.NewCommandHandler()
	
	// Optional: Set a custom prefix function (default is "~")
	handler.SetPrefixFunc(func(guildID string) string {
		// Return prefix based on guild ID (e.g., from database)
		return "!"
	})
}
2. Define Commands
// Create a simple ping command
pingCmd := &dispatch.Command{
	Command:      "ping",
	Category:     dispatch.CommandCategory{Name: "Utility", Description: "Utility commands"},
	Description:  "Responds with pong",
	Aliases:      []string{"p"},
	ArgsRequired: 0,
	Run: func(data *dispatch.Data) error {
		data.Session.ChannelMessageSend(data.Channel.ID, "Pong!")
		return nil
	},
}

// Create a command with arguments
echoCmd := &dispatch.Command{
	Command:      "echo",
	Category:     dispatch.CommandCategory{Name: "Utility"},
	Description:  "Echoes back your message",
	Args:         []*dispatch.Arg{{Name: "text", Type: dispatch.String}},
	ArgsRequired: 1,
	Run: func(data *dispatch.Data) error {
		// Argument values are interface{} and must be type asserted
		text := data.ParsedArgs[0].Value.(string)
		data.Session.ChannelMessageSend(data.Channel.ID, text)
		return nil
	},
}
3. Register Commands
handler.RegisterCommands(pingCmd, echoCmd)
4. Attach to Discord Session
session.AddHandler(handler.HandleMessageCreate)

Argument Types

Dispatch provides several built-in argument types.

Note: Argument values are stored as interface{} and must be type asserted to their expected Go type. Built-in argument types return the following values:

Type Go Type returned Description
dispatch.String string Plain text argument with optional string options
dispatch.Int int Integer argument with optional min/max bounds
dispatch.Int64 int64 Integer argument with optional min/max bounds
dispatch.User *discordgo.User Discord user mention/ID
dispatch.Member *discordgo.Member Discord member mention/ID
dispatch.Duration time.Duration Duration string (e.g., "5m", "1h30m")
Example with Multiple Arguments
kickCmd := &dispatch.Command{
	Command:     "kick",
	Description: "Kick a member with optional reason",
	Args: []*dispatch.Arg{
		{Name: "member", Type: dispatch.Member},
		{Name: "reason", Type: dispatch.String},
	},
	ArgsRequired: 1, // Member required, reason optional
	RequiredBotPerms: []int64{discordgo.PermissionKickMembers},
	Run: func(data *dispatch.Data) error {
		// Argument values are interface{} and must be type asserted
		member := data.ParsedArgs[0].Value.(*discordgo.Member)
		reason := ""
		if len(data.ParsedArgs) > 1 {
			reason = data.ParsedArgs[1].Value.(string)
		}
		
		// Kick the user...
		return nil
	},
}

Permission Management

Enforce permissions with RequiredUserPerms and RequiredBotPerms:

banCmd := &dispatch.Command{
	Command:          "ban",
	Description:      "Ban a member",
	RequiredUserPerms: []int64{discordgo.PermissionBanMembers},
	RequiredBotPerms:  []int64{discordgo.PermissionBanMembers},
	Args:             []*dispatch.Arg{{Name: "user", Type: dispatch.User}},
	ArgsRequired:     1,
	Run: func(data *dispatch.Data) error {
		// Argument values are interface{} and must be type asserted
		user := data.ParsedArgs[0].Value.(*discordgo.User)
		data.Session.GuildBanCreate(data.Guild.ID, user.ID, 0)
		return nil
	},
}

Command Categories

Organize commands logically using categories:

utilityCategory := dispatch.CommandCategory{
	Name:        "Utility",
	Description: "Helpful utility commands",
}

cmd := &dispatch.Command{
	Command:  "help",
	Category: utilityCategory,
	// ... rest of command
}

Data Available in Commands

Each command's Run function receives a *dispatch.Data object with:

  • Session: The Discord session
  • Bot: The bot user information
  • Guild: The guild (server) where the command was invoked
  • Channel: The channel where the command was invoked
  • Author: The user who invoked the command
  • Message: The original Discord message
  • ParsedArgs: Parsed and validated arguments (When accessed, must be type asserted)
  • Handler: Reference to the command handler

Example Discord Bot

package main

import (
	"os"
	"os/signal"
	"syscall"

	"github.com/RhykerWells/dispatch"
	"github.com/bwmarrin/discordgo"
)

func main() {
	session, _ := discordgo.New("Bot " + os.Getenv("DISCORD_TOKEN"))
	handler := dispatch.NewCommandHandler()

	// Create commands
	pingCmd := &dispatch.Command{
		Command:      "ping",
		Description:  "Ping pong",
		Run: func(data *dispatch.Data) error {
			data.Session.ChannelMessageSend(data.Channel.ID, "Pong! 🏓")
			return nil
		},
	}

	handler.RegisterCommands(pingCmd)
	session.AddHandler(handler.HandleMessageCreate)
	session.Open()

	// Wait for interrupt
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
	<-sc

	session.Close()
}

Advanced Usage

Custom Argument Types

Implement the ArgumentType interface to create custom argument validators: The ParseArg method should return both the parsed data & if successfully parsed so that the result stored in ParsedArgs[i].Value can be safely type asserted in the command handler.

type CustomArg struct{}

var _ dispatch.ArgumentType = (*CustomArg)(nil)

func (c *CustomArg) ParseArg(arg *dispatch.ParsedArg, data *dispatch.Data) (any, bool) {
	value := arg.Raw 
	// Validation logic
	// Return the value in whatever type you'd like
	return value, true
}

func (c *CustomArg) Help() string {
	return "Custom argument type"
}
Argument Combinations

Define flexible argument patterns using argument combinations:

cmd := &dispatch.Command{
	Command:     "example",
	Args:        []*dispatch.Arg{...},
	ArgumentCombos: [][]int{
		{0},        // Just first argument
		{0, 1},     // First and second
		{0, 1, 2},  // All three
	},
	Run: func(data *dispatch.Data) error {
		return nil
	},
}

Important Notes

  • Prefix: Default prefix is ~. Customize it with SetPrefixFunc()
  • Aliases: Limited to 3 non-empty aliases per command
  • Bot Detection: Bot messages and self-messages are automatically ignored
  • Case Insensitive: Command names and aliases are matched case-insensitively
  • Permissions: Permission checking is automatic when RequiredUserPerms or RequiredBotPerms are set

Dependencies

  • github.com/bwmarrin/discordgo - Discord API client
  • github.com/RhykerWells/durationutil - Duration parsing utilities
  • github.com/sirupsen/logrus - Logging

License

See LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	String   = &StringArg{}
	Int      = &IntArg{}
	Int64    = &Int64Arg{}
	User     = &UserArg{}
	Member   = &MemberArg{}
	Duration = &DurationArg{}
)
View Source
var PermissionNames = map[int64]string{

	discordgo.PermissionViewChannel:        "View Channel",
	discordgo.PermissionSendMessages:       "Send Messages",
	discordgo.PermissionSendTTSMessages:    "Send TTS Messages",
	discordgo.PermissionManageMessages:     "Manage Messages",
	discordgo.PermissionEmbedLinks:         "Embed Links",
	discordgo.PermissionAttachFiles:        "Attach Files",
	discordgo.PermissionReadMessageHistory: "Read Message History",
	discordgo.PermissionMentionEveryone:    "Mention Everyone",
	discordgo.PermissionAddReactions:       "Add Reactions",

	discordgo.PermissionKickMembers:     "Kick Members",
	discordgo.PermissionBanMembers:      "Ban Members",
	discordgo.PermissionModerateMembers: "Timeout Members",
	discordgo.PermissionAdministrator:   "Administrator",
	discordgo.PermissionManageNicknames: "Manage Nicknames",
	discordgo.PermissionManageRoles:     "Manage Roles",
	discordgo.PermissionManageChannels:  "Manage Channels",
	discordgo.PermissionManageGuild:     "Manage Server",
	discordgo.PermissionViewAuditLogs:   "View Audit Log",

	discordgo.PermissionCreateInstantInvite: "Create Invite",
}

PermissionNames maps Discord permission bit values to human readable names.

Functions

func BuildComboDisplay

func BuildComboDisplay(cmd *Command) string

BuildComboDisplay returns the usage for argument combos Exported for use in custom help functions

func BuildPositionalDisplay

func BuildPositionalDisplay(cmd *Command) string

BuildPositionalDisplay returns the usage for positional args Exported for use in custom help functions

Types

type Arg

type Arg struct {
	Name string
	Type ArgumentType
}

Arg defines the structure to pass argument data with

type ArgumentType

type ArgumentType interface {
	ParseArg(arg *ParsedArg, data *Data) (any, bool)
	Help() string
}

type Command

type Command struct {
	Command     string
	Category    CommandCategory
	Aliases     []string
	Description string

	Args           []*Arg
	ArgsRequired   int // Ignored if using combos
	ArgumentCombos [][]int

	RequiredUserPerms []int64
	RequiredBotPerms  []int64

	Run Run
}

Command defines the general data that must be set during the addition of a new command

type CommandCategory

type CommandCategory struct {
	Name        string
	Description string
}

CommandCategory defines the available category types for commands

type CommandHandler

type CommandHandler struct {
	Prefix func(string) string
	// contains filtered or unexported fields
}

CommandHandler contains the prefix, the full instances of a command and a string map to retireve them

func NewCommandHandler

func NewCommandHandler() *CommandHandler

NewCommandHandler creates a new command handler

func (*CommandHandler) HandleMessageCreate

func (c *CommandHandler) HandleMessageCreate(s *discordgo.Session, e *discordgo.MessageCreate)

Handles all message create events to the bot, to pass them to child functions

func (*CommandHandler) RegisterCommands

func (c *CommandHandler) RegisterCommands(cmds ...*Command)

RegisterCommands adds each command to the command handler

func (*CommandHandler) RegisteredCommands

func (c *CommandHandler) RegisteredCommands() map[string]RegisteredCommand

RegisteredCommands returns an array of each RegisteredCommand

func (*CommandHandler) SetPrefixFunc

func (c *CommandHandler) SetPrefixFunc(prefixFunc func(string) string)

SetPrefixFunc allows you to set a custom function to determine the prefix for a guild

type Data

type Data struct {
	Session *discordgo.Session
	Bot     *discordgo.User

	Guild   *discordgo.Guild
	Channel *discordgo.Channel
	Author  *discordgo.User

	Message    *discordgo.Message
	ParsedArgs []*ParsedArg

	Handler *CommandHandler
}

Data defines the required data passed to each command

type DurationArg

type DurationArg struct{}

func (*DurationArg) Help

func (d *DurationArg) Help() string

func (*DurationArg) ParseArg added in v1.2.0

func (d *DurationArg) ParseArg(arg *ParsedArg, data *Data) (any, bool)

type Int64Arg added in v1.1.0

type Int64Arg struct {
	Min int64
	Max int64
}

func (*Int64Arg) Help added in v1.1.0

func (i *Int64Arg) Help() string

func (*Int64Arg) ParseArg added in v1.2.0

func (i *Int64Arg) ParseArg(arg *ParsedArg, data *Data) (any, bool)

type IntArg

type IntArg struct {
	Min int
	Max int
}

func (*IntArg) Help

func (i *IntArg) Help() string

func (*IntArg) ParseArg added in v1.2.0

func (i *IntArg) ParseArg(arg *ParsedArg, data *Data) (any, bool)

type MemberArg

type MemberArg struct{}

func (*MemberArg) Help

func (m *MemberArg) Help() string

func (*MemberArg) ParseArg added in v1.2.0

func (m *MemberArg) ParseArg(arg *ParsedArg, data *Data) (any, bool)

type ParsedArg

type ParsedArg struct {
	Def   *Arg
	Raw   string
	Value any
}

ParsedArg represents a single argument parsed from a command invocation. Each argument contains the initial argument definition, the raw value passed to it, and the resolved value

type RegisteredCommand

type RegisteredCommand struct {
	Command *Command
}

RegisteredCommand defines the context required to access data surrounding a command

type Run

type Run func(data *Data) error

type StringArg

type StringArg struct {
	Options []string
}

func (*StringArg) Help

func (s *StringArg) Help() string

func (*StringArg) ParseArg added in v1.2.0

func (s *StringArg) ParseArg(arg *ParsedArg, data *Data) (any, bool)

type UserArg

type UserArg struct{}

func (*UserArg) Help

func (u *UserArg) Help() string

func (*UserArg) ParseArg added in v1.2.0

func (u *UserArg) ParseArg(arg *ParsedArg, data *Data) (any, bool)

Jump to

Keyboard shortcuts

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