bot

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Apr 27, 2020 License: ISC Imports: 14 Imported by: 6

README

What are the performance impacts of this library?

Not a lot for a Discord bot:

# Cold functions, or functions that are called once in runtime:
BenchmarkConstructor-8               	  150537	      7617 ns/op
BenchmarkSubcommandConstructor-8     	  155068	      7721 ns/op

# Hot functions, or functions that can be called multiple times:
BenchmarkCall-8                      	 1000000	      1194 ns/op
BenchmarkHelp-8                      	 1751619	       680 ns/op

# Hot functions, but called implicitly on non-message-create events:
BenchmarkReflectChannelID_1Level-8   	10111023	       113 ns/op
BenchmarkReflectChannelID_5Level-8   	 1872080	       686 ns/op

Documentation

Index

Constants

This section is empty.

Variables

View Source
var FlagSeparator = 'ー'
View Source
var HelpUnderline = true

HelpUnderline formats command arguments with an underline, similar to manpages.

View Source
var InvalidUsageString = func(err *ErrInvalidUsage) string {
	if err.Index == 0 {
		return "Invalid usage, error: " + err.Err
	}

	if len(err.Args) == 0 {
		return "Missing arguments. Refer to help."
	}

	body := "Invalid usage at " +

		strings.Join(err.Args[:err.Index], " ") +

		" __" + err.Args[err.Index] + "__ " +

		strings.Join(err.Args[err.Index+1:], " ")

	if err.Err != "" {
		body += "\nError: " + err.Err
	}

	return body
}
View Source
var ParseArgs = func(args string) ([]string, error) {
	return shellwords.Parse(args)
}
View Source
var ShellwordsEscaper = strings.NewReplacer(
	"\\", "\\\\",
)
View Source
var UnknownCommandString = func(err *ErrUnknownCommand) string {
	var header = "Unknown command: "
	if err.Parent != "" {
		header += err.Parent + " " + err.Command
	} else {
		header += err.Command
	}

	return header
}

Functions

func Start

func Start(token string, cmd interface{},
	opts func(*Context) error) (wait func() error, err error)

Start quickly starts a bot with the given command. It will prepend "Bot" into the token automatically. Refer to example/ for usage.

func Wait

func Wait()

Wait blocks until SIGINT.

Types

type Argument added in v0.0.10

type Argument struct {
	String string
	// Rule: pointer for structs, direct for primitives
	Type reflect.Type
	// contains filtered or unexported fields
}

Argument is each argument in a method.

type CanSetup added in v0.0.10

type CanSetup interface {
	// Setup should panic when it has an error.
	Setup(*Subcommand)
}

CanSetup is used for subcommands to change variables, such as Description. This method will be triggered when InitCommands is called, which is during New for Context and during RegisterSubcommand for subcommands.

type CommandContext

type CommandContext struct {
	Description string
	Flag        NameFlag

	MethodName string
	Command    string // empty if Plumb

	// Hidden is true if the method has a hidden nameflag.
	Hidden bool

	Arguments []Argument
	// contains filtered or unexported fields
}

CommandContext is an internal struct containing fields to make this library work. As such, they're all unexported. Description, however, is exported for editing, and may be used to generate more informative help messages.

func (*CommandContext) Usage

func (cctx *CommandContext) Usage() []string

type Content added in v0.0.12

type Content string

CustomArguments implements the CustomParser interface, which sets the string exactly.

func (*Content) CustomParse added in v0.0.12

func (c *Content) CustomParse(content string) error

type Context

