dcmd

package
v2.3.4 Latest Latest
Warning

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

Go to latest
Published: Jun 25, 2022 License: MIT Imports: 16 Imported by: 0

README

dcmd

dcmd is a extensible discord command system based on interfaces.

It's very much work in progress at the moment, if you start using it now you have to be okay with things changing and the fact that you will find bugs.

Note Only works with my fork of discordgo, if you want to use this with bwmarrin/discordgo with go modules use v1.0.0 which is the last version of this that supported that lib: go get -u github.com/jonas747/dcmd@v1.0.0

Features:

For now look in the example folder. Still planning things out.

TODO:

  • Full test coverage (See below for info on progress)
  • Only build middleware chains once?
    • Added ability to prebuild middleware chains
    • Automatically do so
  • Standard Help generator

Test Coverage:

  • Argument parsers
    • int
    • float
    • string
    • user
    • Full line argdef parsing
    • Full line switch parsing
  • System
    • FindPrefix
    • HandleResponse
  • Container
    • Middleware chaining
    • Add/Remove middleware
    • Command searching
  • Other
    • Help
Time of day example

func main() {
      // Create a new command system
      system := dcmd.NewStandardSystem("[")

      // Add the time of day command to the root container of the system
      system.Root.AddCommand(&CmdTimeOfDay{Format: time.RFC822}, dcmd.NewTrigger("Time", "t"))

      // Create the discordgo session
      session, err := discordgo.New(os.Getenv("DG_TOKEN"))
      if err != nil {
            log.Fatal("Failed setting up session:", err)
      }

      // Add the command system handler to discordgo
      session.AddHandler(system.HandleMessageCreate)

      err = session.Open()
      if err != nil {
            log.Fatal("Failed opening gateway connection:", err)
      }
      log.Println("Running, Ctrl-c to stop.")
      select {}
}

type CmdTimeOfDay struct {
      Format string
}

// Descriptions should return a short description (used in the overall help overiview) and one long descriptions for targetted help
func (t *CmdTimeOfDay) Descriptions(d *dcmd.Data) (string, string) {
      return "Responds with the current time in utc", ""
}

// Run implements the dcmd.Cmd interface and gets called when the command is invoked
func (t *CmdTimeOfDay) Run(data *dcmd.Data) (interface{}, error) {
      return time.Now().UTC().Format(t.Format), nil
}


Documentation

Overview

Package dcmd provides a command system for use with discord bots

Index

Constants

This section is empty.

Variables

View Source
var (
	// Create some convenience instances
	Int             = &IntArg{}
	BigInt          = &IntArg{InteractionString: true}
	Float           = &FloatArg{}
	String          = &StringArg{}
	User            = &UserArg{}
	UserID          = &UserIDArg{}
	Channel         = &ChannelArg{}
	ChannelOrThread = &ChannelArg{AllowThreads: true}
	AdvUser         = &AdvUserArg{EnableUserID: true, EnableUsernameSearch: true, RequireMembership: true}
	AdvUserNoMember = &AdvUserArg{EnableUserID: true, EnableUsernameSearch: true}
)
View Source
var (
	ErrNoComboFound       = NewSimpleUserError("No matching combo found")
	ErrNotEnoughArguments = NewSimpleUserError("Not enough arguments passed")
)
View Source
var (
	ErrChannelNotFound               = errors.New("Channel not found")
	ErrGuildNotFound                 = errors.New("Guild not found")
	ErrMemberNotAvailable            = errors.New("Member not provided in message")
	ErrMemberNotAvailableInteraction = errors.New("Member not provided in interaction")
)
View Source
var CmdNameRegex = regexp.MustCompile(`^[\w-]{1,32}$`)

The regex provided for validation from the discord docs

View Source
var CustomUsernameSearchFunc func(state dstate.StateTracker, gs *dstate.GuildSet, query string) (*dstate.MemberState, error)

Functions

func FindCombo

func FindCombo(defs []*ArgDef, combos [][]int, args []*RawArg) (combo []int, ok bool)

Finds a proper argument combo from the provided args

func FindDiscordMemberByName

func FindDiscordMemberByName(state dstate.StateTracker, gs *dstate.GuildSet, str string) (*dstate.MemberState, error)

func GenerateHelp

func GenerateHelp(d *Data, container *Container, formatter HelpFormatter) (embeds []*discordgo.MessageEmbed)

GenerateFullHelp generates full help for a container

func GenerateTargettedHelp

func GenerateTargettedHelp(target string, d *Data, container *Container, formatter HelpFormatter) (embeds []*discordgo.MessageEmbed)

GenerateSingleHelp generates help for a single command

func Indent

func Indent(depth int) string

func IsUserError

func IsUserError(err error) bool

func NewErrArgExpected

func NewErrArgExpected(name string, expected string, got interface{}) error

func NewSimpleUserError

func NewSimpleUserError(args ...interface{}) error

func ParseArgDefs

func ParseArgDefs(defs []*ArgDef, required int, combos [][]int, data *Data, split []*RawArg) error

ParseArgDefs parses ordered argument definition for a CmdWithArgDefs

func ParseCmdArgs

func ParseCmdArgs(data *Data) error

