Skip to content

Commit

Permalink
Added middleware features into package bot. (#3)
Browse files Browse the repository at this point in the history
* Middleware nameflag
* Completed M-Middleware feature
* Changed Namer/Descriptor API to CanSetup API
  • Loading branch information
diamondburned authored Jan 24, 2020
1 parent 09d8c5b commit aadcbd0
Show file tree
Hide file tree
Showing 12 changed files with 459 additions and 159 deletions.
22 changes: 22 additions & 0 deletions _example/advanced_bot/context.go → _example/advanced_bot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,27 @@ type Bot struct {
Ctx *bot.Context
}

// Help prints the default help message.
func (bot *Bot) Help(m *gateway.MessageCreateEvent) error {
_, err := bot.Ctx.SendMessage(m.ChannelID, bot.Ctx.Help(), nil)
return err
}

// Add demonstrates the usage of typed arguments. Run it with "~add 1 2".
func (bot *Bot) Add(m *gateway.MessageCreateEvent, a, b int) error {
content := fmt.Sprintf("%d + %d = %d", a, b, a+b)

_, err := bot.Ctx.SendMessage(m.ChannelID, content, nil)
return err
}

// Ping is a simple ping example, perhaps the most simple you could make it.
func (bot *Bot) Ping(m *gateway.MessageCreateEvent) error {
_, err := bot.Ctx.SendMessage(m.ChannelID, "Pong!", nil)
return err
}

// Say demonstrates how arguments.Flag could be used without the flag library.
func (bot *Bot) Say(m *gateway.MessageCreateEvent, f *arguments.Flag) error {
args := f.String()
if args == "" {
Expand All @@ -47,6 +51,22 @@ func (bot *Bot) Say(m *gateway.MessageCreateEvent, f *arguments.Flag) error {
return err
}

// GuildInfo demonstrates the use of command flags, in this case the GuildOnly
// flag.
func (bot *Bot) GーGuildInfo(m *gateway.MessageCreateEvent) error {
g, err := bot.Ctx.Guild(m.GuildID)
if err != nil {
return fmt.Errorf("Failed to get guild: %v", err)
}

_, err = bot.Ctx.SendMessage(m.ChannelID, fmt.Sprintf(
"Your guild is %s, and its maximum members is %d",
g.Name, g.MaxMembers,
), nil)

return err
}

// Repeat tells the bot to wait for the user's response, then repeat what they
// said.
func (bot *Bot) Repeat(m *gateway.MessageCreateEvent) error {
Expand Down Expand Up @@ -80,6 +100,8 @@ func (bot *Bot) Repeat(m *gateway.MessageCreateEvent) error {
return err
}

// Embed is a simple embed creator. Its purpose is to demonstrate the usage of
// the ParseContent interface, as well as using the stdlib flag package.
func (bot *Bot) Embed(
m *gateway.MessageCreateEvent, f *arguments.Flag) error {

Expand Down
67 changes: 67 additions & 0 deletions _example/advanced_bot/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"fmt"
"log"
"runtime"
"strings"

"github.com/diamondburned/arikawa/bot"
"github.com/diamondburned/arikawa/gateway"
)

// Flag for administrators only.
type Debug struct {
Context *bot.Context
}

// Setup demonstrates the CanSetup interface. This function will never be parsed
// as a callback of any event.
func (d *Debug) Setup(sub *bot.Subcommand) {
// Set a custom command (e.g. "!go ..."):
sub.Command = "go"
// Set a custom description:
sub.Description = "Print Go debugging variables"

// Manually set the usage for each function.

sub.ChangeCommandInfo("GOOS", "",
"Prints the current operating system")

sub.ChangeCommandInfo("GC", "",
"Triggers the garbage collecto")

sub.ChangeCommandInfo("Goroutines", "",
"Prints the current number of Goroutines")
}

// ~go goroutines
func (d *Debug) Goroutines(m *gateway.MessageCreateEvent) error {
_, err := d.Context.SendMessage(m.ChannelID, fmt.Sprintf(
"goroutines: %d",
runtime.NumGoroutine(),
), nil)
return err
}

// ~go GOOS
func (d *Debug) RーGOOS(m *gateway.MessageCreateEvent) error {
_, err := d.Context.SendMessage(
m.ChannelID, strings.Title(runtime.GOOS), nil)
return err
}

// ~go GC
func (d *Debug) RーGC(m *gateway.MessageCreateEvent) error {
runtime.GC()

_, err := d.Context.SendMessage(m.ChannelID, "Done.", nil)
return err
}

// ~go die
// This command will be hidden from ~help by default.
func (d *Debug) AーDie(m *gateway.MessageCreateEvent) error {
log.Fatalln("User", m.Author.Username, "killed the bot x_x")
return nil
}
4 changes: 4 additions & 0 deletions _example/advanced_bot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ func main() {

stop, err := bot.Start(token, commands, func(ctx *bot.Context) error {
ctx.Prefix = "!"

// Subcommand demo, but this can be in another package.
ctx.MustRegisterSubcommand(&Debug{})

return nil
})

Expand Down
4 changes: 4 additions & 0 deletions bot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# What happened here?

We've moved everything to https://github.com/diamondburned/ak-rfrouter, as this
package will be replaced with a [go-chi](https://github.com/go-chi/chi) style router.
9 changes: 9 additions & 0 deletions bot/arguments.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type ManualParseable interface {
ParseContent([]string) error
}

// RawArguments implements ManualParseable, in case you want to implement a
// custom argument parser. It borrows the library's argument parser.
type RawArguments struct {
Arguments []string
}
Expand All @@ -32,6 +34,13 @@ func (r *RawArguments) ParseContent(args []string) error {
return nil
}

// Argument is each argument in a method.
type Argument struct {
String string
Type reflect.Type
fn argumentValueFn
}

// nilV, only used to return an error
var nilV = reflect.Value{}

Expand Down
119 changes: 88 additions & 31 deletions bot/ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"os/signal"
"strings"
"sync"

"github.com/diamondburned/arikawa/gateway"
"github.com/diamondburned/arikawa/state"
Expand Down Expand Up @@ -55,8 +56,13 @@ type Context struct {
// ReplyError when true replies to the user the error.
ReplyError bool

// Subcommands contains all the registered subcommands.
Subcommands []*Subcommand
// Subcommands contains all the registered subcommands. This is not
// exported, as it shouldn't be used directly.
subcommands []*Subcommand

// Quick access map from event types to pointers. This map will never have
// MessageCreateEvent's type.
typeCache sync.Map // map[reflect.Type][]*CommandContext
}

// Start quickly starts a bot with the given command. It will prepend "Bot"
Expand Down Expand Up @@ -143,6 +149,50 @@ func New(s *state.State, cmd interface{}) (*Context, error) {
return ctx, nil
}

func (ctx *Context) Subcommands() []*Subcommand {
// Getter is not useless, refer to the struct doc for reason.
return ctx.subcommands
}

// 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 (ctx *Context) FindCommand(structname, methodname string) *CommandContext {
if structname == "" {
for _, c := range ctx.Commands {
if c.Command == methodname {
return c
}
}

return nil
}

for _, sub := range ctx.subcommands {
if sub.StructName != structname {
continue
}

for _, c := range sub.Commands {
if c.Command == methodname {
return c
}
}
}

return nil
}

// 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 (ctx *Context) MustRegisterSubcommand(cmd interface{}) *Subcommand {
s, err := ctx.RegisterSubcommand(cmd)
if err != nil {
Expand All @@ -168,14 +218,14 @@ func (ctx *Context) RegisterSubcommand(cmd interface{}) (*Subcommand, error) {
}

// Do a collision check
for _, sub := range ctx.Subcommands {
if sub.name == s.name {
for _, sub := range ctx.subcommands {
if sub.Command == s.Command {
return nil, errors.New(
"New subcommand has duplicate name: " + s.name)
"New subcommand has duplicate name: " + s.Command)
}
}

ctx.Subcommands = append(ctx.Subcommands, s)
ctx.subcommands = append(ctx.subcommands, s)
return s, nil
}

Expand All @@ -184,27 +234,33 @@ func (ctx *Context) RegisterSubcommand(cmd interface{}) (*Subcommand, error) {
// Session handlers.
func (ctx *Context) Start() func() {
return ctx.Session.AddHandler(func(v interface{}) {
if err := ctx.callCmd(v); err != nil {
if str := ctx.FormatError(err); str != "" {
// Log the main error first
if !ctx.ReplyError {
ctx.ErrorLogger(errors.Wrap(err, "Command error"))
}

mc, ok := v.(*gateway.MessageCreateEvent)
if !ok {
return
}

if ctx.ReplyError {
_, Merr := ctx.SendMessage(mc.ChannelID, str, nil)
if Merr != nil {
// Then the message error
ctx.ErrorLogger(Merr)
// TODO: there ought to be a better way lol
}
}
}
err := ctx.callCmd(v)
if err == nil {
return
}

str := ctx.FormatError(err)
if str == "" {
return
}

// Log the main error first...
if !ctx.ReplyError {
ctx.ErrorLogger(errors.Wrap(err, "Command error"))
return
}

mc, ok := v.(*gateway.MessageCreateEvent)
if !ok {
return
}

_, err = ctx.SendMessage(mc.ChannelID, str, nil)
if err != nil {
// ...then the message error
ctx.ErrorLogger(err)

// TODO: there ought to be a better way lol
}
})
}
Expand Down Expand Up @@ -247,7 +303,7 @@ func (ctx *Context) Help() string {
continue
}

help.WriteString(" " + ctx.Prefix + cmd.Name())
help.WriteString(" " + ctx.Prefix + cmd.Command)

switch {
case len(cmd.Usage()) > 0:
Expand All @@ -260,14 +316,15 @@ func (ctx *Context) Help() string {
}

var subHelp = strings.Builder{}
var subcommands = ctx.Subcommands()

for _, sub := range ctx.Subcommands {
for _, sub := range subcommands {
if sub.Flag.Is(AdminOnly) {
// Hidden
continue
}

subHelp.WriteString(" " + sub.Name())
subHelp.WriteString(" " + sub.Command)

if sub.Description != "" {
subHelp.WriteString(": " + sub.Description)
Expand All @@ -281,7 +338,7 @@ func (ctx *Context) Help() string {
}

subHelp.WriteString(" " +
ctx.Prefix + sub.Name() + " " + cmd.Name())
ctx.Prefix + sub.Command + " " + cmd.Command)

switch {
case len(cmd.Usage()) > 0:
Expand Down
Loading

0 comments on commit aadcbd0

Please sign in to comment.