type Context struct {
	*Subcommand
	*state.State

	// Descriptive (but optional) bot name
	Name string

	// Descriptive help body
	Description string

	// Called to check a message's prefix. The default prefix is "!". Refer to
	// NewPrefix().
	HasPrefix Prefixer

	// AllowBot makes the router also process MessageCreate events from bots.
	// This is false by default and only applies to MessageCreate.
	AllowBot bool

	// FormatError formats any errors returned by anything, including the method
	// commands or the reflect functions. This also includes invalid usage
	// errors or unknown command errors. Returning an empty string means
	// ignoring the error.
	//
	// By default, this field replaces all @ with @\u200b, which prevents an
	// @everyone mention.
	FormatError func(error) string

	// ErrorLogger logs any error that anything makes and the library can't
	// reply to the client. This includes any event callback errors that aren't
	// Message Create.
	ErrorLogger func(error)

	// ReplyError when true replies to the user the error. This only applies to
	// MessageCreate events.
	ReplyError bool
	// contains filtered or unexported fields
}

Context is the bot state for commands and subcommands.

Commands

A command can be created by making it a method of Commands, or whatever struct was given to the constructor. This following example creates a command with a single integer argument (which can be ran with "~example 123"):

func (c *Commands) Example(
    m *gateway.MessageCreateEvent, i int) (string, error) {

    return fmt.Sprintf("You sent: %d", i)
}

Commands' exported methods will all be used as commands. Messages are parsed with its first argument (the command) mapped accordingly to c.MapName, which capitalizes the first letter automatically to reflect the exported method name.

A command can either return either an error, or data and error. The only data types allowed are string, *discord.Embed, and *api.SendMessageData. Any other return types will invalidate the method.

Events

An event can only have one argument, which is the pointer to the event struct. It can also only return error.

func (c *Commands) Example(o *gateway.TypingStartEvent) error {
    log.Println("Someone's typing!")
    return nil
}

func New

func New(s *state.State, cmd interface{}) (*Context, error)

New makes a new context with a "~" as the prefix. cmds must be a pointer to a struct with a *Context field. Example:

type Commands struct {
    Ctx *Context
}

cmds := &Commands{}
c, err := rfrouter.New(session, cmds)

The default prefix is "~", which means commands must start with "~" followed by the command name in the first argument, else it will be ignored.

c.Start() should be called afterwards to actually handle incoming events.

func (*Context) Call

func (ctx *Context) Call(event interface{}) error

Call should only be used if you know what you're doing.

func (*Context) FindCommand added in v0.0.10

func (ctx *Context) FindCommand(structname, methodname string) *CommandContext

FindCommand finds a command based on the struct and method name. The queried names will have their flags stripped.

Example

// Find a command from the main context:
cmd := ctx.FindCommand("", "Method")
// Find a command from a subcommand:
cmd  = ctx.FindCommand("Starboard", "Reset")

func (*Context) Help

func (ctx *Context) Help() string

Help generates one. This function is used more for reference than an actual help message. As such, it only uses exported fields or methods.

func (*Context) HelpAdmin added in v0.0.11

func (ctx *Context) HelpAdmin() string

func (*Context) MustRegisterSubcommand added in v0.0.9

func (ctx *Context) MustRegisterSubcommand(cmd interface{}) *Subcommand

MustRegisterSubcommand tries to register a subcommand, and will panic if it fails. This is recommended, as subcommands won't change after initializing once in runtime, thus fairly harmless after development.

func (*Context) RegisterSubcommand

func (ctx *Context) RegisterSubcommand(cmd interface{}) (*Subcommand, error)

RegisterSubcommand registers and adds cmd to the list of subcommands. It will also return the resulting Subcommand.

func (*Context) Start

func (ctx *Context) Start() func()

Start adds itself into the discordgo Session handlers. This needs to be run. The returned function is a delete function, which removes itself from the Session handlers.

func (*Context) Subcommands

func (ctx *Context) Subcommands() []*Subcommand

Subcommands returns the slice of subcommands. To add subcommands, use RegisterSubcommand().

type CustomParser added in v0.0.12

type CustomParser interface {
	CustomParse(content string) error
}

CustomParser has a CustomParse method, which would be passed in the full message content with the prefix trimmed (but not the command). This is used for commands that require more advanced parsing than the default CSV reader.