func ParseCmdArgsFromInteraction

func ParseCmdArgsFromInteraction(data *Data) error

func ParseCmdArgsFromMessage

func ParseCmdArgsFromMessage(data *Data) error

ParseCmdArgsFromMessage parses arguments from a MESSAGE will panic if used on slash commands

func RuneByIndex

func RuneByIndex(s string, runePos int) (rune, int)

RuneByIndex Returns the string index from the rune position Panics if utf8.RuneCountInString(s) <= runeIndex or runePos < 0

func SendResponseInterface

func SendResponseInterface(data *Data, reply interface{}, escapeEveryoneMention bool) ([]*discordgo.Message, error)

func SortInteractionOptions

func SortInteractionOptions(data *Data) []*sortedInteractionArg

func SplitSendMessage

func SplitSendMessage(data *Data, contents string, allowedMentions discordgo.AllowedMentions) ([]*discordgo.Message, error)

SplitSendMessage uses SplitString to make sure each message is within 2k characters and splits at last newline before that (if possible)

func SplitString

func SplitString(s string, maxLen int) []string

SplitString uses StrSplitNext to split a string at the last newline before maxLen, throwing away leading and ending whitespaces in the process

func StrSplitNext

func StrSplitNext(s string, runeCount int) (split, rest string)

StrSplitNext Will split "s" before runecount at last possible newline, whitespace or just at "runecount" if there is no whitespace If the runecount in "s" is less than "runeCount" then "last" will be zero

func ValidateCommand

func ValidateCommand(cmd Cmd, trigger *Trigger) error

func ValidateCommandPanic

func ValidateCommandPanic(cmd Cmd, trigger *Trigger)

Types

type AdvUserArg

type AdvUserArg struct {
	EnableUserID         bool // Whether to check for user IDS
	EnableUsernameSearch bool // Whether to search for usernames
	RequireMembership    bool // Whether this requires a membership of the server, if set then Member will always be populated
}

AdvUserArg is a more advanced version of UserArg and UserIDArg, it will return a AdvUserMatch

func (*AdvUserArg) CheckCompatibility

func (u *AdvUserArg) CheckCompatibility(def *ArgDef, part string) CompatibilityResult

func (*AdvUserArg) HelpName

func (u *AdvUserArg) HelpName() string

func (*AdvUserArg) ParseFromInteraction

func (u *AdvUserArg) ParseFromInteraction(def *ArgDef, data *Data, options *SlashCommandsParseOptions) (val interface{}, err error)

func (*AdvUserArg) ParseFromMessage

func (u *AdvUserArg) ParseFromMessage(def *ArgDef, part string, data *Data) (interface{}, error)

func (*AdvUserArg) ParseMention

func (u *AdvUserArg) ParseMention(def *ArgDef, part string, data *Data) (user *discordgo.User)

func (*AdvUserArg) SearchID

func (u *AdvUserArg) SearchID(parsed int64, data *Data) (member *dstate.MemberState, user *discordgo.User)

func (*AdvUserArg) SlashCommandOptions

func (u *AdvUserArg) SlashCommandOptions(def *ArgDef) []*discordgo.ApplicationCommandOption

type AdvUserMatch

type AdvUserMatch struct {
	// Member may not be present if "RequireMembership" is false
	Member *dstate.MemberState

	// User is always present
	User *discordgo.User
}

func (*AdvUserMatch) UsernameOrNickname

func (a *AdvUserMatch) UsernameOrNickname() string

type ArgDef

type ArgDef struct {
	Name    string
	Type    ArgType
	Help    string
	Default interface{}
}

ArgDef represents a argument definition, either a switch or plain arg

func (*ArgDef) NewParsedDef

func (def *ArgDef) NewParsedDef() *ParsedArg

type ArgType

type ArgType interface {
	// CheckCompatibility reports the degree to which the input matches the type.
	CheckCompatibility(def *ArgDef, part string) CompatibilityResult

	// Attempt to parse it, returning any error if one occured.
	ParseFromMessage(def *ArgDef, part string, data *Data) (val interface{}, err error)
	ParseFromInteraction(def *ArgDef, data *Data, options *SlashCommandsParseOptions) (val interface{}, err error)

	// Name as shown in help
	HelpName() string

	SlashCommandOptions(def *ArgDef) []*discordgo.ApplicationCommandOption
}

ArgType is the interface argument types has to implement,

type Category

type Category struct {
	Name        string
	Description string
	HelpEmoji   string
	EmbedColor  int
}

Category represents a command category

type ChannelArg

type ChannelArg struct {
	AllowThreads bool
}

UserIDArg matches a mention or a plain id, the user does not have to be a part of the server The type of the ID is parsed into a int64

func (*ChannelArg) CheckCompatibility

func (ca *ChannelArg) CheckCompatibility(def *ArgDef, part string) CompatibilityResult

func (*ChannelArg) HelpName

func (ca *ChannelArg) HelpName() string

func (*ChannelArg) ParseFromInteraction

func (ca *ChannelArg) ParseFromInteraction(def *ArgDef, data *Data, options *SlashCommandsParseOptions) (val interface{}, err error)

func (*ChannelArg) ParseFromMessage

