From 67430c6d7aed66a2deeebcf3b9cc523602abae8d Mon Sep 17 00:00:00 2001 From: "diamondburned (Forefront)" Date: Tue, 12 May 2020 21:37:06 -0700 Subject: [PATCH] Bot: Added Plumb support, fixed tests Merged Merged --- bot/command.go | 5 +---- bot/ctx.go | 30 +++++++-------------------- bot/ctx_call.go | 13 ++++++------ bot/ctx_plumb_test.go | 46 +++++++++++++++++++++++++++++++++++++++++- bot/ctx_test.go | 4 ++-- bot/subcommand.go | 29 +++++++++++++++----------- bot/subcommand_test.go | 9 ++++++--- 7 files changed, 85 insertions(+), 51 deletions(-) diff --git a/bot/command.go b/bot/command.go index 0e70efe..df87037 100644 --- a/bot/command.go +++ b/bot/command.go @@ -83,10 +83,7 @@ type MethodContext struct { MethodName string // Command is the Discord command used to call the method. - Command string // hidden if empty - - // Hidden is true if the method has a hidden nameflag. - // Hidden bool + Command string // plumb if empty // Variadic is true if the function is a variadic one or if the last // argument accepts multiple strings. diff --git a/bot/ctx.go b/bot/ctx.go index bd0b4d3..a6505fb 100644 --- a/bot/ctx.go +++ b/bot/ctx.go @@ -217,39 +217,23 @@ func (ctx *Context) Subcommands() []*Subcommand { return ctx.subcommands } -// FindMethod finds a method based on the struct and method name. The queried +// 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.FindMethod("", "Method") +// cmd := ctx.FindCommand("", "Method") // // Find a command from a subcommand: -// cmd = ctx.FindMethod("Starboard", "Reset") +// cmd = ctx.FindCommand("Starboard", "Reset") // -func (ctx *Context) FindMethod(structname, methodname string) *MethodContext { +func (ctx *Context) FindCommand(structname, methodname string) *MethodContext { if structname == "" { - for _, c := range ctx.Methods { - if c.MethodName == methodname { - return c - } - } - - return nil + return ctx.Subcommand.FindCommand(methodname) } - for _, sub := range ctx.subcommands { - if sub.StructName != structname { - continue - } - - for _, c := range sub.Methods { - if c.MethodName == methodname { - return c - } + if sub.StructName == structname { + return sub.FindCommand(methodname) } } - return nil } diff --git a/bot/ctx_call.go b/bot/ctx_call.go index 8a900e9..0e5eed4 100644 --- a/bot/ctx_call.go +++ b/bot/ctx_call.go @@ -286,7 +286,7 @@ Call: // findCommand filters. func (ctx *Context) findCommand(parts []string) ([]string, *MethodContext, *Subcommand, error) { // Main command entrypoint cannot have plumb. - for _, c := range ctx.Methods { + for _, c := range ctx.Commands { if c.Command == parts[0] { return parts[1:], c, ctx.Subcommand, nil } @@ -299,15 +299,16 @@ func (ctx *Context) findCommand(parts []string) ([]string, *MethodContext, *Subc continue } - // If there's no second argument, TODO call Help. - - if s.plumbed != nil { + // Only actually plumb if we actually have a plumbed handler AND + // 1. We only have one command handler OR + // 2. We only have the subcommand name but no command. + if s.plumbed != nil && (len(s.Commands) == 1 || len(parts) <= 2) { return parts[1:], s.plumbed, s, nil } if len(parts) >= 2 { - for _, c := range s.Methods { - if c.event == typeMessageCreate && c.Command == parts[1] { + for _, c := range s.Commands { + if c.Command == parts[1] { return parts[2:], c, s, nil } } diff --git a/bot/ctx_plumb_test.go b/bot/ctx_plumb_test.go index 192b31d..66bde42 100644 --- a/bot/ctx_plumb_test.go +++ b/bot/ctx_plumb_test.go @@ -50,7 +50,7 @@ func TestSubcommandPlumb(t *testing.T) { // Try call exactly what's in the Plumb example: m := &gateway.MessageCreateEvent{ Message: discord.Message{ - Content: "hasPlumb test command", + Content: "hasPlumb", }, } @@ -61,6 +61,50 @@ func TestSubcommandPlumb(t *testing.T) { if p.NotPlumbed { t.Fatal("Normal method called for hasPlumb") } +} + +type onlyPlumb struct { + Ctx *Context + Plumbed string +} + +func (h *onlyPlumb) Setup(sub *Subcommand) { + sub.SetPlumb("Plumber") +} + +func (h *onlyPlumb) Plumber(_ *gateway.MessageCreateEvent, c RawArguments) error { + h.Plumbed = string(c) + return nil +} + +func TestSubcommandOnlyPlumb(t *testing.T) { + var state = &state.State{ + Store: state.NewDefaultStore(nil), + } + + c, err := New(state, &testc{}) + if err != nil { + t.Fatal("Failed to create new context:", err) + } + c.HasPrefix = NewPrefix("") + + p := &onlyPlumb{} + + _, err = c.RegisterSubcommand(p) + if err != nil { + t.Fatal("Failed to register hasPlumb:", err) + } + + // Try call exactly what's in the Plumb example: + m := &gateway.MessageCreateEvent{ + Message: discord.Message{ + Content: "onlyPlumb test command", + }, + } + + if err := c.callCmd(m); err != nil { + t.Fatal("Failed to call message:", err) + } if p.Plumbed != "test command" { t.Fatal("Unexpected custom argument for plumbed:", p.Plumbed) diff --git a/bot/ctx_test.go b/bot/ctx_test.go index 45e7d92..792c568 100644 --- a/bot/ctx_test.go +++ b/bot/ctx_test.go @@ -103,7 +103,7 @@ func TestContext(t *testing.T) { }) t.Run("find commands", func(t *testing.T) { - cmd := ctx.FindMethod("", "NoArgs") + cmd := ctx.FindCommand("", "NoArgs") if cmd == nil { t.Fatal("Failed to find NoArgs") } @@ -242,7 +242,7 @@ func TestContext(t *testing.T) { t.Fatal("Unexpected call error:", err) } - if cmd := ctx.FindMethod("testc", "Noop"); cmd == nil { + if cmd := ctx.FindCommand("testc", "Noop"); cmd == nil { t.Fatal("Failed to find subcommand Noop") } }) diff --git a/bot/subcommand.go b/bot/subcommand.go index 02f34ad..0b86439 100644 --- a/bot/subcommand.go +++ b/bot/subcommand.go @@ -82,9 +82,10 @@ type Subcommand struct { // Commands can actually return either a string, an embed, or a // SendMessageData, with error as the second argument. - // All registered method contexts, including commands: - Methods []*MethodContext - plumbed *MethodContext + // All registered method contexts: + Events []*MethodContext + Commands []*MethodContext + plumbed *MethodContext // Global middlewares. globalmws []*MiddlewareContext @@ -136,9 +137,9 @@ func (sub *Subcommand) NeedsName() { sub.Command = lowerFirstLetter(sub.StructName) } -// FindMethod finds the MethodContext. It panics if methodName is not found. -func (sub *Subcommand) FindMethod(methodName string) *MethodContext { - for _, c := range sub.Methods { +// FindCommand finds the MethodContext. It panics if methodName is not found. +func (sub *Subcommand) FindCommand(methodName string) *MethodContext { + for _, c := range sub.Commands { if c.MethodName == methodName { return c } @@ -149,7 +150,7 @@ func (sub *Subcommand) FindMethod(methodName string) *MethodContext { // ChangeCommandInfo changes the matched methodName's Command and Description. // Empty means unchanged. The returned bool is true when the command is found. func (sub *Subcommand) ChangeCommandInfo(methodName, cmd, desc string) bool { - for _, c := range sub.Methods { + for _, c := range sub.Commands { if c.MethodName != methodName || !c.isEvent(typeMessageCreate) { continue } @@ -316,7 +317,11 @@ func (sub *Subcommand) parseCommands() error { } // Append. - sub.Methods = append(sub.Methods, cctx) + if cctx.event == typeMessageCreate { + sub.Commands = append(sub.Commands, cctx) + } else { + sub.Events = append(sub.Events, cctx) + } } return nil @@ -339,7 +344,7 @@ func (sub *Subcommand) AddMiddleware(methodName string, middleware interface{}) sub.globalmws = append(sub.globalmws, mw) } else { // Append middleware to that individual function. - sub.FindMethod(method).addMiddleware(mw) + sub.FindCommand(method).addMiddleware(mw) } } } @@ -363,10 +368,10 @@ func (sub *Subcommand) eventCallers(evT reflect.Type) (callers []caller) { } // Search for specific handlers. - for _, cctx := range sub.Methods { + for _, cctx := range sub.Events { // We only take middlewares and callers if the event matches and is not // a MessageCreate. The other function already handles that. - if cctx.event != typeMessageCreate && cctx.isEvent(evT) { + if cctx.isEvent(evT) { // Add the command's middlewares first. for _, mw := range cctx.middlewares { // Concrete struct to interface conversion done implicitly. @@ -387,7 +392,7 @@ func (sub *Subcommand) SetPlumb(methodName string) { panic("SetPlumb called on a main command with sub.Command empty.") } - method := sub.FindMethod(methodName) + method := sub.FindCommand(methodName) method.Command = "" sub.plumbed = method } diff --git a/bot/subcommand_test.go b/bot/subcommand_test.go index 998e57d..05a1c5f 100644 --- a/bot/subcommand_test.go +++ b/bot/subcommand_test.go @@ -29,8 +29,11 @@ func TestSubcommand(t *testing.T) { } // !!! CHANGE ME - if len(sub.Methods) < 8 { - t.Fatal("too low sub.Methods len", len(sub.Methods)) + if len(sub.Commands) < 8 { + t.Fatal("too low sub.Methods len", len(sub.Commands)) + } + if len(sub.Events) < 1 { + t.Fatal("No events found.") } var ( @@ -39,7 +42,7 @@ func TestSubcommand(t *testing.T) { foundNoArgs bool ) - for _, this := range sub.Methods { + for _, this := range sub.Commands { switch this.Command { case "send": foundSend = true