diff --git a/bot/bot.go b/bot/bot.go index daa2179..ac5037c 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -245,15 +245,17 @@ func (b *bot) RegisterTable(p Plugin, handlers HandlerTable) { func (b *bot) RegisterRegex(p Plugin, kind Kind, r *regexp.Regexp, resp ResponseHandler) { t := reflect.TypeOf(p).String() if _, ok := b.callbacks[t]; !ok { - b.callbacks[t] = make(map[Kind]map[*regexp.Regexp][]ResponseHandler) + b.callbacks[t] = make(map[Kind][]HandlerSpec) } if _, ok := b.callbacks[t][kind]; !ok { - b.callbacks[t][kind] = map[*regexp.Regexp][]ResponseHandler{} + b.callbacks[t][kind] = []HandlerSpec{} } - if _, ok := b.callbacks[t][kind][r]; !ok { - b.callbacks[t][kind][r] = []ResponseHandler{} + spec := HandlerSpec{ + Kind: kind, + Regex: r, + Handler: resp, } - b.callbacks[t][kind][r] = append(b.callbacks[t][kind][r], resp) + b.callbacks[t][kind] = append(b.callbacks[t][kind], spec) } // RegisterRegexCmd is a shortcut to filter non-command messages from a registration diff --git a/bot/handlers.go b/bot/handlers.go index f5d9dbc..c939fd0 100644 --- a/bot/handlers.go +++ b/bot/handlers.go @@ -55,19 +55,17 @@ func ParseValues(r *regexp.Regexp, body string) RegexValues { func (b *bot) runCallback(conn Connector, plugin Plugin, evt Kind, message msg.Message, args ...interface{}) bool { t := reflect.TypeOf(plugin).String() - for r, cbs := range b.callbacks[t][evt] { - if r.MatchString(message.Body) { + for _, spec := range b.callbacks[t][evt] { + if spec.Regex.MatchString(message.Body) { req := Request{ Conn: conn, Kind: evt, Msg: message, - Values: ParseValues(r, message.Body), + Values: ParseValues(spec.Regex, message.Body), Args: args, } - for _, cb := range cbs { - if cb(req) { - return true - } + if spec.Handler(req) { + return true } } } diff --git a/bot/interfaces.go b/bot/interfaces.go index eeec7ed..d034c03 100644 --- a/bot/interfaces.go +++ b/bot/interfaces.go @@ -59,7 +59,7 @@ type Request struct { type Kind int type Callback func(Connector, Kind, msg.Message, ...interface{}) bool type ResponseHandler func(Request) bool -type CallbackMap map[string]map[Kind]map[*regexp.Regexp][]ResponseHandler +type CallbackMap map[string]map[Kind][]HandlerSpec type HandlerSpec struct { Kind Kind diff --git a/plugins/emojifyme/emojifyme.go b/plugins/emojifyme/emojifyme.go index 978ad55..1d1567b 100644 --- a/plugins/emojifyme/emojifyme.go +++ b/plugins/emojifyme/emojifyme.go @@ -7,12 +7,12 @@ import ( "io/ioutil" "math/rand" "net/http" + "regexp" "strings" "github.com/rs/zerolog/log" "github.com/velour/catbase/bot" - "github.com/velour/catbase/bot/msg" ) type EmojifyMePlugin struct { @@ -54,11 +54,13 @@ func New(b bot.Bot) *EmojifyMePlugin { GotBotEmoji: false, Emoji: emojiMap, } - b.Register(ep, bot.Message, ep.message) + b.RegisterRegex(ep, bot.Message, regexp.MustCompile(`.*`), ep.message) return ep } -func (p *EmojifyMePlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool { +func (p *EmojifyMePlugin) message(r bot.Request) bool { + c := r.Conn + message := r.Msg if !p.GotBotEmoji { p.GotBotEmoji = true emojiMap := p.Bot.GetEmojiList() diff --git a/plugins/fact/fact_test.go b/plugins/fact/fact_test.go deleted file mode 100644 index c774146..0000000 --- a/plugins/fact/fact_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package fact - -import ( - "github.com/velour/catbase/plugins/cli" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/velour/catbase/bot" - "github.com/velour/catbase/bot/msg" - "github.com/velour/catbase/bot/user" -) - -var c = &cli.CliPlugin{} - -func makeMessage(nick, payload string) msg.Message { - isCmd := strings.HasPrefix(payload, "!") - if isCmd { - payload = payload[1:] - } - return msg.Message{ - User: &user.User{Name: nick}, - Channel: "test", - Body: payload, - Command: isCmd, - } -} - -func makePlugin(t *testing.T) (*FactoidPlugin, *bot.MockBot) { - mb := bot.NewMockBot() - f := New(mb) // for DB table - return f, mb -} - -func TestReact(t *testing.T) { - msgs := []msg.Message{ - makeMessage("user1", "!testing123 jesus"), - makeMessage("user2", "testing123"), - } - p, mb := makePlugin(t) - - for _, m := range msgs { - p.message(c, bot.Message, m) - } - assert.Len(t, mb.Reactions, 1) - assert.Contains(t, mb.Reactions[0], "jesus") -} - -func TestReactCantLearnSpaces(t *testing.T) { - msgs := []msg.Message{ - makeMessage("user1", "!test jesus christ"), - } - p, mb := makePlugin(t) - - for _, m := range msgs { - p.message(c, bot.Message, m) - } - assert.Len(t, mb.Messages, 1) - assert.Contains(t, mb.Messages[0], "not a valid") -} diff --git a/plugins/fact/factoid.go b/plugins/fact/factoid.go index 42bc075..91ad6d2 100644 --- a/plugins/fact/factoid.go +++ b/plugins/fact/factoid.go @@ -263,6 +263,7 @@ type FactoidPlugin struct { NotFound []string LastFact *Factoid db *sqlx.DB + handlers bot.HandlerTable } // NewFactoid creates a new Factoid with the Plugin interface @@ -320,7 +321,7 @@ func New(botInst bot.Bot) *FactoidPlugin { }(channel) } - botInst.Register(p, bot.Message, p.message) + p.register() botInst.Register(p, bot.Help, p.help) p.registerWeb() @@ -664,68 +665,73 @@ func (p *FactoidPlugin) changeFact(c bot.Connector, message msg.Message) bool { return true } -// Message responds to the bot hook on recieving messages. -// This function returns true if the plugin responds in a meaningful way to the users message. -// Otherwise, the function returns false and the bot continues execution of other plugins. -func (p *FactoidPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool { - if strings.ToLower(message.Body) == "what was that?" { - return p.tellThemWhatThatWas(c, message) - } +func (p *FactoidPlugin) register() { + p.handlers = bot.HandlerTable{ + bot.HandlerSpec{Kind: bot.Message, IsCmd: true, + Regex: regexp.MustCompile(`(?i)^what was that\??$`), + Handler: func(r bot.Request) bool { + return p.tellThemWhatThatWas(r.Conn, r.Msg) + }}, + bot.HandlerSpec{Kind: bot.Message, IsCmd: true, + Regex: regexp.MustCompile(`(?i)^alias (?P\S+) to (?P\S+)$`), + Handler: func(r bot.Request) bool { + from := r.Values["from"] + to := r.Values["to"] + log.Debug().Msgf("alias: %+v", r) + a := aliasFromStrings(from, to) + if err := a.save(p.db); err != nil { + p.Bot.Send(r.Conn, bot.Message, r.Msg.Channel, err.Error()) + } else { + p.Bot.Send(r.Conn, bot.Action, r.Msg.Channel, "learns a new synonym") + } + return true + }}, + bot.HandlerSpec{Kind: bot.Message, IsCmd: true, + Regex: regexp.MustCompile(`(?i)^factoid$`), + Handler: func(r bot.Request) bool { + fact := p.randomFact() + p.sayFact(r.Conn, r.Msg, *fact) + return true + }}, + bot.HandlerSpec{Kind: bot.Message, IsCmd: true, + Regex: regexp.MustCompile(`(?i)^forget that$`), + Handler: func(r bot.Request) bool { + return p.forgetLastFact(r.Conn, r.Msg) + }}, + bot.HandlerSpec{Kind: bot.Message, IsCmd: false, + Regex: regexp.MustCompile(`.*`), + Handler: func(r bot.Request) bool { + message := r.Msg + c := r.Conn - // This plugin has no business with normal messages - if !message.Command { - // look for any triggers in the db matching this message - return p.trigger(c, message) - } + log.Debug().Msgf("Message: %+v", r) - if strings.HasPrefix(strings.ToLower(message.Body), "alias") { - log.Debug(). - Str("alias", message.Body). - Msg("Trying to learn an alias") - m := strings.TrimPrefix(message.Body, "alias ") - parts := strings.SplitN(m, "->", 2) - if len(parts) != 2 { - p.Bot.Send(c, bot.Message, message.Channel, "If you want to alias something, use: `alias this -> that`") - return true - } - a := aliasFromStrings(strings.TrimSpace(parts[1]), strings.TrimSpace(parts[0])) - if err := a.save(p.db); err != nil { - p.Bot.Send(c, bot.Message, message.Channel, err.Error()) - } else { - p.Bot.Send(c, bot.Action, message.Channel, "learns a new synonym") - } - return true - } + // This plugin has no business with normal messages + if !message.Command { + // look for any triggers in the db matching this message + return p.trigger(c, message) + } - if strings.ToLower(message.Body) == "factoid" { - if fact := p.randomFact(); fact != nil { - p.sayFact(c, message, *fact) - return true - } - log.Debug().Msg("Got a nil fact.") - } + if changeOperator(message.Body) != "" { + return p.changeFact(c, message) + } - if strings.ToLower(message.Body) == "forget that" { - return p.forgetLastFact(c, message) - } + action := findAction(message.Body) + if action != "" { + return p.learnAction(c, message, action) + } - if changeOperator(message.Body) != "" { - return p.changeFact(c, message) - } + // look for any triggers in the db matching this message + if p.trigger(c, message) { + return true + } - action := findAction(message.Body) - if action != "" { - return p.learnAction(c, message, action) + // We didn't find anything, panic! + p.Bot.Send(c, bot.Message, message.Channel, p.NotFound[rand.Intn(len(p.NotFound))]) + return true + }}, } - - // look for any triggers in the db matching this message - if p.trigger(c, message) { - return true - } - - // We didn't find anything, panic! - p.Bot.Send(c, bot.Message, message.Channel, p.NotFound[rand.Intn(len(p.NotFound))]) - return true + p.Bot.RegisterTable(p, p.handlers) } // Help responds to help requests. Every plugin must implement a help function.