func (ca *ChannelArg) ParseFromMessage(def *ArgDef, part string, data *Data) (interface{}, error)

func (*ChannelArg) SlashCommandOptions

func (ca *ChannelArg) SlashCommandOptions(def *ArgDef) []*discordgo.ApplicationCommandOption

type ChannelNotFound

type ChannelNotFound struct {
	ID int64
}

func (*ChannelNotFound) Error

func (c *ChannelNotFound) Error() string

func (*ChannelNotFound) IsUserError

func (c *ChannelNotFound) IsUserError() bool

type Cmd

type Cmd interface {
	// Run the command with the provided data,
	// response is either string, error, embed or custom that implements CmdResponse
	// if response is nil, and an error is returned that is not PublicError,
	// the string "An error occured. Contact bot owner" becomes the response
	Run(data *Data) (interface{}, error)
}

Cmd is the interface all commands must implement to be considered a command

type CmdDescArgDefs

type CmdDescArgDefs interface {
	Cmd

	CmdWithDescriptions
	CmdWithArgDefs
}

CmdLongArgDefs is a helper for easily adding a compile time assertion Example: `var _ CmdLongArgDefs = (YourCommandType)(nil)` will fail to compile if you do not implement this interface correctly

type CmdWithArgDefs

type CmdWithArgDefs interface {

	/*
		Returns the argument definitions

		if 'combos' is non nil, then that takes priority over 'required'
		Combos wllows the command to take different combinations and orders of arguments

		Example: a `clean` command that deletes x amount of messages and can optionally filter by user
		could look like this
		“`
		argDefs = []*ArgDef{
		    {Name: "limit", Type: Int, Default: 10},
		    {Name: "user", Type: User, RequireMention: true},
		}
		requiredArgs = 0
		“`
		Here the clean command can take any number of arguments, if no arguments are passed, limit will be 10, and user will be nil.
		if only a limit is provided, then user will be nil and
		Here we have the 2 arguments, and atm we can only invoke this as `clean x` or `clean x @user`
		using combos we can also allow any order here, this is possible because the user arg cannot be a number.
		so if we use the combos:
		“`
		combos = [][]int{
			[]int{0},   // Allow only a limit to be passed
			[]int{0,1}, // Allow both a limit and user to be passed, in that order
			[]int{1},   // Allow only a user to be passed, then limit will have its default value of 10
			[]int{1,0}, // Allow both a user and limit to be passed, in that order
			[]int{},    // Allow no arguments to be passe, limit will then have its default of 10, and user will be nil
		}
		“`
		As you can see, the integers are indexes in the argument definitions returned. The above combos will allow any combination or arguments, even none.

		Argument combos generally work as expected in non-ambiguous cases, but it cannot for example detect the difference between two text arguments.
	*/
	ArgDefs(data *Data) (args []*ArgDef, required int, combos [][]int)
}

CmdWithArgDefs commands will have their arguments parsed following the argdefs, required and combos rules returned if parsing fails, or the conditions are not met, the command will not be invoked, and instead return the reason it failed, and the short help for the command

type CmdWithCanUse

type CmdWithCanUse interface {
	CanUse(data *Data) (bool, error)
}

CmdWithCanUse commands can use this to hide certain commands from people for example

type CmdWithCategory

type CmdWithCategory interface {
	Category() *Category
}

CmdWithCategory puts the command in a category, mostly used for the help generation

type CmdWithCustomParser

type CmdWithCustomParser interface {

	// Parse the arguments and return the command data
	// sripped is the rest of the message with the command name removed
	Parse(stripped string, data *Data) (*Data, error)
}

CmdWithCustomParser is for commands that want to implement their own custom argument parser

type CmdWithDescriptions

type CmdWithDescriptions interface {
	Descriptions(data *Data) (short, long string)
}

CmdWithDescriptions commands will have the descriptions used in the standard help generator short is used for non-targetted help while long is used for targetted help

type CmdWithSwitches

type CmdWithSwitches interface {
	Switches() []*ArgDef
}

CmdWithSwitches commands can define a set of switches, which might be a cleaner alternative to combos This will also set the context value of ContextStrippedSwitches to the arg string without the switches, for further parsing of the remaining switch-less args

if you don't know what a switch is, clean command example using both a switch and single argdef could look like the following: `clean 10` - clean 10 last messages without a user specified `clean -u @user 10` - specify a user, but still clean 10 messages

This is a lot cleaner than using argument combos

type CompatibilityResult

type CompatibilityResult int

CompatibilityResult indicates the degree to which a value matches a type.

const (
	// Incompatible indicates that the value does not match the type at all. For example,
	// the string "abc" would be incompatible with an integer type.
	Incompatible CompatibilityResult = iota

	// CompatibilityPoor indicates that the value superficially matches the
	// type, but violates some required constraint or property. For example, 11
	// would have poor compatibility with an integer type with range limited to
	// [0, 10].
	CompatibilityPoor

	// CompatibilityGood indicates that the value matches the type well. For
	// example, 204255221017214977 would have good compatibility with a user
	// type as it has the correct length for a Discord snowflake though the ID
	// may not correspond to a valid user. Similarly, 10 would have good compatibility
	// with an unbounded integer type.
	CompatibilityGood
)

