fact: refactor

This commit is contained in:
Chris Sexton 2021-02-01 21:25:19 -05:00 committed by Chris Sexton
parent 55a206760f
commit 8cb6b485cb
6 changed files with 80 additions and 132 deletions

View File

@ -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) { func (b *bot) RegisterRegex(p Plugin, kind Kind, r *regexp.Regexp, resp ResponseHandler) {
t := reflect.TypeOf(p).String() t := reflect.TypeOf(p).String()
if _, ok := b.callbacks[t]; !ok { 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 { 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 { spec := HandlerSpec{
b.callbacks[t][kind][r] = []ResponseHandler{} 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 // RegisterRegexCmd is a shortcut to filter non-command messages from a registration

View File

@ -55,22 +55,20 @@ func ParseValues(r *regexp.Regexp, body string) RegexValues {
func (b *bot) runCallback(conn Connector, plugin Plugin, evt Kind, message msg.Message, args ...interface{}) bool { func (b *bot) runCallback(conn Connector, plugin Plugin, evt Kind, message msg.Message, args ...interface{}) bool {
t := reflect.TypeOf(plugin).String() t := reflect.TypeOf(plugin).String()
for r, cbs := range b.callbacks[t][evt] { for _, spec := range b.callbacks[t][evt] {
if r.MatchString(message.Body) { if spec.Regex.MatchString(message.Body) {
req := Request{ req := Request{
Conn: conn, Conn: conn,
Kind: evt, Kind: evt,
Msg: message, Msg: message,
Values: ParseValues(r, message.Body), Values: ParseValues(spec.Regex, message.Body),
Args: args, Args: args,
} }
for _, cb := range cbs { if spec.Handler(req) {
if cb(req) {
return true return true
} }
} }
} }
}
return false return false
} }

View File

@ -59,7 +59,7 @@ type Request struct {
type Kind int type Kind int
type Callback func(Connector, Kind, msg.Message, ...interface{}) bool type Callback func(Connector, Kind, msg.Message, ...interface{}) bool
type ResponseHandler func(Request) 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 { type HandlerSpec struct {
Kind Kind Kind Kind

View File

@ -7,12 +7,12 @@ import (
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
"net/http" "net/http"
"regexp"
"strings" "strings"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/velour/catbase/bot" "github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
) )
type EmojifyMePlugin struct { type EmojifyMePlugin struct {
@ -54,11 +54,13 @@ func New(b bot.Bot) *EmojifyMePlugin {
GotBotEmoji: false, GotBotEmoji: false,
Emoji: emojiMap, Emoji: emojiMap,
} }
b.Register(ep, bot.Message, ep.message) b.RegisterRegex(ep, bot.Message, regexp.MustCompile(`.*`), ep.message)
return ep 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 { if !p.GotBotEmoji {
p.GotBotEmoji = true p.GotBotEmoji = true
emojiMap := p.Bot.GetEmojiList() emojiMap := p.Bot.GetEmojiList()

View File

@ -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 <react> 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 <react> 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")
}

View File

@ -263,6 +263,7 @@ type FactoidPlugin struct {
NotFound []string NotFound []string
LastFact *Factoid LastFact *Factoid
db *sqlx.DB db *sqlx.DB
handlers bot.HandlerTable
} }
// NewFactoid creates a new Factoid with the Plugin interface // NewFactoid creates a new Factoid with the Plugin interface
@ -320,7 +321,7 @@ func New(botInst bot.Bot) *FactoidPlugin {
}(channel) }(channel)
} }
botInst.Register(p, bot.Message, p.message) p.register()
botInst.Register(p, bot.Help, p.help) botInst.Register(p, bot.Help, p.help)
p.registerWeb() p.registerWeb()
@ -664,13 +665,46 @@ func (p *FactoidPlugin) changeFact(c bot.Connector, message msg.Message) bool {
return true return true
} }
// Message responds to the bot hook on recieving messages. func (p *FactoidPlugin) register() {
// This function returns true if the plugin responds in a meaningful way to the users message. p.handlers = bot.HandlerTable{
// Otherwise, the function returns false and the bot continues execution of other plugins. bot.HandlerSpec{Kind: bot.Message, IsCmd: true,
func (p *FactoidPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool { Regex: regexp.MustCompile(`(?i)^what was that\??$`),
if strings.ToLower(message.Body) == "what was that?" { Handler: func(r bot.Request) bool {
return p.tellThemWhatThatWas(c, message) return p.tellThemWhatThatWas(r.Conn, r.Msg)
}},
bot.HandlerSpec{Kind: bot.Message, IsCmd: true,
Regex: regexp.MustCompile(`(?i)^alias (?P<from>\S+) to (?P<to>\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
log.Debug().Msgf("Message: %+v", r)
// This plugin has no business with normal messages // This plugin has no business with normal messages
if !message.Command { if !message.Command {
@ -678,37 +712,6 @@ func (p *FactoidPlugin) message(c bot.Connector, kind bot.Kind, message msg.Mess
return p.trigger(c, message) return p.trigger(c, message)
} }
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
}
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 strings.ToLower(message.Body) == "forget that" {
return p.forgetLastFact(c, message)
}
if changeOperator(message.Body) != "" { if changeOperator(message.Body) != "" {
return p.changeFact(c, message) return p.changeFact(c, message)
} }
@ -726,6 +729,9 @@ func (p *FactoidPlugin) message(c bot.Connector, kind bot.Kind, message msg.Mess
// We didn't find anything, panic! // We didn't find anything, panic!
p.Bot.Send(c, bot.Message, message.Channel, p.NotFound[rand.Intn(len(p.NotFound))]) p.Bot.Send(c, bot.Message, message.Channel, p.NotFound[rand.Intn(len(p.NotFound))])
return true return true
}},
}
p.Bot.RegisterTable(p, p.handlers)
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.