Merge pull request #141 from velour/events

Strict Interfaces => Events
This commit is contained in:
Chris Sexton 2019-02-05 16:27:08 -05:00 committed by GitHub
commit b11815dec3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 866 additions and 1155 deletions

View File

@ -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 = `
<!DOCTYPE html>
<html>
<head>
@ -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)
}

View File

@ -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
}
}
}

View File

@ -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
}

View File

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

8
go.mod
View File

@ -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
)

8
go.sum
View File

@ -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=

View File

@ -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)

50
main.go
View File

@ -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()

View File

@ -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], "<unknown>")
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 }

View File

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

View File

@ -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 }

View File

@ -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("<seabass> This is a message")
k, seabass := makeMessage("<seabass> 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 = "<seabass> This is another message"
res = bp.Message(seabass)
res = bp.message(k, seabass)
seabass.Body = "<seabass> 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)

View File

@ -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 }

View File

@ -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())

View File

@ -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
}

View File

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

View File

@ -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 "+
"<noun>++ and <noun>--. 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
}

View File

@ -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)

View File

@ -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 }

View File

@ -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)

View File

@ -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 <nick>\"")
p.Bot.Send(bot.Message, channel, "Ask me how long one of your friends has been idele with, \"idle <nick>\"")
}
// Empty event handler because this plugin does not do anything on event recv

View File

@ -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 {

View File

@ -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 <has> $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 <has> $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 }

View File

@ -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 <nick> <snippet> 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 }

View File

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

View File

@ -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 }

View File

@ -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 }

View File

@ -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 }

View File

@ -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())

View File

@ -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 }

View File

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

View File

@ -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

View File

@ -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])
}

View File

@ -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 }

View File

@ -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 <user> in <duration> 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 <user> in <duration> 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 }

View File

@ -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)

View File

@ -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
}

View File

@ -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 }

View File

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

View File

@ -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
}

View File

@ -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 }

View File

@ -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)

View File

@ -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 }

View File

@ -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 }

View File

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

View File

@ -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 }

View File

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

View File

@ -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 <zork command>'.")
func (p *ZorkPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.bot.Send(bot.Message, message.Channel, "Play zork using 'zork <zork command>'.")
return true
}
func (p *ZorkPlugin) RegisterWeb() *string { return nil }
func (p *ZorkPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }

View File

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