func DetermineSnowflakeCompatibility

func DetermineSnowflakeCompatibility(s string) CompatibilityResult

DetermineSnowflakeCompatibility returns CompatibilityGood if s could represent a Discord snowflake ID and Incompatible otherwise.

func (CompatibilityResult) String

func (c CompatibilityResult) String() string

type Container

type Container struct {
	// Default mention handler, used when the bot is mentioned without any command specified
	DefaultMention RunFunc

	// Default not found handler, called when no command is found from input
	NotFound RunFunc

	// Default DM not found handler, same as NotFound but for Direct Messages, if none specified
	// will use notfound if set.
	DMNotFound RunFunc

	// Set to ignore bots
	IgnoreBots bool
	// Dumps the stack in a response message when a panic happens in a command
	SendStackOnPanic bool
	// Set to send error messages that a command returned as a response message
	SendError bool
	// Set to also run this muxer in dm's
	RunInDM bool

	// The muxer names
	Names []string
	// The muxer description
	Description string
	// The muxer long description
	LongDescription string

	// Commands this muxer will check
	Commands []*RegisteredCommand

	HelpTitleEmoji string
	HelpColor      int
	HelpOwnEmbed   bool
	Category       string

	Parent *Container
	// contains filtered or unexported fields
}

Container is the standard muxer Containers can be nested by calling Container.Sub(...)

func (*Container) AbsFindCommand

func (c *Container) AbsFindCommand(searchStr string) (cmd *RegisteredCommand, container *Container)

func (*Container) AbsFindCommandWithRest

func (c *Container) AbsFindCommandWithRest(searchStr string) (cmd *RegisteredCommand, container *Container, rest string)

func (*Container) AddCommand

func (c *Container) AddCommand(cmd Cmd, trigger *Trigger) *RegisteredCommand

func (*Container) AddMidlewares

func (c *Container) AddMidlewares(mw ...MiddleWareFunc)

func (*Container) BuildMiddlewareChain

func (c *Container) BuildMiddlewareChain(r RunFunc, cmd *RegisteredCommand) RunFunc

func (*Container) BuildMiddlewareChains

func (c *Container) BuildMiddlewareChains(containerChain []*Container)

BuildMiddlewareChains builds all the middleware chains and chaches them. It is reccomended to call this after adding all commands and middleware to avoid building the chains everytime a command is invoked

func (*Container) Descriptions

func (c *Container) Descriptions(data *Data) (string, string)

func (*Container) FindCommand

func (c *Container) FindCommand(searchStr string) (cmd *RegisteredCommand, rest string)

func (*Container) FullName

func (c *Container) FullName(aliases bool) string

func (*Container) Run

func (c *Container) Run(data *Data) (interface{}, error)

func (*Container) Sub

func (c *Container) Sub(mainName string, aliases ...string) (*Container, *Trigger)

Sub returns a copy of the container but with the following attributes overwritten and no commands registered

type Data

type Data struct {
	Cmd      *RegisteredCommand
	Args     []*ParsedArg
	Switches map[string]*ParsedArg

	// These fields are always available
	ChannelID int64
	Author    *discordgo.User

	// Only set if this command was not ran through slash commands
	TraditionalTriggerData *TraditionalTriggerData

	// Only set if this command was ran through discord slash commands
	SlashCommandTriggerData *SlashCommandTriggerData

	// Only provided if the command was ran in a DM Context
	GuildData *GuildContextData

	// The session that triggered the command
	Session *discordgo.Session

	Source      TriggerSource
	TriggerType TriggerType

	// The chain of containers we went through, first element is always root
	ContainerChain []*Container

	// The system that triggered this command
	System *System
	// contains filtered or unexported fields
}

Data is a struct of data available to commands

func (*Data) Context

func (d *Data) Context() context.Context

Context returns an always non-nil context

func (*Data) SendFollowupMessage

func (d *Data) SendFollowupMessage(reply interface{}, allowedMentions discordgo.AllowedMentions) ([]*discordgo.Message, error)

func (*Data) Switch

func (d *Data) Switch(name string) *ParsedArg

func (*Data) WithContext

func (d *Data) WithContext(ctx context.Context) *Data

WithContext creates a copy of d with the context set to ctx

type ErrArgExpectedType

type ErrArgExpectedType struct {
	Name     string
	Expected string
	Got      string
}

func (*ErrArgExpectedType) Error

func (e *ErrArgExpectedType) Error() string

func (*ErrArgExpectedType) IsUserError

func (e *ErrArgExpectedType) IsUserError() bool

type ErrResolvedNotFound

type ErrResolvedNotFound struct {
	Key  string
	ID   int64
	Type string
}

func (*ErrResolvedNotFound) Error

func (r *ErrResolvedNotFound) Error() string

func (*ErrResolvedNotFound) IsUserError

func (r *ErrResolvedNotFound) IsUserError() bool

type FloatArg

type FloatArg struct {
	Min, Max float64
}

FloatArg matches and parses float arguments If min and max are not equal then the value has to be within min and max or else it will fail parsing

func (*FloatArg) CheckCompatibility