type ErrInvalidUsage

type ErrInvalidUsage struct {
	Args  []string
	Index int
	Err   string

	// TODO: usage generator?
	// Here, as a reminder
	Ctx *CommandContext
}

func (*ErrInvalidUsage) Error

func (err *ErrInvalidUsage) Error() string

type ErrUnknownCommand

type ErrUnknownCommand struct {
	Command string
	Parent  string
	// contains filtered or unexported fields
}

func (*ErrUnknownCommand) Error

func (err *ErrUnknownCommand) Error() string

type ManualParser added in v0.0.12

type ManualParser interface {
	// $0 will have its prefix trimmed.
	ParseContent([]string) error
}

ManualParser has a ParseContent(string) method. If the library sees this for an argument, it will send all of the arguments (including the command) into the method. If used, this should be the only argument followed after the Message Create event. Any more and the router will ignore.

type NameFlag

type NameFlag uint64
const AdminOnly NameFlag = 1 << 2

A - AdminOnly, which tells the library to only run the Subcommand/method if the user is admin or not. This will automatically add GuildOnly as well.

const GuildOnly NameFlag = 1 << 3

G - GuildOnly, which tells the library to only run the Subcommand/method if the user is inside a guild.

const Hidden NameFlag = 1 << 5

H - Hidden/Handler, which tells the router to not add this into the list of commands, hiding it from Help. Handlers that are hidden will not have any arguments parsed. It will be treated as an Event.

const Middleware NameFlag = 1 << 4

M - Middleware, which tells the library that the method is a middleware. The method will be executed anytime a method of the same struct is matched.

Using this flag inside the subcommand will drop all methods (this is an undefined behavior/UB).

const None NameFlag = 0
const Plumb NameFlag = 1 << 6

P - Plumb, which tells the router to call only this handler with all the arguments (except the prefix string). If plumb is used, only this method will be called for the given struct, though all other events as well as methods with the H (Hidden/Handler) flag.

This is different from using H (Hidden/Handler), as handlers are called regardless of command prefixes. Plumb methods are only called once, and no other methods will be called for that struct. That said, a Plumb method would still go into Commands, but only itself will be there.

Note that if there's a Plumb method in the main commands, then none of the subcommands would be called. This is an unintended but expected side effect.

Example

A use for this would be subcommands that don't need a second command, or if the main struct manually handles command switching. This example demonstrates the second use-case:

func (s *Sub) PーMain(
    c *gateway.MessageCreateGateway, c *Content) error {

    // Input:  !sub this is a command
    // Output: this is a command

    log.Println(c.String())
    return nil
}
const Raw NameFlag = 1 << 1

R - Raw, which tells the library to use the method name as-is (flags will still be stripped). For example, if a method is called Reset its command will also be Reset, without being all lower-cased.

func ParseFlag

func ParseFlag(name string) (NameFlag, string)

func (NameFlag) Is

func (f NameFlag) Is(flag NameFlag) bool

type NonFatal added in v0.3.0

type NonFatal interface {
	error
	IgnoreError() // noop method
}

NonFatal is an interface that a method can implement to ignore all errors. This works similarly to Break.

var Break NonFatal = _Break{errors.New("break middleware chain, non-fatal")}

Break is a non-fatal error that could be returned from middlewares or handlers to stop the chain of execution.

Middlewares are guaranteed to be executed before handlers, but the exact order of each are undefined. Main handlers are also guaranteed to be executed before all subcommands. If a main middleware cancels, no subcommand middlewares will be called.

Break implements the NonFatal interface, which causes an error to be ignored.

type Parser added in v0.0.12

type Parser interface {
	Parse(string) error
}

Parser implements a Parse(string) method for data structures that can be used as arguments.

type Prefixer added in v0.2.0

type Prefixer func(*gateway.MessageCreateEvent) (prefix string, ok bool)

