diff --git a/bot/bot.go b/bot/bot.go index 6a69bb2..c5b237d 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -6,6 +6,7 @@ import ( "html/template" "log" "net/http" + "reflect" "strings" "github.com/jmoiron/sqlx" @@ -19,7 +20,7 @@ import ( type bot struct { // Each plugin must be registered in our plugins handler. To come: a map so that this // will allow plugins to respond to specific kinds of events - plugins map[string]Handler + plugins map[string]Plugin pluginOrdering []string // Users holds information about all of our friends @@ -41,13 +42,16 @@ type bot struct { // filters registered by plugins filters map[string]func(string) string + + callbacks CallbackMap } +// Variable represents a $var replacement type Variable struct { Variable, Value string } -// Newbot creates a bot for a given connection and set of handlers. +// New creates a bot for a given connection and set of handlers. func New(config *config.Config, connector Connector) Bot { logIn := make(chan msg.Message) logOut := make(chan msg.Messages) @@ -62,7 +66,7 @@ func New(config *config.Config, connector Connector) Bot { bot := &bot{ config: config, - plugins: make(map[string]Handler), + plugins: make(map[string]Plugin), pluginOrdering: make([]string, 0), conn: connector, users: users, @@ -71,6 +75,7 @@ func New(config *config.Config, connector Connector) Bot { logOut: logOut, httpEndPoints: make(map[string]string), filters: make(map[string]func(string) string), + callbacks: make(CallbackMap), } bot.migrateDB() @@ -79,9 +84,7 @@ func New(config *config.Config, connector Connector) Bot { addr := config.Get("HttpAddr", "127.0.0.1:1337") go http.ListenAndServe(addr, nil) - connector.RegisterMessageReceived(bot.MsgReceived) - connector.RegisterEventReceived(bot.EventReceived) - connector.RegisterReplyMessageReceived(bot.ReplyMsgReceived) + connector.RegisterEvent(bot.Receive) return bot } @@ -109,7 +112,8 @@ func (b *bot) migrateDB() { } // Adds a constructed handler to the bots handlers list -func (b *bot) AddHandler(name string, h Handler) { +func (b *bot) AddPlugin(h Plugin) { + name := reflect.TypeOf(h).String() b.plugins[name] = h b.pluginOrdering = append(b.pluginOrdering, name) if entry := h.RegisterWeb(); entry != nil { @@ -126,7 +130,7 @@ func (b *bot) Who(channel string) []user.User { return users } -var rootIndex string = ` +var rootIndex = ` @@ -166,7 +170,7 @@ func (b *bot) serveRoot(w http.ResponseWriter, r *http.Request) { t.Execute(w, context) } -// Checks if message is a command and returns its curtailed version +// IsCmd checks if message is a command and returns its curtailed version func IsCmd(c *config.Config, message string) (bool, string) { cmdcs := c.GetArray("CommandChar", []string{"!"}) botnick := strings.ToLower(c.Get("Nick", "bot")) @@ -244,3 +248,15 @@ func (b *bot) checkAdmin(nick string) bool { func (b *bot) RegisterFilter(name string, f func(string) string) { b.filters[name] = f } + +// Register a callback +func (b *bot) Register(p Plugin, kind Kind, cb Callback) { + t := reflect.TypeOf(p) + if _, ok := b.callbacks[t]; !ok { + b.callbacks[t] = make(map[Kind][]Callback) + } + if _, ok := b.callbacks[t][kind]; !ok { + b.callbacks[t][kind] = []Callback{} + } + b.callbacks[t][kind] = append(b.callbacks[t][kind], cb) +} diff --git a/bot/handlers.go b/bot/handlers.go index 39b5d72..2108afc 100644 --- a/bot/handlers.go +++ b/bot/handlers.go @@ -8,6 +8,7 @@ import ( "fmt" "log" "math/rand" + "reflect" "regexp" "strconv" "strings" @@ -16,22 +17,20 @@ import ( "github.com/velour/catbase/bot/msg" ) -// Handles incomming PRIVMSG requests -func (b *bot) MsgReceived(msg msg.Message) { - log.Println("Received message: ", msg) +func (b *bot) Receive(kind Kind, msg msg.Message, args ...interface{}) { + log.Println("Received event: ", msg) // msg := b.buildMessage(client, inMsg) // do need to look up user and fix it - if strings.HasPrefix(msg.Body, "help ") && msg.Command { + if kind == Message && strings.HasPrefix(msg.Body, "help ") && msg.Command { parts := strings.Fields(strings.ToLower(msg.Body)) b.checkHelp(msg.Channel, parts) goto RET } for _, name := range b.pluginOrdering { - p := b.plugins[name] - if p.Message(msg) { - break + if b.runCallback(b.plugins[name], kind, msg, args...) { + goto RET } } @@ -40,52 +39,19 @@ RET: return } -// Handle incoming events -func (b *bot) EventReceived(msg msg.Message) { - log.Println("Received event: ", msg) - //msg := b.buildMessage(conn, inMsg) - for _, name := range b.pluginOrdering { - p := b.plugins[name] - if p.Event(msg.Body, msg) { // TODO: could get rid of msg.Body - break +func (b *bot) runCallback(plugin Plugin, evt Kind, message msg.Message, args ...interface{}) bool { + t := reflect.TypeOf(plugin) + for _, cb := range b.callbacks[t][evt] { + if cb(evt, message, args...) { + return true } } + return false } -// Handle incoming replys -func (b *bot) ReplyMsgReceived(msg msg.Message, identifier string) { - log.Println("Received message: ", msg) - - for _, name := range b.pluginOrdering { - p := b.plugins[name] - if p.ReplyMessage(msg, identifier) { - break - } - } -} - -func (b *bot) SendMessage(channel, message string) string { - return b.conn.SendMessage(channel, message) -} - -func (b *bot) SendAction(channel, message string) string { - return b.conn.SendAction(channel, message) -} - -func (b *bot) ReplyToMessageIdentifier(channel, message, identifier string) (string, bool) { - return b.conn.ReplyToMessageIdentifier(channel, message, identifier) -} - -func (b *bot) ReplyToMessage(channel, message string, replyTo msg.Message) (string, bool) { - return b.conn.ReplyToMessage(channel, message, replyTo) -} - -func (b *bot) React(channel, reaction string, message msg.Message) bool { - return b.conn.React(channel, reaction, message) -} - -func (b *bot) Edit(channel, newMessage, identifier string) bool { - return b.conn.Edit(channel, newMessage, identifier) +// Send a message to the connection +func (b *bot) Send(kind Kind, args ...interface{}) (string, error) { + return b.conn.Send(kind, args...) } func (b *bot) GetEmojiList() map[string]string { @@ -100,7 +66,7 @@ func (b *bot) checkHelp(channel string, parts []string) { for name, _ := range b.plugins { topics = fmt.Sprintf("%s, %s", topics, name) } - b.SendMessage(channel, topics) + b.Send(Message, channel, topics) } else { // trigger the proper plugin's help response if parts[1] == "about" { @@ -111,12 +77,12 @@ func (b *bot) checkHelp(channel string, parts []string) { b.listVars(channel, parts) return } - plugin := b.plugins[parts[1]] - if plugin != nil { - plugin.Help(channel, parts) + plugin, ok := b.plugins[parts[1]] + if ok { + b.runCallback(plugin, Help, msg.Message{Channel: channel}, channel, parts) } else { msg := fmt.Sprintf("I'm sorry, I don't know what %s is!", parts[1]) - b.SendMessage(channel, msg) + b.Send(Message, channel, msg) } } } @@ -212,14 +178,14 @@ func (b *bot) listVars(channel string, parts []string) { if len(variables) > 0 { msg += ", " + strings.Join(variables, ", ") } - b.SendMessage(channel, msg) + b.Send(Message, channel, msg) } func (b *bot) Help(channel string, parts []string) { msg := fmt.Sprintf("Hi, I'm based on godeepintir version %s. I'm written in Go, and you "+ "can find my source code on the internet here: "+ "http://github.com/velour/catbase", b.version) - b.SendMessage(channel, msg) + b.Send(Message, channel, msg) } // Send our own musings to the plugins @@ -236,9 +202,8 @@ func (b *bot) selfSaid(channel, message string, action bool) { } for _, name := range b.pluginOrdering { - p := b.plugins[name] - if p.BotMessage(msg) { - break + if b.runCallback(b.plugins[name], SelfMessage, msg) { + return } } } diff --git a/bot/interfaces.go b/bot/interfaces.go index 42b0980..74eed27 100644 --- a/bot/interfaces.go +++ b/bot/interfaces.go @@ -3,56 +3,78 @@ package bot import ( + "reflect" + "github.com/jmoiron/sqlx" "github.com/velour/catbase/bot/msg" "github.com/velour/catbase/bot/user" "github.com/velour/catbase/config" ) +const ( + _ = iota + + // Message any standard chat + Message + // Reply something containing a message reference + Reply + // Action any /me action + Action + // Reaction Icon reaction if service supports it + Reaction + // Edit message ref'd new message to replace + Edit + // Not sure what event is + Event + // Help is used when the bot help system is triggered + Help + // SelfMessage triggers when the bot is sending a message + SelfMessage +) + +type Kind int +type Callback func(Kind, msg.Message, ...interface{}) bool +type CallbackMap map[reflect.Type]map[Kind][]Callback + +// Bot interface serves to allow mocking of the actual bot type Bot interface { + // Config allows access to the bot's configuration system Config() *config.Config + // DB gives access to the current database DB() *sqlx.DB + // Who lists users in a particular channel Who(string) []user.User - AddHandler(string, Handler) - SendMessage(string, string) string - SendAction(string, string) string - ReplyToMessageIdentifier(string, string, string) (string, bool) - ReplyToMessage(string, string, msg.Message) (string, bool) - React(string, string, msg.Message) bool - Edit(string, string, string) bool - MsgReceived(msg.Message) - ReplyMsgReceived(msg.Message, string) - EventReceived(msg.Message) + // AddPlugin registers a new plugin handler + AddPlugin(Plugin) + // First arg should be one of bot.Message/Reply/Action/etc + Send(Kind, ...interface{}) (string, error) + // First arg should be one of bot.Message/Reply/Action/etc + Receive(Kind, msg.Message, ...interface{}) + // Register a callback + Register(Plugin, Kind, Callback) + Filter(msg.Message, string) string LastMessage(string) (msg.Message, error) + CheckAdmin(string) bool GetEmojiList() map[string]string RegisterFilter(string, func(string) string) } +// Connector represents a server connection to a chat service type Connector interface { - RegisterEventReceived(func(message msg.Message)) - RegisterMessageReceived(func(message msg.Message)) - RegisterReplyMessageReceived(func(msg.Message, string)) + RegisterEvent(func(Kind, msg.Message, ...interface{})) + + Send(Kind, ...interface{}) (string, error) - SendMessage(channel, message string) string - SendAction(channel, message string) string - ReplyToMessageIdentifier(string, string, string) (string, bool) - ReplyToMessage(string, string, msg.Message) (string, bool) - React(string, string, msg.Message) bool - Edit(string, string, string) bool GetEmojiList() map[string]string Serve() error Who(string) []string } -// Interface used for compatibility with the Plugin interface -type Handler interface { - Message(message msg.Message) bool - Event(kind string, message msg.Message) bool - ReplyMessage(msg.Message, string) bool - BotMessage(message msg.Message) bool - Help(channel string, parts []string) +// Plugin interface used for compatibility with the Plugin interface +// Probably can disappear once RegisterWeb gets inverted +type Plugin interface { RegisterWeb() *string } diff --git a/bot/mock.go b/bot/mock.go index f3170e4..ea8986e 100644 --- a/bot/mock.go +++ b/bot/mock.go @@ -26,68 +26,67 @@ type MockBot struct { Reactions []string } -func (mb *MockBot) Config() *config.Config { return mb.Cfg } -func (mb *MockBot) DBVersion() int64 { return 1 } -func (mb *MockBot) DB() *sqlx.DB { return mb.Cfg.DB } -func (mb *MockBot) Conn() Connector { return nil } -func (mb *MockBot) Who(string) []user.User { return []user.User{} } -func (mb *MockBot) AddHandler(name string, f Handler) {} -func (mb *MockBot) SendMessage(ch string, msg string) string { - mb.Messages = append(mb.Messages, msg) - return fmt.Sprintf("m-%d", len(mb.Actions)-1) +func (mb *MockBot) Config() *config.Config { return mb.Cfg } +func (mb *MockBot) DB() *sqlx.DB { return mb.Cfg.DB } +func (mb *MockBot) Who(string) []user.User { return []user.User{} } +func (mb *MockBot) Send(kind Kind, args ...interface{}) (string, error) { + switch kind { + case Message: + mb.Messages = append(mb.Messages, args[1].(string)) + return fmt.Sprintf("m-%d", len(mb.Actions)-1), nil + case Action: + mb.Actions = append(mb.Actions, args[1].(string)) + return fmt.Sprintf("a-%d", len(mb.Actions)-1), nil + case Edit: + ch, m, id := args[0].(string), args[1].(string), args[2].(string) + return mb.edit(ch, m, id) + case Reaction: + ch, re, msg := args[0].(string), args[1].(string), args[2].(msg.Message) + return mb.react(ch, re, msg) + } + return "ERR", fmt.Errorf("Mesasge type unhandled") } -func (mb *MockBot) SendAction(ch string, msg string) string { - mb.Actions = append(mb.Actions, msg) - return fmt.Sprintf("a-%d", len(mb.Actions)-1) -} -func (mb *MockBot) ReplyToMessageIdentifier(channel, message, identifier string) (string, bool) { - return "", false -} -func (mb *MockBot) ReplyToMessage(channel, message string, replyTo msg.Message) (string, bool) { - return "", false -} -func (mb *MockBot) MsgReceived(msg msg.Message) {} -func (mb *MockBot) EventReceived(msg msg.Message) {} -func (mb *MockBot) Filter(msg msg.Message, s string) string { return s } -func (mb *MockBot) LastMessage(ch string) (msg.Message, error) { return msg.Message{}, nil } -func (mb *MockBot) CheckAdmin(nick string) bool { return false } +func (mb *MockBot) AddPlugin(f Plugin) {} +func (mb *MockBot) Register(p Plugin, kind Kind, cb Callback) {} +func (mb *MockBot) Receive(kind Kind, msg msg.Message, args ...interface{}) {} +func (mb *MockBot) Filter(msg msg.Message, s string) string { return s } +func (mb *MockBot) LastMessage(ch string) (msg.Message, error) { return msg.Message{}, nil } +func (mb *MockBot) CheckAdmin(nick string) bool { return false } -func (mb *MockBot) React(channel, reaction string, message msg.Message) bool { +func (mb *MockBot) react(channel, reaction string, message msg.Message) (string, error) { mb.Reactions = append(mb.Reactions, reaction) - return false + return "", nil } -func (mb *MockBot) Edit(channel, newMessage, identifier string) bool { +func (mb *MockBot) edit(channel, newMessage, identifier string) (string, error) { isMessage := identifier[0] == 'm' if !isMessage && identifier[0] != 'a' { - log.Printf("failed to parse identifier: %s", identifier) - return false + err := fmt.Errorf("failed to parse identifier: %s", identifier) + log.Println(err) + return "", err } index, err := strconv.Atoi(strings.Split(identifier, "-")[1]) if err != nil { - log.Printf("failed to parse identifier: %s", identifier) - return false + err := fmt.Errorf("failed to parse identifier: %s", identifier) + log.Println(err) + return "", err } if isMessage { if index < len(mb.Messages) { mb.Messages[index] = newMessage } else { - return false + return "", fmt.Errorf("No message") } } else { if index < len(mb.Actions) { mb.Actions[index] = newMessage } else { - return false + return "", fmt.Errorf("No action") } } - return true -} - -func (mb *MockBot) ReplyMsgReceived(msg.Message, string) { - + return "", nil } func (mb *MockBot) GetEmojiList() map[string]string { return make(map[string]string) } diff --git a/go.mod b/go.mod index 313d566..45d8122 100644 --- a/go.mod +++ b/go.mod @@ -5,21 +5,21 @@ require ( github.com/boltdb/bolt v1.3.1 github.com/chrissexton/leftpad v0.0.0-20181207133115-1e93189d2fff github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-sql-driver/mysql v1.4.1 // indirect github.com/gorilla/websocket v1.4.0 // indirect github.com/jmoiron/sqlx v1.2.0 github.com/mattn/go-sqlite3 v1.10.0 github.com/mitchellh/mapstructure v1.1.2 // indirect github.com/mmcdole/gofeed v1.0.0-beta2 github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d // indirect github.com/stretchr/objx v0.1.1 // indirect - github.com/stretchr/testify v1.2.2 + github.com/stretchr/testify v1.3.0 github.com/velour/chat v0.0.0-20180713122344-fd1d1606cb89 github.com/velour/velour v0.0.0-20160303155839-8e090e68d158 github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 - github.com/yuin/gopher-lua v0.0.0-20181214045814-db9ae37725ec - golang.org/x/net v0.0.0-20181217023233-e147a9138326 // indirect + github.com/yuin/gopher-lua v0.0.0-20190125051437-7b9317363aa9 + golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect golang.org/x/text v0.3.0 // indirect gopkg.in/sourcemap.v1 v1.0.5 // indirect ) diff --git a/go.sum b/go.sum index 187d147..2b566da 100644 --- a/go.sum +++ b/go.sum @@ -7,9 +7,11 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2 github.com/chrissexton/leftpad v0.0.0-20181207133115-1e93189d2fff h1:+TEqaP0eO1unI7XHHFeMDhsxhLDIb0x8KYuZbqbAmxA= github.com/chrissexton/leftpad v0.0.0-20181207133115-1e93189d2fff/go.mod h1:QCRjR0b4qiJiNjuP7RFM89bh4UExGJalcWmYeSvlnRc= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= @@ -28,10 +30,13 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d h1:1VUlQbCfkoSGv7qP7Y+ro3ap1P1pPZxgdGVqiTVy5C4= github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/velour/chat v0.0.0-20180713122344-fd1d1606cb89 h1:3D3M900hEBJJAqyKl70QuRHi5weX9+ptlQI1v+FNcQ8= github.com/velour/chat v0.0.0-20180713122344-fd1d1606cb89/go.mod h1:ejwOYCjnDMyO5LXFXRARQJGBZ6xQJZ3rgAHE5drSuMM= github.com/velour/velour v0.0.0-20160303155839-8e090e68d158 h1:p3rTUXxzuKsBOsHlkly7+rj9wagFBKeIsCDKkDII9sw= @@ -40,10 +45,13 @@ github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 h1:noHsffKZsNfU38D github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7/go.mod h1:bbMEM6aU1WDF1ErA5YJ0p91652pGv140gGw4Ww3RGp8= github.com/yuin/gopher-lua v0.0.0-20181214045814-db9ae37725ec h1:vpF8Kxql6/3OvGH4y2SKtpN3WsB17mvJ8f8H1o2vucQ= github.com/yuin/gopher-lua v0.0.0-20181214045814-db9ae37725ec/go.mod h1:fFiAh+CowNFr0NK5VASokuwKwkbacRmHsVA7Yb1Tqac= +github.com/yuin/gopher-lua v0.0.0-20190125051437-7b9317363aa9/go.mod h1:fFiAh+CowNFr0NK5VASokuwKwkbacRmHsVA7Yb1Tqac= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181217023233-e147a9138326 h1:iCzOf0xz39Tstp+Tu/WwyGjUXCk34QhQORRxBeXXTA4= golang.org/x/net v0.0.0-20181217023233-e147a9138326/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= diff --git a/irc/irc.go b/irc/irc.go index 7a89110..58e1359 100644 --- a/irc/irc.go +++ b/irc/irc.go @@ -42,9 +42,7 @@ type Irc struct { config *config.Config quit chan bool - eventReceived func(msg.Message) - messageReceived func(msg.Message) - replyMessageReceived func(msg.Message, string) + event func(bot.Kind, msg.Message, ...interface{}) } func New(c *config.Config) *Irc { @@ -54,16 +52,20 @@ func New(c *config.Config) *Irc { return &i } -func (i *Irc) RegisterEventReceived(f func(msg.Message)) { - i.eventReceived = f +func (i *Irc) RegisterEvent(f func(bot.Kind, msg.Message, ...interface{})) { + i.event = f } -func (i *Irc) RegisterMessageReceived(f func(msg.Message)) { - i.messageReceived = f -} - -func (i *Irc) RegisterReplyMessageReceived(f func(msg.Message, string)) { - i.replyMessageReceived = f +func (i *Irc) Send(kind bot.Kind, args ...interface{}) (string, error) { + switch kind { + case bot.Reply: + case bot.Message: + return i.sendMessage(args[0].(string), args[1].(string)) + case bot.Action: + return i.sendAction(args[0].(string), args[1].(string)) + default: + } + return "", nil } func (i *Irc) JoinChannel(channel string) { @@ -71,7 +73,7 @@ func (i *Irc) JoinChannel(channel string) { i.Client.Out <- irc.Msg{Cmd: irc.JOIN, Args: []string{channel}} } -func (i *Irc) SendMessage(channel, message string) string { +func (i *Irc) sendMessage(channel, message string) (string, error) { for len(message) > 0 { m := irc.Msg{ Cmd: "PRIVMSG", @@ -95,42 +97,23 @@ func (i *Irc) SendMessage(channel, message string) string { i.Client.Out <- m } - return "NO_IRC_IDENTIFIERS" + return "NO_IRC_IDENTIFIERS", nil } // Sends action to channel -func (i *Irc) SendAction(channel, message string) string { +func (i *Irc) sendAction(channel, message string) (string, error) { message = actionPrefix + " " + message + "\x01" - i.SendMessage(channel, message) - return "NO_IRC_IDENTIFIERS" -} - -func (i *Irc) ReplyToMessageIdentifier(channel, message, identifier string) (string, bool) { - return "NO_IRC_IDENTIFIERS", false -} - -func (i *Irc) ReplyToMessage(channel, message string, replyTo msg.Message) (string, bool) { - return "NO_IRC_IDENTIFIERS", false -} - -func (i *Irc) React(channel, reaction string, message msg.Message) bool { - //we're not goign to do anything because it's IRC - return false -} - -func (i *Irc) Edit(channel, newMessage, identifier string) bool { - //we're not goign to do anything because it's IRC - return false + return i.sendMessage(channel, message) } func (i *Irc) GetEmojiList() map[string]string { - //we're not goign to do anything because it's IRC + //we're not going to do anything because it's IRC return make(map[string]string) } func (i *Irc) Serve() error { - if i.eventReceived == nil || i.messageReceived == nil { + if i.event == nil { return fmt.Errorf("Missing an event handler") } @@ -209,53 +192,53 @@ func (i *Irc) handleMsg(msg irc.Msg) { // OK, ignore case irc.ERR_NOSUCHNICK: - i.eventReceived(botMsg) + fallthrough case irc.ERR_NOSUCHCHANNEL: - i.eventReceived(botMsg) + fallthrough case irc.RPL_MOTD: - i.eventReceived(botMsg) + fallthrough case irc.RPL_NAMREPLY: - i.eventReceived(botMsg) + fallthrough case irc.RPL_TOPIC: - i.eventReceived(botMsg) + fallthrough case irc.KICK: - i.eventReceived(botMsg) + fallthrough case irc.TOPIC: - i.eventReceived(botMsg) + fallthrough case irc.MODE: - i.eventReceived(botMsg) + fallthrough case irc.JOIN: - i.eventReceived(botMsg) + fallthrough case irc.PART: - i.eventReceived(botMsg) + fallthrough + + case irc.NOTICE: + fallthrough + + case irc.NICK: + fallthrough + + case irc.RPL_WHOREPLY: + fallthrough + + case irc.RPL_ENDOFWHO: + i.event(bot.Event, botMsg) + + case irc.PRIVMSG: + i.event(bot.Message, botMsg) case irc.QUIT: os.Exit(1) - case irc.NOTICE: - i.eventReceived(botMsg) - - case irc.PRIVMSG: - i.messageReceived(botMsg) - - case irc.NICK: - i.eventReceived(botMsg) - - case irc.RPL_WHOREPLY: - i.eventReceived(botMsg) - - case irc.RPL_ENDOFWHO: - i.eventReceived(botMsg) - default: cmd := irc.CmdNames[msg.Cmd] log.Println("(" + cmd + ") " + msg.Raw) diff --git a/main.go b/main.go index c3d7ebe..6ab529d 100644 --- a/main.go +++ b/main.go @@ -78,32 +78,32 @@ func main() { b := bot.New(c, client) - b.AddHandler("admin", admin.New(b)) - b.AddHandler("first", first.New(b)) - b.AddHandler("leftpad", leftpad.New(b)) - b.AddHandler("talker", talker.New(b)) - b.AddHandler("dice", dice.New(b)) - b.AddHandler("picker", picker.New(b)) - b.AddHandler("beers", beers.New(b)) - b.AddHandler("remember", fact.NewRemember(b)) - b.AddHandler("your", your.New(b)) - b.AddHandler("counter", counter.New(b)) - b.AddHandler("reminder", reminder.New(b)) - b.AddHandler("babbler", babbler.New(b)) - b.AddHandler("zork", zork.New(b)) - b.AddHandler("rss", rss.New(b)) - b.AddHandler("reaction", reaction.New(b)) - b.AddHandler("emojifyme", emojifyme.New(b)) - b.AddHandler("twitch", twitch.New(b)) - b.AddHandler("inventory", inventory.New(b)) - b.AddHandler("rpgORdie", rpgORdie.New(b)) - b.AddHandler("sisyphus", sisyphus.New(b)) - b.AddHandler("tell", tell.New(b)) - b.AddHandler("couldashouldawoulda", couldashouldawoulda.New(b)) - b.AddHandler("nedepedia", nerdepedia.New(b)) + b.AddPlugin(admin.New(b)) + b.AddPlugin(emojifyme.New(b)) + b.AddPlugin(first.New(b)) + b.AddPlugin(leftpad.New(b)) + b.AddPlugin(talker.New(b)) + b.AddPlugin(dice.New(b)) + b.AddPlugin(picker.New(b)) + b.AddPlugin(beers.New(b)) + b.AddPlugin(fact.NewRemember(b)) + b.AddPlugin(your.New(b)) + b.AddPlugin(counter.New(b)) + b.AddPlugin(reminder.New(b)) + b.AddPlugin(babbler.New(b)) + b.AddPlugin(zork.New(b)) + b.AddPlugin(rss.New(b)) + b.AddPlugin(reaction.New(b)) + b.AddPlugin(twitch.New(b)) + b.AddPlugin(inventory.New(b)) + b.AddPlugin(rpgORdie.New(b)) + b.AddPlugin(sisyphus.New(b)) + b.AddPlugin(tell.New(b)) + b.AddPlugin(couldashouldawoulda.New(b)) + b.AddPlugin(nerdepedia.New(b)) // catches anything left, will always return true - b.AddHandler("factoid", fact.New(b)) - b.AddHandler("db", db.New(b)) + b.AddPlugin(fact.New(b)) + b.AddPlugin(db.New(b)) for { err := client.Serve() diff --git a/plugins/admin/admin.go b/plugins/admin/admin.go index a7e45c8..5de34f0 100644 --- a/plugins/admin/admin.go +++ b/plugins/admin/admin.go @@ -25,12 +25,14 @@ type AdminPlugin struct { } // NewAdminPlugin creates a new AdminPlugin with the Plugin interface -func New(bot bot.Bot) *AdminPlugin { +func New(b bot.Bot) *AdminPlugin { p := &AdminPlugin{ - Bot: bot, - db: bot.DB(), - cfg: bot.Config(), + Bot: b, + db: b.DB(), + cfg: b.Config(), } + b.Register(p, bot.Message, p.message) + b.Register(p, bot.Help, p.help) return p } @@ -44,7 +46,7 @@ var forbiddenKeys = map[string]bool{ // 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 *AdminPlugin) Message(message msg.Message) bool { +func (p *AdminPlugin) message(k bot.Kind, message msg.Message, args ...interface{}) bool { body := message.Body if p.quiet { @@ -62,7 +64,7 @@ func (p *AdminPlugin) Message(message msg.Message) bool { if strings.ToLower(body) == "shut up" { dur := time.Duration(p.cfg.GetInt("quietDuration", 5)) * time.Minute log.Printf("Going to sleep for %v, %v", dur, time.Now().Add(dur)) - p.Bot.SendMessage(message.Channel, "Okay. I'll be back later.") + p.Bot.Send(bot.Message, message.Channel, "Okay. I'll be back later.") p.quiet = true go func() { select { @@ -76,19 +78,19 @@ func (p *AdminPlugin) Message(message msg.Message) bool { parts := strings.Split(body, " ") if parts[0] == "set" && len(parts) > 2 && forbiddenKeys[parts[1]] { - p.Bot.SendMessage(message.Channel, "You cannot access that key") + p.Bot.Send(bot.Message, message.Channel, "You cannot access that key") return true } else if parts[0] == "set" && len(parts) > 2 { p.cfg.Set(parts[1], strings.Join(parts[2:], " ")) - p.Bot.SendMessage(message.Channel, fmt.Sprintf("Set %s", parts[1])) + p.Bot.Send(bot.Message, message.Channel, fmt.Sprintf("Set %s", parts[1])) return true } if parts[0] == "get" && len(parts) == 2 && forbiddenKeys[parts[1]] { - p.Bot.SendMessage(message.Channel, "You cannot access that key") + p.Bot.Send(bot.Message, message.Channel, "You cannot access that key") return true } else if parts[0] == "get" && len(parts) == 2 { v := p.cfg.Get(parts[1], "") - p.Bot.SendMessage(message.Channel, fmt.Sprintf("%s: %s", parts[1], v)) + p.Bot.Send(bot.Message, message.Channel, fmt.Sprintf("%s: %s", parts[1], v)) return true } @@ -102,10 +104,10 @@ func (p *AdminPlugin) handleVariables(message msg.Message) bool { _, err := p.db.Exec(`delete from variables where name=? and value=?`, variable, value) if err != nil { - p.Bot.SendMessage(message.Channel, "I'm broke and need attention in my variable creation code.") + p.Bot.Send(bot.Message, message.Channel, "I'm broke and need attention in my variable creation code.") log.Println("[admin]: ", err) } else { - p.Bot.SendMessage(message.Channel, "Removed.") + p.Bot.Send(bot.Message, message.Channel, "Removed.") } return true @@ -123,43 +125,32 @@ func (p *AdminPlugin) handleVariables(message msg.Message) bool { row := p.db.QueryRow(`select count(*) from variables where value = ?`, variable, value) err := row.Scan(&count) if err != nil { - p.Bot.SendMessage(message.Channel, "I'm broke and need attention in my variable creation code.") + p.Bot.Send(bot.Message, message.Channel, "I'm broke and need attention in my variable creation code.") log.Println("[admin]: ", err) return true } if count > 0 { - p.Bot.SendMessage(message.Channel, "I've already got that one.") + p.Bot.Send(bot.Message, message.Channel, "I've already got that one.") } else { _, err := p.db.Exec(`INSERT INTO variables (name, value) VALUES (?, ?)`, variable, value) if err != nil { - p.Bot.SendMessage(message.Channel, "I'm broke and need attention in my variable creation code.") + p.Bot.Send(bot.Message, message.Channel, "I'm broke and need attention in my variable creation code.") log.Println("[admin]: ", err) return true } - p.Bot.SendMessage(message.Channel, "Added.") + p.Bot.Send(bot.Message, message.Channel, "Added.") } return true } // Help responds to help requests. Every plugin must implement a help function. -func (p *AdminPlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "This does super secret things that you're not allowed to know about.") -} - -// Empty event handler because this plugin does not do anything on event recv -func (p *AdminPlugin) Event(kind string, message msg.Message) bool { - return false -} - -// Handler for bot's own messages -func (p *AdminPlugin) BotMessage(message msg.Message) bool { - return false +func (p *AdminPlugin) help(kind bot.Kind, m msg.Message, args ...interface{}) bool { + p.Bot.Send(bot.Message, m.Channel, "This does super secret things that you're not allowed to know about.") + return true } // Register any web URLs desired func (p *AdminPlugin) RegisterWeb() *string { return nil } - -func (p *AdminPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/admin/admin_test.go b/plugins/admin/admin_test.go index f4ce489..069b21d 100644 --- a/plugins/admin/admin_test.go +++ b/plugins/admin/admin_test.go @@ -22,12 +22,12 @@ func setup(t *testing.T) (*AdminPlugin, *bot.MockBot) { return a, mb } -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -38,7 +38,7 @@ func makeMessage(payload string) msg.Message { func TestSet(t *testing.T) { a, mb := setup(t) expected := "test value" - a.Message(makeMessage("!set test.key " + expected)) + a.message(makeMessage("!set test.key " + expected)) actual := mb.Config().Get("test.key", "ERR") assert.Equal(t, expected, actual) } @@ -47,7 +47,7 @@ func TestGetValue(t *testing.T) { a, mb := setup(t) expected := "value" mb.Config().Set("test.key", "value") - a.Message(makeMessage("!get test.key")) + a.message(makeMessage("!get test.key")) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], expected) } @@ -55,7 +55,7 @@ func TestGetValue(t *testing.T) { func TestGetEmpty(t *testing.T) { a, mb := setup(t) expected := "test.key: " - a.Message(makeMessage("!get test.key")) + a.message(makeMessage("!get test.key")) assert.Len(t, mb.Messages, 1) assert.Equal(t, expected, mb.Messages[0]) } @@ -63,7 +63,7 @@ func TestGetEmpty(t *testing.T) { func TestGetForbidden(t *testing.T) { a, mb := setup(t) expected := "cannot access" - a.Message(makeMessage("!get slack.token")) + a.message(makeMessage("!get slack.token")) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], expected) } diff --git a/plugins/babbler/babbler.go b/plugins/babbler/babbler.go index 78ba647..efc0c63 100644 --- a/plugins/babbler/babbler.go +++ b/plugins/babbler/babbler.go @@ -52,24 +52,24 @@ type BabblerArc struct { Frequency int64 `db:"frequency"` } -func New(bot bot.Bot) *BabblerPlugin { +func New(b bot.Bot) *BabblerPlugin { log.SetFlags(log.LstdFlags | log.Lshortfile) - if _, err := bot.DB().Exec(`create table if not exists babblers ( + if _, err := b.DB().Exec(`create table if not exists babblers ( id integer primary key, babbler string );`); err != nil { log.Fatal(err) } - if _, err := bot.DB().Exec(`create table if not exists babblerWords ( + if _, err := b.DB().Exec(`create table if not exists babblerWords ( id integer primary key, word string );`); err != nil { log.Fatal(err) } - if _, err := bot.DB().Exec(`create table if not exists babblerNodes ( + if _, err := b.DB().Exec(`create table if not exists babblerNodes ( id integer primary key, babblerId integer, wordId integer, @@ -79,7 +79,7 @@ func New(bot bot.Bot) *BabblerPlugin { log.Fatal(err) } - if _, err := bot.DB().Exec(`create table if not exists babblerArcs ( + if _, err := b.DB().Exec(`create table if not exists babblerArcs ( id integer primary key, fromNodeId integer, toNodeId interger, @@ -89,17 +89,20 @@ func New(bot bot.Bot) *BabblerPlugin { } plugin := &BabblerPlugin{ - Bot: bot, - db: bot.DB(), + Bot: b, + db: b.DB(), WithGoRoutines: true, } plugin.createNewWord("") + b.Register(plugin, bot.Message, plugin.message) + b.Register(plugin, bot.Help, plugin.help) + return plugin } -func (p *BabblerPlugin) Message(message msg.Message) bool { +func (p *BabblerPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { lowercase := strings.ToLower(message.Body) tokens := strings.Fields(lowercase) numTokens := len(tokens) @@ -141,12 +144,12 @@ func (p *BabblerPlugin) Message(message msg.Message) bool { } if saidSomething { - p.Bot.SendMessage(message.Channel, saidWhat) + p.Bot.Send(bot.Message, message.Channel, saidWhat) } return saidSomething } -func (p *BabblerPlugin) Help(channel string, parts []string) { +func (p *BabblerPlugin) help(kind bot.Kind, msg msg.Message, args ...interface{}) bool { commands := []string{ "initialize babbler for seabass", "merge babbler drseabass into seabass", @@ -155,15 +158,8 @@ func (p *BabblerPlugin) Help(channel string, parts []string) { "seabass says-middle-out ...", "seabass says-bridge ... | ...", } - p.Bot.SendMessage(channel, strings.Join(commands, "\n\n")) -} - -func (p *BabblerPlugin) Event(kind string, message msg.Message) bool { - return false -} - -func (p *BabblerPlugin) BotMessage(message msg.Message) bool { - return false + p.Bot.Send(bot.Message, msg.Channel, strings.Join(commands, "\n\n")) + return true } func (p *BabblerPlugin) RegisterWeb() *string { @@ -934,5 +930,3 @@ func (p *BabblerPlugin) babbleSeedBookends(babblerName string, start, end []stri return strings.Join(words, " "), nil } - -func (p *BabblerPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/babbler/babbler_test.go b/plugins/babbler/babbler_test.go index b07b75f..1631257 100644 --- a/plugins/babbler/babbler_test.go +++ b/plugins/babbler/babbler_test.go @@ -12,12 +12,12 @@ import ( "github.com/velour/catbase/bot/user" ) -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -41,7 +41,7 @@ func TestBabblerNoBabbler(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - bp.Message(makeMessage("!seabass2 says")) + bp.message(makeMessage("!seabass2 says")) res := assert.Len(t, mb.Messages, 0) assert.True(t, res) // assert.Contains(t, mb.Messages[0], "seabass2 babbler not found") @@ -51,9 +51,9 @@ func TestBabblerNothingSaid(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - res := bp.Message(makeMessage("initialize babbler for seabass")) + res := bp.message(makeMessage("initialize babbler for seabass")) assert.True(t, res) - res = bp.Message(makeMessage("!seabass says")) + res = bp.message(makeMessage("!seabass says")) assert.True(t, res) assert.Len(t, mb.Messages, 2) assert.Contains(t, mb.Messages[0], "okay.") @@ -64,14 +64,14 @@ func TestBabbler(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("This is a message") + k, seabass := makeMessage("This is a message") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) + res := bp.message(k, seabass) seabass.Body = "This is another message" - res = bp.Message(seabass) + res = bp.message(k, seabass) seabass.Body = "This is a long message" - res = bp.Message(seabass) - res = bp.Message(makeMessage("!seabass says")) + res = bp.message(k, seabass) + res = bp.message(makeMessage("!seabass says")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "this is") @@ -82,14 +82,14 @@ func TestBabblerSeed(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("This is a message") + k, seabass := makeMessage("This is a message") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) + res := bp.message(k, seabass) seabass.Body = "This is another message" - res = bp.Message(seabass) + res = bp.message(k, seabass) seabass.Body = "This is a long message" - res = bp.Message(seabass) - res = bp.Message(makeMessage("!seabass says long")) + res = bp.message(k, seabass) + res = bp.message(makeMessage("!seabass says long")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "long message") @@ -99,14 +99,14 @@ func TestBabblerMultiSeed(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("This is a message") + k, seabass := makeMessage("This is a message") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) + res := bp.message(k, seabass) seabass.Body = "This is another message" - res = bp.Message(seabass) + res = bp.message(k, seabass) seabass.Body = "This is a long message" - res = bp.Message(seabass) - res = bp.Message(makeMessage("!seabass says This is a long")) + res = bp.message(k, seabass) + res = bp.message(makeMessage("!seabass says This is a long")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "this is a long message") @@ -116,14 +116,14 @@ func TestBabblerMultiSeed2(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("This is a message") + k, seabass := makeMessage("This is a message") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) + res := bp.message(k, seabass) seabass.Body = "This is another message" - res = bp.Message(seabass) + res = bp.message(k, seabass) seabass.Body = "This is a long message" - res = bp.Message(seabass) - res = bp.Message(makeMessage("!seabass says is a long")) + res = bp.message(k, seabass) + res = bp.message(makeMessage("!seabass says is a long")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "is a long message") @@ -133,14 +133,14 @@ func TestBabblerBadSeed(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("This is a message") + k, seabass := makeMessage("This is a message") seabass.User = &user.User{Name: "seabass"} - bp.Message(seabass) + bp.message(k, seabass) seabass.Body = "This is another message" - bp.Message(seabass) + bp.message(k, seabass) seabass.Body = "This is a long message" - bp.Message(seabass) - bp.Message(makeMessage("!seabass says noooo this is bad")) + bp.message(k, seabass) + bp.message(makeMessage("!seabass says noooo this is bad")) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "seabass never said 'noooo this is bad'") } @@ -149,14 +149,14 @@ func TestBabblerBadSeed2(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("This is a message") + k, seabass := makeMessage("This is a message") seabass.User = &user.User{Name: "seabass"} - bp.Message(seabass) + bp.message(k, seabass) seabass.Body = "This is another message" - bp.Message(seabass) + bp.message(k, seabass) seabass.Body = "This is a long message" - bp.Message(seabass) - bp.Message(makeMessage("!seabass says This is a really")) + bp.message(k, seabass) + bp.message(makeMessage("!seabass says This is a really")) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "seabass never said 'this is a really'") } @@ -165,15 +165,15 @@ func TestBabblerSuffixSeed(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("This is message one") + k, seabass := makeMessage("This is message one") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) + res := bp.message(k, seabass) seabass.Body = "It's easier to test with unique messages" - res = bp.Message(seabass) + res = bp.message(k, seabass) seabass.Body = "hi there" - res = bp.Message(seabass) - res = bp.Message(makeMessage("!seabass says-tail message one")) - res = bp.Message(makeMessage("!seabass says-tail with unique")) + res = bp.message(k, seabass) + res = bp.message(makeMessage("!seabass says-tail message one")) + res = bp.message(makeMessage("!seabass says-tail with unique")) assert.Len(t, mb.Messages, 2) assert.True(t, res) assert.Contains(t, mb.Messages[0], "this is message one") @@ -184,14 +184,14 @@ func TestBabblerBadSuffixSeed(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("This is message one") + k, seabass := makeMessage("This is message one") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) + res := bp.message(k, seabass) seabass.Body = "It's easier to test with unique messages" - res = bp.Message(seabass) + res = bp.message(k, seabass) seabass.Body = "hi there" - res = bp.Message(seabass) - res = bp.Message(makeMessage("!seabass says-tail anything true")) + res = bp.message(k, seabass) + res = bp.message(makeMessage("!seabass says-tail anything true")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "seabass never said 'anything true'") @@ -201,10 +201,10 @@ func TestBabblerBookendSeed(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("It's easier to test with unique messages") + k, seabass := makeMessage("It's easier to test with unique messages") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) - res = bp.Message(makeMessage("!seabass says-bridge It's easier | unique messages")) + res := bp.message(k, seabass) + res = bp.message(makeMessage("!seabass says-bridge It's easier | unique messages")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "it's easier to test with unique messages") @@ -214,10 +214,10 @@ func TestBabblerBookendSeedShort(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("It's easier to test with unique messages") + k, seabass := makeMessage("It's easier to test with unique messages") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) - res = bp.Message(makeMessage("!seabass says-bridge It's easier to test with | unique messages")) + res := bp.message(k, seabass) + res = bp.message(makeMessage("!seabass says-bridge It's easier to test with | unique messages")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "it's easier to test with unique messages") @@ -227,10 +227,10 @@ func TestBabblerBadBookendSeed(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("It's easier to test with unique messages") + k, seabass := makeMessage("It's easier to test with unique messages") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) - res = bp.Message(makeMessage("!seabass says-bridge It's easier | not unique messages")) + res := bp.message(k, seabass) + res = bp.message(makeMessage("!seabass says-bridge It's easier | not unique messages")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "seabass never said 'it's easier ... not unique messages'") @@ -240,10 +240,10 @@ func TestBabblerMiddleOutSeed(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("It's easier to test with unique messages") + k, seabass := makeMessage("It's easier to test with unique messages") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) - res = bp.Message(makeMessage("!seabass says-middle-out test with")) + res := bp.message(k, seabass) + res = bp.message(makeMessage("!seabass says-middle-out test with")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "it's easier to test with unique messages") @@ -253,10 +253,10 @@ func TestBabblerBadMiddleOutSeed(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("It's easier to test with unique messages") + k, seabass := makeMessage("It's easier to test with unique messages") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) - res = bp.Message(makeMessage("!seabass says-middle-out anything true")) + res := bp.message(k, seabass) + res = bp.message(makeMessage("!seabass says-middle-out anything true")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Equal(t, mb.Messages[0], "seabass never said 'anything true'") @@ -266,10 +266,10 @@ func TestBabblerBatch(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage("batch learn for seabass This is a message! This is another message. This is not a long message? This is not a message! This is not another message. This is a long message?") - res := bp.Message(seabass) + k, seabass := makeMessage("batch learn for seabass This is a message! This is another message. This is not a long message? This is not a message! This is not another message. This is a long message?") + res := bp.message(k, seabass) assert.Len(t, mb.Messages, 1) - res = bp.Message(makeMessage("!seabass says")) + res = bp.message(makeMessage("!seabass says")) assert.Len(t, mb.Messages, 2) assert.True(t, res) assert.Contains(t, mb.Messages[1], "this is") @@ -281,23 +281,23 @@ func TestBabblerMerge(t *testing.T) { bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - seabass := makeMessage(" This is a message") + k, seabass := makeMessage(" This is a message") seabass.User = &user.User{Name: "seabass"} - res := bp.Message(seabass) + res := bp.message(k, seabass) assert.Len(t, mb.Messages, 0) seabass.Body = " This is another message" - res = bp.Message(seabass) + res = bp.message(k, seabass) seabass.Body = " This is a long message" - res = bp.Message(seabass) + res = bp.message(k, seabass) - res = bp.Message(makeMessage("!merge babbler seabass into seabass2")) + res = bp.message(makeMessage("!merge babbler seabass into seabass2")) assert.True(t, res) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "mooooiggged") - res = bp.Message(makeMessage("!seabass2 says")) + res = bp.message(makeMessage("!seabass2 says")) assert.True(t, res) assert.Len(t, mb.Messages, 2) @@ -309,24 +309,10 @@ func TestHelp(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) assert.NotNil(t, bp) - bp.Help("channel", []string{}) + bp.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) assert.Len(t, mb.Messages, 1) } -func TestBotMessage(t *testing.T) { - mb := bot.NewMockBot() - bp := newBabblerPlugin(mb) - assert.NotNil(t, bp) - assert.False(t, bp.BotMessage(makeMessage("test"))) -} - -func TestEvent(t *testing.T) { - mb := bot.NewMockBot() - bp := newBabblerPlugin(mb) - assert.NotNil(t, bp) - assert.False(t, bp.Event("dummy", makeMessage("test"))) -} - func TestRegisterWeb(t *testing.T) { mb := bot.NewMockBot() bp := newBabblerPlugin(mb) diff --git a/plugins/beers/beers.go b/plugins/beers/beers.go index d4da158..7736936 100644 --- a/plugins/beers/beers.go +++ b/plugins/beers/beers.go @@ -38,8 +38,8 @@ type untappdUser struct { } // New BeersPlugin creates a new BeersPlugin with the Plugin interface -func New(bot bot.Bot) *BeersPlugin { - if _, err := bot.DB().Exec(`create table if not exists untappd ( +func New(b bot.Bot) *BeersPlugin { + if _, err := b.DB().Exec(`create table if not exists untappd ( id integer primary key, untappdUser string, channel string, @@ -49,19 +49,21 @@ func New(bot bot.Bot) *BeersPlugin { log.Fatal(err) } p := BeersPlugin{ - Bot: bot, - db: bot.DB(), + Bot: b, + db: b.DB(), } - for _, channel := range bot.Config().GetArray("Untappd.Channels", []string{}) { + for _, channel := range b.Config().GetArray("Untappd.Channels", []string{}) { go p.untappdLoop(channel) } + b.Register(p, bot.Message, p.message) + b.Register(p, bot.Help, p.help) return &p } // 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 *BeersPlugin) Message(message msg.Message) bool { +func (p *BeersPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { parts := strings.Fields(message.Body) if len(parts) == 0 { @@ -81,13 +83,13 @@ func (p *BeersPlugin) Message(message msg.Message) bool { count, err := strconv.Atoi(parts[2]) if err != nil { // if it's not a number, maybe it's a nick! - p.Bot.SendMessage(channel, "Sorry, that didn't make any sense.") + p.Bot.Send(bot.Message, channel, "Sorry, that didn't make any sense.") } if count < 0 { // you can't be negative msg := fmt.Sprintf("Sorry %s, you can't have negative beers!", nick) - p.Bot.SendMessage(channel, msg) + p.Bot.Send(bot.Message, channel, msg) return true } if parts[1] == "+=" { @@ -101,14 +103,14 @@ func (p *BeersPlugin) Message(message msg.Message) bool { p.randomReply(channel) } } else { - p.Bot.SendMessage(channel, "I don't know your math.") + p.Bot.Send(bot.Message, channel, "I don't know your math.") } } else if len(parts) == 2 { if p.doIKnow(parts[1]) { p.reportCount(parts[1], channel, false) } else { msg := fmt.Sprintf("Sorry, I don't know %s.", parts[1]) - p.Bot.SendMessage(channel, msg) + p.Bot.Send(bot.Message, channel, msg) } } else if len(parts) == 1 { p.reportCount(nick, channel, true) @@ -132,7 +134,7 @@ func (p *BeersPlugin) Message(message msg.Message) bool { channel := message.Channel if len(parts) < 2 { - p.Bot.SendMessage(channel, "You must also provide a user name.") + p.Bot.Send(bot.Message, channel, "You must also provide a user name.") } else if len(parts) == 3 { chanNick = parts[2] } else if len(parts) == 4 { @@ -154,7 +156,7 @@ func (p *BeersPlugin) Message(message msg.Message) bool { log.Println("Error registering untappd: ", err) } if count > 0 { - p.Bot.SendMessage(channel, "I'm already watching you.") + p.Bot.Send(bot.Message, channel, "I'm already watching you.") return true } _, err = p.db.Exec(`insert into untappd ( @@ -170,11 +172,11 @@ func (p *BeersPlugin) Message(message msg.Message) bool { ) if err != nil { log.Println("Error registering untappd: ", err) - p.Bot.SendMessage(channel, "I can't see.") + p.Bot.Send(bot.Message, channel, "I can't see.") return true } - p.Bot.SendMessage(channel, "I'll be watching you.") + p.Bot.Send(bot.Message, channel, "I'll be watching you.") p.checkUntappd(channel) @@ -190,17 +192,13 @@ func (p *BeersPlugin) Message(message msg.Message) bool { return false } -// Empty event handler because this plugin does not do anything on event recv -func (p *BeersPlugin) Event(kind string, message msg.Message) bool { - return false -} - // Help responds to help requests. Every plugin must implement a help function. -func (p *BeersPlugin) Help(channel string, parts []string) { +func (p *BeersPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { msg := "Beers: imbibe by using either beers +=,=,++ or with the !imbibe/drink " + "commands. I'll keep a count of how many beers you've had and then if you want " + "to reset, just !puke it all up!" - p.Bot.SendMessage(channel, msg) + p.Bot.Send(bot.Message, message.Channel, msg) + return true } func getUserBeers(db *sqlx.DB, user string) counter.Item { @@ -239,13 +237,13 @@ func (p *BeersPlugin) reportCount(nick, channel string, himself bool) { msg = fmt.Sprintf("You've had %d beers so far, %s.", beers, nick) } } - p.Bot.SendMessage(channel, msg) + p.Bot.Send(bot.Message, channel, msg) } func (p *BeersPlugin) puke(user string, channel string) { p.setBeers(user, 0) msg := fmt.Sprintf("Ohhhhhh, and a reversal of fortune for %s!", user) - p.Bot.SendMessage(channel, msg) + p.Bot.Send(bot.Message, channel, msg) } func (p *BeersPlugin) doIKnow(nick string) bool { @@ -260,7 +258,7 @@ func (p *BeersPlugin) doIKnow(nick string) bool { // Sends random affirmation to the channel. This could be better (with a datastore for sayings) func (p *BeersPlugin) randomReply(channel string) { replies := []string{"ZIGGY! ZAGGY!", "HIC!", "Stay thirsty, my friend!"} - p.Bot.SendMessage(channel, replies[rand.Intn(len(replies))]) + p.Bot.Send(bot.Message, channel, replies[rand.Intn(len(replies))]) } type checkin struct { @@ -421,7 +419,7 @@ func (p *BeersPlugin) checkUntappd(channel string) { } log.Println("checkin id:", checkin.Checkin_id, "Message:", msg) - p.Bot.SendMessage(channel, msg) + p.Bot.Send(bot.Message, channel, msg) } } @@ -439,14 +437,7 @@ func (p *BeersPlugin) untappdLoop(channel string) { } } -// Handler for bot's own messages -func (p *BeersPlugin) BotMessage(message msg.Message) bool { - return false -} - // Register any web URLs desired -func (p *BeersPlugin) RegisterWeb() *string { +func (p BeersPlugin) RegisterWeb() *string { return nil } - -func (p *BeersPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/beers/beers_test.go b/plugins/beers/beers_test.go index 99964e3..bb34008 100644 --- a/plugins/beers/beers_test.go +++ b/plugins/beers/beers_test.go @@ -13,12 +13,12 @@ import ( "github.com/velour/catbase/plugins/counter" ) -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -31,8 +31,8 @@ func makeBeersPlugin(t *testing.T) (*BeersPlugin, *bot.MockBot) { counter.New(mb) mb.DB().MustExec(`delete from counter; delete from counter_alias;`) b := New(mb) - b.Message(makeMessage("!mkalias beer :beer:")) - b.Message(makeMessage("!mkalias beers :beer:")) + b.message(makeMessage("!mkalias beer :beer:")) + b.message(makeMessage("!mkalias beers :beer:")) return b, mb } @@ -49,9 +49,9 @@ func TestCounter(t *testing.T) { func TestImbibe(t *testing.T) { b, mb := makeBeersPlugin(t) - b.Message(makeMessage("!imbibe")) + b.message(makeMessage("!imbibe")) assert.Len(t, mb.Messages, 1) - b.Message(makeMessage("!imbibe")) + b.message(makeMessage("!imbibe")) assert.Len(t, mb.Messages, 2) it, err := counter.GetItem(mb.DB(), "tester", itemName) assert.Nil(t, err) @@ -59,7 +59,7 @@ func TestImbibe(t *testing.T) { } func TestEq(t *testing.T) { b, mb := makeBeersPlugin(t) - b.Message(makeMessage("!beers = 3")) + b.message(makeMessage("!beers = 3")) assert.Len(t, mb.Messages, 1) it, err := counter.GetItem(mb.DB(), "tester", itemName) assert.Nil(t, err) @@ -68,7 +68,7 @@ func TestEq(t *testing.T) { func TestEqNeg(t *testing.T) { b, mb := makeBeersPlugin(t) - b.Message(makeMessage("!beers = -3")) + b.message(makeMessage("!beers = -3")) assert.Len(t, mb.Messages, 1) it, err := counter.GetItem(mb.DB(), "tester", itemName) assert.Nil(t, err) @@ -77,8 +77,8 @@ func TestEqNeg(t *testing.T) { func TestEqZero(t *testing.T) { b, mb := makeBeersPlugin(t) - b.Message(makeMessage("beers += 5")) - b.Message(makeMessage("!beers = 0")) + b.message(makeMessage("beers += 5")) + b.message(makeMessage("!beers = 0")) assert.Len(t, mb.Messages, 2) assert.Contains(t, mb.Messages[1], "reversal of fortune") it, err := counter.GetItem(mb.DB(), "tester", itemName) @@ -88,9 +88,9 @@ func TestEqZero(t *testing.T) { func TestBeersPlusEq(t *testing.T) { b, mb := makeBeersPlugin(t) - b.Message(makeMessage("beers += 5")) + b.message(makeMessage("beers += 5")) assert.Len(t, mb.Messages, 1) - b.Message(makeMessage("beers += 5")) + b.message(makeMessage("beers += 5")) assert.Len(t, mb.Messages, 2) it, err := counter.GetItem(mb.DB(), "tester", itemName) assert.Nil(t, err) @@ -99,11 +99,11 @@ func TestBeersPlusEq(t *testing.T) { func TestPuke(t *testing.T) { b, mb := makeBeersPlugin(t) - b.Message(makeMessage("beers += 5")) + b.message(makeMessage("beers += 5")) it, err := counter.GetItem(mb.DB(), "tester", itemName) assert.Nil(t, err) assert.Equal(t, 5, it.Count) - b.Message(makeMessage("puke")) + b.message(makeMessage("puke")) it, err = counter.GetItem(mb.DB(), "tester", itemName) assert.Nil(t, err) assert.Equal(t, 0, it.Count) @@ -111,30 +111,20 @@ func TestPuke(t *testing.T) { func TestBeersReport(t *testing.T) { b, mb := makeBeersPlugin(t) - b.Message(makeMessage("beers += 5")) + b.message(makeMessage("beers += 5")) it, err := counter.GetItem(mb.DB(), "tester", itemName) assert.Nil(t, err) assert.Equal(t, 5, it.Count) - b.Message(makeMessage("beers")) + b.message(makeMessage("beers")) assert.Contains(t, mb.Messages[1], "5 beers") } func TestHelp(t *testing.T) { b, mb := makeBeersPlugin(t) - b.Help("channel", []string{}) + b.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) assert.Len(t, mb.Messages, 1) } -func TestBotMessage(t *testing.T) { - b, _ := makeBeersPlugin(t) - assert.False(t, b.BotMessage(makeMessage("test"))) -} - -func TestEvent(t *testing.T) { - b, _ := makeBeersPlugin(t) - assert.False(t, b.Event("dummy", makeMessage("test"))) -} - func TestRegisterWeb(t *testing.T) { b, _ := makeBeersPlugin(t) assert.Nil(t, b.RegisterWeb()) diff --git a/plugins/couldashouldawoulda/csw.go b/plugins/couldashouldawoulda/csw.go index 455c45d..9d2d7a3 100644 --- a/plugins/couldashouldawoulda/csw.go +++ b/plugins/couldashouldawoulda/csw.go @@ -17,13 +17,15 @@ type CSWPlugin struct { Config *config.Config } -func New(bot bot.Bot) *CSWPlugin { - return &CSWPlugin{ - Bot: bot, +func New(b bot.Bot) *CSWPlugin { + csw := &CSWPlugin{ + Bot: b, } + b.Register(csw, bot.Message, csw.message) + return csw } -func (p *CSWPlugin) Message(message msg.Message) bool { +func (p *CSWPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { if !message.Command { return false } @@ -63,25 +65,13 @@ func (p *CSWPlugin) Message(message msg.Message) bool { } } - p.Bot.SendMessage(message.Channel, responses[rand.Intn(len(responses))]) + p.Bot.Send(bot.Message, message.Channel, responses[rand.Intn(len(responses))]) return true } return false } -func (p *CSWPlugin) Help(channel string, parts []string) {} - -func (p *CSWPlugin) Event(kind string, message msg.Message) bool { - return false -} - -func (p *CSWPlugin) BotMessage(message msg.Message) bool { - return false -} - -func (p *CSWPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } - func (p *CSWPlugin) RegisterWeb() *string { return nil } diff --git a/plugins/couldashouldawoulda/csw_test.go b/plugins/couldashouldawoulda/csw_test.go index 73b5261..de60dc9 100644 --- a/plugins/couldashouldawoulda/csw_test.go +++ b/plugins/couldashouldawoulda/csw_test.go @@ -12,12 +12,12 @@ import ( "github.com/velour/catbase/bot/user" ) -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -29,7 +29,7 @@ func Test0(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!should I drink a beer?")) + res := c.message(makeMessage("!should I drink a beer?")) assert.Len(t, mb.Messages, 1) assert.True(t, res) possibilities := []string{"Yes.", "No.", "Maybe.", "For fucks sake, how should I know?"} @@ -47,7 +47,7 @@ func Test1(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!should I drink a beer or a bourbon?")) + res := c.message(makeMessage("!should I drink a beer or a bourbon?")) assert.Len(t, mb.Messages, 1) assert.True(t, res) possibilities := []string{"The former.", "The latter.", "Obviously the former.", "Clearly the latter.", "Can't it be both?"} @@ -65,7 +65,7 @@ func Test2(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!could I drink a beer or a bourbon?")) + res := c.message(makeMessage("!could I drink a beer or a bourbon?")) assert.Len(t, mb.Messages, 1) assert.True(t, res) possibilities := []string{"Yes.", "No.", "Maybe.", "For fucks sake, how should I know?"} @@ -83,7 +83,7 @@ func Test3(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!would I die if I drank too much bourbon?")) + res := c.message(makeMessage("!would I die if I drank too much bourbon?")) assert.Len(t, mb.Messages, 1) assert.True(t, res) possibilities := []string{"Yes.", "No.", "Maybe.", "For fucks sake, how should I know?"} @@ -101,7 +101,7 @@ func Test4(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!would I die or be sick if I drank all the bourbon?")) + res := c.message(makeMessage("!would I die or be sick if I drank all the bourbon?")) assert.Len(t, mb.Messages, 1) assert.True(t, res) possibilities := []string{"The former.", "The latter.", "Obviously the former.", "Clearly the latter.", "Can't it be both?"} @@ -119,7 +119,7 @@ func Test5(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!should I have another beer or bourbon or tequila?")) + res := c.message(makeMessage("!should I have another beer or bourbon or tequila?")) assert.Len(t, mb.Messages, 1) assert.True(t, res) possibilities := []string{"I'd say option", "You'd be an idiot not to choose the"} diff --git a/plugins/counter/counter.go b/plugins/counter/counter.go index 2c7668a..6d0e0ed 100644 --- a/plugins/counter/counter.go +++ b/plugins/counter/counter.go @@ -172,31 +172,34 @@ func (i *Item) Delete() error { } // NewCounterPlugin creates a new CounterPlugin with the Plugin interface -func New(bot bot.Bot) *CounterPlugin { - tx := bot.DB().MustBegin() - bot.DB().MustExec(`create table if not exists counter ( +func New(b bot.Bot) *CounterPlugin { + tx := b.DB().MustBegin() + b.DB().MustExec(`create table if not exists counter ( id integer primary key, nick string, item string, count integer );`) - bot.DB().MustExec(`create table if not exists counter_alias ( + b.DB().MustExec(`create table if not exists counter_alias ( id integer PRIMARY KEY AUTOINCREMENT, item string NOT NULL UNIQUE, points_to string NOT NULL );`) tx.Commit() - return &CounterPlugin{ - Bot: bot, - DB: bot.DB(), + cp := &CounterPlugin{ + Bot: b, + DB: b.DB(), } + b.Register(cp, bot.Message, cp.message) + b.Register(cp, bot.Help, cp.help) + return cp } // 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 *CounterPlugin) Message(message msg.Message) bool { +func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { // This bot does not reply to anything nick := message.User.Name channel := message.Channel @@ -211,7 +214,7 @@ func (p *CounterPlugin) Message(message msg.Message) bool { log.Println(err) return false } - p.Bot.SendMessage(channel, fmt.Sprintf("Created alias %s -> %s", + p.Bot.Send(bot.Message, channel, fmt.Sprintf("Created alias %s -> %s", parts[1], parts[2])) return true } else if strings.ToLower(parts[0]) == "leaderboard" { @@ -241,7 +244,7 @@ func (p *CounterPlugin) Message(message msg.Message) bool { it.Item, ) } - p.Bot.SendMessage(channel, out) + p.Bot.Send(bot.Message, channel, out) return true } else if match := teaMatcher.MatchString(message.Body); match { // check for tea match TTT @@ -250,14 +253,14 @@ func (p *CounterPlugin) Message(message msg.Message) bool { items, err := GetItems(p.DB, strings.ToLower(nick)) if err != nil { log.Printf("Error getting items to reset %s: %s", nick, err) - p.Bot.SendMessage(channel, "Something is technically wrong with your counters.") + p.Bot.Send(bot.Message, channel, "Something is technically wrong with your counters.") return true } log.Printf("Items: %+v", items) for _, item := range items { item.Delete() } - p.Bot.SendMessage(channel, fmt.Sprintf("%s, you are as new, my son.", nick)) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s, you are as new, my son.", nick)) return true } else if message.Command && parts[0] == "inspect" && len(parts) == 2 { var subject string @@ -273,7 +276,7 @@ func (p *CounterPlugin) Message(message msg.Message) bool { items, err := GetItems(p.DB, subject) if err != nil { log.Fatalf("Error retrieving items for %s: %s", subject, err) - p.Bot.SendMessage(channel, "Something went wrong finding that counter;") + p.Bot.Send(bot.Message, channel, "Something went wrong finding that counter;") return true } @@ -293,11 +296,11 @@ func (p *CounterPlugin) Message(message msg.Message) bool { resp += "." if count == 0 { - p.Bot.SendMessage(channel, fmt.Sprintf("%s has no counters.", subject)) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has no counters.", subject)) return true } - p.Bot.SendMessage(channel, resp) + p.Bot.Send(bot.Message, channel, resp) return true } else if message.Command && len(parts) == 2 && parts[0] == "clear" { subject := strings.ToLower(nick) @@ -306,17 +309,17 @@ func (p *CounterPlugin) Message(message msg.Message) bool { it, err := GetItem(p.DB, subject, itemName) if err != nil { log.Printf("Error getting item to remove %s.%s: %s", subject, itemName, err) - p.Bot.SendMessage(channel, "Something went wrong removing that counter;") + p.Bot.Send(bot.Message, channel, "Something went wrong removing that counter;") return true } err = it.Delete() if err != nil { log.Printf("Error removing item %s.%s: %s", subject, itemName, err) - p.Bot.SendMessage(channel, "Something went wrong removing that counter;") + p.Bot.Send(bot.Message, channel, "Something went wrong removing that counter;") return true } - p.Bot.SendAction(channel, fmt.Sprintf("chops a few %s out of his brain", + p.Bot.Send(bot.Action, channel, fmt.Sprintf("chops a few %s out of his brain", itemName)) return true @@ -339,7 +342,7 @@ func (p *CounterPlugin) Message(message msg.Message) bool { item, err := GetItem(p.DB, subject, itemName) switch { case err == sql.ErrNoRows: - p.Bot.SendMessage(channel, fmt.Sprintf("I don't think %s has any %s.", + p.Bot.Send(bot.Message, channel, fmt.Sprintf("I don't think %s has any %s.", subject, itemName)) return true case err != nil: @@ -348,7 +351,7 @@ func (p *CounterPlugin) Message(message msg.Message) bool { return true } - p.Bot.SendMessage(channel, fmt.Sprintf("%s has %d %s.", subject, item.Count, + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject, item.Count, itemName)) return true @@ -379,7 +382,7 @@ func (p *CounterPlugin) Message(message msg.Message) bool { } log.Printf("About to update item: %#v", item) item.UpdateDelta(1) - p.Bot.SendMessage(channel, fmt.Sprintf("%s has %d %s.", subject, + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject, item.Count, item.Item)) return true } else if strings.HasSuffix(parts[0], "--") { @@ -391,7 +394,7 @@ func (p *CounterPlugin) Message(message msg.Message) bool { return false } item.UpdateDelta(-1) - p.Bot.SendMessage(channel, fmt.Sprintf("%s has %d %s.", subject, + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject, item.Count, item.Item)) return true } @@ -420,7 +423,7 @@ func (p *CounterPlugin) Message(message msg.Message) bool { n, _ := strconv.Atoi(parts[2]) log.Printf("About to update item by %d: %#v", n, item) item.UpdateDelta(n) - p.Bot.SendMessage(channel, fmt.Sprintf("%s has %d %s.", subject, + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject, item.Count, item.Item)) return true } else if parts[1] == "-=" { @@ -434,7 +437,7 @@ func (p *CounterPlugin) Message(message msg.Message) bool { n, _ := strconv.Atoi(parts[2]) log.Printf("About to update item by -%d: %#v", n, item) item.UpdateDelta(-n) - p.Bot.SendMessage(channel, fmt.Sprintf("%s has %d %s.", subject, + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject, item.Count, item.Item)) return true } @@ -444,21 +447,12 @@ func (p *CounterPlugin) Message(message msg.Message) bool { } // Help responds to help requests. Every plugin must implement a help function. -func (p *CounterPlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "You can set counters incrementally by using "+ +func (p *CounterPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.Bot.Send(bot.Message, message.Channel, "You can set counters incrementally by using "+ "++ and --. You can see all of your counters using "+ "\"inspect\", erase them with \"clear\", and view single counters with "+ "\"count\".") -} - -// Empty event handler because this plugin does not do anything on event recv -func (p *CounterPlugin) Event(kind string, message msg.Message) bool { - return false -} - -// Handler for bot's own messages -func (p *CounterPlugin) BotMessage(message msg.Message) bool { - return false + return true } // Register any web URLs desired @@ -466,8 +460,6 @@ func (p *CounterPlugin) RegisterWeb() *string { return nil } -func (p *CounterPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } - func (p *CounterPlugin) checkMatch(message msg.Message) bool { nick := message.User.Name channel := message.Channel @@ -487,7 +479,7 @@ func (p *CounterPlugin) checkMatch(message msg.Message) bool { } log.Printf("About to update item: %#v", item) item.UpdateDelta(1) - p.Bot.SendMessage(channel, fmt.Sprintf("bleep-bloop-blop... %s has %d %s", + p.Bot.Send(bot.Message, channel, fmt.Sprintf("bleep-bloop-blop... %s has %d %s", nick, item.Count, itemName)) return true } diff --git a/plugins/counter/counter_test.go b/plugins/counter/counter_test.go index f1c673c..1cbec0c 100644 --- a/plugins/counter/counter_test.go +++ b/plugins/counter/counter_test.go @@ -22,12 +22,12 @@ func setup(t *testing.T) (*bot.MockBot, *CounterPlugin) { return mb, c } -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -38,8 +38,8 @@ func makeMessage(payload string) msg.Message { func TestThreeSentencesExists(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Message(makeMessage(":beer:++")) - c.Message(makeMessage(":beer:. Earl Grey. Hot.")) + c.message(makeMessage(":beer:++")) + c.message(makeMessage(":beer:. Earl Grey. Hot.")) item, err := GetItem(mb.DB(), "tester", ":beer:") assert.Nil(t, err) assert.Equal(t, 2, item.Count) @@ -49,7 +49,7 @@ func TestThreeSentencesNotExists(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) item, err := GetItem(mb.DB(), "tester", ":beer:") - c.Message(makeMessage(":beer:. Earl Grey. Hot.")) + c.message(makeMessage(":beer:. Earl Grey. Hot.")) item, err = GetItem(mb.DB(), "tester", ":beer:") assert.Nil(t, err) assert.Equal(t, 0, item.Count) @@ -58,8 +58,8 @@ func TestThreeSentencesNotExists(t *testing.T) { func TestTeaEarlGreyHot(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Message(makeMessage("Tea. Earl Grey. Hot.")) - c.Message(makeMessage("Tea. Earl Grey. Hot.")) + c.message(makeMessage("Tea. Earl Grey. Hot.")) + c.message(makeMessage("Tea. Earl Grey. Hot.")) item, err := GetItem(mb.DB(), "tester", ":tea:") assert.Nil(t, err) assert.Equal(t, 2, item.Count) @@ -68,8 +68,8 @@ func TestTeaEarlGreyHot(t *testing.T) { func TestTeaTwoPeriods(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Message(makeMessage("Tea. Earl Grey.")) - c.Message(makeMessage("Tea. Earl Grey.")) + c.message(makeMessage("Tea. Earl Grey.")) + c.message(makeMessage("Tea. Earl Grey.")) item, err := GetItem(mb.DB(), "tester", ":tea:") assert.Nil(t, err) assert.Equal(t, 0, item.Count) @@ -78,8 +78,8 @@ func TestTeaTwoPeriods(t *testing.T) { func TestTeaMultiplePeriods(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Message(makeMessage("Tea. Earl Grey. Spiked. Hot.")) - c.Message(makeMessage("Tea. Earl Grey. Spiked. Hot.")) + c.message(makeMessage("Tea. Earl Grey. Spiked. Hot.")) + c.message(makeMessage("Tea. Earl Grey. Spiked. Hot.")) item, err := GetItem(mb.DB(), "tester", ":tea:") assert.Nil(t, err) assert.Equal(t, 2, item.Count) @@ -88,9 +88,9 @@ func TestTeaMultiplePeriods(t *testing.T) { func TestTeaGreenHot(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Message(makeMessage("Tea. Green. Hot.")) - c.Message(makeMessage("Tea. Green. Hot")) - c.Message(makeMessage("Tea. Green. Iced.")) + c.message(makeMessage("Tea. Green. Hot.")) + c.message(makeMessage("Tea. Green. Hot")) + c.message(makeMessage("Tea. Green. Iced.")) item, err := GetItem(mb.DB(), "tester", ":tea:") assert.Nil(t, err) assert.Equal(t, 3, item.Count) @@ -99,8 +99,8 @@ func TestTeaGreenHot(t *testing.T) { func TestTeaUnrelated(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Message(makeMessage("Tea.")) - c.Message(makeMessage("Tea. It's great.")) + c.message(makeMessage("Tea.")) + c.message(makeMessage("Tea. It's great.")) item, err := GetItem(mb.DB(), "tester", ":tea:") assert.Nil(t, err) assert.Equal(t, 0, item.Count) @@ -109,7 +109,7 @@ func TestTeaUnrelated(t *testing.T) { func TestTeaSkieselQuote(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Message(makeMessage("blah, this is a whole page of explanation where \"we did local search and used a tabu list\" would have sufficed")) + c.message(makeMessage("blah, this is a whole page of explanation where \"we did local search and used a tabu list\" would have sufficed")) item, err := GetItem(mb.DB(), "tester", ":tea:") assert.Nil(t, err) assert.Equal(t, 0, item.Count) @@ -117,7 +117,7 @@ func TestTeaSkieselQuote(t *testing.T) { func TestTeaUnicodeJapanese(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Message(makeMessage("Tea. おちや. Hot.")) + c.message(makeMessage("Tea. おちや. Hot.")) item, err := GetItem(mb.DB(), "tester", ":tea:") assert.Nil(t, err) assert.Equal(t, 1, item.Count) @@ -126,8 +126,8 @@ func TestTeaUnicodeJapanese(t *testing.T) { func TestResetMe(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Message(makeMessage("test++")) - c.Message(makeMessage("!reset me")) + c.message(makeMessage("test++")) + c.message(makeMessage("!reset me")) items, err := GetItems(mb.DB(), "tester") assert.Nil(t, err) assert.Len(t, items, 0) @@ -136,7 +136,7 @@ func TestResetMe(t *testing.T) { func TestCounterOne(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Message(makeMessage("test++")) + c.message(makeMessage("test++")) assert.Len(t, mb.Messages, 1) assert.Equal(t, mb.Messages[0], "tester has 1 test.") } @@ -144,7 +144,7 @@ func TestCounterOne(t *testing.T) { func TestCounterOneWithSpace(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Message(makeMessage(":test: ++")) + c.message(makeMessage(":test: ++")) assert.Len(t, mb.Messages, 1) assert.Equal(t, mb.Messages[0], "tester has 1 :test:.") } @@ -153,7 +153,7 @@ func TestCounterFour(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) for i := 0; i < 4; i++ { - c.Message(makeMessage("test++")) + c.message(makeMessage("test++")) } assert.Len(t, mb.Messages, 4) assert.Equal(t, mb.Messages[3], "tester has 4 test.") @@ -163,10 +163,10 @@ func TestCounterDecrement(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) for i := 0; i < 4; i++ { - c.Message(makeMessage("test++")) + c.message(makeMessage("test++")) assert.Equal(t, mb.Messages[i], fmt.Sprintf("tester has %d test.", i+1)) } - c.Message(makeMessage("test--")) + c.message(makeMessage("test--")) assert.Len(t, mb.Messages, 5) assert.Equal(t, mb.Messages[4], "tester has 3 test.") } @@ -175,10 +175,10 @@ func TestFriendCounterDecrement(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) for i := 0; i < 4; i++ { - c.Message(makeMessage("other.test++")) + c.message(makeMessage("other.test++")) assert.Equal(t, mb.Messages[i], fmt.Sprintf("other has %d test.", i+1)) } - c.Message(makeMessage("other.test--")) + c.message(makeMessage("other.test--")) assert.Len(t, mb.Messages, 5) assert.Equal(t, mb.Messages[4], "other has 3 test.") } @@ -187,12 +187,12 @@ func TestDecrementZero(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) for i := 0; i < 4; i++ { - c.Message(makeMessage("test++")) + c.message(makeMessage("test++")) assert.Equal(t, mb.Messages[i], fmt.Sprintf("tester has %d test.", i+1)) } j := 4 for i := 4; i > 0; i-- { - c.Message(makeMessage("test--")) + c.message(makeMessage("test--")) assert.Equal(t, mb.Messages[j], fmt.Sprintf("tester has %d test.", i-1)) j++ } @@ -204,10 +204,10 @@ func TestClear(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) for i := 0; i < 4; i++ { - c.Message(makeMessage("test++")) + c.message(makeMessage("test++")) assert.Equal(t, mb.Messages[i], fmt.Sprintf("tester has %d test.", i+1)) } - res := c.Message(makeMessage("!clear test")) + res := c.message(makeMessage("!clear test")) assert.True(t, res) assert.Len(t, mb.Actions, 1) assert.Equal(t, mb.Actions[0], "chops a few test out of his brain") @@ -217,10 +217,10 @@ func TestCount(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) for i := 0; i < 4; i++ { - c.Message(makeMessage("test++")) + c.message(makeMessage("test++")) assert.Equal(t, mb.Messages[i], fmt.Sprintf("tester has %d test.", i+1)) } - res := c.Message(makeMessage("!count test")) + res := c.message(makeMessage("!count test")) assert.True(t, res) assert.Len(t, mb.Messages, 5) assert.Equal(t, mb.Messages[4], "tester has 4 test.") @@ -230,18 +230,18 @@ func TestInspectMe(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) for i := 0; i < 4; i++ { - c.Message(makeMessage("test++")) + c.message(makeMessage("test++")) assert.Equal(t, mb.Messages[i], fmt.Sprintf("tester has %d test.", i+1)) } for i := 0; i < 2; i++ { - c.Message(makeMessage("fucks++")) + c.message(makeMessage("fucks++")) assert.Equal(t, mb.Messages[i+4], fmt.Sprintf("tester has %d fucks.", i+1)) } for i := 0; i < 20; i++ { - c.Message(makeMessage("cheese++")) + c.message(makeMessage("cheese++")) assert.Equal(t, mb.Messages[i+6], fmt.Sprintf("tester has %d cheese.", i+1)) } - res := c.Message(makeMessage("!inspect me")) + res := c.message(makeMessage("!inspect me")) assert.True(t, res) assert.Len(t, mb.Messages, 27) assert.Equal(t, mb.Messages[26], "tester has the following counters: test: 4, fucks: 2, cheese: 20.") @@ -250,22 +250,10 @@ func TestInspectMe(t *testing.T) { func TestHelp(t *testing.T) { mb, c := setup(t) assert.NotNil(t, c) - c.Help("channel", []string{}) + c.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) assert.Len(t, mb.Messages, 1) } -func TestBotMessage(t *testing.T) { - _, c := setup(t) - assert.NotNil(t, c) - assert.False(t, c.BotMessage(makeMessage("test"))) -} - -func TestEvent(t *testing.T) { - _, c := setup(t) - assert.NotNil(t, c) - assert.False(t, c.Event("dummy", makeMessage("test"))) -} - func TestRegisterWeb(t *testing.T) { _, c := setup(t) assert.NotNil(t, c) diff --git a/plugins/dice/dice.go b/plugins/dice/dice.go index 104e016..721a652 100644 --- a/plugins/dice/dice.go +++ b/plugins/dice/dice.go @@ -19,10 +19,13 @@ type DicePlugin struct { } // NewDicePlugin creates a new DicePlugin with the Plugin interface -func New(bot bot.Bot) *DicePlugin { - return &DicePlugin{ - Bot: bot, +func New(b bot.Bot) *DicePlugin { + dp := &DicePlugin{ + Bot: b, } + b.Register(dp, bot.Message, dp.message) + b.Register(dp, bot.Help, dp.help) + return dp } func rollDie(sides int) int { @@ -32,7 +35,7 @@ func rollDie(sides int) int { // 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 *DicePlugin) Message(message msg.Message) bool { +func (p *DicePlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { if !message.Command { return false } @@ -46,7 +49,7 @@ func (p *DicePlugin) Message(message msg.Message) bool { } if sides < 2 || nDice < 1 || nDice > 20 { - p.Bot.SendMessage(channel, "You're a dick.") + p.Bot.Send(bot.Message, channel, "You're a dick.") return true } @@ -61,29 +64,18 @@ func (p *DicePlugin) Message(message msg.Message) bool { } } - p.Bot.SendMessage(channel, rolls) + p.Bot.Send(bot.Message, channel, rolls) return true } // Help responds to help requests. Every plugin must implement a help function. -func (p *DicePlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "Roll dice using notation XdY. Try \"3d20\".") -} - -// Empty event handler because this plugin does not do anything on event recv -func (p *DicePlugin) Event(kind string, message msg.Message) bool { - return false -} - -// Handler for bot's own messages -func (p *DicePlugin) BotMessage(message msg.Message) bool { - return false +func (p *DicePlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.Bot.Send(bot.Message, message.Channel, "Roll dice using notation XdY. Try \"3d20\".") + return true } // Register any web URLs desired func (p *DicePlugin) RegisterWeb() *string { return nil } - -func (p *DicePlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/dice/dice_test.go b/plugins/dice/dice_test.go index ec060de..bcb7a26 100644 --- a/plugins/dice/dice_test.go +++ b/plugins/dice/dice_test.go @@ -12,12 +12,12 @@ import ( "github.com/velour/catbase/bot/user" ) -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -29,7 +29,7 @@ func TestDie(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!1d6")) + res := c.message(makeMessage("!1d6")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "tester, you rolled:") @@ -39,7 +39,7 @@ func TestDice(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!5d6")) + res := c.message(makeMessage("!5d6")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "tester, you rolled:") @@ -49,7 +49,7 @@ func TestNotCommand(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("1d6")) + res := c.message(makeMessage("1d6")) assert.False(t, res) assert.Len(t, mb.Messages, 0) } @@ -58,7 +58,7 @@ func TestBadDice(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!aued6")) + res := c.message(makeMessage("!aued6")) assert.False(t, res) assert.Len(t, mb.Messages, 0) } @@ -67,7 +67,7 @@ func TestBadSides(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!1daoeu")) + res := c.message(makeMessage("!1daoeu")) assert.False(t, res) assert.Len(t, mb.Messages, 0) } @@ -76,7 +76,7 @@ func TestLotsOfDice(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!100d100")) + res := c.message(makeMessage("!100d100")) assert.True(t, res) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "You're a dick.") @@ -86,24 +86,10 @@ func TestHelp(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - c.Help("channel", []string{}) + c.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) assert.Len(t, mb.Messages, 1) } -func TestBotMessage(t *testing.T) { - mb := bot.NewMockBot() - c := New(mb) - assert.NotNil(t, c) - assert.False(t, c.BotMessage(makeMessage("test"))) -} - -func TestEvent(t *testing.T) { - mb := bot.NewMockBot() - c := New(mb) - assert.NotNil(t, c) - assert.False(t, c.Event("dummy", makeMessage("test"))) -} - func TestRegisterWeb(t *testing.T) { mb := bot.NewMockBot() c := New(mb) diff --git a/plugins/downtime/downtime.go b/plugins/downtime/downtime.go index e50df61..335886e 100644 --- a/plugins/downtime/downtime.go +++ b/plugins/downtime/downtime.go @@ -142,9 +142,9 @@ func (p *DowntimePlugin) Message(message msg.Message) bool { } if !entry.id.Valid { // couldn't find em - p.Bot.SendMessage(channel, fmt.Sprintf("Sorry, I don't know %s.", nick)) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("Sorry, I don't know %s.", nick)) } else { - p.Bot.SendMessage(channel, fmt.Sprintf("%s has been idle for: %s", + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has been idle for: %s", nick, time.Now().Sub(entry.lastSeen))) } ret = true @@ -165,7 +165,7 @@ func (p *DowntimePlugin) Message(message msg.Message) bool { tops = fmt.Sprintf("%s%s: %s ", tops, e.nick, time.Now().Sub(e.lastSeen)) } } - p.Bot.SendMessage(channel, tops) + p.Bot.Send(bot.Message, channel, tops) ret = true } @@ -197,7 +197,7 @@ func (p *DowntimePlugin) remove(user string) error { // Help responds to help requests. Every plugin must implement a help function. func (p *DowntimePlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "Ask me how long one of your friends has been idele with, \"idle \"") + p.Bot.Send(bot.Message, channel, "Ask me how long one of your friends has been idele with, \"idle \"") } // Empty event handler because this plugin does not do anything on event recv diff --git a/plugins/emojifyme/emojifyme.go b/plugins/emojifyme/emojifyme.go index 26b5b72..03b7fbb 100644 --- a/plugins/emojifyme/emojifyme.go +++ b/plugins/emojifyme/emojifyme.go @@ -20,7 +20,7 @@ type EmojifyMePlugin struct { Emoji map[string]string } -func New(bot bot.Bot) *EmojifyMePlugin { +func New(b bot.Bot) *EmojifyMePlugin { resp, err := http.Get("https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json") if err != nil { log.Fatalf("Error generic emoji list: %s", err) @@ -48,14 +48,16 @@ func New(bot bot.Bot) *EmojifyMePlugin { } } - return &EmojifyMePlugin{ - Bot: bot, + ep := &EmojifyMePlugin{ + Bot: b, GotBotEmoji: false, Emoji: emojiMap, } + b.Register(ep, bot.Message, ep.message) + return ep } -func (p *EmojifyMePlugin) Message(message msg.Message) bool { +func (p *EmojifyMePlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { if !p.GotBotEmoji { p.GotBotEmoji = true emojiMap := p.Bot.GetEmojiList() @@ -90,31 +92,17 @@ func (p *EmojifyMePlugin) Message(message msg.Message) bool { if emojied > 0 && rand.Float64() <= p.Bot.Config().GetFloat64("Emojify.Chance", 0.02)*emojied { for _, e := range emojys { - p.Bot.React(message.Channel, e, message) + p.Bot.Send(bot.Reaction, message.Channel, e, message) } - return true + return false } return false } -func (p *EmojifyMePlugin) Help(channel string, parts []string) { - -} - -func (p *EmojifyMePlugin) Event(kind string, message msg.Message) bool { - return false -} - -func (p *EmojifyMePlugin) BotMessage(message msg.Message) bool { - return false -} - func (p *EmojifyMePlugin) RegisterWeb() *string { return nil } -func (p *EmojifyMePlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } - func stringsContain(haystack []string, needle string) bool { for _, s := range haystack { if s == needle { diff --git a/plugins/fact/factoid.go b/plugins/fact/factoid.go index c53c934..b8b40c2 100644 --- a/plugins/fact/factoid.go +++ b/plugins/fact/factoid.go @@ -314,6 +314,9 @@ func New(botInst bot.Bot) *Factoid { }(channel) } + botInst.Register(p, bot.Message, p.message) + botInst.Register(p, bot.Help, p.help) + return p } @@ -405,13 +408,13 @@ func (p *Factoid) sayFact(message msg.Message, fact factoid) { } if fact.Verb == "action" { - p.Bot.SendAction(message.Channel, msg) + p.Bot.Send(bot.Action, message.Channel, msg) } else if fact.Verb == "react" { - p.Bot.React(message.Channel, msg, message) + p.Bot.Send(bot.Reaction, message.Channel, msg, message) } else if fact.Verb == "reply" { - p.Bot.SendMessage(message.Channel, msg) + p.Bot.Send(bot.Message, message.Channel, msg) } else { - p.Bot.SendMessage(message.Channel, full) + p.Bot.Send(bot.Message, message.Channel, full) } } @@ -457,7 +460,7 @@ func (p *Factoid) tellThemWhatThatWas(message msg.Message) bool { msg = fmt.Sprintf("That was (#%d) '%s <%s> %s'", fact.id.Int64, fact.Fact, fact.Verb, fact.Tidbit) } - p.Bot.SendMessage(message.Channel, msg) + p.Bot.Send(bot.Message, message.Channel, msg) return true } @@ -475,21 +478,21 @@ func (p *Factoid) learnAction(message msg.Message, action string) bool { action = strings.TrimSpace(action) if len(trigger) == 0 || len(fact) == 0 || len(action) == 0 { - p.Bot.SendMessage(message.Channel, "I don't want to learn that.") + p.Bot.Send(bot.Message, message.Channel, "I don't want to learn that.") return true } if len(strings.Split(fact, "$and")) > 4 { - p.Bot.SendMessage(message.Channel, "You can't use more than 4 $and operators.") + p.Bot.Send(bot.Message, message.Channel, "You can't use more than 4 $and operators.") return true } strippedaction := strings.Replace(strings.Replace(action, "<", "", 1), ">", "", 1) if err := p.learnFact(message, trigger, strippedaction, fact); err != nil { - p.Bot.SendMessage(message.Channel, err.Error()) + p.Bot.Send(bot.Message, message.Channel, err.Error()) } else { - p.Bot.SendMessage(message.Channel, fmt.Sprintf("Okay, %s.", message.User.Name)) + p.Bot.Send(bot.Message, message.Channel, fmt.Sprintf("Okay, %s.", message.User.Name)) } return true @@ -509,7 +512,7 @@ func changeOperator(body string) string { // an admin, it may be deleted func (p *Factoid) forgetLastFact(message msg.Message) bool { if p.LastFact == nil { - p.Bot.SendMessage(message.Channel, "I refuse.") + p.Bot.Send(bot.Message, message.Channel, "I refuse.") return true } @@ -519,7 +522,7 @@ func (p *Factoid) forgetLastFact(message msg.Message) bool { } fmt.Printf("Forgot #%d: %s %s %s\n", p.LastFact.id.Int64, p.LastFact.Fact, p.LastFact.Verb, p.LastFact.Tidbit) - p.Bot.SendAction(message.Channel, "hits himself over the head with a skillet") + p.Bot.Send(bot.Action, message.Channel, "hits himself over the head with a skillet") p.LastFact = nil return true @@ -539,7 +542,7 @@ func (p *Factoid) changeFact(message msg.Message) bool { if len(parts) == 4 { // replacement if parts[0] != "s" { - p.Bot.SendMessage(message.Channel, "Nah.") + p.Bot.Send(bot.Message, message.Channel, "Nah.") } find := parts[1] replace := parts[2] @@ -554,10 +557,10 @@ func (p *Factoid) changeFact(message msg.Message) bool { } // make the changes msg := fmt.Sprintf("Changing %d facts.", len(result)) - p.Bot.SendMessage(message.Channel, msg) + p.Bot.Send(bot.Message, message.Channel, msg) reg, err := regexp.Compile(find) if err != nil { - p.Bot.SendMessage(message.Channel, "I don't really want to.") + p.Bot.Send(bot.Message, message.Channel, "I don't really want to.") return false } for _, fact := range result { @@ -574,12 +577,12 @@ func (p *Factoid) changeFact(message msg.Message) bool { result, err := getFacts(p.db, trigger, parts[1]) if err != nil { log.Println("Error getting facts: ", trigger, err) - p.Bot.SendMessage(message.Channel, "bzzzt") + p.Bot.Send(bot.Message, message.Channel, "bzzzt") return true } count := len(result) if count == 0 { - p.Bot.SendMessage(message.Channel, "I didn't find any facts like that.") + p.Bot.Send(bot.Message, message.Channel, "I didn't find any facts like that.") return true } if parts[2] == "g" && len(result) > 4 { @@ -599,9 +602,9 @@ func (p *Factoid) changeFact(message msg.Message) bool { if count > 4 { msg = fmt.Sprintf("%s | ...and %d others", msg, count) } - p.Bot.SendMessage(message.Channel, msg) + p.Bot.Send(bot.Message, message.Channel, msg) } else { - p.Bot.SendMessage(message.Channel, "I don't know what you mean.") + p.Bot.Send(bot.Message, message.Channel, "I don't know what you mean.") } return true } @@ -609,7 +612,7 @@ func (p *Factoid) changeFact(message msg.Message) bool { // 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 *Factoid) Message(message msg.Message) bool { +func (p *Factoid) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { if strings.ToLower(message.Body) == "what was that?" { return p.tellThemWhatThatWas(message) } @@ -625,14 +628,14 @@ func (p *Factoid) Message(message msg.Message) bool { m := strings.TrimPrefix(message.Body, "alias ") parts := strings.SplitN(m, "->", 2) if len(parts) != 2 { - p.Bot.SendMessage(message.Channel, "If you want to alias something, use: `alias this -> that`") + p.Bot.Send(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.SendMessage(message.Channel, err.Error()) + p.Bot.Send(bot.Message, message.Channel, err.Error()) } else { - p.Bot.SendAction(message.Channel, "learns a new synonym") + p.Bot.Send(bot.Action, message.Channel, "learns a new synonym") } return true } @@ -664,19 +667,15 @@ func (p *Factoid) Message(message msg.Message) bool { } // We didn't find anything, panic! - p.Bot.SendMessage(message.Channel, p.NotFound[rand.Intn(len(p.NotFound))]) + p.Bot.Send(bot.Message, message.Channel, p.NotFound[rand.Intn(len(p.NotFound))]) return true } // Help responds to help requests. Every plugin must implement a help function. -func (p *Factoid) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "I can learn facts and spit them back out. You can say \"this is that\" or \"he $5\". Later, trigger the factoid by just saying the trigger word, \"this\" or \"he\" in these examples.") - p.Bot.SendMessage(channel, "I can also figure out some variables including: $nonzero, $digit, $nick, and $someone.") -} - -// Empty event handler because this plugin does not do anything on event recv -func (p *Factoid) Event(kind string, message msg.Message) bool { - return false +func (p *Factoid) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.Bot.Send(bot.Message, message.Channel, "I can learn facts and spit them back out. You can say \"this is that\" or \"he $5\". Later, trigger the factoid by just saying the trigger word, \"this\" or \"he\" in these examples.") + p.Bot.Send(bot.Message, message.Channel, "I can also figure out some variables including: $nonzero, $digit, $nick, and $someone.") + return true } // Pull a fact at random from the database @@ -737,11 +736,6 @@ func (p *Factoid) factTimer(channel string) { } } -// Handler for bot's own messages -func (p *Factoid) BotMessage(message msg.Message) bool { - return false -} - // Register any web URLs desired func (p *Factoid) RegisterWeb() *string { http.HandleFunc("/factoid/req", p.serveQuery) @@ -784,5 +778,3 @@ func (p *Factoid) serveQuery(w http.ResponseWriter, r *http.Request) { log.Println(err) } } - -func (p *Factoid) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/fact/remember.go b/plugins/fact/remember.go index 1038eb7..87485f1 100644 --- a/plugins/fact/remember.go +++ b/plugins/fact/remember.go @@ -29,6 +29,8 @@ func NewRemember(b bot.Bot) *RememberPlugin { Log: make(map[string][]msg.Message), db: b.DB(), } + b.Register(p, bot.Message, p.message) + b.Register(p, bot.Message, p.help) return &p } @@ -36,11 +38,11 @@ func NewRemember(b bot.Bot) *RememberPlugin { // 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 *RememberPlugin) Message(message msg.Message) bool { +func (p *RememberPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { if strings.ToLower(message.Body) == "quote" && message.Command { q := p.randQuote() - p.Bot.SendMessage(message.Channel, q) + p.Bot.Send(bot.Message, message.Channel, q) // is it evil not to remember that the user said quote? return true @@ -87,7 +89,7 @@ func (p *RememberPlugin) Message(message msg.Message) bool { } if err := fact.save(p.db); err != nil { log.Println("ERROR!!!!:", err) - p.Bot.SendMessage(message.Channel, "Tell somebody I'm broke.") + p.Bot.Send(bot.Message, message.Channel, "Tell somebody I'm broke.") } log.Println("Remembering factoid:", msg) @@ -95,14 +97,14 @@ func (p *RememberPlugin) Message(message msg.Message) bool { // sorry, not creative with names so we're reusing msg msg = fmt.Sprintf("Okay, %s, remembering '%s'.", message.User.Name, msg) - p.Bot.SendMessage(message.Channel, msg) + p.Bot.Send(bot.Message, message.Channel, msg) p.recordMsg(message) return true } } - p.Bot.SendMessage(message.Channel, "Sorry, I don't know that phrase.") + p.Bot.Send(bot.Message, message.Channel, "Sorry, I don't know that phrase.") p.recordMsg(message) return true } @@ -111,14 +113,15 @@ func (p *RememberPlugin) Message(message msg.Message) bool { } // Help responds to help requests. Every plugin must implement a help function. -func (p *RememberPlugin) Help(channel string, parts []string) { +func (p *RememberPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { msg := "!remember will let you quote your idiot friends. Just type " + "!remember to remember what they said. Snippet can " + "be any part of their message. Later on, you can ask for a random " + "!quote." - p.Bot.SendMessage(channel, msg) + p.Bot.Send(bot.Message, message.Channel, msg) + return true } // deliver a random quote out of the db. @@ -150,19 +153,8 @@ func (p *RememberPlugin) randQuote() string { return f.Tidbit } -// Empty event handler because this plugin does not do anything on event recv -func (p *RememberPlugin) Event(kind string, message msg.Message) bool { - return false -} - -// Record what the bot says in the log -func (p *RememberPlugin) BotMessage(message msg.Message) bool { - p.recordMsg(message) - return false -} - // Register any web URLs desired -func (p *RememberPlugin) RegisterWeb() *string { +func (p RememberPlugin) RegisterWeb() *string { return nil } @@ -170,5 +162,3 @@ func (p *RememberPlugin) recordMsg(message msg.Message) { log.Printf("Logging message: %s: %s", message.User.Name, message.Body) p.Log[message.Channel] = append(p.Log[message.Channel], message) } - -func (p *RememberPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/fact/remember_test.go b/plugins/fact/remember_test.go index 6e8c8a1..a3ded28 100644 --- a/plugins/fact/remember_test.go +++ b/plugins/fact/remember_test.go @@ -42,7 +42,7 @@ func TestCornerCaseBug(t *testing.T) { p, _, mb := makePlugin(t) for _, m := range msgs { - p.Message(m) + p.message(bot.Message, m) } assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "horse dick") @@ -59,7 +59,7 @@ func TestReact(t *testing.T) { _, p, mb := makePlugin(t) for _, m := range msgs { - p.Message(m) + p.message(bot.Message, m) } assert.Len(t, mb.Reactions, 1) assert.Contains(t, mb.Reactions[0], "jesus") @@ -72,7 +72,7 @@ func TestReactCantLearnSpaces(t *testing.T) { _, p, mb := makePlugin(t) for _, m := range msgs { - p.Message(m) + p.message(bot.Message, m) } assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "not a valid") diff --git a/plugins/first/first.go b/plugins/first/first.go index a70f6e7..9771dbf 100644 --- a/plugins/first/first.go +++ b/plugins/first/first.go @@ -66,11 +66,14 @@ func New(b bot.Bot) *FirstPlugin { log.Fatal("Could not initialize first plugin: ", err) } - return &FirstPlugin{ + fp := &FirstPlugin{ Bot: b, db: b.DB(), First: first, } + b.Register(fp, bot.Message, fp.message) + b.Register(fp, bot.Help, fp.help) + return fp } func getLastFirst(db *sqlx.DB) (*FirstEntry, error) { @@ -123,7 +126,7 @@ func isToday(t time.Time) bool { // 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 *FirstPlugin) Message(message msg.Message) bool { +func (p *FirstPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { // This bot does not reply to anything if p.First == nil && p.allowed(message) { @@ -195,7 +198,7 @@ func (p *FirstPlugin) recordFirst(message msg.Message) { func (p *FirstPlugin) announceFirst(message msg.Message) { c := message.Channel if p.First != nil { - p.Bot.SendMessage(c, fmt.Sprintf("%s had first at %s with the message: \"%s\"", + p.Bot.Send(bot.Message, c, fmt.Sprintf("%s had first at %s with the message: \"%s\"", p.First.nick, p.First.time.Format("15:04"), p.First.body)) } } @@ -208,23 +211,12 @@ func (p *FirstPlugin) LoadData() { } // Help responds to help requests. Every plugin must implement a help function. -func (p *FirstPlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "Sorry, First does not do a goddamn thing.") -} - -// Empty event handler because this plugin does not do anything on event recv -func (p *FirstPlugin) Event(kind string, message msg.Message) bool { - return false -} - -// Handler for bot's own messages -func (p *FirstPlugin) BotMessage(message msg.Message) bool { - return false +func (p *FirstPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.Bot.Send(bot.Message, message.Channel, "Sorry, First does not do a goddamn thing.") + return true } // Register any web URLs desired func (p *FirstPlugin) RegisterWeb() *string { return nil } - -func (p *FirstPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/inventory/inventory.go b/plugins/inventory/inventory.go index 708639f..50fb6f3 100644 --- a/plugins/inventory/inventory.go +++ b/plugins/inventory/inventory.go @@ -24,8 +24,8 @@ type InventoryPlugin struct { } // New creates a new InventoryPlugin with the Plugin interface -func New(bot bot.Bot) *InventoryPlugin { - config := bot.Config() +func New(b bot.Bot) *InventoryPlugin { + config := b.Config() nick := config.Get("nick", "bot") r1, err := regexp.Compile("take this (.+)") checkerr(err) @@ -38,15 +38,15 @@ func New(bot bot.Bot) *InventoryPlugin { r5, err := regexp.Compile(fmt.Sprintf("gives (.+) to %s([^a-zA-Z].*)?", nick)) checkerr(err) - p := InventoryPlugin{ - DB: bot.DB(), - bot: bot, + p := &InventoryPlugin{ + DB: b.DB(), + bot: b, config: config, r1: r1, r2: r2, r3: r3, r4: r4, r5: r5, } - bot.RegisterFilter("$item", p.itemFilter) - bot.RegisterFilter("$giveitem", p.giveItemFilter) + b.RegisterFilter("$item", p.itemFilter) + b.RegisterFilter("$giveitem", p.giveItemFilter) _, err = p.DB.Exec(`create table if not exists inventory ( item string primary key @@ -56,7 +56,9 @@ func New(bot bot.Bot) *InventoryPlugin { log.Fatal(err) } - return &p + b.Register(p, bot.Message, p.message) + + return p } func (p *InventoryPlugin) giveItemFilter(input string) string { @@ -75,7 +77,7 @@ func (p *InventoryPlugin) itemFilter(input string) string { return input } -func (p *InventoryPlugin) Message(message msg.Message) bool { +func (p *InventoryPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { m := message.Body log.Printf("inventory trying to read %+v", message) if message.Command { @@ -86,7 +88,7 @@ func (p *InventoryPlugin) Message(message msg.Message) bool { log.Printf("I think I have more than 0 items: %+v, len(items)=%d", items, len(items)) say = fmt.Sprintf("I'm currently holding %s", strings.Join(items, ", ")) } - p.bot.SendMessage(message.Channel, say) + p.bot.Send(bot.Message, message.Channel, say) return true } @@ -197,7 +199,7 @@ func (p *InventoryPlugin) remove(i string) { func (p *InventoryPlugin) addItem(m msg.Message, i string) bool { if p.exists(i) { - p.bot.SendMessage(m.Channel, fmt.Sprintf("I already have %s.", i)) + p.bot.Send(bot.Message, m.Channel, fmt.Sprintf("I already have %s.", i)) return true } var removed string @@ -210,9 +212,9 @@ func (p *InventoryPlugin) addItem(m msg.Message, i string) bool { log.Printf("Error inserting new inventory item: %s", err) } if removed != "" { - p.bot.SendAction(m.Channel, fmt.Sprintf("dropped %s and took %s from %s", removed, i, m.User.Name)) + p.bot.Send(bot.Action, m.Channel, fmt.Sprintf("dropped %s and took %s from %s", removed, i, m.User.Name)) } else { - p.bot.SendAction(m.Channel, fmt.Sprintf("takes %s from %s", i, m.User.Name)) + p.bot.Send(bot.Action, m.Channel, fmt.Sprintf("takes %s from %s", i, m.User.Name)) } return true } @@ -223,20 +225,7 @@ func checkerr(e error) { } } -func (p *InventoryPlugin) Event(e string, message msg.Message) bool { - return false -} - -func (p *InventoryPlugin) BotMessage(message msg.Message) bool { - return false -} - -func (p *InventoryPlugin) Help(e string, m []string) { -} - func (p *InventoryPlugin) RegisterWeb() *string { // nothing to register return nil } - -func (p *InventoryPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/leftpad/leftpad.go b/plugins/leftpad/leftpad.go index cf5327e..e005b46 100644 --- a/plugins/leftpad/leftpad.go +++ b/plugins/leftpad/leftpad.go @@ -20,19 +20,20 @@ type LeftpadPlugin struct { } // New creates a new LeftpadPlugin with the Plugin interface -func New(bot bot.Bot) *LeftpadPlugin { - p := LeftpadPlugin{ - bot: bot, - config: bot.Config(), +func New(b bot.Bot) *LeftpadPlugin { + p := &LeftpadPlugin{ + bot: b, + config: b.Config(), } - return &p + b.Register(p, bot.Message, p.message) + return p } type leftpadResp struct { Str string } -func (p *LeftpadPlugin) Message(message msg.Message) bool { +func (p *LeftpadPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { if !message.Command { return false } @@ -42,40 +43,27 @@ func (p *LeftpadPlugin) Message(message msg.Message) bool { padchar := parts[1] length, err := strconv.Atoi(parts[2]) if err != nil { - p.bot.SendMessage(message.Channel, "Invalid padding number") + p.bot.Send(bot.Message, message.Channel, "Invalid padding number") return true } maxLen, who := p.config.GetInt("LeftPad.MaxLen", 50), p.config.Get("LeftPad.Who", "Putin") if length > maxLen && maxLen > 0 { msg := fmt.Sprintf("%s would kill me if I did that.", who) - p.bot.SendMessage(message.Channel, msg) + p.bot.Send(bot.Message, message.Channel, msg) return true } text := strings.Join(parts[3:], " ") res := leftpad.LeftPad(text, length, padchar) - p.bot.SendMessage(message.Channel, res) + p.bot.Send(bot.Message, message.Channel, res) return true } return false } -func (p *LeftpadPlugin) Event(e string, message msg.Message) bool { - return false -} - -func (p *LeftpadPlugin) BotMessage(message msg.Message) bool { - return false -} - -func (p *LeftpadPlugin) Help(e string, m []string) { -} - func (p *LeftpadPlugin) RegisterWeb() *string { // nothing to register return nil } - -func (p *LeftpadPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/leftpad/leftpad_test.go b/plugins/leftpad/leftpad_test.go index 21e4af4..1d97488 100644 --- a/plugins/leftpad/leftpad_test.go +++ b/plugins/leftpad/leftpad_test.go @@ -13,12 +13,12 @@ import ( "github.com/velour/catbase/plugins/counter" ) -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -37,28 +37,28 @@ func makePlugin(t *testing.T) (*LeftpadPlugin, *bot.MockBot) { func TestLeftpad(t *testing.T) { p, mb := makePlugin(t) - p.Message(makeMessage("!leftpad test 8 test")) + p.message(makeMessage("!leftpad test 8 test")) assert.Contains(t, mb.Messages[0], "testtest") assert.Len(t, mb.Messages, 1) } func TestBadNumber(t *testing.T) { p, mb := makePlugin(t) - p.Message(makeMessage("!leftpad test fuck test")) + p.message(makeMessage("!leftpad test fuck test")) assert.Contains(t, mb.Messages[0], "Invalid") assert.Len(t, mb.Messages, 1) } func TestNotCommand(t *testing.T) { p, mb := makePlugin(t) - p.Message(makeMessage("leftpad test fuck test")) + p.message(makeMessage("leftpad test fuck test")) assert.Len(t, mb.Messages, 0) } func TestNoMaxLen(t *testing.T) { p, mb := makePlugin(t) p.config.Set("LeftPad.MaxLen", "0") - p.Message(makeMessage("!leftpad dicks 100 dicks")) + p.message(makeMessage("!leftpad dicks 100 dicks")) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "dicks") } @@ -67,7 +67,7 @@ func Test50Padding(t *testing.T) { p, mb := makePlugin(t) p.config.Set("LeftPad.MaxLen", "50") assert.Equal(t, 50, p.config.GetInt("LeftPad.MaxLen", 100)) - p.Message(makeMessage("!leftpad dicks 100 dicks")) + p.message(makeMessage("!leftpad dicks 100 dicks")) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "kill me") } @@ -75,33 +75,17 @@ func Test50Padding(t *testing.T) { func TestUnder50Padding(t *testing.T) { p, mb := makePlugin(t) p.config.Set("LeftPad.MaxLen", "50") - p.Message(makeMessage("!leftpad dicks 49 dicks")) + p.message(makeMessage("!leftpad dicks 49 dicks")) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "dicks") } func TestNotPadding(t *testing.T) { p, mb := makePlugin(t) - p.Message(makeMessage("!lololol")) + p.message(makeMessage("!lololol")) assert.Len(t, mb.Messages, 0) } -func TestHelp(t *testing.T) { - p, mb := makePlugin(t) - p.Help("channel", []string{}) - assert.Len(t, mb.Messages, 0) -} - -func TestBotMessage(t *testing.T) { - p, _ := makePlugin(t) - assert.False(t, p.BotMessage(makeMessage("test"))) -} - -func TestEvent(t *testing.T) { - p, _ := makePlugin(t) - assert.False(t, p.Event("dummy", makeMessage("test"))) -} - func TestRegisterWeb(t *testing.T) { p, _ := makePlugin(t) assert.Nil(t, p.RegisterWeb()) diff --git a/plugins/nerdepedia/nerdepedia.go b/plugins/nerdepedia/nerdepedia.go index 1da2514..eb040a7 100644 --- a/plugins/nerdepedia/nerdepedia.go +++ b/plugins/nerdepedia/nerdepedia.go @@ -27,17 +27,20 @@ type NerdepediaPlugin struct { } // NewNerdepediaPlugin creates a new NerdepediaPlugin with the Plugin interface -func New(bot bot.Bot) *NerdepediaPlugin { - return &NerdepediaPlugin{ - bot: bot, - config: bot.Config(), +func New(b bot.Bot) *NerdepediaPlugin { + np := &NerdepediaPlugin{ + bot: b, + config: b.Config(), } + b.Register(np, bot.Message, np.message) + b.Register(np, bot.Help, np.help) + return np } // 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 *NerdepediaPlugin) Message(message msg.Message) bool { +func (p *NerdepediaPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { lowerCase := strings.ToLower(message.Body) query := "" if lowerCase == "may the force be with you" || lowerCase == "help me obi-wan" { @@ -78,7 +81,7 @@ func (p *NerdepediaPlugin) Message(message msg.Message) bool { } if description != "" && link != "" { - p.bot.SendMessage(message.Channel, fmt.Sprintf("%s (%s)", description, link)) + p.bot.Send(bot.Message, message.Channel, fmt.Sprintf("%s (%s)", description, link)) return true } } @@ -87,23 +90,12 @@ func (p *NerdepediaPlugin) Message(message msg.Message) bool { } // Help responds to help requests. Every plugin must implement a help function. -func (p *NerdepediaPlugin) Help(channel string, parts []string) { - p.bot.SendMessage(channel, "nerd stuff") -} - -// Empty event handler because this plugin does not do anything on event recv -func (p *NerdepediaPlugin) Event(kind string, message msg.Message) bool { - return false -} - -// Handler for bot's own messages -func (p *NerdepediaPlugin) BotMessage(message msg.Message) bool { - return false +func (p *NerdepediaPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.bot.Send(bot.Message, message.Channel, "nerd stuff") + return true } // Register any web URLs desired func (p *NerdepediaPlugin) RegisterWeb() *string { return nil } - -func (p *NerdepediaPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/nerdepedia/nerdepeida_test.go b/plugins/nerdepedia/nerdepeida_test.go index 5cba343..eb59807 100644 --- a/plugins/nerdepedia/nerdepeida_test.go +++ b/plugins/nerdepedia/nerdepeida_test.go @@ -12,12 +12,12 @@ import ( "github.com/velour/catbase/bot/user" ) -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -29,7 +29,7 @@ func TestWars(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("help me obi-wan")) + res := c.message(makeMessage("help me obi-wan")) assert.Len(t, mb.Messages, 1) assert.True(t, res) } @@ -38,7 +38,7 @@ func TestTrek(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("live long and prosper")) + res := c.message(makeMessage("live long and prosper")) assert.Len(t, mb.Messages, 1) assert.True(t, res) } @@ -47,7 +47,7 @@ func TestDune(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("bless the maker")) + res := c.message(makeMessage("bless the maker")) assert.Len(t, mb.Messages, 1) assert.True(t, res) } @@ -56,7 +56,7 @@ func TestPoke(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("gotta catch em all")) + res := c.message(makeMessage("gotta catch em all")) assert.Len(t, mb.Messages, 1) assert.True(t, res) } diff --git a/plugins/picker/picker.go b/plugins/picker/picker.go index 53b5f4b..3a1c320 100644 --- a/plugins/picker/picker.go +++ b/plugins/picker/picker.go @@ -20,30 +20,33 @@ type PickerPlugin struct { } // NewPickerPlugin creates a new PickerPlugin with the Plugin interface -func New(bot bot.Bot) *PickerPlugin { - return &PickerPlugin{ - Bot: bot, +func New(b bot.Bot) *PickerPlugin { + pp := &PickerPlugin{ + Bot: b, } + b.Register(pp, bot.Message, pp.message) + b.Register(pp, bot.Help, pp.help) + return pp } // 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 *PickerPlugin) Message(message msg.Message) bool { +func (p *PickerPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { if !strings.HasPrefix(message.Body, "pick") { return false } n, items, err := p.parse(message.Body) if err != nil { - p.Bot.SendMessage(message.Channel, err.Error()) + p.Bot.Send(bot.Message, message.Channel, err.Error()) return true } if n == 1 { item := items[rand.Intn(len(items))] out := fmt.Sprintf("I've chosen %q for you.", strings.TrimSpace(item)) - p.Bot.SendMessage(message.Channel, out) + p.Bot.Send(bot.Message, message.Channel, out) return true } @@ -59,7 +62,7 @@ func (p *PickerPlugin) Message(message msg.Message) bool { fmt.Fprintf(&b, ", %q", item) } b.WriteString(" }") - p.Bot.SendMessage(message.Channel, b.String()) + p.Bot.Send(bot.Message, message.Channel, b.String()) return true } @@ -108,18 +111,9 @@ func (p *PickerPlugin) parse(body string) (int, []string, error) { } // Help responds to help requests. Every plugin must implement a help function. -func (p *PickerPlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "Choose from a list of options. Try \"pick {a,b,c}\".") -} - -// Empty event handler because this plugin does not do anything on event recv -func (p *PickerPlugin) Event(kind string, message msg.Message) bool { - return false -} - -// Handler for bot's own messages -func (p *PickerPlugin) BotMessage(message msg.Message) bool { - return false +func (p *PickerPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.Bot.Send(bot.Message, message.Channel, "Choose from a list of options. Try \"pick {a,b,c}\".") + return true } // Register any web URLs desired diff --git a/plugins/picker/picker_test.go b/plugins/picker/picker_test.go index 0c2dd60..b01684a 100644 --- a/plugins/picker/picker_test.go +++ b/plugins/picker/picker_test.go @@ -12,12 +12,12 @@ import ( "github.com/velour/catbase/bot/user" ) -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -29,7 +29,7 @@ func TestPick2(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!pick 2 { a, b,c}")) + res := c.message(makeMessage("!pick 2 { a, b,c}")) assert.Len(t, mb.Messages, 1) if !res { t.Fatalf("expected a successful choice, got %q", mb.Messages[0]) @@ -40,7 +40,7 @@ func TestPickDefault(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - _ = c.Message(makeMessage("!pick { a}")) + _ = c.message(makeMessage("!pick { a}")) assert.Len(t, mb.Messages, 1) assert.Equal(t, `I've chosen "a" for you.`, mb.Messages[0]) } diff --git a/plugins/reaction/reaction.go b/plugins/reaction/reaction.go index 6f98d55..c964022 100644 --- a/plugins/reaction/reaction.go +++ b/plugins/reaction/reaction.go @@ -15,14 +15,16 @@ type ReactionPlugin struct { Config *config.Config } -func New(bot bot.Bot) *ReactionPlugin { - return &ReactionPlugin{ - Bot: bot, - Config: bot.Config(), +func New(b bot.Bot) *ReactionPlugin { + rp := &ReactionPlugin{ + Bot: b, + Config: b.Config(), } + b.Register(rp, bot.Message, rp.message) + return rp } -func (p *ReactionPlugin) Message(message msg.Message) bool { +func (p *ReactionPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { harrass := false for _, nick := range p.Config.GetArray("Reaction.HarrassList", []string{}) { if message.User.Name == nick { @@ -56,26 +58,12 @@ func (p *ReactionPlugin) Message(message msg.Message) bool { reaction = p.Config.GetArray("Reaction.NegativeReactions", []string{})[index] } - p.Bot.React(message.Channel, reaction, message) + p.Bot.Send(bot.Reaction, message.Channel, reaction, message) } return false } -func (p *ReactionPlugin) Help(channel string, parts []string) { - -} - -func (p *ReactionPlugin) Event(kind string, message msg.Message) bool { - return false -} - -func (p *ReactionPlugin) BotMessage(message msg.Message) bool { - return false -} - func (p *ReactionPlugin) RegisterWeb() *string { return nil } - -func (p *ReactionPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/reminder/reminder.go b/plugins/reminder/reminder.go index 4e00e9c..93d4dec 100644 --- a/plugins/reminder/reminder.go +++ b/plugins/reminder/reminder.go @@ -38,9 +38,9 @@ type Reminder struct { channel string } -func New(bot bot.Bot) *ReminderPlugin { +func New(b bot.Bot) *ReminderPlugin { log.SetFlags(log.LstdFlags | log.Lshortfile) - if _, err := bot.DB().Exec(`create table if not exists reminders ( + if _, err := b.DB().Exec(`create table if not exists reminders ( id integer primary key, fromWho string, toWho string, @@ -56,21 +56,24 @@ func New(bot bot.Bot) *ReminderPlugin { timer.Stop() plugin := &ReminderPlugin{ - Bot: bot, - db: bot.DB(), + Bot: b, + db: b.DB(), mutex: &sync.Mutex{}, timer: timer, - config: bot.Config(), + config: b.Config(), } plugin.queueUpNextReminder() go reminderer(plugin) + b.Register(plugin, bot.Message, plugin.message) + b.Register(plugin, bot.Help, plugin.help) + return plugin } -func (p *ReminderPlugin) Message(message msg.Message) bool { +func (p *ReminderPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { channel := message.Channel from := message.User.Name @@ -85,7 +88,7 @@ func (p *ReminderPlugin) Message(message msg.Message) bool { dur, err := time.ParseDuration(parts[3]) if err != nil { - p.Bot.SendMessage(channel, "Easy cowboy, not sure I can parse that duration.") + p.Bot.Send(bot.Message, channel, "Easy cowboy, not sure I can parse that duration.") return true } @@ -113,7 +116,7 @@ func (p *ReminderPlugin) Message(message msg.Message) bool { //remind who every dur for dur2 blah dur2, err := time.ParseDuration(parts[5]) if err != nil { - p.Bot.SendMessage(channel, "Easy cowboy, not sure I can parse that duration.") + p.Bot.Send(bot.Message, channel, "Easy cowboy, not sure I can parse that duration.") return true } @@ -124,7 +127,7 @@ func (p *ReminderPlugin) Message(message msg.Message) bool { max := p.config.GetInt("Reminder.MaxBatchAdd", 10) for i := 0; when.Before(endTime); i++ { if i >= max { - p.Bot.SendMessage(channel, "Easy cowboy, that's a lot of reminders. I'll add some of them.") + p.Bot.Send(bot.Message, channel, "Easy cowboy, that's a lot of reminders. I'll add some of them.") doConfirm = false break } @@ -141,14 +144,14 @@ func (p *ReminderPlugin) Message(message msg.Message) bool { when = when.Add(dur) } } else { - p.Bot.SendMessage(channel, "Easy cowboy, not sure I comprehend what you're asking.") + p.Bot.Send(bot.Message, channel, "Easy cowboy, not sure I comprehend what you're asking.") return true } if doConfirm && from == who { - p.Bot.SendMessage(channel, fmt.Sprintf("Okay. I'll remind you.")) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("Okay. I'll remind you.")) } else if doConfirm { - p.Bot.SendMessage(channel, fmt.Sprintf("Sure %s, I'll remind %s.", from, who)) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("Sure %s, I'll remind %s.", from, who)) } p.queueUpNextReminder() @@ -168,22 +171,22 @@ func (p *ReminderPlugin) Message(message msg.Message) bool { } } if err != nil { - p.Bot.SendMessage(channel, "listing failed.") + p.Bot.Send(bot.Message, channel, "listing failed.") } else { - p.Bot.SendMessage(channel, response) + p.Bot.Send(bot.Message, channel, response) } return true } else if len(parts) == 3 && strings.ToLower(parts[0]) == "cancel" && strings.ToLower(parts[1]) == "reminder" { id, err := strconv.ParseInt(parts[2], 10, 64) if err != nil { - p.Bot.SendMessage(channel, fmt.Sprintf("couldn't parse id: %s", parts[2])) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("couldn't parse id: %s", parts[2])) } else { err := p.deleteReminder(id) if err == nil { - p.Bot.SendMessage(channel, fmt.Sprintf("successfully canceled reminder: %s", parts[2])) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("successfully canceled reminder: %s", parts[2])) } else { - p.Bot.SendMessage(channel, fmt.Sprintf("failed to find and cancel reminder: %s", parts[2])) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("failed to find and cancel reminder: %s", parts[2])) } } return true @@ -192,16 +195,9 @@ func (p *ReminderPlugin) Message(message msg.Message) bool { return false } -func (p *ReminderPlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "Pester someone with a reminder. Try \"remind in message\".\n\nUnsure about duration syntax? Check https://golang.org/pkg/time/#ParseDuration") -} - -func (p *ReminderPlugin) Event(kind string, message msg.Message) bool { - return false -} - -func (p *ReminderPlugin) BotMessage(message msg.Message) bool { - return false +func (p *ReminderPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.Bot.Send(bot.Message, message.Channel, "Pester someone with a reminder. Try \"remind in message\".\n\nUnsure about duration syntax? Check https://golang.org/pkg/time/#ParseDuration") + return true } func (p *ReminderPlugin) RegisterWeb() *string { @@ -353,7 +349,7 @@ func reminderer(p *ReminderPlugin) { message = fmt.Sprintf("Hey %s, %s wanted you to be reminded: %s", reminder.who, reminder.from, reminder.what) } - p.Bot.SendMessage(reminder.channel, message) + p.Bot.Send(bot.Message, reminder.channel, message) if err := p.deleteReminder(reminder.id); err != nil { log.Print(reminder.id) @@ -365,5 +361,3 @@ func reminderer(p *ReminderPlugin) { p.queueUpNextReminder() } } - -func (p *ReminderPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/reminder/reminder_test.go b/plugins/reminder/reminder_test.go index d3c6155..c4a4e9e 100644 --- a/plugins/reminder/reminder_test.go +++ b/plugins/reminder/reminder_test.go @@ -14,25 +14,16 @@ import ( "github.com/velour/catbase/bot/user" ) -func makeMessage(payload string) msg.Message { - isCmd := strings.HasPrefix(payload, "!") - if isCmd { - payload = payload[1:] - } - return msg.Message{ - User: &user.User{Name: "tester"}, - Channel: "test", - Body: payload, - Command: isCmd, - } +func makeMessage(payload string) (bot.Kind, msg.Message) { + return makeMessageBy(payload, "tester") } -func makeMessageBy(payload, by string) msg.Message { +func makeMessageBy(payload, by string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: by}, Channel: "test", Body: payload, @@ -49,7 +40,7 @@ func setup(t *testing.T) (*ReminderPlugin, *bot.MockBot) { func TestMeReminder(t *testing.T) { c, mb := setup(t) - res := c.Message(makeMessage("!remind me in 1s don't fail this test")) + res := c.message(makeMessage("!remind me in 1s don't fail this test")) time.Sleep(2 * time.Second) assert.Len(t, mb.Messages, 2) assert.True(t, res) @@ -59,7 +50,7 @@ func TestMeReminder(t *testing.T) { func TestReminder(t *testing.T) { c, mb := setup(t) - res := c.Message(makeMessage("!remind testuser in 1s don't fail this test")) + res := c.message(makeMessage("!remind testuser in 1s don't fail this test")) time.Sleep(2 * time.Second) assert.Len(t, mb.Messages, 2) assert.True(t, res) @@ -69,9 +60,9 @@ func TestReminder(t *testing.T) { func TestReminderReorder(t *testing.T) { c, mb := setup(t) - res := c.Message(makeMessage("!remind testuser in 2s don't fail this test 2")) + res := c.message(makeMessage("!remind testuser in 2s don't fail this test 2")) assert.True(t, res) - res = c.Message(makeMessage("!remind testuser in 1s don't fail this test 1")) + res = c.message(makeMessage("!remind testuser in 1s don't fail this test 1")) assert.True(t, res) time.Sleep(5 * time.Second) assert.Len(t, mb.Messages, 4) @@ -83,7 +74,7 @@ func TestReminderReorder(t *testing.T) { func TestReminderParse(t *testing.T) { c, mb := setup(t) - res := c.Message(makeMessage("!remind testuser in unparseable don't fail this test")) + res := c.message(makeMessage("!remind testuser in unparseable don't fail this test")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "Easy cowboy, not sure I can parse that duration.") @@ -91,7 +82,7 @@ func TestReminderParse(t *testing.T) { func TestEmptyList(t *testing.T) { c, mb := setup(t) - res := c.Message(makeMessage("!list reminders")) + res := c.message(makeMessage("!list reminders")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "no pending reminders") @@ -99,11 +90,11 @@ func TestEmptyList(t *testing.T) { func TestList(t *testing.T) { c, mb := setup(t) - res := c.Message(makeMessage("!remind testuser in 5m don't fail this test 1")) + res := c.message(makeMessage("!remind testuser in 5m don't fail this test 1")) assert.True(t, res) - res = c.Message(makeMessage("!remind testuser in 5m don't fail this test 2")) + res = c.message(makeMessage("!remind testuser in 5m don't fail this test 2")) assert.True(t, res) - res = c.Message(makeMessage("!list reminders")) + res = c.message(makeMessage("!list reminders")) assert.True(t, res) assert.Len(t, mb.Messages, 3) assert.Contains(t, mb.Messages[2], "1) tester -> testuser :: don't fail this test 1 @ ") @@ -112,11 +103,11 @@ func TestList(t *testing.T) { func TestListBy(t *testing.T) { c, mb := setup(t) - res := c.Message(makeMessageBy("!remind testuser in 5m don't fail this test 1", "testuser")) + res := c.message(makeMessageBy("!remind testuser in 5m don't fail this test 1", "testuser")) assert.True(t, res) - res = c.Message(makeMessageBy("!remind testuser in 5m don't fail this test 2", "testuser2")) + res = c.message(makeMessageBy("!remind testuser in 5m don't fail this test 2", "testuser2")) assert.True(t, res) - res = c.Message(makeMessage("!list reminders from testuser")) + res = c.message(makeMessage("!list reminders from testuser")) assert.True(t, res) assert.Len(t, mb.Messages, 3) assert.Contains(t, mb.Messages[2], "don't fail this test 1 @ ") @@ -125,11 +116,11 @@ func TestListBy(t *testing.T) { func TestListTo(t *testing.T) { c, mb := setup(t) - res := c.Message(makeMessageBy("!remind testuser2 in 5m don't fail this test 1", "testuser")) + res := c.message(makeMessageBy("!remind testuser2 in 5m don't fail this test 1", "testuser")) assert.True(t, res) - res = c.Message(makeMessageBy("!remind testuser in 5m don't fail this test 2", "testuser2")) + res = c.message(makeMessageBy("!remind testuser in 5m don't fail this test 2", "testuser2")) assert.True(t, res) - res = c.Message(makeMessage("!list reminders to testuser")) + res = c.message(makeMessage("!list reminders to testuser")) assert.True(t, res) assert.Len(t, mb.Messages, 3) assert.NotContains(t, mb.Messages[2], "don't fail this test 1 @ ") @@ -138,11 +129,11 @@ func TestListTo(t *testing.T) { func TestToEmptyList(t *testing.T) { c, mb := setup(t) - res := c.Message(makeMessageBy("!remind testuser2 in 5m don't fail this test 1", "testuser")) + res := c.message(makeMessageBy("!remind testuser2 in 5m don't fail this test 1", "testuser")) assert.True(t, res) - res = c.Message(makeMessageBy("!remind testuser in 5m don't fail this test 2", "testuser2")) + res = c.message(makeMessageBy("!remind testuser in 5m don't fail this test 2", "testuser2")) assert.True(t, res) - res = c.Message(makeMessage("!list reminders to test")) + res = c.message(makeMessage("!list reminders to test")) assert.True(t, res) assert.Len(t, mb.Messages, 3) assert.Contains(t, mb.Messages[2], "no pending reminders") @@ -150,11 +141,11 @@ func TestToEmptyList(t *testing.T) { func TestFromEmptyList(t *testing.T) { c, mb := setup(t) - res := c.Message(makeMessageBy("!remind testuser2 in 5m don't fail this test 1", "testuser")) + res := c.message(makeMessageBy("!remind testuser2 in 5m don't fail this test 1", "testuser")) assert.True(t, res) - res = c.Message(makeMessageBy("!remind testuser in 5m don't fail this test 2", "testuser2")) + res = c.message(makeMessageBy("!remind testuser in 5m don't fail this test 2", "testuser2")) assert.True(t, res) - res = c.Message(makeMessage("!list reminders from test")) + res = c.message(makeMessage("!list reminders from test")) assert.True(t, res) assert.Len(t, mb.Messages, 3) assert.Contains(t, mb.Messages[2], "no pending reminders") @@ -164,9 +155,9 @@ func TestBatchMax(t *testing.T) { c, mb := setup(t) c.config.Set("Reminder.MaxBatchAdd", "10") assert.NotNil(t, c) - res := c.Message(makeMessage("!remind testuser every 1h for 24h yikes")) + res := c.message(makeMessage("!remind testuser every 1h for 24h yikes")) assert.True(t, res) - res = c.Message(makeMessage("!list reminders")) + res = c.message(makeMessage("!list reminders")) assert.True(t, res) time.Sleep(6 * time.Second) assert.Len(t, mb.Messages, 2) @@ -180,11 +171,11 @@ func TestBatchMax(t *testing.T) { func TestCancel(t *testing.T) { c, mb := setup(t) assert.NotNil(t, c) - res := c.Message(makeMessage("!remind testuser in 1m don't fail this test")) + res := c.message(makeMessage("!remind testuser in 1m don't fail this test")) assert.True(t, res) - res = c.Message(makeMessage("!cancel reminder 1")) + res = c.message(makeMessage("!cancel reminder 1")) assert.True(t, res) - res = c.Message(makeMessage("!list reminders")) + res = c.message(makeMessage("!list reminders")) assert.True(t, res) assert.Len(t, mb.Messages, 3) assert.Contains(t, mb.Messages[0], "Sure tester, I'll remind testuser.") @@ -195,7 +186,7 @@ func TestCancel(t *testing.T) { func TestCancelMiss(t *testing.T) { c, mb := setup(t) assert.NotNil(t, c) - res := c.Message(makeMessage("!cancel reminder 1")) + res := c.message(makeMessage("!cancel reminder 1")) assert.True(t, res) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "failed to find and cancel reminder: 1") @@ -208,13 +199,13 @@ func TestLimitList(t *testing.T) { assert.NotNil(t, c) //Someone can redo this with a single batch add, but I can't locally due to an old version of sqllite (maybe). - res := c.Message(makeMessage("!remind testuser every 1h for 10h don't fail this test")) + res := c.message(makeMessage("!remind testuser every 1h for 10h don't fail this test")) assert.True(t, res) - res = c.Message(makeMessage("!remind testuser every 1h for 10h don't fail this test")) + res = c.message(makeMessage("!remind testuser every 1h for 10h don't fail this test")) assert.True(t, res) - res = c.Message(makeMessage("!remind testuser every 1h for 10h don't fail this test")) + res = c.message(makeMessage("!remind testuser every 1h for 10h don't fail this test")) assert.True(t, res) - res = c.Message(makeMessage("!list reminders")) + res = c.message(makeMessage("!list reminders")) assert.True(t, res) assert.Len(t, mb.Messages, 4) assert.Contains(t, mb.Messages[0], "Sure tester, I'll remind testuser.") @@ -232,22 +223,10 @@ func TestLimitList(t *testing.T) { func TestHelp(t *testing.T) { c, mb := setup(t) assert.NotNil(t, c) - c.Help("channel", []string{}) + c.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) assert.Len(t, mb.Messages, 1) } -func TestBotMessage(t *testing.T) { - c, _ := setup(t) - assert.NotNil(t, c) - assert.False(t, c.BotMessage(makeMessage("test"))) -} - -func TestEvent(t *testing.T) { - c, _ := setup(t) - assert.NotNil(t, c) - assert.False(t, c.Event("dummy", makeMessage("test"))) -} - func TestRegisterWeb(t *testing.T) { c, _ := setup(t) assert.NotNil(t, c) diff --git a/plugins/rpgORdie/rpgORdie.go b/plugins/rpgORdie/rpgORdie.go index 48394d4..8b5a40e 100644 --- a/plugins/rpgORdie/rpgORdie.go +++ b/plugins/rpgORdie/rpgORdie.go @@ -98,44 +98,38 @@ func (b *board) checkAndMove(dx, dy int) int { } func New(b bot.Bot) *RPGPlugin { - return &RPGPlugin{ + rpg := &RPGPlugin{ Bot: b, listenFor: map[string]*board{}, } + b.Register(rpg, bot.Message, rpg.message) + b.Register(rpg, bot.Reply, rpg.replyMessage) + b.Register(rpg, bot.Help, rpg.help) + return rpg } -func (p *RPGPlugin) Message(message msg.Message) bool { +func (p *RPGPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { if strings.ToLower(message.Body) == "start rpg" { b := NewRandomBoard() - ts := p.Bot.SendMessage(message.Channel, b.toMessageString()) + ts, _ := p.Bot.Send(bot.Message, message.Channel, b.toMessageString()) p.listenFor[ts] = b - p.Bot.ReplyToMessageIdentifier(message.Channel, "Over here.", ts) + p.Bot.Send(bot.Reply, message.Channel, "Over here.", ts) return true } return false } -func (p *RPGPlugin) LoadData() { - -} - -func (p *RPGPlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "Go find a walkthrough or something.") -} - -func (p *RPGPlugin) Event(kind string, message msg.Message) bool { - return false -} - -func (p *RPGPlugin) BotMessage(message msg.Message) bool { - return false +func (p *RPGPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.Bot.Send(bot.Message, message.Channel, "Go find a walkthrough or something.") + return true } func (p *RPGPlugin) RegisterWeb() *string { return nil } -func (p *RPGPlugin) ReplyMessage(message msg.Message, identifier string) bool { +func (p *RPGPlugin) replyMessage(kind bot.Kind, message msg.Message, args ...interface{}) bool { + identifier := args[0].(string) if strings.ToLower(message.User.Name) != strings.ToLower(p.Bot.Config().Get("Nick", "bot")) { if b, ok := p.listenFor[identifier]; ok { @@ -155,12 +149,12 @@ func (p *RPGPlugin) ReplyMessage(message msg.Message, identifier string) bool { switch res { case OK: - p.Bot.Edit(message.Channel, b.toMessageString(), identifier) + p.Bot.Send(bot.Edit, message.Channel, b.toMessageString(), identifier) case WIN: - p.Bot.Edit(message.Channel, b.toMessageString(), identifier) - p.Bot.ReplyToMessageIdentifier(message.Channel, "congratulations, you beat the easiest level imaginable.", identifier) + p.Bot.Send(bot.Edit, message.Channel, b.toMessageString(), identifier) + p.Bot.Send(bot.Reply, message.Channel, "congratulations, you beat the easiest level imaginable.", identifier) case INVALID: - p.Bot.ReplyToMessageIdentifier(message.Channel, fmt.Sprintf("you can't move %s", message.Body), identifier) + p.Bot.Send(bot.Reply, message.Channel, fmt.Sprintf("you can't move %s", message.Body), identifier) } return true } diff --git a/plugins/rss/rss.go b/plugins/rss/rss.go index 37a5b1d..b25d749 100644 --- a/plugins/rss/rss.go +++ b/plugins/rss/rss.go @@ -49,28 +49,31 @@ func (c *cacheItem) getCurrentPage(maxLines int) string { return page } -func New(bot bot.Bot) *RSSPlugin { - return &RSSPlugin{ - Bot: bot, +func New(b bot.Bot) *RSSPlugin { + rss := &RSSPlugin{ + Bot: b, cache: map[string]*cacheItem{}, - shelfLife: time.Minute * 20, - maxLines: 5, + shelfLife: time.Minute * time.Duration(b.Config().GetInt("rss.shelfLife", 20)), + maxLines: b.Config().GetInt("rss.maxLines", 5), } + b.Register(rss, bot.Message, rss.message) + b.Register(rss, bot.Help, rss.help) + return rss } -func (p *RSSPlugin) Message(message msg.Message) bool { +func (p *RSSPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { tokens := strings.Fields(message.Body) numTokens := len(tokens) if numTokens == 2 && strings.ToLower(tokens[0]) == "rss" { if item, ok := p.cache[strings.ToLower(tokens[1])]; ok && time.Now().Before(item.expiration) { - p.Bot.SendMessage(message.Channel, item.getCurrentPage(p.maxLines)) + p.Bot.Send(bot.Message, message.Channel, item.getCurrentPage(p.maxLines)) return true } else { fp := gofeed.NewParser() feed, err := fp.ParseURL(tokens[1]) if err != nil { - p.Bot.SendMessage(message.Channel, fmt.Sprintf("RSS error: %s", err.Error())) + p.Bot.Send(bot.Message, message.Channel, fmt.Sprintf("RSS error: %s", err.Error())) return true } item := &cacheItem{ @@ -86,7 +89,7 @@ func (p *RSSPlugin) Message(message msg.Message) bool { p.cache[strings.ToLower(tokens[1])] = item - p.Bot.SendMessage(message.Channel, item.getCurrentPage(p.maxLines)) + p.Bot.Send(bot.Message, message.Channel, item.getCurrentPage(p.maxLines)) return true } } @@ -94,28 +97,13 @@ func (p *RSSPlugin) Message(message msg.Message) bool { return false } -func (p *RSSPlugin) LoadData() { - // This bot has no data to load -} - // Help responds to help requests. Every plugin must implement a help function. -func (p *RSSPlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "try '!rss http://rss.cnn.com/rss/edition.rss'") -} - -// Empty event handler because this plugin does not do anything on event recv -func (p *RSSPlugin) Event(kind string, message msg.Message) bool { - return false -} - -// Handler for bot's own messages -func (p *RSSPlugin) BotMessage(message msg.Message) bool { - return false +func (p *RSSPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.Bot.Send(bot.Message, message.Channel, "try '!rss http://rss.cnn.com/rss/edition.rss'") + return true } // Register any web URLs desired func (p *RSSPlugin) RegisterWeb() *string { return nil } - -func (p *RSSPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/rss/rss_test.go b/plugins/rss/rss_test.go index 7c20fd1..900031a 100644 --- a/plugins/rss/rss_test.go +++ b/plugins/rss/rss_test.go @@ -11,12 +11,12 @@ import ( "github.com/velour/catbase/bot/user" ) -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -28,7 +28,7 @@ func TestRSS(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!rss http://rss.cnn.com/rss/edition.rss")) + res := c.message(makeMessage("!rss http://rss.cnn.com/rss/edition.rss")) assert.Len(t, mb.Messages, 1) assert.True(t, res) } @@ -38,7 +38,7 @@ func TestRSSPaging(t *testing.T) { c := New(mb) assert.NotNil(t, c) for i := 0; i < 20; i++ { - res := c.Message(makeMessage("!rss http://rss.cnn.com/rss/edition.rss")) + res := c.message(makeMessage("!rss http://rss.cnn.com/rss/edition.rss")) assert.True(t, res) } diff --git a/plugins/sisyphus/sisyphus.go b/plugins/sisyphus/sisyphus.go index 91a9fbc..5a0a8b8 100644 --- a/plugins/sisyphus/sisyphus.go +++ b/plugins/sisyphus/sisyphus.go @@ -37,17 +37,17 @@ type game struct { nextAns int } -func NewRandomGame(bot bot.Bot, channel, who string) *game { +func NewRandomGame(b bot.Bot, channel, who string) *game { size := rand.Intn(9) + 2 g := game{ channel: channel, - bot: bot, + bot: b, who: who, start: time.Now(), size: size, current: size / 2, } - g.id = bot.SendMessage(channel, g.toMessageString()) + g.id, _ = b.Send(bot.Message, channel, g.toMessageString()) g.schedulePush() g.scheduleDecrement() @@ -98,11 +98,11 @@ func (g *game) endGame() { func (g *game) handleDecrement() { g.current++ - g.bot.Edit(g.channel, g.toMessageString(), g.id) + g.bot.Send(bot.Edit, g.channel, g.toMessageString(), g.id) if g.current > g.size-2 { - g.bot.ReplyToMessageIdentifier(g.channel, "you lose", g.id) + g.bot.Send(bot.Reply, g.channel, "you lose", g.id) msg := fmt.Sprintf("%s just lost the game after %s", g.who, time.Now().Sub(g.start)) - g.bot.SendMessage(g.channel, msg) + g.bot.Send(bot.Message, g.channel, msg) g.endGame() } else { g.scheduleDecrement() @@ -110,7 +110,7 @@ func (g *game) handleDecrement() { } func (g *game) handleNotify() { - g.bot.ReplyToMessageIdentifier(g.channel, "You can push now.\n"+g.generateQuestion(), g.id) + g.bot.Send(bot.Reply, g.channel, "You can push now.\n"+g.generateQuestion(), g.id) } func (g *game) generateQuestion() string { @@ -162,39 +162,37 @@ func (g *game) toMessageString() string { } func New(b bot.Bot) *SisyphusPlugin { - return &SisyphusPlugin{ + sp := &SisyphusPlugin{ Bot: b, listenFor: map[string]*game{}, } + b.Register(sp, bot.Message, sp.message) + b.Register(sp, bot.Reply, sp.replyMessage) + b.Register(sp, bot.Help, sp.help) + return sp } -func (p *SisyphusPlugin) Message(message msg.Message) bool { +func (p *SisyphusPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { if strings.ToLower(message.Body) == "start sisyphus" { b := NewRandomGame(p.Bot, message.Channel, message.User.Name) p.listenFor[b.id] = b - p.Bot.ReplyToMessageIdentifier(message.Channel, "Over here.", b.id) + p.Bot.Send(bot.Reply, message.Channel, "Over here.", b.id) return true } return false } -func (p *SisyphusPlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "https://en.wikipedia.org/wiki/Sisyphus") -} - -func (p *SisyphusPlugin) Event(kind string, message msg.Message) bool { - return false -} - -func (p *SisyphusPlugin) BotMessage(message msg.Message) bool { - return false +func (p *SisyphusPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.Bot.Send(bot.Message, message.Channel, "https://en.wikipedia.org/wiki/Sisyphus") + return true } func (p *SisyphusPlugin) RegisterWeb() *string { return nil } -func (p *SisyphusPlugin) ReplyMessage(message msg.Message, identifier string) bool { +func (p *SisyphusPlugin) replyMessage(kind bot.Kind, message msg.Message, args ...interface{}) bool { + identifier := args[0].(string) if strings.ToLower(message.User.Name) != strings.ToLower(p.Bot.Config().Get("Nick", "bot")) { if g, ok := p.listenFor[identifier]; ok { @@ -211,18 +209,18 @@ func (p *SisyphusPlugin) ReplyMessage(message msg.Message, identifier string) bo if time.Now().After(g.nextPush) { if g.checkAnswer(message.Body) { - p.Bot.Edit(message.Channel, g.toMessageString(), identifier) + p.Bot.Send(bot.Edit, message.Channel, g.toMessageString(), identifier) g.schedulePush() msg := fmt.Sprintf("Ok. You can push again in %s", g.nextPush.Sub(time.Now())) - p.Bot.ReplyToMessageIdentifier(message.Channel, msg, identifier) + p.Bot.Send(bot.Reply, message.Channel, msg, identifier) } else { - p.Bot.ReplyToMessageIdentifier(message.Channel, "you lose", identifier) + p.Bot.Send(bot.Reply, message.Channel, "you lose", identifier) msg := fmt.Sprintf("%s just lost the sisyphus game after %s", g.who, time.Now().Sub(g.start)) - p.Bot.SendMessage(message.Channel, msg) + p.Bot.Send(bot.Message, message.Channel, msg) g.endGame() } } else { - p.Bot.ReplyToMessageIdentifier(message.Channel, "you cannot push yet", identifier) + p.Bot.Send(bot.Reply, message.Channel, "you cannot push yet", identifier) } return true } diff --git a/plugins/talker/talker.go b/plugins/talker/talker.go index b96866f..46ba61a 100644 --- a/plugins/talker/talker.go +++ b/plugins/talker/talker.go @@ -43,13 +43,16 @@ type TalkerPlugin struct { sayings []string } -func New(bot bot.Bot) *TalkerPlugin { - return &TalkerPlugin{ - Bot: bot, +func New(b bot.Bot) *TalkerPlugin { + tp := &TalkerPlugin{ + Bot: b, } + b.Register(tp, bot.Message, tp.message) + b.Register(tp, bot.Help, tp.help) + return tp } -func (p *TalkerPlugin) Message(message msg.Message) bool { +func (p *TalkerPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { channel := message.Channel body := message.Body lowermessage := strings.ToLower(body) @@ -57,7 +60,7 @@ func (p *TalkerPlugin) Message(message msg.Message) bool { // TODO: This ought to be space split afterwards to remove any punctuation if message.Command && strings.HasPrefix(lowermessage, "say") { msg := strings.TrimSpace(body[3:]) - p.Bot.SendMessage(channel, msg) + p.Bot.Send(bot.Message, channel, msg) return true } @@ -73,30 +76,19 @@ func (p *TalkerPlugin) Message(message msg.Message) bool { line = strings.Replace(line, "{nick}", nick, 1) output += line + "\n" } - p.Bot.SendMessage(channel, output) + p.Bot.Send(bot.Message, channel, output) return true } return false } -func (p *TalkerPlugin) Help(channel string, parts []string) { - p.Bot.SendMessage(channel, "Hi, this is talker. I like to talk about FredFelps!") -} - -// Empty event handler because this plugin does not do anything on event recv -func (p *TalkerPlugin) Event(kind string, message msg.Message) bool { - return false -} - -// Handler for bot's own messages -func (p *TalkerPlugin) BotMessage(message msg.Message) bool { - return false +func (p *TalkerPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.Bot.Send(bot.Message, message.Channel, "Hi, this is talker. I like to talk about FredFelps!") + return true } // Register any web URLs desired func (p *TalkerPlugin) RegisterWeb() *string { return nil } - -func (p *TalkerPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/talker/talker_test.go b/plugins/talker/talker_test.go index dd45ec1..2408f45 100644 --- a/plugins/talker/talker_test.go +++ b/plugins/talker/talker_test.go @@ -12,12 +12,12 @@ import ( "github.com/velour/catbase/bot/user" ) -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -29,7 +29,7 @@ func TestGoatse(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("goatse")) + res := c.message(makeMessage("goatse")) assert.Len(t, mb.Messages, 0) assert.False(t, res) } @@ -38,7 +38,7 @@ func TestGoatseCommand(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!goatse")) + res := c.message(makeMessage("!goatse")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "g o a t s e") @@ -48,7 +48,7 @@ func TestGoatseWithNickCommand(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!goatse seabass")) + res := c.message(makeMessage("!goatse seabass")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "g o a t s e") @@ -59,7 +59,7 @@ func TestSay(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("say hello")) + res := c.message(makeMessage("say hello")) assert.Len(t, mb.Messages, 0) assert.False(t, res) } @@ -68,7 +68,7 @@ func TestSayCommand(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - res := c.Message(makeMessage("!say hello")) + res := c.message(makeMessage("!say hello")) assert.Len(t, mb.Messages, 1) assert.True(t, res) assert.Contains(t, mb.Messages[0], "hello") @@ -78,24 +78,10 @@ func TestHelp(t *testing.T) { mb := bot.NewMockBot() c := New(mb) assert.NotNil(t, c) - c.Help("channel", []string{}) + c.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) assert.Len(t, mb.Messages, 1) } -func TestBotMessage(t *testing.T) { - mb := bot.NewMockBot() - c := New(mb) - assert.NotNil(t, c) - assert.False(t, c.BotMessage(makeMessage("test"))) -} - -func TestEvent(t *testing.T) { - mb := bot.NewMockBot() - c := New(mb) - assert.NotNil(t, c) - assert.False(t, c.Event("dummy", makeMessage("test"))) -} - func TestRegisterWeb(t *testing.T) { mb := bot.NewMockBot() c := New(mb) diff --git a/plugins/tell/tell.go b/plugins/tell/tell.go index 05bab8a..c852141 100644 --- a/plugins/tell/tell.go +++ b/plugins/tell/tell.go @@ -16,23 +16,25 @@ type TellPlugin struct { } func New(b bot.Bot) *TellPlugin { - return &TellPlugin{b, make(map[string][]string)} + tp := &TellPlugin{b, make(map[string][]string)} + b.Register(tp, bot.Message, tp.message) + return tp } -func (t *TellPlugin) Message(message msg.Message) bool { +func (t *TellPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { if strings.HasPrefix(strings.ToLower(message.Body), "tell") { parts := strings.Split(message.Body, " ") target := strings.ToLower(parts[1]) newMessage := strings.Join(parts[2:], " ") newMessage = fmt.Sprintf("Hey, %s. %s said: %s", target, message.User.Name, newMessage) t.users[target] = append(t.users[target], newMessage) - t.b.SendMessage(message.Channel, fmt.Sprintf("Okay. I'll tell %s.", target)) + t.b.Send(bot.Message, message.Channel, fmt.Sprintf("Okay. I'll tell %s.", target)) return true } uname := strings.ToLower(message.User.Name) if msg, ok := t.users[uname]; ok && len(msg) > 0 { for _, m := range msg { - t.b.SendMessage(message.Channel, string(m)) + t.b.Send(bot.Message, message.Channel, string(m)) } t.users[uname] = []string{} return true @@ -40,8 +42,4 @@ func (t *TellPlugin) Message(message msg.Message) bool { return false } -func (t *TellPlugin) Event(kind string, message msg.Message) bool { return false } -func (t *TellPlugin) ReplyMessage(msg.Message, string) bool { return false } -func (t *TellPlugin) BotMessage(message msg.Message) bool { return false } -func (t *TellPlugin) Help(channel string, parts []string) {} -func (t *TellPlugin) RegisterWeb() *string { return nil } +func (t *TellPlugin) RegisterWeb() *string { return nil } diff --git a/plugins/twitch/twitch.go b/plugins/twitch/twitch.go index 9b2c2b7..0c17a24 100644 --- a/plugins/twitch/twitch.go +++ b/plugins/twitch/twitch.go @@ -51,10 +51,10 @@ type stream struct { } `json:"pagination"` } -func New(bot bot.Bot) *TwitchPlugin { +func New(b bot.Bot) *TwitchPlugin { p := &TwitchPlugin{ - Bot: bot, - config: bot.Config(), + Bot: b, + config: b.Config(), twitchList: map[string]*Twitcher{}, } @@ -70,13 +70,10 @@ func New(bot bot.Bot) *TwitchPlugin { go p.twitchLoop(ch) } + b.Register(p, bot.Message, p.message) return p } -func (p *TwitchPlugin) BotMessage(message msg.Message) bool { - return false -} - func (p *TwitchPlugin) RegisterWeb() *string { http.HandleFunc("/isstreaming/", p.serveStreaming) tmp := "/isstreaming" @@ -114,7 +111,7 @@ func (p *TwitchPlugin) serveStreaming(w http.ResponseWriter, r *http.Request) { } } -func (p *TwitchPlugin) Message(message msg.Message) bool { +func (p *TwitchPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { if strings.ToLower(message.Body) == "twitch status" { channel := message.Channel if users := p.config.GetArray("Twitch."+channel+".Users", []string{}); len(users) > 0 { @@ -130,17 +127,10 @@ func (p *TwitchPlugin) Message(message msg.Message) bool { return false } -func (p *TwitchPlugin) Event(kind string, message msg.Message) bool { - return false -} - -func (p *TwitchPlugin) LoadData() { - -} - -func (p *TwitchPlugin) Help(channel string, parts []string) { +func (p *TwitchPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { msg := "There's no help for you here." - p.Bot.SendMessage(channel, msg) + p.Bot.Send(bot.Message, message.Channel, msg) + return true } func (p *TwitchPlugin) twitchLoop(channel string) { @@ -224,21 +214,19 @@ func (p *TwitchPlugin) checkTwitch(channel string, twitcher *Twitcher, alwaysPri streamWord := p.config.Get("Twitch.StreamWord", "streaming") if alwaysPrintStatus { if game == "" { - p.Bot.SendMessage(channel, fmt.Sprintf("%s is not %s.", twitcher.name, streamWord)) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s is not %s.", twitcher.name, streamWord)) } else { - p.Bot.SendMessage(channel, fmt.Sprintf("%s is %s %s at %s", twitcher.name, streamWord, game, twitcher.URL())) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s is %s %s at %s", twitcher.name, streamWord, game, twitcher.URL())) } } else if game == "" { if twitcher.game != "" { - p.Bot.SendMessage(channel, fmt.Sprintf("%s just stopped %s.", twitcher.name, streamWord)) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s just stopped %s.", twitcher.name, streamWord)) } twitcher.game = "" } else { if twitcher.game != game { - p.Bot.SendMessage(channel, fmt.Sprintf("%s is %s %s at %s", twitcher.name, streamWord, game, twitcher.URL())) + p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s is %s %s at %s", twitcher.name, streamWord, game, twitcher.URL())) } twitcher.game = game } } - -func (p *TwitchPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/twitch/twitch_test.go b/plugins/twitch/twitch_test.go index 728fe00..b3d88ec 100644 --- a/plugins/twitch/twitch_test.go +++ b/plugins/twitch/twitch_test.go @@ -12,12 +12,12 @@ import ( "github.com/velour/catbase/bot/user" ) -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -44,6 +44,6 @@ func makeTwitchPlugin(t *testing.T) (*TwitchPlugin, *bot.MockBot) { func TestTwitch(t *testing.T) { b, mb := makeTwitchPlugin(t) - b.Message(makeMessage("!twitch status")) + b.message(makeMessage("!twitch status")) assert.NotEmpty(t, mb.Messages) } diff --git a/plugins/your/your.go b/plugins/your/your.go index c7c8b45..a319039 100644 --- a/plugins/your/your.go +++ b/plugins/your/your.go @@ -17,17 +17,20 @@ type YourPlugin struct { } // NewYourPlugin creates a new YourPlugin with the Plugin interface -func New(bot bot.Bot) *YourPlugin { - return &YourPlugin{ - bot: bot, - config: bot.Config(), +func New(b bot.Bot) *YourPlugin { + yp := &YourPlugin{ + bot: b, + config: b.Config(), } + b.Register(yp, bot.Message, yp.message) + b.Register(yp, bot.Help, yp.help) + return yp } // 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 *YourPlugin) Message(message msg.Message) bool { +func (p *YourPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { maxLen := p.config.GetInt("your.maxlength", 140) if len(message.Body) > maxLen { return false @@ -43,30 +46,19 @@ func (p *YourPlugin) Message(message msg.Message) bool { } } if msg != message.Body { - p.bot.SendMessage(message.Channel, msg) + p.bot.Send(bot.Message, message.Channel, msg) return true } return false } // Help responds to help requests. Every plugin must implement a help function. -func (p *YourPlugin) Help(channel string, parts []string) { - p.bot.SendMessage(channel, "Your corrects people's grammar.") -} - -// Empty event handler because this plugin does not do anything on event recv -func (p *YourPlugin) Event(kind string, message msg.Message) bool { - return false -} - -// Handler for bot's own messages -func (p *YourPlugin) BotMessage(message msg.Message) bool { - return false +func (p *YourPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.bot.Send(bot.Message, message.Channel, "Your corrects people's grammar.") + return true } // Register any web URLs desired func (p *YourPlugin) RegisterWeb() *string { return nil } - -func (p *YourPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/plugins/your/your_test.go b/plugins/your/your_test.go index 75a0ace..3c6136a 100644 --- a/plugins/your/your_test.go +++ b/plugins/your/your_test.go @@ -12,12 +12,12 @@ import ( "github.com/velour/catbase/bot/user" ) -func makeMessage(payload string) msg.Message { +func makeMessage(payload string) (bot.Kind, msg.Message) { isCmd := strings.HasPrefix(payload, "!") if isCmd { payload = payload[1:] } - return msg.Message{ + return bot.Message, msg.Message{ User: &user.User{Name: "tester"}, Channel: "test", Body: payload, @@ -39,7 +39,7 @@ func TestReplacement(t *testing.T) { c.config.Set("your.replacements.0.freq", "1.0") c.config.Set("your.replacements.0.this", "fuck") c.config.Set("your.replacements.0.that", "duck") - res := c.Message(makeMessage("fuck a duck")) + res := c.message(makeMessage("fuck a duck")) assert.True(t, res) assert.Len(t, mb.Messages, 1) assert.Contains(t, mb.Messages[0], "duck a duck") @@ -60,6 +60,6 @@ func TestNoReplacement(t *testing.T) { c.config.Set("your.replacements.2.freq", "1.0") c.config.Set("your.replacements.2.this", "Fuck") c.config.Set("your.replacements.2.that", "duck") - c.Message(makeMessage("fuck a duck")) + c.message(makeMessage("fuck a duck")) assert.Len(t, mb.Messages, 0) } diff --git a/plugins/zork/zork.go b/plugins/zork/zork.go index 7e5d0af..b5ac682 100644 --- a/plugins/zork/zork.go +++ b/plugins/zork/zork.go @@ -26,11 +26,14 @@ type ZorkPlugin struct { zorks map[string]io.WriteCloser } -func New(b bot.Bot) bot.Handler { - return &ZorkPlugin{ +func New(b bot.Bot) bot.Plugin { + z := &ZorkPlugin{ bot: b, zorks: make(map[string]io.WriteCloser), } + b.Register(z, bot.Message, z.message) + b.Register(z, bot.Help, z.help) + return z } func (p *ZorkPlugin) runZork(ch string) error { @@ -75,7 +78,7 @@ func (p *ZorkPlugin) runZork(ch string) error { m := strings.Replace(s.Text(), ">", "", -1) m = strings.Replace(m, "\n", "\n>", -1) m = ">" + m + "\n" - p.bot.SendMessage(ch, m) + p.bot.Send(bot.Message, ch, m) } }() go func() { @@ -91,7 +94,7 @@ func (p *ZorkPlugin) runZork(ch string) error { return nil } -func (p *ZorkPlugin) Message(message msg.Message) bool { +func (p *ZorkPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { m := strings.ToLower(message.Body) log.Printf("got message [%s]\n", m) if ts := strings.Fields(m); len(ts) < 1 || ts[0] != "zork" { @@ -104,7 +107,7 @@ func (p *ZorkPlugin) Message(message msg.Message) bool { defer p.Unlock() if p.zorks[ch] == nil { if err := p.runZork(ch); err != nil { - p.bot.SendMessage(ch, "failed to run zork: "+err.Error()) + p.bot.Send(bot.Message, ch, "failed to run zork: "+err.Error()) return true } } @@ -113,14 +116,9 @@ func (p *ZorkPlugin) Message(message msg.Message) bool { return true } -func (p *ZorkPlugin) Event(_ string, _ msg.Message) bool { return false } - -func (p *ZorkPlugin) BotMessage(_ msg.Message) bool { return false } - -func (p *ZorkPlugin) Help(ch string, _ []string) { - p.bot.SendMessage(ch, "Play zork using 'zork '.") +func (p *ZorkPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { + p.bot.Send(bot.Message, message.Channel, "Play zork using 'zork '.") + return true } func (p *ZorkPlugin) RegisterWeb() *string { return nil } - -func (p *ZorkPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false } diff --git a/slack/slack.go b/slack/slack.go index 3c1049b..dcf616b 100644 --- a/slack/slack.go +++ b/slack/slack.go @@ -44,9 +44,7 @@ type Slack struct { emoji map[string]string - eventReceived func(msg.Message) - messageReceived func(msg.Message) - replyMessageReceived func(msg.Message, string) + event func(bot.Kind, msg.Message, ...interface{}) } var idCounter uint64 @@ -177,7 +175,31 @@ func New(c *config.Config) *Slack { } } -func checkReturnStatus(response *http.Response) bool { +func (s *Slack) Send(kind bot.Kind, args ...interface{}) (string, error) { + switch kind { + case bot.Message: + return s.sendMessage(args[0].(string), args[1].(string)) + case bot.Action: + return s.sendAction(args[0].(string), args[1].(string)) + case bot.Edit: + return s.edit(args[0].(string), args[1].(string), args[2].(string)) + case bot.Reply: + switch args[2].(type) { + case msg.Message: + return s.replyToMessage(args[0].(string), args[1].(string), args[2].(msg.Message)) + case string: + return s.replyToMessageIdentifier(args[0].(string), args[1].(string), args[2].(string)) + default: + return "", fmt.Errorf("Invalid types given to Reply") + } + case bot.Reaction: + return s.react(args[0].(string), args[1].(string), args[2].(msg.Message)) + default: + } + return "", fmt.Errorf("No handler for message type %d", kind) +} + +func checkReturnStatus(response *http.Response) error { type Response struct { OK bool `json:"ok"` } @@ -185,32 +207,24 @@ func checkReturnStatus(response *http.Response) bool { body, err := ioutil.ReadAll(response.Body) response.Body.Close() if err != nil { - log.Printf("Error reading Slack API body: %s", err) - return false + err := fmt.Errorf("Error reading Slack API body: %s", err) + return err } var resp Response err = json.Unmarshal(body, &resp) if err != nil { - log.Printf("Error parsing message response: %s", err) - return false + err := fmt.Errorf("Error parsing message response: %s", err) + return err } - return resp.OK + return nil } -func (s *Slack) RegisterEventReceived(f func(msg.Message)) { - s.eventReceived = f +func (s *Slack) RegisterEvent(f func(bot.Kind, msg.Message, ...interface{})) { + s.event = f } -func (s *Slack) RegisterMessageReceived(f func(msg.Message)) { - s.messageReceived = f -} - -func (s *Slack) RegisterReplyMessageReceived(f func(msg.Message, string)) { - s.replyMessageReceived = f -} - -func (s *Slack) SendMessageType(channel, message string, meMessage bool) (string, error) { +func (s *Slack) sendMessageType(channel, message string, meMessage bool) (string, error) { postUrl := "https://slack.com/api/chat.postMessage" if meMessage { postUrl = "https://slack.com/api/chat.meMessage" @@ -262,19 +276,19 @@ func (s *Slack) SendMessageType(channel, message string, meMessage bool) (string return mr.Timestamp, err } -func (s *Slack) SendMessage(channel, message string) string { +func (s *Slack) sendMessage(channel, message string) (string, error) { log.Printf("Sending message to %s: %s", channel, message) - identifier, _ := s.SendMessageType(channel, message, false) - return identifier + identifier, err := s.sendMessageType(channel, message, false) + return identifier, err } -func (s *Slack) SendAction(channel, message string) string { +func (s *Slack) sendAction(channel, message string) (string, error) { log.Printf("Sending action to %s: %s", channel, message) - identifier, _ := s.SendMessageType(channel, "_"+message+"_", true) - return identifier + identifier, err := s.sendMessageType(channel, "_"+message+"_", true) + return identifier, err } -func (s *Slack) ReplyToMessageIdentifier(channel, message, identifier string) (string, bool) { +func (s *Slack) replyToMessageIdentifier(channel, message, identifier string) (string, error) { nick := s.config.Get("Nick", "bot") icon := s.config.Get("IconURL", "https://placekitten.com/128/128") @@ -288,15 +302,15 @@ func (s *Slack) ReplyToMessageIdentifier(channel, message, identifier string) (s }) if err != nil { - log.Printf("Error sending Slack reply: %s", err) - return "", false + err := fmt.Errorf("Error sending Slack reply: %s", err) + return "", err } body, err := ioutil.ReadAll(resp.Body) resp.Body.Close() if err != nil { - log.Printf("Error reading Slack API body: %s", err) - return "", false + err := fmt.Errorf("Error reading Slack API body: %s", err) + return "", err } log.Println(string(body)) @@ -309,22 +323,22 @@ func (s *Slack) ReplyToMessageIdentifier(channel, message, identifier string) (s var mr MessageResponse err = json.Unmarshal(body, &mr) if err != nil { - log.Printf("Error parsing message response: %s", err) - return "", false + err := fmt.Errorf("Error parsing message response: %s", err) + return "", err } if !mr.OK { - return "", false + return "", fmt.Errorf("Got !OK from slack message response") } - return mr.Timestamp, err == nil + return mr.Timestamp, err } -func (s *Slack) ReplyToMessage(channel, message string, replyTo msg.Message) (string, bool) { - return s.ReplyToMessageIdentifier(channel, message, replyTo.AdditionalData["RAW_SLACK_TIMESTAMP"]) +func (s *Slack) replyToMessage(channel, message string, replyTo msg.Message) (string, error) { + return s.replyToMessageIdentifier(channel, message, replyTo.AdditionalData["RAW_SLACK_TIMESTAMP"]) } -func (s *Slack) React(channel, reaction string, message msg.Message) bool { +func (s *Slack) react(channel, reaction string, message msg.Message) (string, error) { log.Printf("Reacting in %s: %s", channel, reaction) resp, err := http.PostForm("https://slack.com/api/reactions.add", url.Values{"token": {s.token}, @@ -332,13 +346,13 @@ func (s *Slack) React(channel, reaction string, message msg.Message) bool { "channel": {channel}, "timestamp": {message.AdditionalData["RAW_SLACK_TIMESTAMP"]}}) if err != nil { - log.Printf("reaction failed: %s", err) - return false + err := fmt.Errorf("reaction failed: %s", err) + return "", err } - return checkReturnStatus(resp) + return "", checkReturnStatus(resp) } -func (s *Slack) Edit(channel, newMessage, identifier string) bool { +func (s *Slack) edit(channel, newMessage, identifier string) (string, error) { log.Printf("Editing in (%s) %s: %s", identifier, channel, newMessage) resp, err := http.PostForm("https://slack.com/api/chat.update", url.Values{"token": {s.token}, @@ -346,10 +360,10 @@ func (s *Slack) Edit(channel, newMessage, identifier string) bool { "text": {newMessage}, "ts": {identifier}}) if err != nil { - log.Printf("edit failed: %s", err) - return false + err := fmt.Errorf("edit failed: %s", err) + return "", err } - return checkReturnStatus(resp) + return "", checkReturnStatus(resp) } func (s *Slack) GetEmojiList() map[string]string { @@ -441,11 +455,11 @@ func (s *Slack) Serve() error { log.Printf("Ignoring message: %+v\nlastRecieved: %v msg: %v", msg.ID, s.lastRecieved, m.Time) } else { s.lastRecieved = m.Time - s.messageReceived(m) + s.event(bot.Message, m) } } else if msg.ThreadTs != "" { //we're throwing away some information here by not parsing the correct reply object type, but that's okay - s.replyMessageReceived(s.buildLightReplyMessage(msg), msg.ThreadTs) + s.event(bot.Reply, s.buildLightReplyMessage(msg), msg.ThreadTs) } else { log.Printf("THAT MESSAGE WAS HIDDEN: %+v", msg.ID) }