func (f *FloatArg) CheckCompatibility(def *ArgDef, part string) CompatibilityResult

func (*FloatArg) HelpName

func (f *FloatArg) HelpName() string

func (*FloatArg) ParseFromInteraction

func (f *FloatArg) ParseFromInteraction(def *ArgDef, data *Data, options *SlashCommandsParseOptions) (val interface{}, err error)

func (*FloatArg) ParseFromMessage

func (f *FloatArg) ParseFromMessage(def *ArgDef, part string, data *Data) (interface{}, error)

func (*FloatArg) SlashCommandOptions

func (f *FloatArg) SlashCommandOptions(def *ArgDef) []*discordgo.ApplicationCommandOption

type GuildContextData

type GuildContextData struct {
	CS *dstate.ChannelState
	GS *dstate.GuildSet
	MS *dstate.MemberState
}

type HelpFormatter

type HelpFormatter interface {
	// Called when there is help generated for 2 or more commands
	ShortCmdHelp(cmd *RegisteredCommand, container *Container, data *Data) string

	// Called when help is only generated for 1 command
	// You are supposed to dump all command detilas such as arguments
	// the long description if it has one, switches and whatever else you have in mind.
	FullCmdHelp(cmd *RegisteredCommand, container *Container, data *Data) *discordgo.MessageEmbed
}

HelpFormatter is a interface for help formatters, for an example see StdHelpFormatter

type ImproperMention

type ImproperMention struct {
	Part string
}

func (*ImproperMention) Error

func (i *ImproperMention) Error() string

func (*ImproperMention) IsUserError

func (i *ImproperMention) IsUserError() bool

type IntArg

type IntArg struct {
	Min, Max int64

	// if we wanna support large numbers like snowflakes we have to use strings with interactions
	InteractionString bool
}

IntArg matches and parses integer arguments If min and max are not equal then the value has to be within min and max or else it will fail parsing

func (*IntArg) CheckCompatibility

func (i *IntArg) CheckCompatibility(def *ArgDef, part string) CompatibilityResult

func (*IntArg) HelpName

func (i *IntArg) HelpName() string

func (*IntArg) ParseFromInteraction

func (i *IntArg) ParseFromInteraction(def *ArgDef, data *Data, options *SlashCommandsParseOptions) (val interface{}, err error)

func (*IntArg) ParseFromMessage

func (i *IntArg) ParseFromMessage(def *ArgDef, part string, data *Data) (interface{}, error)

func (*IntArg) SlashCommandOptions

func (i *IntArg) SlashCommandOptions(def *ArgDef) []*discordgo.ApplicationCommandOption

type InvalidFloat

type InvalidFloat struct {
	Part string
}

func (*InvalidFloat) Error

func (i *InvalidFloat) Error() string

func (*InvalidFloat) IsUserError

func (i *InvalidFloat) IsUserError() bool

type InvalidInt

type InvalidInt struct {
	Part string
}

func (*InvalidInt) Error

func (i *InvalidInt) Error() string

func (*InvalidInt) IsUserError

func (i *InvalidInt) IsUserError() bool

type MiddleWareFunc

type MiddleWareFunc func(next RunFunc) RunFunc

type NoMention

type NoMention struct {
	Part string
}

func (*NoMention) Error

func (i *NoMention) Error() string

func (*NoMention) IsUserError

func (i *NoMention) IsUserError() bool

type OutOfRangeError

type OutOfRangeError struct {
	Min, Max interface{}
	Got      interface{}
	Float    bool
	ArgName  string
}

func (*OutOfRangeError) Error

func (o *OutOfRangeError) Error() string

func (*OutOfRangeError) IsUserError

func (o *OutOfRangeError) IsUserError() bool

type ParsedArg

type ParsedArg struct {
	Def   *ArgDef
	Value interface{}
	Raw   *RawArg
}

func NewParsedArgs

func NewParsedArgs(defs []*ArgDef) []*ParsedArg

NewParsedArgs creates a new ParsedArg slice from defs passed, also filling default values

func (*ParsedArg) AdvUser

func (p *ParsedArg) AdvUser() *AdvUserMatch

func (*ParsedArg) Bool

func (p *ParsedArg) Bool() bool

func (*ParsedArg) Int

func (p *ParsedArg) Int() int

TODO: GO-Generate the number ones

func (*ParsedArg) Int64

func (p *ParsedArg) Int64() int64

func (*ParsedArg) MemberState

func (p *ParsedArg) MemberState() *dstate.MemberState

func (*ParsedArg) Str

func (p *ParsedArg) Str() string

func (*ParsedArg) User

func (p *ParsedArg) User() *discordgo.User

type PrefixProvider

type PrefixProvider interface {
	Prefix(data *Data) string
}

Retrieves the prefix that might be different on a per server basis

func NewSimplePrefixProvider

func NewSimplePrefixProvider(prefix string) PrefixProvider

type RawArg

type RawArg struct {
	Str       string
	Container rune
}

func ParseSwitches

func ParseSwitches(switches []*ArgDef, data *Data, split []*RawArg) ([]*RawArg, error)

ParseSwitches parses all switches for a CmdWithSwitches, and also takes them out of the raw args