Prefixer checks a message if it starts with the desired prefix. By default, NewPrefix() is used.

func NewPrefix added in v0.2.0

func NewPrefix(prefixes ...string) Prefixer

NewPrefix creates a simple prefix checker using strings. As the default prefix is "!", the function is called as NewPrefix("!").

type RawArguments

type RawArguments struct {
	Command   string
	Arguments []string
}

RawArguments implements ManualParseable, in case you want to implement a custom argument parser. It borrows the library's argument parser.

func (RawArguments) After added in v0.0.12

func (r RawArguments) After(n int) string

func (RawArguments) Arg added in v0.0.12

func (r RawArguments) Arg(n int) string

func (RawArguments) Length added in v0.0.12

func (r RawArguments) Length() int

func (*RawArguments) ParseContent

func (r *RawArguments) ParseContent(args []string) error

func (RawArguments) String added in v0.0.12

func (r RawArguments) String() string

type Subcommand

type Subcommand struct {
	Description string

	// Raw struct name, including the flag (only filled for actual subcommands,
	// will be empty for Context):
	StructName string
	// Parsed command name:
	Command string

	// struct flags
	Flag NameFlag

	// SanitizeMessage is executed on the message content if the method returns
	// a string content or a SendMessageData.
	SanitizeMessage func(content string) string

	// QuietUnknownCommand, if true, will not make the bot reply with an unknown
	// command error into the chat. If this is set in Context, it will apply to
	// all other subcommands.
	QuietUnknownCommand bool

	// All registered command contexts:
	Commands []*CommandContext
	Events   []*CommandContext
	// contains filtered or unexported fields
}

Subcommand is any form of command, which could be a top-level command or a subcommand.

Allowed method signatures

These are the acceptable function signatures that would be parsed as commands or events. A return type <T> implies that return value will be ignored.

func(*gateway.MessageCreateEvent, ...) (string, error)
func(*gateway.MessageCreateEvent, ...) (*discord.Embed, error)
func(*gateway.MessageCreateEvent, ...) (*api.SendMessageData, error)
func(*gateway.MessageCreateEvent, ...) (T, error)
func(*gateway.MessageCreateEvent, ...) error
func(*gateway.MessageCreateEvent, ...)
func(<AnyEvent>) (T, error)
func(<AnyEvent>) error
func(<AnyEvent>)

func NewSubcommand

func NewSubcommand(cmd interface{}) (*Subcommand, error)

NewSubcommand is used to make a new subcommand. You usually wouldn't call this function, but instead use (*Context).RegisterSubcommand().

func (*Subcommand) ChangeCommandInfo added in v0.0.10

func (sub *Subcommand) ChangeCommandInfo(methodName, cmd, desc string) bool

ChangeCommandInfo changes the matched methodName's Command and Description. Empty means unchanged. The returned bool is true when the method is found.

func (*Subcommand) FindCommand added in v0.3.0

func (sub *Subcommand) FindCommand(methodName string) *CommandContext

FindCommand finds the command. Nil is returned if nothing is found. It's a better idea to not handle nil, as they would become very subtle bugs.

func (*Subcommand) Help added in v0.0.11

func (sub *Subcommand) Help(indent string, hideAdmin bool) string

func (*Subcommand) InitCommands

func (sub *Subcommand) InitCommands(ctx *Context) error

InitCommands fills a Subcommand with a context. This shouldn't be called at all, rather you should use the RegisterSubcommand method of a Context.

func (*Subcommand) NeedsName

func (sub *Subcommand) NeedsName()

NeedsName sets the name for this subcommand. Like InitCommands, this shouldn't be called at all, rather you should use RegisterSubcommand.

type Usager

type Usager interface {
	Usage() string
}

Usager is used in place of the automatically parsed struct name for Parser and other interfaces.

Directories

Path Synopsis
extras

Jump to

Keyboard shortcuts

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