func SplitArgs

func SplitArgs(in string) []*RawArg

SplitArgs splits the string into fields

type RegisteredCommand

type RegisteredCommand struct {
	Command Cmd
	Trigger *Trigger
	// contains filtered or unexported fields
}

RegisteredCommand represents a registered command to the system. RegisteredCommand.Cmd may exist in other RegisteredCommands but the RegisteredCommand wrapper itself is unique per route

func (*RegisteredCommand) FormatNames

func (r *RegisteredCommand) FormatNames(includeAliases bool, seperator string) string

FormatNames returns a string with names and if includedAliases is true, aliases seperated by seperator Falls back to reflection if no names are available

type Response

type Response interface {
	// Channel, session, command etc can all be found in this context
	Send(data *Data) ([]*discordgo.Message, error)
}

type ResponseSender

type ResponseSender interface {
	SendResponse(cmdData *Data, resp interface{}, err error) error
}

type RunFunc

type RunFunc func(data *Data) (interface{}, error)

func ArgParserMW

func ArgParserMW(inner RunFunc) RunFunc

type SimpleCmd

type SimpleCmd struct {
	ShortDesc, LongDesc string

	CmdArgDefs      []*ArgDef
	RequiredArgDefs int
	ArgDefCombos    [][]int

	CmdSwitches []*ArgDef

	RunFunc func(data *Data) (interface{}, error)
}

func (*SimpleCmd) ArgDefs

func (s *SimpleCmd) ArgDefs(data *Data) (args []*ArgDef, required int, combos [][]int)

func (*SimpleCmd) Descriptions

func (s *SimpleCmd) Descriptions(data *Data) (short, long string)

func (*SimpleCmd) Run

func (s *SimpleCmd) Run(data *Data) (interface{}, error)

func (*SimpleCmd) Switches

func (s *SimpleCmd) Switches() []*ArgDef

type SimplePrefixProvider

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

Simple Prefix provider for global fixed prefixes

func (*SimplePrefixProvider) Prefix

func (pp *SimplePrefixProvider) Prefix(d *Data) string

type SlashCommandTriggerData

type SlashCommandTriggerData struct {
	Interaction *discordgo.Interaction
	// The options slice for the command options themselves
	// This is a helper so you don't have to dig it out yourself in the case of nested subcommands
	Options []*discordgo.ApplicationCommandInteractionDataOption
}

type SlashCommandsParseOptions

type SlashCommandsParseOptions struct {
	Options     map[string]*discordgo.ApplicationCommandInteractionDataOption
	Interaction *discordgo.Interaction
}

func (*SlashCommandsParseOptions) ExpectAny

func (sopts *SlashCommandsParseOptions) ExpectAny(name string) (interface{}, error)

func (*SlashCommandsParseOptions) ExpectAnyOpt

func (sopts *SlashCommandsParseOptions) ExpectAnyOpt(name string) (interface{}, bool)

func (*SlashCommandsParseOptions) ExpectBool

func (sopts *SlashCommandsParseOptions) ExpectBool(name string) (bool, error)

func (*SlashCommandsParseOptions) ExpectBoolOpt

func (sopts *SlashCommandsParseOptions) ExpectBoolOpt(name string) (bool, bool, error)

func (*SlashCommandsParseOptions) ExpectChannel

func (sopts *SlashCommandsParseOptions) ExpectChannel(name string) (*discordgo.Channel, error)

func (*SlashCommandsParseOptions) ExpectChannelOpt

func (sopts *SlashCommandsParseOptions) ExpectChannelOpt(name string) (*discordgo.Channel, bool, error)

func (*SlashCommandsParseOptions) ExpectInt64

func (sopts *SlashCommandsParseOptions) ExpectInt64(name string) (int64, error)

func (*SlashCommandsParseOptions) ExpectInt64Opt

func (sopts *SlashCommandsParseOptions) ExpectInt64Opt(name string) (int64, bool, error)

func (*SlashCommandsParseOptions) ExpectMember

func (sopts *SlashCommandsParseOptions) ExpectMember(name string) (*discordgo.Member, error)

func (*SlashCommandsParseOptions) ExpectMemberOpt

func (sopts *SlashCommandsParseOptions) ExpectMemberOpt(name string) (*discordgo.Member, bool, error)

func (*SlashCommandsParseOptions) ExpectRole

func (sopts *SlashCommandsParseOptions) ExpectRole(name string) (*discordgo.Role, error)

func (*SlashCommandsParseOptions) ExpectRoleOpt

func (sopts *SlashCommandsParseOptions) ExpectRoleOpt(name string) (*discordgo.Role, bool, error)

func (*SlashCommandsParseOptions) ExpectString

func (sopts *SlashCommandsParseOptions) ExpectString(name string) (string, error)

func (*SlashCommandsParseOptions) ExpectStringOpt

func (sopts *SlashCommandsParseOptions) ExpectStringOpt(name string) (string, bool, error)

func (*SlashCommandsParseOptions) ExpectUser

func (sopts *SlashCommandsParseOptions) ExpectUser(name string) (*discordgo.User, error)

func (*SlashCommandsParseOptions) ExpectUserOpt

func (sopts *SlashCommandsParseOptions) ExpectUserOpt(name string) (*discordgo.User, bool, error)

type SortedCommandEntry

type SortedCommandEntry struct {
	Cmd       *RegisteredCommand
	Container *Container
}

SortedCommandEntry represents an entry in the SortdCommandSet

type SortedCommandSet

type SortedCommandSet struct {
	Commands []*SortedCommandEntry

	// Set if this is a set of commands grouped by categories
	Category *Category

	// Set if this is a container helpContainer
	Container *Container
}

SortedCommandSet groups a set of commands by either container or category

func FindSortedCommands

func FindSortedCommands(sets []*SortedCommandSet, cat *Category, container *Container) *SortedCommandSet

FindSortedCommands finds a command set by category or container

func SortCommands

func SortCommands(closestGroupContainer *Container, cmdContainer *Container) []*SortedCommandSet

SortCommands groups commands into sorted command sets

func (*SortedCommandSet) Color

func (s *SortedCommandSet) Color() int

func (*SortedCommandSet) Emoji

func (s *SortedCommandSet) Emoji() string

func (*SortedCommandSet) Name

func (s *SortedCommandSet) Name() string

type StdHelpCommand

type StdHelpCommand struct {
	SendFullInDM      bool
	SendTargettedInDM bool

	Formatter HelpFormatter
}

func NewStdHelpCommand

func NewStdHelpCommand() *StdHelpCommand

func (*StdHelpCommand) ArgDefs

func (h *StdHelpCommand) ArgDefs(data *Data) (args []*ArgDef, required int, combos [][]int)

func (*StdHelpCommand) Descriptions

func (h *StdHelpCommand) Descriptions(data *Data) (string, string)

func (*StdHelpCommand) Run

func (h *StdHelpCommand) Run(d *Data) (interface{}, error)

type StdHelpFormatter

type StdHelpFormatter struct{}

func (*StdHelpFormatter) ArgDef

func (s *StdHelpFormatter) ArgDef(arg *ArgDef) (str string)

func (*StdHelpFormatter) ArgDefLine

func (s *StdHelpFormatter) ArgDefLine(argDefs []*ArgDef, required int) (str string)

func (*StdHelpFormatter) ArgDefs

func (s *StdHelpFormatter) ArgDefs(cmd *RegisteredCommand, data *Data) (str string)

func (*StdHelpFormatter) CmdNameString

func (s *StdHelpFormatter) CmdNameString(cmd *RegisteredCommand, container *Container, containerAliases bool) string

func (*StdHelpFormatter) FullCmdHelp

func (s *StdHelpFormatter) FullCmdHelp(cmd *RegisteredCommand, container *Container, data *Data) *discordgo.MessageEmbed

func (*StdHelpFormatter) ShortCmdHelp

func (s *StdHelpFormatter) ShortCmdHelp(cmd *RegisteredCommand, container *Container, data *Data) string

func (*StdHelpFormatter) Switches

func (s *StdHelpFormatter) Switches(cmd Cmd) (str string)

type StdResponseSender

type StdResponseSender struct {
	LogErrors bool
}

func (*StdResponseSender) SendResponse

func (s *StdResponseSender) SendResponse(cmdData *Data, resp interface{}, err error) error

type StringArg

type StringArg struct{}

StringArg matches and parses float arguments

func (*StringArg) CheckCompatibility

func (s *StringArg) CheckCompatibility(def *ArgDef, part string) CompatibilityResult

func (*StringArg) HelpName

func (s *StringArg) HelpName() string

func (*StringArg) ParseFromInteraction

func (s *StringArg) ParseFromInteraction(def *ArgDef, data *Data, options *SlashCommandsParseOptions) (val interface{}, err error)

func (*StringArg) ParseFromMessage

func (s *StringArg) ParseFromMessage(def *ArgDef, part string, data *Data) (interface{}, error)

func (*StringArg) SlashCommandOptions

func (s *StringArg) SlashCommandOptions(def *ArgDef) []*discordgo.ApplicationCommandOption

type System

type System struct {
	Root           *Container
	Prefix         PrefixProvider
	ResponseSender ResponseSender
	State          dstate.StateTracker
}

func NewStandardSystem

func NewStandardSystem(staticPrefix string) (system *System)

func (*System) CheckInteraction

func (sys *System) CheckInteraction(s *discordgo.Session, interaction *discordgo.Interaction) error

CheckInteraction checks a interaction and runs a command if found

func (*System) CheckMessage

func (sys *System) CheckMessage(s *discordgo.Session, m *discordgo.MessageCreate) error

CheckMessage checks the message for commands, and triggers any command that the message should trigger you should not add this as an discord handler directly, if you want to do that you should add "system.HandleMessageCreate" instead.

func (*System) CheckMessageWtihPrefetchedPrefix

func (sys *System) CheckMessageWtihPrefetchedPrefix(s *discordgo.Session, m *discordgo.MessageCreate, prefetchedPrefix string) error

CheckMessageWtihPrefetchedPrefix is the same as CheckMessage but you pass in a prefetched command prefix

func (*System) FillDataInteraction

func (sys *System) FillDataInteraction(s *discordgo.Session, interaction *discordgo.Interaction) (*Data, error)

func (*System) FillDataLegacyMessage

func (sys *System) FillDataLegacyMessage(s *discordgo.Session, m *discordgo.Message) (*Data, error)

func (*System) FindMentionPrefix

func (sys *System) FindMentionPrefix(data *Data) (found bool)

func (*System) FindPrefix

func (sys *System) FindPrefix(data *Data) (found bool)

FindPrefix checks if the message has a proper command prefix (either from the PrefixProvider or a direction mention to the bot) It sets the source field, and MsgStripped in data if found

func (*System) FindPrefixWithPrefetched

func (sys *System) FindPrefixWithPrefetched(data *Data, commandPrefix string) (found bool)

FindPrefixWithPrefetched is the same as FindPrefix but you pass in a prefetched command prefix

func (*System) HandleMessageCreate

func (sys *System) HandleMessageCreate(s *discordgo.Session, m *discordgo.MessageCreate)

You can add this as a handler directly to discordgo, it will recover from any panics that occured in commands and log errors using the standard logger

type TemporaryResponse

type TemporaryResponse struct {
	Response       interface{}
	Duration       time.Duration
	EscapeEveryone bool
}

Temporary response deletes the inner response after Duration

func NewTemporaryResponse

func NewTemporaryResponse(d time.Duration, inner interface{}, escapeEveryoneMention bool) *TemporaryResponse

func (*TemporaryResponse) Send

func (t *TemporaryResponse) Send(data *Data) ([]*discordgo.Message, error)

type TraditionalTriggerData

type TraditionalTriggerData struct {
	Message               *discordgo.Message
	MessageStrippedPrefix string
	PrefixUsed            string
}

type Trigger

type Trigger struct {
	Names       []string
	Middlewares []MiddleWareFunc

	HideFromHelp bool

	EnableInDM            bool
	EnableInGuildChannels bool
	EnableInThreads       bool
}

func NewTrigger

func NewTrigger(name string, aliases ...string) *Trigger

func (*Trigger) SetEnableInDM

func (t *Trigger) SetEnableInDM(enable bool) *Trigger

func (*Trigger) SetEnableInGuildChannels

func (t *Trigger) SetEnableInGuildChannels(enable bool) *Trigger

func (*Trigger) SetEnabledInThreads

func (t *Trigger) SetEnabledInThreads(enable bool) *Trigger

func (*Trigger) SetHideFromHelp

func (t *Trigger) SetHideFromHelp(hide bool) *Trigger

func (*Trigger) SetMiddlewares

func (t *Trigger) SetMiddlewares(mw ...MiddleWareFunc) *Trigger

type TriggerSource

type TriggerSource int

Where this command comes from

const (
	TriggerSourceGuild TriggerSource = iota
	TriggerSourceDM
)

type TriggerType

type TriggerType int
const (
	// triggered directly somehow, no prefix
	TriggerTypeDirect TriggerType = iota

	// triggered through a mention trigger
	TriggerTypeMention

	// triggered thourgh a prefix trigger
	TriggerTypePrefix

	// triggered through slash commands
	TriggerTypeSlashCommands
)

type UserArg

type UserArg struct{}

UserArg matches and parses user argument (mention/ID)

func (*UserArg) CheckCompatibility

func (u *UserArg) CheckCompatibility(def *ArgDef, part string) CompatibilityResult

func (*UserArg) HelpName

func (u *UserArg) HelpName() string

func (*UserArg) ParseFromInteraction

func (u *UserArg) ParseFromInteraction(def *ArgDef, data *Data, options *SlashCommandsParseOptions) (val interface{}, err error)

func (*UserArg) ParseFromMessage

func (u *UserArg) ParseFromMessage(def *ArgDef, part string, data *Data) (interface{}, error)

func (*UserArg) SlashCommandOptions

func (u *UserArg) SlashCommandOptions(def *ArgDef) []*discordgo.ApplicationCommandOption

type UserError

type UserError interface {
	IsUserError() bool
}

type UserIDArg

type UserIDArg struct{}

UserIDArg matches a mention or a plain id, the user does not have to be a part of the server The type of the ID is parsed into a int64

func (*UserIDArg) CheckCompatibility

func (u *UserIDArg) CheckCompatibility(def *ArgDef, part string) CompatibilityResult

func (*UserIDArg) HelpName

func (u *UserIDArg) HelpName() string

func (*UserIDArg) ParseFromInteraction

func (u *UserIDArg) ParseFromInteraction(def *ArgDef, data *Data, options *SlashCommandsParseOptions) (val interface{}, err error)

func (*UserIDArg) ParseFromMessage

func (u *UserIDArg) ParseFromMessage(def *ArgDef, part string, data *Data) (interface{}, error)

func (*UserIDArg) SlashCommandOptions

func (u *UserIDArg) SlashCommandOptions(def *ArgDef) []*discordgo.ApplicationCommandOption

type UserNotFound

type UserNotFound struct {
	Part string
}

func (*UserNotFound) Error

func (i *UserNotFound) Error() string

func (*UserNotFound) IsUserError

func (i *UserNotFound) IsUserError() bool

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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