cli: make a new plugin

This commit is contained in:
Chris Sexton 2019-05-27 19:21:53 -04:00
parent 4230ddf765
commit 42f7f52bfb
57 changed files with 744 additions and 498 deletions

1
.gitignore vendored
View File

@ -68,3 +68,4 @@ Temporary Items
util/*/files util/*/files
util/*/files util/*/files
run.sh run.sh
.idea

View File

@ -59,7 +59,7 @@ func New(config *config.Config, connector Connector) Bot {
msglog.RunNew(logIn, logOut) msglog.RunNew(logIn, logOut)
users := []user.User{ users := []user.User{
user.User{ {
Name: config.Get("Nick", "bot"), Name: config.Get("Nick", "bot"),
}, },
} }
@ -87,6 +87,14 @@ func New(config *config.Config, connector Connector) Bot {
return bot return bot
} }
func (b *bot) DefaultConnector() Connector {
return b.conn
}
func (b *bot) WhoAmI() string {
return b.me.Name
}
// Config gets the configuration that the bot is using // Config gets the configuration that the bot is using
func (b *bot) Config() *config.Config { func (b *bot) Config() *config.Config {
return b.config return b.config

View File

@ -17,7 +17,7 @@ import (
"github.com/velour/catbase/bot/msg" "github.com/velour/catbase/bot/msg"
) )
func (b *bot) Receive(kind Kind, msg msg.Message, args ...interface{}) bool { func (b *bot) Receive(conn Connector, kind Kind, msg msg.Message, args ...interface{}) bool {
log.Debug(). log.Debug().
Interface("msg", msg). Interface("msg", msg).
Msg("Received event") Msg("Received event")
@ -26,13 +26,13 @@ func (b *bot) Receive(kind Kind, msg msg.Message, args ...interface{}) bool {
// do need to look up user and fix it // do need to look up user and fix it
if kind == Message && strings.HasPrefix(msg.Body, "help") && msg.Command { if kind == Message && strings.HasPrefix(msg.Body, "help") && msg.Command {
parts := strings.Fields(strings.ToLower(msg.Body)) parts := strings.Fields(strings.ToLower(msg.Body))
b.checkHelp(msg.Channel, parts) b.checkHelp(conn, msg.Channel, parts)
log.Debug().Msg("Handled a help, returning") log.Debug().Msg("Handled a help, returning")
goto RET goto RET
} }
for _, name := range b.pluginOrdering { for _, name := range b.pluginOrdering {
if b.runCallback(b.plugins[name], kind, msg, args...) { if b.runCallback(conn, b.plugins[name], kind, msg, args...) {
goto RET goto RET
} }
} }
@ -42,10 +42,10 @@ RET:
return true return true
} }
func (b *bot) runCallback(plugin Plugin, evt Kind, message msg.Message, args ...interface{}) bool { func (b *bot) runCallback(conn Connector, plugin Plugin, evt Kind, message msg.Message, args ...interface{}) bool {
t := reflect.TypeOf(plugin).String() t := reflect.TypeOf(plugin).String()
for _, cb := range b.callbacks[t][evt] { for _, cb := range b.callbacks[t][evt] {
if cb(evt, message, args...) { if cb(conn, evt, message, args...) {
return true return true
} }
} }
@ -53,8 +53,8 @@ func (b *bot) runCallback(plugin Plugin, evt Kind, message msg.Message, args ...
} }
// Send a message to the connection // Send a message to the connection
func (b *bot) Send(kind Kind, args ...interface{}) (string, error) { func (b *bot) Send(conn Connector, kind Kind, args ...interface{}) (string, error) {
return b.conn.Send(kind, args...) return conn.Send(kind, args...)
} }
func (b *bot) GetEmojiList() map[string]string { func (b *bot) GetEmojiList() map[string]string {
@ -62,38 +62,38 @@ func (b *bot) GetEmojiList() map[string]string {
} }
// Checks to see if the user is asking for help, returns true if so and handles the situation. // Checks to see if the user is asking for help, returns true if so and handles the situation.
func (b *bot) checkHelp(channel string, parts []string) { func (b *bot) checkHelp(conn Connector, channel string, parts []string) {
if len(parts) == 1 { if len(parts) == 1 {
// just print out a list of help topics // just print out a list of help topics
topics := "Help topics: about variables" topics := "Help topics: about variables"
for name, _ := range b.plugins { for name := range b.plugins {
name = strings.Split(strings.TrimPrefix(name, "*"), ".")[0] name = strings.Split(strings.TrimPrefix(name, "*"), ".")[0]
topics = fmt.Sprintf("%s, %s", topics, name) topics = fmt.Sprintf("%s, %s", topics, name)
} }
b.Send(Message, channel, topics) b.Send(conn, Message, channel, topics)
} else { } else {
// trigger the proper plugin's help response // trigger the proper plugin's help response
if parts[1] == "about" { if parts[1] == "about" {
b.Help(channel, parts) b.Help(conn, channel, parts)
return return
} }
if parts[1] == "variables" { if parts[1] == "variables" {
b.listVars(channel, parts) b.listVars(conn, channel, parts)
return return
} }
for name, plugin := range b.plugins { for name, plugin := range b.plugins {
if strings.HasPrefix(name, "*"+parts[1]) { if strings.HasPrefix(name, "*"+parts[1]) {
if b.runCallback(plugin, Help, msg.Message{Channel: channel}, channel, parts) { if b.runCallback(conn, plugin, Help, msg.Message{Channel: channel}, channel, parts) {
return return
} else { } else {
msg := fmt.Sprintf("I'm sorry, I don't know how to help you with %s.", parts[1]) msg := fmt.Sprintf("I'm sorry, I don't know how to help you with %s.", parts[1])
b.Send(Message, channel, msg) b.Send(conn, Message, channel, msg)
return return
} }
} }
} }
msg := fmt.Sprintf("I'm sorry, I don't know what %s is!", strings.Join(parts, " ")) msg := fmt.Sprintf("I'm sorry, I don't know what %s is!", strings.Join(parts, " "))
b.Send(Message, channel, msg) b.Send(conn, Message, channel, msg)
} }
} }
@ -178,7 +178,7 @@ func (b *bot) getVar(varName string) (string, error) {
return text, nil return text, nil
} }
func (b *bot) listVars(channel string, parts []string) { func (b *bot) listVars(conn Connector, channel string, parts []string) {
var variables []string var variables []string
err := b.DB().Select(&variables, `select name from variables group by name`) err := b.DB().Select(&variables, `select name from variables group by name`)
if err != nil { if err != nil {
@ -188,18 +188,18 @@ func (b *bot) listVars(channel string, parts []string) {
if len(variables) > 0 { if len(variables) > 0 {
msg += ", " + strings.Join(variables, ", ") msg += ", " + strings.Join(variables, ", ")
} }
b.Send(Message, channel, msg) b.Send(conn, Message, channel, msg)
} }
func (b *bot) Help(channel string, parts []string) { func (b *bot) Help(conn Connector, channel string, parts []string) {
msg := fmt.Sprintf("Hi, I'm based on godeepintir version %s. I'm written in Go, and you "+ 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: "+ "can find my source code on the internet here: "+
"http://github.com/velour/catbase", b.version) "http://github.com/velour/catbase", b.version)
b.Send(Message, channel, msg) b.Send(conn, Message, channel, msg)
} }
// Send our own musings to the plugins // Send our own musings to the plugins
func (b *bot) selfSaid(channel, message string, action bool) { func (b *bot) selfSaid(conn Connector, channel, message string, action bool) {
msg := msg.Message{ msg := msg.Message{
User: &b.me, // hack User: &b.me, // hack
Channel: channel, Channel: channel,
@ -212,7 +212,7 @@ func (b *bot) selfSaid(channel, message string, action bool) {
} }
for _, name := range b.pluginOrdering { for _, name := range b.pluginOrdering {
if b.runCallback(b.plugins[name], SelfMessage, msg) { if b.runCallback(conn, b.plugins[name], SelfMessage, msg) {
return return
} }
} }

View File

@ -36,7 +36,7 @@ type ImageAttachment struct {
} }
type Kind int type Kind int
type Callback func(Kind, msg.Message, ...interface{}) bool type Callback func(Connector, Kind, msg.Message, ...interface{}) bool
type CallbackMap map[string]map[Kind][]Callback type CallbackMap map[string]map[Kind][]Callback
// Bot interface serves to allow mocking of the actual bot // Bot interface serves to allow mocking of the actual bot
@ -47,12 +47,14 @@ type Bot interface {
DB() *sqlx.DB DB() *sqlx.DB
// Who lists users in a particular channel // Who lists users in a particular channel
Who(string) []user.User Who(string) []user.User
// WhoAmI gives a nick for the bot
WhoAmI() string
// AddPlugin registers a new plugin handler // AddPlugin registers a new plugin handler
AddPlugin(Plugin) AddPlugin(Plugin)
// First arg should be one of bot.Message/Reply/Action/etc // First arg should be one of bot.Message/Reply/Action/etc
Send(Kind, ...interface{}) (string, error) Send(Connector, Kind, ...interface{}) (string, error)
// First arg should be one of bot.Message/Reply/Action/etc // First arg should be one of bot.Message/Reply/Action/etc
Receive(Kind, msg.Message, ...interface{}) bool Receive(Connector, Kind, msg.Message, ...interface{}) bool
// Register a callback // Register a callback
Register(Plugin, Kind, Callback) Register(Plugin, Kind, Callback)
@ -63,6 +65,7 @@ type Bot interface {
GetEmojiList() map[string]string GetEmojiList() map[string]string
RegisterFilter(string, func(string) string) RegisterFilter(string, func(string) string)
RegisterWeb(string, string) RegisterWeb(string, string)
DefaultConnector() Connector
} }
// Connector represents a server connection to a chat service // Connector represents a server connection to a chat service

View File

@ -30,7 +30,9 @@ type MockBot struct {
func (mb *MockBot) Config() *config.Config { return mb.Cfg } func (mb *MockBot) Config() *config.Config { return mb.Cfg }
func (mb *MockBot) DB() *sqlx.DB { return mb.Cfg.DB } func (mb *MockBot) DB() *sqlx.DB { return mb.Cfg.DB }
func (mb *MockBot) Who(string) []user.User { return []user.User{} } func (mb *MockBot) Who(string) []user.User { return []user.User{} }
func (mb *MockBot) Send(kind Kind, args ...interface{}) (string, error) { func (mb *MockBot) WhoAmI() string { return "tester" }
func (mb *MockBot) DefaultConnector() Connector { return nil }
func (mb *MockBot) Send(c Connector, kind Kind, args ...interface{}) (string, error) {
switch kind { switch kind {
case Message: case Message:
mb.Messages = append(mb.Messages, args[1].(string)) mb.Messages = append(mb.Messages, args[1].(string))
@ -40,27 +42,29 @@ func (mb *MockBot) Send(kind Kind, args ...interface{}) (string, error) {
return fmt.Sprintf("a-%d", len(mb.Actions)-1), nil return fmt.Sprintf("a-%d", len(mb.Actions)-1), nil
case Edit: case Edit:
ch, m, id := args[0].(string), args[1].(string), args[2].(string) ch, m, id := args[0].(string), args[1].(string), args[2].(string)
return mb.edit(ch, m, id) return mb.edit(c, ch, m, id)
case Reaction: case Reaction:
ch, re, msg := args[0].(string), args[1].(string), args[2].(msg.Message) ch, re, msg := args[0].(string), args[1].(string), args[2].(msg.Message)
return mb.react(ch, re, msg) return mb.react(c, ch, re, msg)
} }
return "ERR", fmt.Errorf("Mesasge type unhandled") return "ERR", fmt.Errorf("Mesasge type unhandled")
} }
func (mb *MockBot) AddPlugin(f Plugin) {} func (mb *MockBot) AddPlugin(f Plugin) {}
func (mb *MockBot) Register(p Plugin, kind Kind, cb Callback) {} func (mb *MockBot) Register(p Plugin, kind Kind, cb Callback) {}
func (mb *MockBot) RegisterWeb(_, _ string) {} func (mb *MockBot) RegisterWeb(_, _ string) {}
func (mb *MockBot) Receive(kind Kind, msg msg.Message, args ...interface{}) bool { return false } func (mb *MockBot) Receive(c Connector, kind Kind, msg msg.Message, args ...interface{}) bool {
return false
}
func (mb *MockBot) Filter(msg msg.Message, s string) string { return s } 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) LastMessage(ch string) (msg.Message, error) { return msg.Message{}, nil }
func (mb *MockBot) CheckAdmin(nick string) bool { return false } func (mb *MockBot) CheckAdmin(nick string) bool { return false }
func (mb *MockBot) react(channel, reaction string, message msg.Message) (string, error) { func (mb *MockBot) react(c Connector, channel, reaction string, message msg.Message) (string, error) {
mb.Reactions = append(mb.Reactions, reaction) mb.Reactions = append(mb.Reactions, reaction)
return "", nil return "", nil
} }
func (mb *MockBot) edit(channel, newMessage, identifier string) (string, error) { func (mb *MockBot) edit(c Connector, channel, newMessage, identifier string) (string, error) {
isMessage := identifier[0] == 'm' isMessage := identifier[0] == 'm'
if !isMessage && identifier[0] != 'a' { if !isMessage && identifier[0] != 'a' {
err := fmt.Errorf("failed to parse identifier: %s", identifier) err := fmt.Errorf("failed to parse identifier: %s", identifier)

View File

@ -11,7 +11,7 @@ import (
"strings" "strings"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
sqlite3 "github.com/mattn/go-sqlite3" "github.com/mattn/go-sqlite3"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@ -98,8 +98,8 @@ func (c *Config) GetArray(key string, fallback []string) []string {
// Note, this is always a string. Use the SetArray for an array helper // Note, this is always a string. Use the SetArray for an array helper
func (c *Config) Set(key, value string) error { func (c *Config) Set(key, value string) error {
key = strings.ToLower(key) key = strings.ToLower(key)
q := (`insert into config (key,value) values (?, ?) q := `insert into config (key,value) values (?, ?)
on conflict(key) do update set value=?;`) on conflict(key) do update set value=?;`
tx, err := c.Begin() tx, err := c.Begin()
if err != nil { if err != nil {
return err return err

View File

@ -248,10 +248,10 @@ func (i *Irc) handleMsg(msg irc.Msg) {
fallthrough fallthrough
case irc.RPL_ENDOFWHO: case irc.RPL_ENDOFWHO:
i.event(bot.Event, botMsg) i.event(i, bot.Event, botMsg)
case irc.PRIVMSG: case irc.PRIVMSG:
i.event(bot.Message, botMsg) i.event(i, bot.Message, botMsg)
case irc.QUIT: case irc.QUIT:
os.Exit(1) os.Exit(1)

View File

@ -455,11 +455,11 @@ func (s *Slack) Serve() error {
log.Debug().Msgf("Ignoring message: %+v\nlastRecieved: %v msg: %v", msg.ID, s.lastRecieved, m.Time) log.Debug().Msgf("Ignoring message: %+v\nlastRecieved: %v msg: %v", msg.ID, s.lastRecieved, m.Time)
} else { } else {
s.lastRecieved = m.Time s.lastRecieved = m.Time
s.event(bot.Message, m) s.event(s, bot.Message, m)
} }
} else if msg.ThreadTs != "" { } else if msg.ThreadTs != "" {
//we're throwing away some information here by not parsing the correct reply object type, but that's okay //we're throwing away some information here by not parsing the correct reply object type, but that's okay
s.event(bot.Reply, s.buildLightReplyMessage(msg), msg.ThreadTs) s.event(s, bot.Reply, s.buildLightReplyMessage(msg), msg.ThreadTs)
} else { } else {
log.Debug().Msgf("THAT MESSAGE WAS HIDDEN: %+v", msg.ID) log.Debug().Msgf("THAT MESSAGE WAS HIDDEN: %+v", msg.ID)
} }

View File

@ -167,11 +167,11 @@ func (s *SlackApp) msgReceivd(msg *slackevents.MessageEvent) {
Msg("Ignoring message") Msg("Ignoring message")
} else { } else {
s.lastRecieved = m.Time s.lastRecieved = m.Time
s.event(bot.Message, m) s.event(s, bot.Message, m)
} }
} else if msg.ThreadTimeStamp != "" { } else if msg.ThreadTimeStamp != "" {
//we're throwing away some information here by not parsing the correct reply object type, but that's okay //we're throwing away some information here by not parsing the correct reply object type, but that's okay
s.event(bot.Reply, s.buildMessage(msg), msg.ThreadTimeStamp) s.event(s, bot.Reply, s.buildMessage(msg), msg.ThreadTimeStamp)
} else { } else {
log.Debug(). log.Debug().
Str("text", msg.Text). Str("text", msg.Text).

View File

@ -4,6 +4,7 @@ package main
import ( import (
"flag" "flag"
"github.com/velour/catbase/plugins/cli"
"math/rand" "math/rand"
"net/http" "net/http"
"os" "os"
@ -121,6 +122,7 @@ func main() {
b.AddPlugin(couldashouldawoulda.New(b)) b.AddPlugin(couldashouldawoulda.New(b))
b.AddPlugin(nerdepedia.New(b)) b.AddPlugin(nerdepedia.New(b))
b.AddPlugin(tldr.New(b)) b.AddPlugin(tldr.New(b))
b.AddPlugin(cli.New(b))
// catches anything left, will always return true // catches anything left, will always return true
b.AddPlugin(fact.New(b)) b.AddPlugin(fact.New(b))

View File

@ -47,7 +47,7 @@ var forbiddenKeys = map[string]bool{
// Message responds to the bot hook on recieving messages. // 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. // 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. // Otherwise, the function returns false and the bot continues execution of other plugins.
func (p *AdminPlugin) message(k bot.Kind, message msg.Message, args ...interface{}) bool { func (p *AdminPlugin) message(conn bot.Connector, k bot.Kind, message msg.Message, args ...interface{}) bool {
body := message.Body body := message.Body
if p.quiet { if p.quiet {
@ -55,7 +55,7 @@ func (p *AdminPlugin) message(k bot.Kind, message msg.Message, args ...interface
} }
if len(body) > 0 && body[0] == '$' { if len(body) > 0 && body[0] == '$' {
return p.handleVariables(message) return p.handleVariables(conn, message)
} }
if !message.Command { if !message.Command {
@ -65,7 +65,7 @@ func (p *AdminPlugin) message(k bot.Kind, message msg.Message, args ...interface
if strings.ToLower(body) == "shut up" { if strings.ToLower(body) == "shut up" {
dur := time.Duration(p.cfg.GetInt("quietDuration", 5)) * time.Minute dur := time.Duration(p.cfg.GetInt("quietDuration", 5)) * time.Minute
log.Info().Msgf("Going to sleep for %v, %v", dur, time.Now().Add(dur)) log.Info().Msgf("Going to sleep for %v, %v", dur, time.Now().Add(dur))
p.Bot.Send(bot.Message, message.Channel, "Okay. I'll be back later.") p.Bot.Send(conn, bot.Message, message.Channel, "Okay. I'll be back later.")
p.quiet = true p.quiet = true
go func() { go func() {
select { select {
@ -79,36 +79,36 @@ func (p *AdminPlugin) message(k bot.Kind, message msg.Message, args ...interface
parts := strings.Split(body, " ") parts := strings.Split(body, " ")
if parts[0] == "set" && len(parts) > 2 && forbiddenKeys[parts[1]] { if parts[0] == "set" && len(parts) > 2 && forbiddenKeys[parts[1]] {
p.Bot.Send(bot.Message, message.Channel, "You cannot access that key") p.Bot.Send(conn, bot.Message, message.Channel, "You cannot access that key")
return true return true
} else if parts[0] == "set" && len(parts) > 2 { } else if parts[0] == "set" && len(parts) > 2 {
p.cfg.Set(parts[1], strings.Join(parts[2:], " ")) p.cfg.Set(parts[1], strings.Join(parts[2:], " "))
p.Bot.Send(bot.Message, message.Channel, fmt.Sprintf("Set %s", parts[1])) p.Bot.Send(conn, bot.Message, message.Channel, fmt.Sprintf("Set %s", parts[1]))
return true return true
} }
if parts[0] == "get" && len(parts) == 2 && forbiddenKeys[parts[1]] { if parts[0] == "get" && len(parts) == 2 && forbiddenKeys[parts[1]] {
p.Bot.Send(bot.Message, message.Channel, "You cannot access that key") p.Bot.Send(conn, bot.Message, message.Channel, "You cannot access that key")
return true return true
} else if parts[0] == "get" && len(parts) == 2 { } else if parts[0] == "get" && len(parts) == 2 {
v := p.cfg.Get(parts[1], "<unknown>") v := p.cfg.Get(parts[1], "<unknown>")
p.Bot.Send(bot.Message, message.Channel, fmt.Sprintf("%s: %s", parts[1], v)) p.Bot.Send(conn, bot.Message, message.Channel, fmt.Sprintf("%s: %s", parts[1], v))
return true return true
} }
return false return false
} }
func (p *AdminPlugin) handleVariables(message msg.Message) bool { func (p *AdminPlugin) handleVariables(conn bot.Connector, message msg.Message) bool {
if parts := strings.SplitN(message.Body, "!=", 2); len(parts) == 2 { if parts := strings.SplitN(message.Body, "!=", 2); len(parts) == 2 {
variable := strings.ToLower(strings.TrimSpace(parts[0])) variable := strings.ToLower(strings.TrimSpace(parts[0]))
value := strings.TrimSpace(parts[1]) value := strings.TrimSpace(parts[1])
_, err := p.db.Exec(`delete from variables where name=? and value=?`, variable, value) _, err := p.db.Exec(`delete from variables where name=? and value=?`, variable, value)
if err != nil { if err != nil {
p.Bot.Send(bot.Message, message.Channel, "I'm broke and need attention in my variable creation code.") p.Bot.Send(conn, bot.Message, message.Channel, "I'm broke and need attention in my variable creation code.")
log.Error().Err(err) log.Error().Err(err)
} else { } else {
p.Bot.Send(bot.Message, message.Channel, "Removed.") p.Bot.Send(conn, bot.Message, message.Channel, "Removed.")
} }
return true return true
@ -126,27 +126,27 @@ func (p *AdminPlugin) handleVariables(message msg.Message) bool {
row := p.db.QueryRow(`select count(*) from variables where value = ?`, variable, value) row := p.db.QueryRow(`select count(*) from variables where value = ?`, variable, value)
err := row.Scan(&count) err := row.Scan(&count)
if err != nil { if err != nil {
p.Bot.Send(bot.Message, message.Channel, "I'm broke and need attention in my variable creation code.") p.Bot.Send(conn, bot.Message, message.Channel, "I'm broke and need attention in my variable creation code.")
log.Error().Err(err) log.Error().Err(err)
return true return true
} }
if count > 0 { if count > 0 {
p.Bot.Send(bot.Message, message.Channel, "I've already got that one.") p.Bot.Send(conn, bot.Message, message.Channel, "I've already got that one.")
} else { } else {
_, err := p.db.Exec(`INSERT INTO variables (name, value) VALUES (?, ?)`, variable, value) _, err := p.db.Exec(`INSERT INTO variables (name, value) VALUES (?, ?)`, variable, value)
if err != nil { if err != nil {
p.Bot.Send(bot.Message, message.Channel, "I'm broke and need attention in my variable creation code.") p.Bot.Send(conn, bot.Message, message.Channel, "I'm broke and need attention in my variable creation code.")
log.Error().Err(err) log.Error().Err(err)
return true return true
} }
p.Bot.Send(bot.Message, message.Channel, "Added.") p.Bot.Send(conn, bot.Message, message.Channel, "Added.")
} }
return true return true
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.
func (p *AdminPlugin) help(kind bot.Kind, m msg.Message, args ...interface{}) bool { func (p *AdminPlugin) help(conn bot.Connector, 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.") p.Bot.Send(conn, bot.Message, m.Channel, "This does super secret things that you're not allowed to know about.")
return true return true
} }

View File

@ -1,6 +1,7 @@
package admin package admin
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -22,12 +23,13 @@ func setup(t *testing.T) (*AdminPlugin, *bot.MockBot) {
return a, mb return a, mb
} }
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ c := cli.CliPlugin{}
return &c, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,

View File

@ -101,7 +101,7 @@ func New(b bot.Bot) *BabblerPlugin {
return plugin return plugin
} }
func (p *BabblerPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *BabblerPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
lowercase := strings.ToLower(message.Body) lowercase := strings.ToLower(message.Body)
tokens := strings.Fields(lowercase) tokens := strings.Fields(lowercase)
numTokens := len(tokens) numTokens := len(tokens)
@ -143,12 +143,12 @@ func (p *BabblerPlugin) message(kind bot.Kind, message msg.Message, args ...inte
} }
if saidSomething { if saidSomething {
p.Bot.Send(bot.Message, message.Channel, saidWhat) p.Bot.Send(c, bot.Message, message.Channel, saidWhat)
} }
return saidSomething return saidSomething
} }
func (p *BabblerPlugin) help(kind bot.Kind, msg msg.Message, args ...interface{}) bool { func (p *BabblerPlugin) help(c bot.Connector, kind bot.Kind, msg msg.Message, args ...interface{}) bool {
commands := []string{ commands := []string{
"initialize babbler for seabass", "initialize babbler for seabass",
"merge babbler drseabass into seabass", "merge babbler drseabass into seabass",
@ -157,7 +157,7 @@ func (p *BabblerPlugin) help(kind bot.Kind, msg msg.Message, args ...interface{}
"seabass says-middle-out ...", "seabass says-middle-out ...",
"seabass says-bridge ... | ...", "seabass says-bridge ... | ...",
} }
p.Bot.Send(bot.Message, msg.Channel, strings.Join(commands, "\n\n")) p.Bot.Send(c, bot.Message, msg.Channel, strings.Join(commands, "\n\n"))
return true return true
} }
@ -851,7 +851,7 @@ func (p *BabblerPlugin) babbleSeedBookends(babblerName string, start, end []stri
previous *searchNode previous *searchNode
} }
open := []*searchNode{&searchNode{startWordNode.NodeId, nil}} open := []*searchNode{{startWordNode.NodeId, nil}}
closed := map[int64]*searchNode{startWordNode.NodeId: open[0]} closed := map[int64]*searchNode{startWordNode.NodeId: open[0]}
goalNodeId := int64(-1) goalNodeId := int64(-1)

View File

@ -3,6 +3,7 @@
package babbler package babbler
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -12,12 +13,13 @@ import (
"github.com/velour/catbase/bot/user" "github.com/velour/catbase/bot/user"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
c := &cli.CliPlugin{}
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return c, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,
@ -64,13 +66,13 @@ func TestBabbler(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("This is a message") c, k, seabass := makeMessage("This is a message")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
seabass.Body = "This is another message" seabass.Body = "This is another message"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
seabass.Body = "This is a long message" seabass.Body = "This is a long message"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
res = bp.message(makeMessage("!seabass says")) res = bp.message(makeMessage("!seabass says"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.True(t, res) assert.True(t, res)
@ -82,13 +84,13 @@ func TestBabblerSeed(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("This is a message") c, k, seabass := makeMessage("This is a message")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
seabass.Body = "This is another message" seabass.Body = "This is another message"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
seabass.Body = "This is a long message" seabass.Body = "This is a long message"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
res = bp.message(makeMessage("!seabass says long")) res = bp.message(makeMessage("!seabass says long"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.True(t, res) assert.True(t, res)
@ -99,13 +101,13 @@ func TestBabblerMultiSeed(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("This is a message") c, k, seabass := makeMessage("This is a message")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
seabass.Body = "This is another message" seabass.Body = "This is another message"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
seabass.Body = "This is a long message" seabass.Body = "This is a long message"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
res = bp.message(makeMessage("!seabass says This is a long")) res = bp.message(makeMessage("!seabass says This is a long"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.True(t, res) assert.True(t, res)
@ -116,13 +118,13 @@ func TestBabblerMultiSeed2(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("This is a message") c, k, seabass := makeMessage("This is a message")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
seabass.Body = "This is another message" seabass.Body = "This is another message"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
seabass.Body = "This is a long message" seabass.Body = "This is a long message"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
res = bp.message(makeMessage("!seabass says is a long")) res = bp.message(makeMessage("!seabass says is a long"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.True(t, res) assert.True(t, res)
@ -133,13 +135,13 @@ func TestBabblerBadSeed(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("This is a message") c, k, seabass := makeMessage("This is a message")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
bp.message(k, seabass) bp.message(c, k, seabass)
seabass.Body = "This is another message" seabass.Body = "This is another message"
bp.message(k, seabass) bp.message(c, k, seabass)
seabass.Body = "This is a long message" seabass.Body = "This is a long message"
bp.message(k, seabass) bp.message(c, k, seabass)
bp.message(makeMessage("!seabass says noooo this is bad")) bp.message(makeMessage("!seabass says noooo this is bad"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.Contains(t, mb.Messages[0], "seabass never said 'noooo this is bad'") assert.Contains(t, mb.Messages[0], "seabass never said 'noooo this is bad'")
@ -149,13 +151,13 @@ func TestBabblerBadSeed2(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("This is a message") c, k, seabass := makeMessage("This is a message")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
bp.message(k, seabass) bp.message(c, k, seabass)
seabass.Body = "This is another message" seabass.Body = "This is another message"
bp.message(k, seabass) bp.message(c, k, seabass)
seabass.Body = "This is a long message" seabass.Body = "This is a long message"
bp.message(k, seabass) bp.message(c, k, seabass)
bp.message(makeMessage("!seabass says This is a really")) bp.message(makeMessage("!seabass says This is a really"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.Contains(t, mb.Messages[0], "seabass never said 'this is a really'") assert.Contains(t, mb.Messages[0], "seabass never said 'this is a really'")
@ -165,13 +167,13 @@ func TestBabblerSuffixSeed(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("This is message one") c, k, seabass := makeMessage("This is message one")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
seabass.Body = "It's easier to test with unique messages" seabass.Body = "It's easier to test with unique messages"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
seabass.Body = "hi there" seabass.Body = "hi there"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
res = bp.message(makeMessage("!seabass says-tail message one")) res = bp.message(makeMessage("!seabass says-tail message one"))
res = bp.message(makeMessage("!seabass says-tail with unique")) res = bp.message(makeMessage("!seabass says-tail with unique"))
assert.Len(t, mb.Messages, 2) assert.Len(t, mb.Messages, 2)
@ -184,13 +186,13 @@ func TestBabblerBadSuffixSeed(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("This is message one") c, k, seabass := makeMessage("This is message one")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
seabass.Body = "It's easier to test with unique messages" seabass.Body = "It's easier to test with unique messages"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
seabass.Body = "hi there" seabass.Body = "hi there"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
res = bp.message(makeMessage("!seabass says-tail anything true")) res = bp.message(makeMessage("!seabass says-tail anything true"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.True(t, res) assert.True(t, res)
@ -201,9 +203,9 @@ func TestBabblerBookendSeed(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("It's easier to test with unique messages") c, k, seabass := makeMessage("It's easier to test with unique messages")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
res = bp.message(makeMessage("!seabass says-bridge It's easier | unique messages")) res = bp.message(makeMessage("!seabass says-bridge It's easier | unique messages"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.True(t, res) assert.True(t, res)
@ -214,9 +216,9 @@ func TestBabblerBookendSeedShort(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("It's easier to test with unique messages") c, k, seabass := makeMessage("It's easier to test with unique messages")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
res = bp.message(makeMessage("!seabass says-bridge It's easier to test with | unique messages")) res = bp.message(makeMessage("!seabass says-bridge It's easier to test with | unique messages"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.True(t, res) assert.True(t, res)
@ -227,9 +229,9 @@ func TestBabblerBadBookendSeed(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("It's easier to test with unique messages") c, k, seabass := makeMessage("It's easier to test with unique messages")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
res = bp.message(makeMessage("!seabass says-bridge It's easier | not unique messages")) res = bp.message(makeMessage("!seabass says-bridge It's easier | not unique messages"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.True(t, res) assert.True(t, res)
@ -240,9 +242,9 @@ func TestBabblerMiddleOutSeed(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("It's easier to test with unique messages") c, k, seabass := makeMessage("It's easier to test with unique messages")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
res = bp.message(makeMessage("!seabass says-middle-out test with")) res = bp.message(makeMessage("!seabass says-middle-out test with"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.True(t, res) assert.True(t, res)
@ -253,9 +255,9 @@ func TestBabblerBadMiddleOutSeed(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("It's easier to test with unique messages") c, k, seabass := makeMessage("It's easier to test with unique messages")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
res = bp.message(makeMessage("!seabass says-middle-out anything true")) res = bp.message(makeMessage("!seabass says-middle-out anything true"))
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.True(t, res) assert.True(t, res)
@ -266,8 +268,8 @@ func TestBabblerBatch(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
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?") c, 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) res := bp.message(c, k, seabass)
assert.Len(t, mb.Messages, 1) 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.Len(t, mb.Messages, 2)
@ -281,16 +283,16 @@ func TestBabblerMerge(t *testing.T) {
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
k, seabass := makeMessage("<seabass> This is a message") c, k, seabass := makeMessage("<seabass> This is a message")
seabass.User = &user.User{Name: "seabass"} seabass.User = &user.User{Name: "seabass"}
res := bp.message(k, seabass) res := bp.message(c, k, seabass)
assert.Len(t, mb.Messages, 0) assert.Len(t, mb.Messages, 0)
seabass.Body = "<seabass> This is another message" seabass.Body = "<seabass> This is another message"
res = bp.message(k, seabass) res = bp.message(c, k, seabass)
seabass.Body = "<seabass> This is a long message" seabass.Body = "<seabass> This is a long message"
res = bp.message(k, seabass) res = bp.message(c, 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.True(t, res)
@ -309,6 +311,7 @@ func TestHelp(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
bp := newBabblerPlugin(mb) bp := newBabblerPlugin(mb)
assert.NotNil(t, bp) assert.NotNil(t, bp)
bp.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) c := &cli.CliPlugin{}
bp.help(c, bot.Help, msg.Message{Channel: "channel"}, []string{})
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
} }

View File

@ -53,7 +53,7 @@ func New(b bot.Bot) *BeersPlugin {
db: b.DB(), db: b.DB(),
} }
for _, channel := range b.Config().GetArray("Untappd.Channels", []string{}) { for _, channel := range b.Config().GetArray("Untappd.Channels", []string{}) {
go p.untappdLoop(channel) go p.untappdLoop(b.DefaultConnector(), channel)
} }
b.Register(p, bot.Message, p.message) b.Register(p, bot.Message, p.message)
b.Register(p, bot.Help, p.help) b.Register(p, bot.Help, p.help)
@ -63,7 +63,7 @@ func New(b bot.Bot) *BeersPlugin {
// Message responds to the bot hook on recieving messages. // 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. // 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. // Otherwise, the function returns false and the bot continues execution of other plugins.
func (p *BeersPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *BeersPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
parts := strings.Fields(message.Body) parts := strings.Fields(message.Body)
if len(parts) == 0 { if len(parts) == 0 {
@ -83,49 +83,49 @@ func (p *BeersPlugin) message(kind bot.Kind, message msg.Message, args ...interf
count, err := strconv.Atoi(parts[2]) count, err := strconv.Atoi(parts[2])
if err != nil { if err != nil {
// if it's not a number, maybe it's a nick! // if it's not a number, maybe it's a nick!
p.Bot.Send(bot.Message, channel, "Sorry, that didn't make any sense.") p.Bot.Send(c, bot.Message, channel, "Sorry, that didn't make any sense.")
} }
if count < 0 { if count < 0 {
// you can't be negative // you can't be negative
msg := fmt.Sprintf("Sorry %s, you can't have negative beers!", nick) msg := fmt.Sprintf("Sorry %s, you can't have negative beers!", nick)
p.Bot.Send(bot.Message, channel, msg) p.Bot.Send(c, bot.Message, channel, msg)
return true return true
} }
if parts[1] == "+=" { if parts[1] == "+=" {
p.addBeers(nick, count) p.addBeers(nick, count)
p.randomReply(channel) p.randomReply(c, channel)
} else if parts[1] == "=" { } else if parts[1] == "=" {
if count == 0 { if count == 0 {
p.puke(nick, channel) p.puke(c, nick, channel)
} else { } else {
p.setBeers(nick, count) p.setBeers(nick, count)
p.randomReply(channel) p.randomReply(c, channel)
} }
} else { } else {
p.Bot.Send(bot.Message, channel, "I don't know your math.") p.Bot.Send(c, bot.Message, channel, "I don't know your math.")
} }
} else if len(parts) == 2 { } else if len(parts) == 2 {
if p.doIKnow(parts[1]) { if p.doIKnow(parts[1]) {
p.reportCount(parts[1], channel, false) p.reportCount(c, parts[1], channel, false)
} else { } else {
msg := fmt.Sprintf("Sorry, I don't know %s.", parts[1]) msg := fmt.Sprintf("Sorry, I don't know %s.", parts[1])
p.Bot.Send(bot.Message, channel, msg) p.Bot.Send(c, bot.Message, channel, msg)
} }
} else if len(parts) == 1 { } else if len(parts) == 1 {
p.reportCount(nick, channel, true) p.reportCount(c, nick, channel, true)
} }
// no matter what, if we're in here, then we've responded // no matter what, if we're in here, then we've responded
return true return true
} else if parts[0] == "puke" { } else if parts[0] == "puke" {
p.puke(nick, channel) p.puke(c, nick, channel)
return true return true
} }
if message.Command && parts[0] == "imbibe" { if message.Command && parts[0] == "imbibe" {
p.addBeers(nick, 1) p.addBeers(nick, 1)
p.randomReply(channel) p.randomReply(c, channel)
return true return true
} }
@ -134,7 +134,7 @@ func (p *BeersPlugin) message(kind bot.Kind, message msg.Message, args ...interf
channel := message.Channel channel := message.Channel
if len(parts) < 2 { if len(parts) < 2 {
p.Bot.Send(bot.Message, channel, "You must also provide a user name.") p.Bot.Send(c, bot.Message, channel, "You must also provide a user name.")
} else if len(parts) == 3 { } else if len(parts) == 3 {
chanNick = parts[2] chanNick = parts[2]
} else if len(parts) == 4 { } else if len(parts) == 4 {
@ -159,7 +159,7 @@ func (p *BeersPlugin) message(kind bot.Kind, message msg.Message, args ...interf
log.Error().Err(err).Msgf("Error registering untappd") log.Error().Err(err).Msgf("Error registering untappd")
} }
if count > 0 { if count > 0 {
p.Bot.Send(bot.Message, channel, "I'm already watching you.") p.Bot.Send(c, bot.Message, channel, "I'm already watching you.")
return true return true
} }
_, err = p.db.Exec(`insert into untappd ( _, err = p.db.Exec(`insert into untappd (
@ -175,13 +175,13 @@ func (p *BeersPlugin) message(kind bot.Kind, message msg.Message, args ...interf
) )
if err != nil { if err != nil {
log.Error().Err(err).Msgf("Error registering untappd") log.Error().Err(err).Msgf("Error registering untappd")
p.Bot.Send(bot.Message, channel, "I can't see.") p.Bot.Send(c, bot.Message, channel, "I can't see.")
return true return true
} }
p.Bot.Send(bot.Message, channel, "I'll be watching you.") p.Bot.Send(c, bot.Message, channel, "I'll be watching you.")
p.checkUntappd(channel) p.checkUntappd(c, channel)
return true return true
} }
@ -190,7 +190,7 @@ func (p *BeersPlugin) message(kind bot.Kind, message msg.Message, args ...interf
log.Info(). log.Info().
Str("user", message.User.Name). Str("user", message.User.Name).
Msgf("Checking untappd at request of user.") Msgf("Checking untappd at request of user.")
p.checkUntappd(channel) p.checkUntappd(c, channel)
return true return true
} }
@ -198,11 +198,11 @@ func (p *BeersPlugin) message(kind bot.Kind, message msg.Message, args ...interf
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.
func (p *BeersPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *BeersPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
msg := "Beers: imbibe by using either beers +=,=,++ or with the !imbibe/drink " + 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 " + "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!" "to reset, just !puke it all up!"
p.Bot.Send(bot.Message, message.Channel, msg) p.Bot.Send(c, bot.Message, message.Channel, msg)
return true return true
} }
@ -232,7 +232,7 @@ func (p *BeersPlugin) getBeers(nick string) int {
return ub.Count return ub.Count
} }
func (p *BeersPlugin) reportCount(nick, channel string, himself bool) { func (p *BeersPlugin) reportCount(c bot.Connector, nick, channel string, himself bool) {
beers := p.getBeers(nick) beers := p.getBeers(nick)
msg := fmt.Sprintf("%s has had %d beers so far.", nick, beers) msg := fmt.Sprintf("%s has had %d beers so far.", nick, beers)
if himself { if himself {
@ -242,13 +242,13 @@ func (p *BeersPlugin) reportCount(nick, channel string, himself bool) {
msg = fmt.Sprintf("You've had %d beers so far, %s.", beers, nick) msg = fmt.Sprintf("You've had %d beers so far, %s.", beers, nick)
} }
} }
p.Bot.Send(bot.Message, channel, msg) p.Bot.Send(c, bot.Message, channel, msg)
} }
func (p *BeersPlugin) puke(user string, channel string) { func (p *BeersPlugin) puke(c bot.Connector, user string, channel string) {
p.setBeers(user, 0) p.setBeers(user, 0)
msg := fmt.Sprintf("Ohhhhhh, and a reversal of fortune for %s!", user) msg := fmt.Sprintf("Ohhhhhh, and a reversal of fortune for %s!", user)
p.Bot.Send(bot.Message, channel, msg) p.Bot.Send(c, bot.Message, channel, msg)
} }
func (p *BeersPlugin) doIKnow(nick string) bool { func (p *BeersPlugin) doIKnow(nick string) bool {
@ -261,9 +261,9 @@ func (p *BeersPlugin) doIKnow(nick string) bool {
} }
// Sends random affirmation to the channel. This could be better (with a datastore for sayings) // Sends random affirmation to the channel. This could be better (with a datastore for sayings)
func (p *BeersPlugin) randomReply(channel string) { func (p *BeersPlugin) randomReply(c bot.Connector, channel string) {
replies := []string{"ZIGGY! ZAGGY!", "HIC!", "Stay thirsty, my friend!"} replies := []string{"ZIGGY! ZAGGY!", "HIC!", "Stay thirsty, my friend!"}
p.Bot.Send(bot.Message, channel, replies[rand.Intn(len(replies))]) p.Bot.Send(c, bot.Message, channel, replies[rand.Intn(len(replies))])
} }
type checkin struct { type checkin struct {
@ -343,7 +343,7 @@ func (p *BeersPlugin) pullUntappd() ([]checkin, error) {
return beers.Response.Checkins.Items, nil return beers.Response.Checkins.Items, nil
} }
func (p *BeersPlugin) checkUntappd(channel string) { func (p *BeersPlugin) checkUntappd(c bot.Connector, channel string) {
token := p.Bot.Config().Get("Untappd.Token", "NONE") token := p.Bot.Config().Get("Untappd.Token", "NONE")
if token == "NONE" { if token == "NONE" {
log.Info(). log.Info().
@ -434,11 +434,11 @@ func (p *BeersPlugin) checkUntappd(channel string) {
Int("checkin_id", checkin.Checkin_id). Int("checkin_id", checkin.Checkin_id).
Str("msg", msg). Str("msg", msg).
Msg("checkin") Msg("checkin")
p.Bot.Send(bot.Message, args...) p.Bot.Send(c, bot.Message, args...)
} }
} }
func (p *BeersPlugin) untappdLoop(channel string) { func (p *BeersPlugin) untappdLoop(c bot.Connector, channel string) {
frequency := p.Bot.Config().GetInt("Untappd.Freq", 120) frequency := p.Bot.Config().GetInt("Untappd.Freq", 120)
if frequency == 0 { if frequency == 0 {
return return
@ -448,6 +448,6 @@ func (p *BeersPlugin) untappdLoop(channel string) {
for { for {
time.Sleep(time.Duration(frequency) * time.Second) time.Sleep(time.Duration(frequency) * time.Second)
p.checkUntappd(channel) p.checkUntappd(c, channel)
} }
} }

View File

@ -3,6 +3,7 @@
package beers package beers
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -13,12 +14,13 @@ import (
"github.com/velour/catbase/plugins/counter" "github.com/velour/catbase/plugins/counter"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ c := &cli.CliPlugin{}
return c, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,
@ -121,6 +123,6 @@ func TestBeersReport(t *testing.T) {
func TestHelp(t *testing.T) { func TestHelp(t *testing.T) {
b, mb := makeBeersPlugin(t) b, mb := makeBeersPlugin(t)
b.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) b.help(&cli.CliPlugin{}, bot.Help, msg.Message{Channel: "channel"}, []string{})
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
} }

114
plugins/cli/cli.go Normal file
View File

@ -0,0 +1,114 @@
// © 2013 the CatBase Authors under the WTFPL. See AUTHORS for the list of authors.
package cli
import (
"encoding/json"
"fmt"
"github.com/jmoiron/sqlx"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
"github.com/velour/catbase/bot/user"
"io/ioutil"
"net/http"
"time"
)
type CliPlugin struct {
bot bot.Bot
db *sqlx.DB
cache string
counter int
}
func New(b bot.Bot) *CliPlugin {
cp := &CliPlugin{
bot: b,
}
cp.registerWeb()
return cp
}
func (p *CliPlugin) registerWeb() {
http.HandleFunc("/cli/api", p.handleWebAPI)
http.HandleFunc("/cli", p.handleWeb)
p.bot.RegisterWeb("/cli", "CLI")
}
func (p *CliPlugin) handleWebAPI(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
fmt.Fprintf(w, "Incorrect HTTP method")
return
}
info := struct {
User string `json:"user"`
Payload string `json:"payload"`
}{}
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&info)
if err != nil {
w.WriteHeader(500)
fmt.Fprint(w, err)
return
}
log.Debug().
Interface("postbody", info).
Msg("Got a POST")
p.bot.Receive(p, bot.Message, msg.Message{
User: &user.User{
ID: info.User,
Name: info.User,
Admin: false,
},
Channel: "web",
Body: info.Payload,
Raw: info.Payload,
Command: true,
Time: time.Now(),
})
info.User = p.bot.WhoAmI()
info.Payload = p.cache
p.cache = ""
data, err := json.Marshal(info)
if err != nil {
w.WriteHeader(500)
fmt.Fprint(w, err)
return
}
w.Write(data)
}
func (p *CliPlugin) handleWeb(w http.ResponseWriter, r *http.Request) {
f, err := ioutil.ReadFile("plugins/cli/index.html")
if err != nil {
w.WriteHeader(500)
fmt.Fprint(w, err)
return
}
w.Write(f)
}
// Completing the Connector interface, but will not actually be a connector
func (p *CliPlugin) RegisterEvent(cb bot.Callback) {}
func (p *CliPlugin) Send(kind bot.Kind, args ...interface{}) (string, error) {
switch kind {
case bot.Message:
fallthrough
case bot.Action:
fallthrough
case bot.Reply:
fallthrough
case bot.Reaction:
p.cache += args[1].(string) + "\n"
}
id := fmt.Sprintf("%d", p.counter)
p.counter++
return id, nil
}
func (p *CliPlugin) GetEmojiList() map[string]string { return nil }
func (p *CliPlugin) Serve() error { return nil }
func (p *CliPlugin) Who(s string) []string { return nil }

137
plugins/cli/index.html Normal file
View File

@ -0,0 +1,137 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Load required Bootstrap and BootstrapVue CSS -->
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.css" />
<!-- Load polyfills to support older browsers -->
<script src="//polyfill.io/v3/polyfill.min.js?features=es2015%2CMutationObserver"></script>
<!-- Load Vue followed by BootstrapVue -->
<script src="//unpkg.com/vue@latest/dist/vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<meta charset="UTF-8">
<title>CLI</title>
</head>
<body>
<div id="app">
<h1>CLI</h1>
<b-alert
dismissable
variant="error"
v-if="err"
@dismissed="err = ''">
{{ err }}
</b-alert>
<b-container>
<b-row>
<b-form-group
:label="humanTest"
label-for="input-1"
label-cols="8"
autofocus>
<b-input v-model="answer" id="input-1" autocomplete="off"></b-input>
</b-form-group>
</b-row>
<b-row>
<b-form-textarea
v-sticky-scroll
disabled
id="textarea"
v-model="text"
placeholder="The bot will respond here..."
rows="10"
max-rows="10"
no-resize
></b-form-textarea>
</b-row>
<b-form
@submit="send">
<b-row>
<b-col>
<b-form-input
type="text"
placeholder="Username"
v-model="user"></b-form-input>
</b-col>
<b-col>
<b-form-input
type="text"
placeholder="Enter something to send to the bot"
v-model="input"
autocomplete="off"
></b-form-input>
</b-col>
<b-col>
<b-button type="submit" :disabled="!authenticated">Send</b-button>
</b-col>
</b-row>
</b-form>
</b-container>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
answer: '',
correct: 0,
textarea: [],
user: '',
input: '',
err: '',
},
computed: {
authenticated: function() {
if (Number(this.answer) === this.correct && this.user !== '')
return true;
return false;
},
humanTest: function() {
const x = Math.floor(Math.random() * 100);
const y = Math.floor(Math.random() * 100);
const z = Math.floor(Math.random() * 100);
const ops = ['+', '-', '*'];
const op1 = ops[Math.floor(Math.random()*3)];
const op2 = ops[Math.floor(Math.random()*3)];
const eq = ""+x+op1+y+op2+z;
this.correct = eval(eq);
return "Human test: What is " + eq + "?";
},
text: function() {
return this.textarea.join('\n');
}
},
methods: {
addText(user, text) {
this.textarea.push(user + ": " + text);
const len = this.textarea.length;
if (this.textarea.length > 10)
this.textarea = this.textarea.slice(len-10, len);
},
send(evt) {
evt.preventDefault();
if (!this.authenticated) {
console.log("User is a bot.");
this.err = "User appears to be a bot.";
return;
}
const payload = {user: this.user, payload: this.input};
console.log("Would have posted to /cli/api:" + JSON.stringify(payload));
this.addText(this.user, this.input);
axios.post('/cli/api', payload)
.then(resp => {
console.log(JSON.stringify(resp.data));
const data = resp.data;
this.addText(data.user, data.payload.trim());
})
.catch(err => (this.err = err));
}
}
})
</script>
</body>
</html>

View File

@ -25,7 +25,7 @@ func New(b bot.Bot) *CSWPlugin {
return csw return csw
} }
func (p *CSWPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *CSWPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if !message.Command { if !message.Command {
return false return false
} }
@ -65,7 +65,7 @@ func (p *CSWPlugin) message(kind bot.Kind, message msg.Message, args ...interfac
} }
} }
p.Bot.Send(bot.Message, message.Channel, responses[rand.Intn(len(responses))]) p.Bot.Send(c, bot.Message, message.Channel, responses[rand.Intn(len(responses))])
return true return true
} }

View File

@ -3,6 +3,7 @@
package couldashouldawoulda package couldashouldawoulda
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -12,12 +13,12 @@ import (
"github.com/velour/catbase/bot/user" "github.com/velour/catbase/bot/user"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,

View File

@ -225,7 +225,7 @@ func New(b bot.Bot) *CounterPlugin {
// This function returns true if the plugin responds in a meaningful way to the // 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 // users message. Otherwise, the function returns false and the bot continues
// execution of other plugins. // execution of other plugins.
func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *CounterPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
// This bot does not reply to anything // This bot does not reply to anything
nick := message.User.Name nick := message.User.Name
channel := message.Channel channel := message.Channel
@ -240,7 +240,7 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
log.Error().Err(err) log.Error().Err(err)
return false return false
} }
p.Bot.Send(bot.Message, channel, fmt.Sprintf("Created alias %s -> %s", p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("Created alias %s -> %s",
parts[1], parts[2])) parts[1], parts[2]))
return true return true
} else if strings.ToLower(parts[0]) == "leaderboard" { } else if strings.ToLower(parts[0]) == "leaderboard" {
@ -270,11 +270,11 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
it.Item, it.Item,
) )
} }
p.Bot.Send(bot.Message, channel, out) p.Bot.Send(c, bot.Message, channel, out)
return true return true
} else if match := teaMatcher.MatchString(message.Body); match { } else if match := teaMatcher.MatchString(message.Body); match {
// check for tea match TTT // check for tea match TTT
return p.checkMatch(message) return p.checkMatch(c, message)
} else if message.Command && message.Body == "reset me" { } else if message.Command && message.Body == "reset me" {
items, err := GetItems(p.DB, strings.ToLower(nick)) items, err := GetItems(p.DB, strings.ToLower(nick))
if err != nil { if err != nil {
@ -282,14 +282,14 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
Err(err). Err(err).
Str("nick", nick). Str("nick", nick).
Msg("Error getting items to reset") Msg("Error getting items to reset")
p.Bot.Send(bot.Message, channel, "Something is technically wrong with your counters.") p.Bot.Send(c, bot.Message, channel, "Something is technically wrong with your counters.")
return true return true
} }
log.Debug().Msgf("Items: %+v", items) log.Debug().Msgf("Items: %+v", items)
for _, item := range items { for _, item := range items {
item.Delete() item.Delete()
} }
p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s, you are as new, my son.", nick)) p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("%s, you are as new, my son.", nick))
return true return true
} else if message.Command && parts[0] == "inspect" && len(parts) == 2 { } else if message.Command && parts[0] == "inspect" && len(parts) == 2 {
var subject string var subject string
@ -310,7 +310,7 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
Err(err). Err(err).
Str("subject", subject). Str("subject", subject).
Msg("Error retrieving items") Msg("Error retrieving items")
p.Bot.Send(bot.Message, channel, "Something went wrong finding that counter;") p.Bot.Send(c, bot.Message, channel, "Something went wrong finding that counter;")
return true return true
} }
@ -330,11 +330,11 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
resp += "." resp += "."
if count == 0 { if count == 0 {
p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has no counters.", subject)) p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("%s has no counters.", subject))
return true return true
} }
p.Bot.Send(bot.Message, channel, resp) p.Bot.Send(c, bot.Message, channel, resp)
return true return true
} else if message.Command && len(parts) == 2 && parts[0] == "clear" { } else if message.Command && len(parts) == 2 && parts[0] == "clear" {
subject := strings.ToLower(nick) subject := strings.ToLower(nick)
@ -347,7 +347,7 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
Str("subject", subject). Str("subject", subject).
Str("itemName", itemName). Str("itemName", itemName).
Msg("Error getting item to remove") Msg("Error getting item to remove")
p.Bot.Send(bot.Message, channel, "Something went wrong removing that counter;") p.Bot.Send(c, bot.Message, channel, "Something went wrong removing that counter;")
return true return true
} }
err = it.Delete() err = it.Delete()
@ -357,11 +357,11 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
Str("subject", subject). Str("subject", subject).
Str("itemName", itemName). Str("itemName", itemName).
Msg("Error removing item") Msg("Error removing item")
p.Bot.Send(bot.Message, channel, "Something went wrong removing that counter;") p.Bot.Send(c, bot.Message, channel, "Something went wrong removing that counter;")
return true return true
} }
p.Bot.Send(bot.Action, channel, fmt.Sprintf("chops a few %s out of his brain", p.Bot.Send(c, bot.Action, channel, fmt.Sprintf("chops a few %s out of his brain",
itemName)) itemName))
return true return true
@ -384,7 +384,7 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
item, err := GetItem(p.DB, subject, itemName) item, err := GetItem(p.DB, subject, itemName)
switch { switch {
case err == sql.ErrNoRows: case err == sql.ErrNoRows:
p.Bot.Send(bot.Message, channel, fmt.Sprintf("I don't think %s has any %s.", p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("I don't think %s has any %s.",
subject, itemName)) subject, itemName))
return true return true
case err != nil: case err != nil:
@ -396,7 +396,7 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
return true return true
} }
p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject, item.Count, p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject, item.Count,
itemName)) itemName))
return true return true
@ -431,7 +431,7 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
} }
log.Debug().Msgf("About to update item: %#v", item) log.Debug().Msgf("About to update item: %#v", item)
item.UpdateDelta(1) item.UpdateDelta(1)
p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject, p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject,
item.Count, item.Item)) item.Count, item.Item))
return true return true
} else if strings.HasSuffix(parts[0], "--") { } else if strings.HasSuffix(parts[0], "--") {
@ -447,7 +447,7 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
return false return false
} }
item.UpdateDelta(-1) item.UpdateDelta(-1)
p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject, p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject,
item.Count, item.Item)) item.Count, item.Item))
return true return true
} }
@ -480,7 +480,7 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
n, _ := strconv.Atoi(parts[2]) n, _ := strconv.Atoi(parts[2])
log.Debug().Msgf("About to update item by %d: %#v", n, item) log.Debug().Msgf("About to update item by %d: %#v", n, item)
item.UpdateDelta(n) item.UpdateDelta(n)
p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject, p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject,
item.Count, item.Item)) item.Count, item.Item))
return true return true
} else if parts[1] == "-=" { } else if parts[1] == "-=" {
@ -498,7 +498,7 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
n, _ := strconv.Atoi(parts[2]) n, _ := strconv.Atoi(parts[2])
log.Debug().Msgf("About to update item by -%d: %#v", n, item) log.Debug().Msgf("About to update item by -%d: %#v", n, item)
item.UpdateDelta(-n) item.UpdateDelta(-n)
p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject, p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("%s has %d %s.", subject,
item.Count, item.Item)) item.Count, item.Item))
return true return true
} }
@ -508,15 +508,15 @@ func (p *CounterPlugin) message(kind bot.Kind, message msg.Message, args ...inte
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.
func (p *CounterPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *CounterPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.Bot.Send(bot.Message, message.Channel, "You can set counters incrementally by using "+ p.Bot.Send(c, bot.Message, message.Channel, "You can set counters incrementally by using "+
"<noun>++ and <noun>--. You can see all of your counters using "+ "<noun>++ and <noun>--. You can see all of your counters using "+
"\"inspect\", erase them with \"clear\", and view single counters with "+ "\"inspect\", erase them with \"clear\", and view single counters with "+
"\"count\".") "\"count\".")
return true return true
} }
func (p *CounterPlugin) checkMatch(message msg.Message) bool { func (p *CounterPlugin) checkMatch(c bot.Connector, message msg.Message) bool {
nick := message.User.Name nick := message.User.Name
channel := message.Channel channel := message.Channel
@ -538,7 +538,7 @@ func (p *CounterPlugin) checkMatch(message msg.Message) bool {
} }
log.Debug().Msgf("About to update item: %#v", item) log.Debug().Msgf("About to update item: %#v", item)
item.UpdateDelta(1) item.UpdateDelta(1)
p.Bot.Send(bot.Message, channel, fmt.Sprintf("%s... %s has %d %s", p.Bot.Send(c, bot.Message, channel, fmt.Sprintf("%s... %s has %d %s",
strings.Join(everyDayImShuffling([]string{"bleep", "bloop", "blop"}), "-"), nick, item.Count, itemName)) strings.Join(everyDayImShuffling([]string{"bleep", "bloop", "blop"}), "-"), nick, item.Count, itemName))
return true return true
} }

View File

@ -4,6 +4,7 @@ package counter
import ( import (
"fmt" "fmt"
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -22,12 +23,12 @@ func setup(t *testing.T) (*bot.MockBot, *CounterPlugin) {
return mb, c return mb, c
} }
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,
@ -250,6 +251,6 @@ func TestInspectMe(t *testing.T) {
func TestHelp(t *testing.T) { func TestHelp(t *testing.T) {
mb, c := setup(t) mb, c := setup(t)
assert.NotNil(t, c) assert.NotNil(t, c)
c.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) c.help(&cli.CliPlugin{}, bot.Help, msg.Message{Channel: "channel"}, []string{})
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
} }

View File

@ -1,46 +0,0 @@
package db
import (
"fmt"
"net/http"
"os"
"time"
"github.com/rs/zerolog/log"
"github.com/velour/catbase/bot"
"github.com/velour/catbase/bot/msg"
"github.com/velour/catbase/config"
)
type DBPlugin struct {
bot bot.Bot
config *config.Config
}
func New(b bot.Bot) *DBPlugin {
p := &DBPlugin{b, b.Config()}
p.registerWeb()
return p
}
func (p *DBPlugin) Message(message msg.Message) bool { return false }
func (p *DBPlugin) Event(kind string, message msg.Message) bool { return false }
func (p *DBPlugin) ReplyMessage(msg.Message, string) bool { return false }
func (p *DBPlugin) BotMessage(message msg.Message) bool { return false }
func (p *DBPlugin) Help(channel string, parts []string) {}
func (p *DBPlugin) registerWeb() {
http.HandleFunc("/db/catbase.db", p.serveQuery)
}
func (p *DBPlugin) serveQuery(w http.ResponseWriter, r *http.Request) {
f, err := os.Open(p.bot.Config().DBFile)
defer f.Close()
if err != nil {
log.Error().Err(err).Msg("Error opening DB for web service")
fmt.Fprintf(w, "Error opening DB")
return
}
http.ServeContent(w, r, "catbase.db", time.Now(), f)
}

View File

@ -35,7 +35,7 @@ func rollDie(sides int) int {
// Message responds to the bot hook on recieving messages. // 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. // 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. // Otherwise, the function returns false and the bot continues execution of other plugins.
func (p *DicePlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *DicePlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if !message.Command { if !message.Command {
return false return false
} }
@ -49,7 +49,7 @@ func (p *DicePlugin) message(kind bot.Kind, message msg.Message, args ...interfa
} }
if sides < 2 || nDice < 1 || nDice > 20 { if sides < 2 || nDice < 1 || nDice > 20 {
p.Bot.Send(bot.Message, channel, "You're a dick.") p.Bot.Send(c, bot.Message, channel, "You're a dick.")
return true return true
} }
@ -64,13 +64,13 @@ func (p *DicePlugin) message(kind bot.Kind, message msg.Message, args ...interfa
} }
} }
p.Bot.Send(bot.Message, channel, rolls) p.Bot.Send(c, bot.Message, channel, rolls)
return true return true
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.
func (p *DicePlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *DicePlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.Bot.Send(bot.Message, message.Channel, "Roll dice using notation XdY. Try \"3d20\".") p.Bot.Send(c, bot.Message, message.Channel, "Roll dice using notation XdY. Try \"3d20\".")
return true return true
} }

View File

@ -3,6 +3,7 @@
package dice package dice
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -12,12 +13,12 @@ import (
"github.com/velour/catbase/bot/user" "github.com/velour/catbase/bot/user"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,
@ -86,6 +87,6 @@ func TestHelp(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
c := New(mb) c := New(mb)
assert.NotNil(t, c) assert.NotNil(t, c)
c.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) c.help(&cli.CliPlugin{}, bot.Help, msg.Message{Channel: "channel"}, []string{})
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
} }

View File

@ -58,7 +58,7 @@ func New(b bot.Bot) *EmojifyMePlugin {
return ep return ep
} }
func (p *EmojifyMePlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *EmojifyMePlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if !p.GotBotEmoji { if !p.GotBotEmoji {
p.GotBotEmoji = true p.GotBotEmoji = true
emojiMap := p.Bot.GetEmojiList() emojiMap := p.Bot.GetEmojiList()
@ -93,7 +93,7 @@ func (p *EmojifyMePlugin) message(kind bot.Kind, message msg.Message, args ...in
if emojied > 0 && rand.Float64() <= p.Bot.Config().GetFloat64("Emojify.Chance", 0.02)*emojied { if emojied > 0 && rand.Float64() <= p.Bot.Config().GetFloat64("Emojify.Chance", 0.02)*emojied {
for _, e := range emojys { for _, e := range emojys {
p.Bot.Send(bot.Reaction, message.Channel, e, message) p.Bot.Send(c, bot.Reaction, message.Channel, e, message)
} }
return false return false
} }

View File

@ -1,6 +1,7 @@
package fact package fact
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -10,6 +11,8 @@ import (
"github.com/velour/catbase/bot/user" "github.com/velour/catbase/bot/user"
) )
var c = &cli.CliPlugin{}
func makeMessage(nick, payload string) msg.Message { func makeMessage(nick, payload string) msg.Message {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
@ -37,7 +40,7 @@ func TestReact(t *testing.T) {
p, mb := makePlugin(t) p, mb := makePlugin(t)
for _, m := range msgs { for _, m := range msgs {
p.message(bot.Message, m) p.message(c, bot.Message, m)
} }
assert.Len(t, mb.Reactions, 1) assert.Len(t, mb.Reactions, 1)
assert.Contains(t, mb.Reactions[0], "jesus") assert.Contains(t, mb.Reactions[0], "jesus")
@ -50,7 +53,7 @@ func TestReactCantLearnSpaces(t *testing.T) {
p, mb := makePlugin(t) p, mb := makePlugin(t)
for _, m := range msgs { for _, m := range msgs {
p.message(bot.Message, m) p.message(c, bot.Message, m)
} }
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.Contains(t, mb.Messages[0], "not a valid") assert.Contains(t, mb.Messages[0], "not a valid")

View File

@ -277,6 +277,8 @@ func New(botInst bot.Bot) *FactoidPlugin {
db: botInst.DB(), db: botInst.DB(),
} }
c := botInst.DefaultConnector()
if _, err := p.db.Exec(`create table if not exists factoid ( if _, err := p.db.Exec(`create table if not exists factoid (
id integer primary key, id integer primary key,
fact string, fact string,
@ -299,13 +301,13 @@ func New(botInst bot.Bot) *FactoidPlugin {
} }
for _, channel := range botInst.Config().GetArray("channels", []string{}) { for _, channel := range botInst.Config().GetArray("channels", []string{}) {
go p.factTimer(channel) go p.factTimer(c, channel)
go func(ch string) { go func(ch string) {
// Some random time to start up // Some random time to start up
time.Sleep(time.Duration(15) * time.Second) time.Sleep(time.Duration(15) * time.Second)
if ok, fact := p.findTrigger(p.Bot.Config().Get("Factoid.StartupFact", "speed test")); ok { if ok, fact := p.findTrigger(p.Bot.Config().Get("Factoid.StartupFact", "speed test")); ok {
p.sayFact(msg.Message{ p.sayFact(c, msg.Message{
Channel: ch, Channel: ch,
Body: "speed test", // BUG: This is defined in the config too Body: "speed test", // BUG: This is defined in the config too
Command: true, Command: true,
@ -399,7 +401,7 @@ func (p *FactoidPlugin) findTrigger(fact string) (bool, *Factoid) {
// sayFact spits out a fact to the channel and updates the fact in the database // sayFact spits out a fact to the channel and updates the fact in the database
// with new time and count information // with new time and count information
func (p *FactoidPlugin) sayFact(message msg.Message, fact Factoid) { func (p *FactoidPlugin) sayFact(c bot.Connector, message msg.Message, fact Factoid) {
msg := p.Bot.Filter(message, fact.Tidbit) msg := p.Bot.Filter(message, fact.Tidbit)
full := p.Bot.Filter(message, fmt.Sprintf("%s %s %s", full := p.Bot.Filter(message, fmt.Sprintf("%s %s %s",
fact.Fact, fact.Verb, fact.Tidbit, fact.Fact, fact.Verb, fact.Tidbit,
@ -411,13 +413,13 @@ func (p *FactoidPlugin) sayFact(message msg.Message, fact Factoid) {
} }
if fact.Verb == "action" { if fact.Verb == "action" {
p.Bot.Send(bot.Action, message.Channel, msg) p.Bot.Send(c, bot.Action, message.Channel, msg)
} else if fact.Verb == "react" { } else if fact.Verb == "react" {
p.Bot.Send(bot.Reaction, message.Channel, msg, message) p.Bot.Send(c, bot.Reaction, message.Channel, msg, message)
} else if fact.Verb == "reply" { } else if fact.Verb == "reply" {
p.Bot.Send(bot.Message, message.Channel, msg) p.Bot.Send(c, bot.Message, message.Channel, msg)
} else { } else {
p.Bot.Send(bot.Message, message.Channel, full) p.Bot.Send(c, bot.Message, message.Channel, full)
} }
} }
@ -436,17 +438,17 @@ func (p *FactoidPlugin) sayFact(message msg.Message, fact Factoid) {
// trigger checks the message for its fitness to be a factoid and then hauls // trigger checks the message for its fitness to be a factoid and then hauls
// the message off to sayFact for processing if it is in fact a trigger // the message off to sayFact for processing if it is in fact a trigger
func (p *FactoidPlugin) trigger(message msg.Message) bool { func (p *FactoidPlugin) trigger(c bot.Connector, message msg.Message) bool {
minLen := p.Bot.Config().GetInt("Factoid.MinLen", 4) minLen := p.Bot.Config().GetInt("Factoid.MinLen", 4)
if len(message.Body) > minLen || message.Command || message.Body == "..." { if len(message.Body) > minLen || message.Command || message.Body == "..." {
if ok, fact := p.findTrigger(message.Body); ok { if ok, fact := p.findTrigger(message.Body); ok {
p.sayFact(message, *fact) p.sayFact(c, message, *fact)
return true return true
} }
r := strings.NewReplacer("'", "", "\"", "", ",", "", ".", "", ":", "", r := strings.NewReplacer("'", "", "\"", "", ",", "", ".", "", ":", "",
"?", "", "!", "") "?", "", "!", "")
if ok, fact := p.findTrigger(r.Replace(message.Body)); ok { if ok, fact := p.findTrigger(r.Replace(message.Body)); ok {
p.sayFact(message, *fact) p.sayFact(c, message, *fact)
return true return true
} }
} }
@ -455,7 +457,7 @@ func (p *FactoidPlugin) trigger(message msg.Message) bool {
} }
// tellThemWhatThatWas is a hilarious name for a function. // tellThemWhatThatWas is a hilarious name for a function.
func (p *FactoidPlugin) tellThemWhatThatWas(message msg.Message) bool { func (p *FactoidPlugin) tellThemWhatThatWas(c bot.Connector, message msg.Message) bool {
fact := p.LastFact fact := p.LastFact
var msg string var msg string
if fact == nil { if fact == nil {
@ -464,11 +466,11 @@ func (p *FactoidPlugin) tellThemWhatThatWas(message msg.Message) bool {
msg = fmt.Sprintf("That was (#%d) '%s <%s> %s'", msg = fmt.Sprintf("That was (#%d) '%s <%s> %s'",
fact.ID.Int64, fact.Fact, fact.Verb, fact.Tidbit) fact.ID.Int64, fact.Fact, fact.Verb, fact.Tidbit)
} }
p.Bot.Send(bot.Message, message.Channel, msg) p.Bot.Send(c, bot.Message, message.Channel, msg)
return true return true
} }
func (p *FactoidPlugin) learnAction(message msg.Message, action string) bool { func (p *FactoidPlugin) learnAction(c bot.Connector, message msg.Message, action string) bool {
body := message.Body body := message.Body
parts := strings.SplitN(body, action, 2) parts := strings.SplitN(body, action, 2)
@ -482,21 +484,21 @@ func (p *FactoidPlugin) learnAction(message msg.Message, action string) bool {
action = strings.TrimSpace(action) action = strings.TrimSpace(action)
if len(trigger) == 0 || len(fact) == 0 || len(action) == 0 { if len(trigger) == 0 || len(fact) == 0 || len(action) == 0 {
p.Bot.Send(bot.Message, message.Channel, "I don't want to learn that.") p.Bot.Send(c, bot.Message, message.Channel, "I don't want to learn that.")
return true return true
} }
if len(strings.Split(fact, "$and")) > 4 { if len(strings.Split(fact, "$and")) > 4 {
p.Bot.Send(bot.Message, message.Channel, "You can't use more than 4 $and operators.") p.Bot.Send(c, bot.Message, message.Channel, "You can't use more than 4 $and operators.")
return true return true
} }
strippedaction := strings.Replace(strings.Replace(action, "<", "", 1), ">", "", 1) strippedaction := strings.Replace(strings.Replace(action, "<", "", 1), ">", "", 1)
if err := p.learnFact(message, trigger, strippedaction, fact); err != nil { if err := p.learnFact(message, trigger, strippedaction, fact); err != nil {
p.Bot.Send(bot.Message, message.Channel, err.Error()) p.Bot.Send(c, bot.Message, message.Channel, err.Error())
} else { } else {
p.Bot.Send(bot.Message, message.Channel, fmt.Sprintf("Okay, %s.", message.User.Name)) p.Bot.Send(c, bot.Message, message.Channel, fmt.Sprintf("Okay, %s.", message.User.Name))
} }
return true return true
@ -514,9 +516,9 @@ func changeOperator(body string) string {
// If the user requesting forget is either the owner of the last learned fact or // If the user requesting forget is either the owner of the last learned fact or
// an admin, it may be deleted // an admin, it may be deleted
func (p *FactoidPlugin) forgetLastFact(message msg.Message) bool { func (p *FactoidPlugin) forgetLastFact(c bot.Connector, message msg.Message) bool {
if p.LastFact == nil { if p.LastFact == nil {
p.Bot.Send(bot.Message, message.Channel, "I refuse.") p.Bot.Send(c, bot.Message, message.Channel, "I refuse.")
return true return true
} }
@ -529,14 +531,14 @@ func (p *FactoidPlugin) forgetLastFact(message msg.Message) bool {
} }
fmt.Printf("Forgot #%d: %s %s %s\n", p.LastFact.ID.Int64, p.LastFact.Fact, fmt.Printf("Forgot #%d: %s %s %s\n", p.LastFact.ID.Int64, p.LastFact.Fact,
p.LastFact.Verb, p.LastFact.Tidbit) p.LastFact.Verb, p.LastFact.Tidbit)
p.Bot.Send(bot.Action, message.Channel, "hits himself over the head with a skillet") p.Bot.Send(c, bot.Action, message.Channel, "hits himself over the head with a skillet")
p.LastFact = nil p.LastFact = nil
return true return true
} }
// Allow users to change facts with a simple regexp // Allow users to change facts with a simple regexp
func (p *FactoidPlugin) changeFact(message msg.Message) bool { func (p *FactoidPlugin) changeFact(c bot.Connector, message msg.Message) bool {
oper := changeOperator(message.Body) oper := changeOperator(message.Body)
parts := strings.SplitN(message.Body, oper, 2) parts := strings.SplitN(message.Body, oper, 2)
userexp := strings.TrimSpace(parts[1]) userexp := strings.TrimSpace(parts[1])
@ -553,7 +555,7 @@ func (p *FactoidPlugin) changeFact(message msg.Message) bool {
if len(parts) == 4 { if len(parts) == 4 {
// replacement // replacement
if parts[0] != "s" { if parts[0] != "s" {
p.Bot.Send(bot.Message, message.Channel, "Nah.") p.Bot.Send(c, bot.Message, message.Channel, "Nah.")
} }
find := parts[1] find := parts[1]
replace := parts[2] replace := parts[2]
@ -571,10 +573,10 @@ func (p *FactoidPlugin) changeFact(message msg.Message) bool {
} }
// make the changes // make the changes
msg := fmt.Sprintf("Changing %d facts.", len(result)) msg := fmt.Sprintf("Changing %d facts.", len(result))
p.Bot.Send(bot.Message, message.Channel, msg) p.Bot.Send(c, bot.Message, message.Channel, msg)
reg, err := regexp.Compile(find) reg, err := regexp.Compile(find)
if err != nil { if err != nil {
p.Bot.Send(bot.Message, message.Channel, "I don't really want to.") p.Bot.Send(c, bot.Message, message.Channel, "I don't really want to.")
return false return false
} }
for _, fact := range result { for _, fact := range result {
@ -594,19 +596,19 @@ func (p *FactoidPlugin) changeFact(message msg.Message) bool {
Err(err). Err(err).
Str("trigger", trigger). Str("trigger", trigger).
Msg("Error getting facts") Msg("Error getting facts")
p.Bot.Send(bot.Message, message.Channel, "bzzzt") p.Bot.Send(c, bot.Message, message.Channel, "bzzzt")
return true return true
} }
count := len(result) count := len(result)
if count == 0 { if count == 0 {
p.Bot.Send(bot.Message, message.Channel, "I didn't find any facts like that.") p.Bot.Send(c, bot.Message, message.Channel, "I didn't find any facts like that.")
return true return true
} }
if parts[2] == "g" && len(result) > 4 { if parts[2] == "g" && len(result) > 4 {
// summarize // summarize
result = result[:4] result = result[:4]
} else { } else {
p.sayFact(message, *result[0]) p.sayFact(c, message, *result[0])
return true return true
} }
msg := fmt.Sprintf("%s ", trigger) msg := fmt.Sprintf("%s ", trigger)
@ -619,9 +621,9 @@ func (p *FactoidPlugin) changeFact(message msg.Message) bool {
if count > 4 { if count > 4 {
msg = fmt.Sprintf("%s | ...and %d others", msg, count) msg = fmt.Sprintf("%s | ...and %d others", msg, count)
} }
p.Bot.Send(bot.Message, message.Channel, msg) p.Bot.Send(c, bot.Message, message.Channel, msg)
} else { } else {
p.Bot.Send(bot.Message, message.Channel, "I don't know what you mean.") p.Bot.Send(c, bot.Message, message.Channel, "I don't know what you mean.")
} }
return true return true
} }
@ -629,15 +631,15 @@ func (p *FactoidPlugin) changeFact(message msg.Message) bool {
// Message responds to the bot hook on recieving messages. // 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. // 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. // Otherwise, the function returns false and the bot continues execution of other plugins.
func (p *FactoidPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *FactoidPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if strings.ToLower(message.Body) == "what was that?" { if strings.ToLower(message.Body) == "what was that?" {
return p.tellThemWhatThatWas(message) return p.tellThemWhatThatWas(c, message)
} }
// This plugin has no business with normal messages // This plugin has no business with normal messages
if !message.Command { if !message.Command {
// look for any triggers in the db matching this message // look for any triggers in the db matching this message
return p.trigger(message) return p.trigger(c, message)
} }
if strings.HasPrefix(strings.ToLower(message.Body), "alias") { if strings.HasPrefix(strings.ToLower(message.Body), "alias") {
@ -647,53 +649,53 @@ func (p *FactoidPlugin) message(kind bot.Kind, message msg.Message, args ...inte
m := strings.TrimPrefix(message.Body, "alias ") m := strings.TrimPrefix(message.Body, "alias ")
parts := strings.SplitN(m, "->", 2) parts := strings.SplitN(m, "->", 2)
if len(parts) != 2 { if len(parts) != 2 {
p.Bot.Send(bot.Message, message.Channel, "If you want to alias something, use: `alias this -> that`") p.Bot.Send(c, bot.Message, message.Channel, "If you want to alias something, use: `alias this -> that`")
return true return true
} }
a := aliasFromStrings(strings.TrimSpace(parts[1]), strings.TrimSpace(parts[0])) a := aliasFromStrings(strings.TrimSpace(parts[1]), strings.TrimSpace(parts[0]))
if err := a.save(p.db); err != nil { if err := a.save(p.db); err != nil {
p.Bot.Send(bot.Message, message.Channel, err.Error()) p.Bot.Send(c, bot.Message, message.Channel, err.Error())
} else { } else {
p.Bot.Send(bot.Action, message.Channel, "learns a new synonym") p.Bot.Send(c, bot.Action, message.Channel, "learns a new synonym")
} }
return true return true
} }
if strings.ToLower(message.Body) == "factoid" { if strings.ToLower(message.Body) == "factoid" {
if fact := p.randomFact(); fact != nil { if fact := p.randomFact(); fact != nil {
p.sayFact(message, *fact) p.sayFact(c, message, *fact)
return true return true
} }
log.Debug().Msg("Got a nil fact.") log.Debug().Msg("Got a nil fact.")
} }
if strings.ToLower(message.Body) == "forget that" { if strings.ToLower(message.Body) == "forget that" {
return p.forgetLastFact(message) return p.forgetLastFact(c, message)
} }
if changeOperator(message.Body) != "" { if changeOperator(message.Body) != "" {
return p.changeFact(message) return p.changeFact(c, message)
} }
action := findAction(message.Body) action := findAction(message.Body)
if action != "" { if action != "" {
return p.learnAction(message, action) return p.learnAction(c, message, action)
} }
// look for any triggers in the db matching this message // look for any triggers in the db matching this message
if p.trigger(message) { if p.trigger(c, message) {
return true return true
} }
// We didn't find anything, panic! // We didn't find anything, panic!
p.Bot.Send(bot.Message, message.Channel, p.NotFound[rand.Intn(len(p.NotFound))]) p.Bot.Send(c, bot.Message, message.Channel, p.NotFound[rand.Intn(len(p.NotFound))])
return true return true
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.
func (p *FactoidPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *FactoidPlugin) help(c bot.Connector, 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(c, 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.") p.Bot.Send(c, bot.Message, message.Channel, "I can also figure out some variables including: $nonzero, $digit, $nick, and $someone.")
return true return true
} }
@ -708,7 +710,7 @@ func (p *FactoidPlugin) randomFact() *Factoid {
} }
// factTimer spits out a fact at a given interval and with given probability // factTimer spits out a fact at a given interval and with given probability
func (p *FactoidPlugin) factTimer(channel string) { func (p *FactoidPlugin) factTimer(c bot.Connector, channel string) {
quoteTime := p.Bot.Config().GetInt("Factoid.QuoteTime", 30) quoteTime := p.Bot.Config().GetInt("Factoid.QuoteTime", 30)
if quoteTime == 0 { if quoteTime == 0 {
quoteTime = 30 quoteTime = 30
@ -749,7 +751,7 @@ func (p *FactoidPlugin) factTimer(channel string) {
User: &users[rand.Intn(len(users))], User: &users[rand.Intn(len(users))],
Channel: channel, Channel: channel,
} }
p.sayFact(message, *fact) p.sayFact(c, message, *fact)
myLastMsg = time.Now() myLastMsg = time.Now()
} }
} }

View File

@ -7,7 +7,7 @@ package fact
// 2016-01-15 Later note, why are these in plugins and the server is in bot? // 2016-01-15 Later note, why are these in plugins and the server is in bot?
var factoidIndex string = ` var factoidIndex = `
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>

View File

@ -132,14 +132,14 @@ func isToday(t time.Time) bool {
// Message responds to the bot hook on recieving messages. // 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. // 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. // Otherwise, the function returns false and the bot continues execution of other plugins.
func (p *FirstPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *FirstPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
// This bot does not reply to anything // This bot does not reply to anything
if p.First == nil && p.allowed(message) { if p.First == nil && p.allowed(message) {
log.Debug(). log.Debug().
Str("body", message.Body). Str("body", message.Body).
Msg("No previous first. Recording new first") Msg("No previous first. Recording new first")
p.recordFirst(message) p.recordFirst(c, message)
return false return false
} else if p.First != nil { } else if p.First != nil {
if isToday(p.First.time) && p.allowed(message) { if isToday(p.First.time) && p.allowed(message) {
@ -148,7 +148,7 @@ func (p *FirstPlugin) message(kind bot.Kind, message msg.Message, args ...interf
Time("t0", p.First.time). Time("t0", p.First.time).
Time("t1", time.Now()). Time("t1", time.Now()).
Msg("Recording first") Msg("Recording first")
p.recordFirst(message) p.recordFirst(c, message)
return false return false
} }
} }
@ -157,7 +157,7 @@ func (p *FirstPlugin) message(kind bot.Kind, message msg.Message, args ...interf
"?", "", "!", "") "?", "", "!", "")
msg := strings.ToLower(message.Body) msg := strings.ToLower(message.Body)
if r.Replace(msg) == "whos on first" { if r.Replace(msg) == "whos on first" {
p.announceFirst(message) p.announceFirst(c, message)
return true return true
} }
@ -199,7 +199,7 @@ func (p *FirstPlugin) allowed(message msg.Message) bool {
return true return true
} }
func (p *FirstPlugin) recordFirst(message msg.Message) { func (p *FirstPlugin) recordFirst(c bot.Connector, message msg.Message) {
log.Info(). log.Info().
Str("user", message.User.Name). Str("user", message.User.Name).
Str("body", message.Body). Str("body", message.Body).
@ -216,13 +216,13 @@ func (p *FirstPlugin) recordFirst(message msg.Message) {
log.Error().Err(err).Msg("Error saving first entry") log.Error().Err(err).Msg("Error saving first entry")
return return
} }
p.announceFirst(message) p.announceFirst(c, message)
} }
func (p *FirstPlugin) announceFirst(message msg.Message) { func (p *FirstPlugin) announceFirst(c bot.Connector, message msg.Message) {
c := message.Channel ch := message.Channel
if p.First != nil { if p.First != nil {
p.Bot.Send(bot.Message, c, fmt.Sprintf("%s had first at %s with the message: \"%s\"", p.Bot.Send(c, bot.Message, ch, fmt.Sprintf("%s had first at %s with the message: \"%s\"",
p.First.nick, p.First.time.Format("15:04"), p.First.body)) p.First.nick, p.First.time.Format("15:04"), p.First.body))
} }
} }
@ -235,7 +235,7 @@ func (p *FirstPlugin) LoadData() {
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.
func (p *FirstPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *FirstPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.Bot.Send(bot.Message, message.Channel, "Sorry, First does not do a goddamn thing.") p.Bot.Send(c, bot.Message, message.Channel, "Sorry, First does not do a goddamn thing.")
return true return true
} }

View File

@ -78,7 +78,7 @@ func (p *InventoryPlugin) itemFilter(input string) string {
return input return input
} }
func (p *InventoryPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *InventoryPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
m := message.Body m := message.Body
log.Debug().Msgf("inventory trying to read %+v", message) log.Debug().Msgf("inventory trying to read %+v", message)
if message.Command { if message.Command {
@ -89,7 +89,7 @@ func (p *InventoryPlugin) message(kind bot.Kind, message msg.Message, args ...in
log.Debug().Msgf("I think I have more than 0 items: %+v, len(items)=%d", items, len(items)) log.Debug().Msgf("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, ", ")) say = fmt.Sprintf("I'm currently holding %s", strings.Join(items, ", "))
} }
p.bot.Send(bot.Message, message.Channel, say) p.bot.Send(c, bot.Message, message.Channel, say)
return true return true
} }
@ -97,11 +97,11 @@ func (p *InventoryPlugin) message(kind bot.Kind, message msg.Message, args ...in
// <Randall> Bucket[:,] have a (.+) // <Randall> Bucket[:,] have a (.+)
if matches := p.r1.FindStringSubmatch(m); len(matches) > 0 { if matches := p.r1.FindStringSubmatch(m); len(matches) > 0 {
log.Debug().Msgf("Found item to add: %s", matches[1]) log.Debug().Msgf("Found item to add: %s", matches[1])
return p.addItem(message, matches[1]) return p.addItem(c, message, matches[1])
} }
if matches := p.r2.FindStringSubmatch(m); len(matches) > 0 { if matches := p.r2.FindStringSubmatch(m); len(matches) > 0 {
log.Debug().Msgf("Found item to add: %s", matches[1]) log.Debug().Msgf("Found item to add: %s", matches[1])
return p.addItem(message, matches[1]) return p.addItem(c, message, matches[1])
} }
} }
if message.Action { if message.Action {
@ -112,15 +112,15 @@ func (p *InventoryPlugin) message(kind bot.Kind, message msg.Message, args ...in
if matches := p.r3.FindStringSubmatch(m); len(matches) > 0 { if matches := p.r3.FindStringSubmatch(m); len(matches) > 0 {
log.Debug().Msgf("Found item to add: %s", matches[1]) log.Debug().Msgf("Found item to add: %s", matches[1])
return p.addItem(message, matches[1]) return p.addItem(c, message, matches[1])
} }
if matches := p.r4.FindStringSubmatch(m); len(matches) > 0 { if matches := p.r4.FindStringSubmatch(m); len(matches) > 0 {
log.Debug().Msgf("Found item to add: %s", matches[1]) log.Debug().Msgf("Found item to add: %s", matches[1])
return p.addItem(message, matches[1]) return p.addItem(c, message, matches[1])
} }
if matches := p.r5.FindStringSubmatch(m); len(matches) > 0 { if matches := p.r5.FindStringSubmatch(m); len(matches) > 0 {
log.Debug().Msgf("Found item to add: %s", matches[1]) log.Debug().Msgf("Found item to add: %s", matches[1])
return p.addItem(message, matches[1]) return p.addItem(c, message, matches[1])
} }
} }
return false return false
@ -198,9 +198,9 @@ func (p *InventoryPlugin) remove(i string) {
} }
} }
func (p *InventoryPlugin) addItem(m msg.Message, i string) bool { func (p *InventoryPlugin) addItem(c bot.Connector, m msg.Message, i string) bool {
if p.exists(i) { if p.exists(i) {
p.bot.Send(bot.Message, m.Channel, fmt.Sprintf("I already have %s.", i)) p.bot.Send(c, bot.Message, m.Channel, fmt.Sprintf("I already have %s.", i))
return true return true
} }
var removed string var removed string
@ -213,9 +213,9 @@ func (p *InventoryPlugin) addItem(m msg.Message, i string) bool {
log.Error().Err(err).Msg("Error inserting new inventory item") log.Error().Err(err).Msg("Error inserting new inventory item")
} }
if removed != "" { if removed != "" {
p.bot.Send(bot.Action, m.Channel, fmt.Sprintf("dropped %s and took %s from %s", removed, i, m.User.Name)) p.bot.Send(c, bot.Action, m.Channel, fmt.Sprintf("dropped %s and took %s from %s", removed, i, m.User.Name))
} else { } else {
p.bot.Send(bot.Action, m.Channel, fmt.Sprintf("takes %s from %s", i, m.User.Name)) p.bot.Send(c, bot.Action, m.Channel, fmt.Sprintf("takes %s from %s", i, m.User.Name))
} }
return true return true
} }

View File

@ -33,7 +33,7 @@ type leftpadResp struct {
Str string Str string
} }
func (p *LeftpadPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *LeftpadPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if !message.Command { if !message.Command {
return false return false
} }
@ -43,20 +43,20 @@ func (p *LeftpadPlugin) message(kind bot.Kind, message msg.Message, args ...inte
padchar := parts[1] padchar := parts[1]
length, err := strconv.Atoi(parts[2]) length, err := strconv.Atoi(parts[2])
if err != nil { if err != nil {
p.bot.Send(bot.Message, message.Channel, "Invalid padding number") p.bot.Send(c, bot.Message, message.Channel, "Invalid padding number")
return true return true
} }
maxLen, who := p.config.GetInt("LeftPad.MaxLen", 50), p.config.Get("LeftPad.Who", "Putin") maxLen, who := p.config.GetInt("LeftPad.MaxLen", 50), p.config.Get("LeftPad.Who", "Putin")
if length > maxLen && maxLen > 0 { if length > maxLen && maxLen > 0 {
msg := fmt.Sprintf("%s would kill me if I did that.", who) msg := fmt.Sprintf("%s would kill me if I did that.", who)
p.bot.Send(bot.Message, message.Channel, msg) p.bot.Send(c, bot.Message, message.Channel, msg)
return true return true
} }
text := strings.Join(parts[3:], " ") text := strings.Join(parts[3:], " ")
res := leftpad.LeftPad(text, length, padchar) res := leftpad.LeftPad(text, length, padchar)
p.bot.Send(bot.Message, message.Channel, res) p.bot.Send(c, bot.Message, message.Channel, res)
return true return true
} }

View File

@ -3,6 +3,7 @@
package leftpad package leftpad
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -13,12 +14,12 @@ import (
"github.com/velour/catbase/plugins/counter" "github.com/velour/catbase/plugins/counter"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,

View File

@ -40,7 +40,7 @@ func New(b bot.Bot) *NerdepediaPlugin {
// Message responds to the bot hook on recieving messages. // 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. // 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. // Otherwise, the function returns false and the bot continues execution of other plugins.
func (p *NerdepediaPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *NerdepediaPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
lowerCase := strings.ToLower(message.Body) lowerCase := strings.ToLower(message.Body)
query := "" query := ""
if lowerCase == "may the force be with you" || lowerCase == "help me obi-wan" { if lowerCase == "may the force be with you" || lowerCase == "help me obi-wan" {
@ -81,7 +81,7 @@ func (p *NerdepediaPlugin) message(kind bot.Kind, message msg.Message, args ...i
} }
if description != "" && link != "" { if description != "" && link != "" {
p.bot.Send(bot.Message, message.Channel, fmt.Sprintf("%s (%s)", description, link)) p.bot.Send(c, bot.Message, message.Channel, fmt.Sprintf("%s (%s)", description, link))
return true return true
} }
} }
@ -90,7 +90,7 @@ func (p *NerdepediaPlugin) message(kind bot.Kind, message msg.Message, args ...i
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.
func (p *NerdepediaPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *NerdepediaPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.bot.Send(bot.Message, message.Channel, "nerd stuff") p.bot.Send(c, bot.Message, message.Channel, "nerd stuff")
return true return true
} }

View File

@ -3,6 +3,7 @@
package nerdepedia package nerdepedia
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -12,12 +13,12 @@ import (
"github.com/velour/catbase/bot/user" "github.com/velour/catbase/bot/user"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,

View File

@ -16,13 +16,13 @@ import (
) )
type PickerPlugin struct { type PickerPlugin struct {
Bot bot.Bot bot bot.Bot
} }
// NewPickerPlugin creates a new PickerPlugin with the Plugin interface // NewPickerPlugin creates a new PickerPlugin with the Plugin interface
func New(b bot.Bot) *PickerPlugin { func New(b bot.Bot) *PickerPlugin {
pp := &PickerPlugin{ pp := &PickerPlugin{
Bot: b, bot: b,
} }
b.Register(pp, bot.Message, pp.message) b.Register(pp, bot.Message, pp.message)
b.Register(pp, bot.Help, pp.help) b.Register(pp, bot.Help, pp.help)
@ -32,21 +32,21 @@ func New(b bot.Bot) *PickerPlugin {
// Message responds to the bot hook on recieving messages. // 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. // 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. // Otherwise, the function returns false and the bot continues execution of other plugins.
func (p *PickerPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *PickerPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if !strings.HasPrefix(message.Body, "pick") { if !strings.HasPrefix(message.Body, "pick") {
return false return false
} }
n, items, err := p.parse(message.Body) n, items, err := p.parse(message.Body)
if err != nil { if err != nil {
p.Bot.Send(bot.Message, message.Channel, err.Error()) p.bot.Send(c, bot.Message, message.Channel, err.Error())
return true return true
} }
if n == 1 { if n == 1 {
item := items[rand.Intn(len(items))] item := items[rand.Intn(len(items))]
out := fmt.Sprintf("I've chosen %q for you.", strings.TrimSpace(item)) out := fmt.Sprintf("I've chosen %q for you.", strings.TrimSpace(item))
p.Bot.Send(bot.Message, message.Channel, out) p.bot.Send(c, bot.Message, message.Channel, out)
return true return true
} }
@ -62,7 +62,7 @@ func (p *PickerPlugin) message(kind bot.Kind, message msg.Message, args ...inter
fmt.Fprintf(&b, ", %q", item) fmt.Fprintf(&b, ", %q", item)
} }
b.WriteString(" }") b.WriteString(" }")
p.Bot.Send(bot.Message, message.Channel, b.String()) p.bot.Send(c, bot.Message, message.Channel, b.String())
return true return true
} }
@ -111,7 +111,7 @@ func (p *PickerPlugin) parse(body string) (int, []string, error) {
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.
func (p *PickerPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *PickerPlugin) help(c bot.Connector, 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}\".") p.bot.Send(c, bot.Message, message.Channel, "Choose from a list of options. Try \"pick {a,b,c}\".")
return true return true
} }

View File

@ -3,6 +3,7 @@
package picker package picker
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -12,12 +13,12 @@ import (
"github.com/velour/catbase/bot/user" "github.com/velour/catbase/bot/user"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,

View File

@ -11,38 +11,38 @@ import (
) )
type ReactionPlugin struct { type ReactionPlugin struct {
Bot bot.Bot bot bot.Bot
Config *config.Config config *config.Config
} }
func New(b bot.Bot) *ReactionPlugin { func New(b bot.Bot) *ReactionPlugin {
rp := &ReactionPlugin{ rp := &ReactionPlugin{
Bot: b, bot: b,
Config: b.Config(), config: b.Config(),
} }
b.Register(rp, bot.Message, rp.message) b.Register(rp, bot.Message, rp.message)
return rp return rp
} }
func (p *ReactionPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *ReactionPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
harrass := false harrass := false
for _, nick := range p.Config.GetArray("Reaction.HarrassList", []string{}) { for _, nick := range p.config.GetArray("Reaction.HarrassList", []string{}) {
if message.User.Name == nick { if message.User.Name == nick {
harrass = true harrass = true
break break
} }
} }
chance := p.Config.GetFloat64("Reaction.GeneralChance", 0.01) chance := p.config.GetFloat64("Reaction.GeneralChance", 0.01)
negativeWeight := 1 negativeWeight := 1
if harrass { if harrass {
chance = p.Config.GetFloat64("Reaction.HarrassChance", 0.05) chance = p.config.GetFloat64("Reaction.HarrassChance", 0.05)
negativeWeight = p.Config.GetInt("Reaction.NegativeHarrassmentMultiplier", 2) negativeWeight = p.config.GetInt("Reaction.NegativeHarrassmentMultiplier", 2)
} }
if rand.Float64() < chance { if rand.Float64() < chance {
numPositiveReactions := len(p.Config.GetArray("Reaction.PositiveReactions", []string{})) numPositiveReactions := len(p.config.GetArray("Reaction.PositiveReactions", []string{}))
numNegativeReactions := len(p.Config.GetArray("Reaction.NegativeReactions", []string{})) numNegativeReactions := len(p.config.GetArray("Reaction.NegativeReactions", []string{}))
maxIndex := numPositiveReactions + numNegativeReactions*negativeWeight maxIndex := numPositiveReactions + numNegativeReactions*negativeWeight
@ -51,14 +51,14 @@ func (p *ReactionPlugin) message(kind bot.Kind, message msg.Message, args ...int
reaction := "" reaction := ""
if index < numPositiveReactions { if index < numPositiveReactions {
reaction = p.Config.GetArray("Reaction.PositiveReactions", []string{})[index] reaction = p.config.GetArray("Reaction.PositiveReactions", []string{})[index]
} else { } else {
index -= numPositiveReactions index -= numPositiveReactions
index %= numNegativeReactions index %= numNegativeReactions
reaction = p.Config.GetArray("Reaction.NegativeReactions", []string{})[index] reaction = p.config.GetArray("Reaction.NegativeReactions", []string{})[index]
} }
p.Bot.Send(bot.Reaction, message.Channel, reaction, message) p.bot.Send(c, bot.Reaction, message.Channel, reaction, message)
} }
return false return false

View File

@ -32,10 +32,10 @@ func New(b bot.Bot) *RememberPlugin {
return p return p
} }
func (p *RememberPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *RememberPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if strings.ToLower(message.Body) == "quote" && message.Command { if strings.ToLower(message.Body) == "quote" && message.Command {
q := p.randQuote() q := p.randQuote()
p.bot.Send(bot.Message, message.Channel, q) p.bot.Send(c, bot.Message, message.Channel, q)
// is it evil not to remember that the user said quote? // is it evil not to remember that the user said quote?
return true return true
@ -82,7 +82,7 @@ func (p *RememberPlugin) message(kind bot.Kind, message msg.Message, args ...int
} }
if err := fact.Save(p.db); err != nil { if err := fact.Save(p.db); err != nil {
log.Error().Err(err) log.Error().Err(err)
p.bot.Send(bot.Message, message.Channel, "Tell somebody I'm broke.") p.bot.Send(c, bot.Message, message.Channel, "Tell somebody I'm broke.")
} }
log.Info(). log.Info().
@ -92,13 +92,13 @@ func (p *RememberPlugin) message(kind bot.Kind, message msg.Message, args ...int
// sorry, not creative with names so we're reusing msg // sorry, not creative with names so we're reusing msg
msg = fmt.Sprintf("Okay, %s, remembering '%s'.", msg = fmt.Sprintf("Okay, %s, remembering '%s'.",
message.User.Name, msg) message.User.Name, msg)
p.bot.Send(bot.Message, message.Channel, msg) p.bot.Send(c, bot.Message, message.Channel, msg)
p.recordMsg(message) p.recordMsg(message)
return true return true
} }
} }
p.bot.Send(bot.Message, message.Channel, "Sorry, I don't know that phrase.") p.bot.Send(c, bot.Message, message.Channel, "Sorry, I don't know that phrase.")
p.recordMsg(message) p.recordMsg(message)
return true return true
} }
@ -107,13 +107,13 @@ func (p *RememberPlugin) message(kind bot.Kind, message msg.Message, args ...int
return false return false
} }
func (p *RememberPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *RememberPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
msg := "remember will let you quote your idiot friends. Just type " + msg := "remember will let you quote your idiot friends. Just type " +
"!remember <nick> <snippet> to remember what they said. Snippet can " + "!remember <nick> <snippet> to remember what they said. Snippet can " +
"be any part of their message. Later on, you can ask for a random " + "be any part of their message. Later on, you can ask for a random " +
"!quote." "!quote."
p.bot.Send(bot.Message, message.Channel, msg) p.bot.Send(c, bot.Message, message.Channel, msg)
return true return true
} }

View File

@ -1,6 +1,7 @@
package remember package remember
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -43,7 +44,7 @@ func TestCornerCaseBug(t *testing.T) {
p, _, mb := makePlugin(t) p, _, mb := makePlugin(t)
for _, m := range msgs { for _, m := range msgs {
p.message(bot.Message, m) p.message(&cli.CliPlugin{}, bot.Message, m)
} }
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
assert.Contains(t, mb.Messages[0], "horse dick") assert.Contains(t, mb.Messages[0], "horse dick")

View File

@ -27,7 +27,7 @@ const (
) )
type ReminderPlugin struct { type ReminderPlugin struct {
Bot bot.Bot bot bot.Bot
db *sqlx.DB db *sqlx.DB
mutex *sync.Mutex mutex *sync.Mutex
timer *time.Timer timer *time.Timer
@ -65,7 +65,7 @@ func New(b bot.Bot) *ReminderPlugin {
w.Add(common.All...) w.Add(common.All...)
plugin := &ReminderPlugin{ plugin := &ReminderPlugin{
Bot: b, bot: b,
db: b.DB(), db: b.DB(),
mutex: &sync.Mutex{}, mutex: &sync.Mutex{},
timer: timer, timer: timer,
@ -75,7 +75,7 @@ func New(b bot.Bot) *ReminderPlugin {
plugin.queueUpNextReminder() plugin.queueUpNextReminder()
go reminderer(plugin) go reminderer(b.DefaultConnector(), plugin)
b.Register(plugin, bot.Message, plugin.message) b.Register(plugin, bot.Message, plugin.message)
b.Register(plugin, bot.Help, plugin.help) b.Register(plugin, bot.Help, plugin.help)
@ -83,7 +83,7 @@ func New(b bot.Bot) *ReminderPlugin {
return plugin return plugin
} }
func (p *ReminderPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *ReminderPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
channel := message.Channel channel := message.Channel
from := message.User.Name from := message.User.Name
@ -109,7 +109,7 @@ func (p *ReminderPlugin) message(kind bot.Kind, message msg.Message, args ...int
dur, err = time.ParseDuration(parts[3]) dur, err = time.ParseDuration(parts[3])
if err != nil { if err != nil {
p.Bot.Send(bot.Message, channel, "Easy cowboy, not sure I can parse that duration. Try something like '1.5h' or '2h45m'.") p.bot.Send(c, bot.Message, channel, "Easy cowboy, not sure I can parse that duration. Try something like '1.5h' or '2h45m'.")
return true return true
} }
@ -137,7 +137,7 @@ func (p *ReminderPlugin) message(kind bot.Kind, message msg.Message, args ...int
dur2, err = time.ParseDuration(parts[5]) dur2, err = time.ParseDuration(parts[5])
if err != nil { if err != nil {
log.Error().Err(err) log.Error().Err(err)
p.Bot.Send(bot.Message, channel, "Easy cowboy, not sure I can parse that duration. Try something like '1.5h' or '2h45m'.") p.bot.Send(c, bot.Message, channel, "Easy cowboy, not sure I can parse that duration. Try something like '1.5h' or '2h45m'.")
return true return true
} }
@ -148,7 +148,7 @@ func (p *ReminderPlugin) message(kind bot.Kind, message msg.Message, args ...int
max := p.config.GetInt("Reminder.MaxBatchAdd", 10) max := p.config.GetInt("Reminder.MaxBatchAdd", 10)
for i := 0; when.Before(endTime); i++ { for i := 0; when.Before(endTime); i++ {
if i >= max { if i >= max {
p.Bot.Send(bot.Message, channel, "Easy cowboy, that's a lot of reminders. I'll add some of them.") p.bot.Send(c, bot.Message, channel, "Easy cowboy, that's a lot of reminders. I'll add some of them.")
doConfirm = false doConfirm = false
break break
} }
@ -165,14 +165,14 @@ func (p *ReminderPlugin) message(kind bot.Kind, message msg.Message, args ...int
when = when.Add(dur) when = when.Add(dur)
} }
} else { } else {
p.Bot.Send(bot.Message, channel, "Easy cowboy, not sure I comprehend what you're asking.") p.bot.Send(c, bot.Message, channel, "Easy cowboy, not sure I comprehend what you're asking.")
return true return true
} }
if doConfirm && from == who { if doConfirm && from == who {
p.Bot.Send(bot.Message, channel, fmt.Sprintf("Okay. I'll remind you.")) p.bot.Send(c, bot.Message, channel, fmt.Sprintf("Okay. I'll remind you."))
} else if doConfirm { } else if doConfirm {
p.Bot.Send(bot.Message, channel, fmt.Sprintf("Sure %s, I'll remind %s.", from, who)) p.bot.Send(c, bot.Message, channel, fmt.Sprintf("Sure %s, I'll remind %s.", from, who))
} }
p.queueUpNextReminder() p.queueUpNextReminder()
@ -192,22 +192,22 @@ func (p *ReminderPlugin) message(kind bot.Kind, message msg.Message, args ...int
} }
} }
if err != nil { if err != nil {
p.Bot.Send(bot.Message, channel, "listing failed.") p.bot.Send(c, bot.Message, channel, "listing failed.")
} else { } else {
p.Bot.Send(bot.Message, channel, response) p.bot.Send(c, bot.Message, channel, response)
} }
return true return true
} else if len(parts) == 3 && strings.ToLower(parts[0]) == "cancel" && strings.ToLower(parts[1]) == "reminder" { } else if len(parts) == 3 && strings.ToLower(parts[0]) == "cancel" && strings.ToLower(parts[1]) == "reminder" {
id, err := strconv.ParseInt(parts[2], 10, 64) id, err := strconv.ParseInt(parts[2], 10, 64)
if err != nil { if err != nil {
p.Bot.Send(bot.Message, channel, fmt.Sprintf("couldn't parse id: %s", parts[2])) p.bot.Send(c, bot.Message, channel, fmt.Sprintf("couldn't parse id: %s", parts[2]))
} else { } else {
err := p.deleteReminder(id) err := p.deleteReminder(id)
if err == nil { if err == nil {
p.Bot.Send(bot.Message, channel, fmt.Sprintf("successfully canceled reminder: %s", parts[2])) p.bot.Send(c, bot.Message, channel, fmt.Sprintf("successfully canceled reminder: %s", parts[2]))
} else { } else {
p.Bot.Send(bot.Message, channel, fmt.Sprintf("failed to find and cancel reminder: %s", parts[2])) p.bot.Send(c, bot.Message, channel, fmt.Sprintf("failed to find and cancel reminder: %s", parts[2]))
} }
} }
return true return true
@ -216,8 +216,8 @@ func (p *ReminderPlugin) message(kind bot.Kind, message msg.Message, args ...int
return false return false
} }
func (p *ReminderPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *ReminderPlugin) help(c bot.Connector, 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") p.bot.Send(c, 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 return true
} }
@ -351,7 +351,7 @@ func (p *ReminderPlugin) queueUpNextReminder() {
} }
} }
func reminderer(p *ReminderPlugin) { func reminderer(c bot.Connector, p *ReminderPlugin) {
for { for {
<-p.timer.C <-p.timer.C
@ -366,7 +366,7 @@ func reminderer(p *ReminderPlugin) {
message = fmt.Sprintf("Hey %s, %s wanted you to be reminded: %s", reminder.who, reminder.from, reminder.what) message = fmt.Sprintf("Hey %s, %s wanted you to be reminded: %s", reminder.who, reminder.from, reminder.what)
} }
p.Bot.Send(bot.Message, reminder.channel, message) p.bot.Send(c, bot.Message, reminder.channel, message)
if err := p.deleteReminder(reminder.id); err != nil { if err := p.deleteReminder(reminder.id); err != nil {
log.Error(). log.Error().

View File

@ -4,6 +4,7 @@ package reminder
import ( import (
"fmt" "fmt"
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -14,16 +15,16 @@ import (
"github.com/velour/catbase/bot/user" "github.com/velour/catbase/bot/user"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
return makeMessageBy(payload, "tester") return makeMessageBy(payload, "tester")
} }
func makeMessageBy(payload, by string) (bot.Kind, msg.Message) { func makeMessageBy(payload, by string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: by}, User: &user.User{Name: by},
Channel: "test", Channel: "test",
Body: payload, Body: payload,
@ -223,6 +224,6 @@ func TestLimitList(t *testing.T) {
func TestHelp(t *testing.T) { func TestHelp(t *testing.T) {
c, mb := setup(t) c, mb := setup(t)
assert.NotNil(t, c) assert.NotNil(t, c)
c.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) c.help(&cli.CliPlugin{}, bot.Help, msg.Message{Channel: "channel"}, []string{})
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
} }

View File

@ -20,7 +20,7 @@ const (
) )
type RPGPlugin struct { type RPGPlugin struct {
Bot bot.Bot bot bot.Bot
listenFor map[string]*board listenFor map[string]*board
} }
@ -99,7 +99,7 @@ func (b *board) checkAndMove(dx, dy int) int {
func New(b bot.Bot) *RPGPlugin { func New(b bot.Bot) *RPGPlugin {
rpg := &RPGPlugin{ rpg := &RPGPlugin{
Bot: b, bot: b,
listenFor: map[string]*board{}, listenFor: map[string]*board{},
} }
b.Register(rpg, bot.Message, rpg.message) b.Register(rpg, bot.Message, rpg.message)
@ -108,25 +108,25 @@ func New(b bot.Bot) *RPGPlugin {
return rpg return rpg
} }
func (p *RPGPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *RPGPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if strings.ToLower(message.Body) == "start rpg" { if strings.ToLower(message.Body) == "start rpg" {
b := NewRandomBoard() b := NewRandomBoard()
ts, _ := p.Bot.Send(bot.Message, message.Channel, b.toMessageString()) ts, _ := p.bot.Send(c, bot.Message, message.Channel, b.toMessageString())
p.listenFor[ts] = b p.listenFor[ts] = b
p.Bot.Send(bot.Reply, message.Channel, "Over here.", ts) p.bot.Send(c, bot.Reply, message.Channel, "Over here.", ts)
return true return true
} }
return false return false
} }
func (p *RPGPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *RPGPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.Bot.Send(bot.Message, message.Channel, "Go find a walkthrough or something.") p.bot.Send(c, bot.Message, message.Channel, "Go find a walkthrough or something.")
return true return true
} }
func (p *RPGPlugin) replyMessage(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *RPGPlugin) replyMessage(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
identifier := args[0].(string) identifier := args[0].(string)
if strings.ToLower(message.User.Name) != strings.ToLower(p.Bot.Config().Get("Nick", "bot")) { if strings.ToLower(message.User.Name) != strings.ToLower(p.bot.Config().Get("Nick", "bot")) {
if b, ok := p.listenFor[identifier]; ok { if b, ok := p.listenFor[identifier]; ok {
var res int var res int
@ -145,12 +145,12 @@ func (p *RPGPlugin) replyMessage(kind bot.Kind, message msg.Message, args ...int
switch res { switch res {
case OK: case OK:
p.Bot.Send(bot.Edit, message.Channel, b.toMessageString(), identifier) p.bot.Send(c, bot.Edit, message.Channel, b.toMessageString(), identifier)
case WIN: case WIN:
p.Bot.Send(bot.Edit, message.Channel, b.toMessageString(), identifier) p.bot.Send(c, bot.Edit, message.Channel, b.toMessageString(), identifier)
p.Bot.Send(bot.Reply, message.Channel, "congratulations, you beat the easiest level imaginable.", identifier) p.bot.Send(c, bot.Reply, message.Channel, "congratulations, you beat the easiest level imaginable.", identifier)
case INVALID: case INVALID:
p.Bot.Send(bot.Reply, message.Channel, fmt.Sprintf("you can't move %s", message.Body), identifier) p.bot.Send(c, bot.Reply, message.Channel, fmt.Sprintf("you can't move %s", message.Body), identifier)
} }
return true return true
} }

View File

@ -1,3 +1 @@
package rpgORdie package rpgORdie
import ()

View File

@ -12,7 +12,7 @@ import (
) )
type RSSPlugin struct { type RSSPlugin struct {
Bot bot.Bot bot bot.Bot
cache map[string]*cacheItem cache map[string]*cacheItem
shelfLife time.Duration shelfLife time.Duration
maxLines int maxLines int
@ -51,7 +51,7 @@ func (c *cacheItem) getCurrentPage(maxLines int) string {
func New(b bot.Bot) *RSSPlugin { func New(b bot.Bot) *RSSPlugin {
rss := &RSSPlugin{ rss := &RSSPlugin{
Bot: b, bot: b,
cache: map[string]*cacheItem{}, cache: map[string]*cacheItem{},
shelfLife: time.Minute * time.Duration(b.Config().GetInt("rss.shelfLife", 20)), shelfLife: time.Minute * time.Duration(b.Config().GetInt("rss.shelfLife", 20)),
maxLines: b.Config().GetInt("rss.maxLines", 5), maxLines: b.Config().GetInt("rss.maxLines", 5),
@ -61,19 +61,19 @@ func New(b bot.Bot) *RSSPlugin {
return rss return rss
} }
func (p *RSSPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *RSSPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
tokens := strings.Fields(message.Body) tokens := strings.Fields(message.Body)
numTokens := len(tokens) numTokens := len(tokens)
if numTokens == 2 && strings.ToLower(tokens[0]) == "rss" { if numTokens == 2 && strings.ToLower(tokens[0]) == "rss" {
if item, ok := p.cache[strings.ToLower(tokens[1])]; ok && time.Now().Before(item.expiration) { if item, ok := p.cache[strings.ToLower(tokens[1])]; ok && time.Now().Before(item.expiration) {
p.Bot.Send(bot.Message, message.Channel, item.getCurrentPage(p.maxLines)) p.bot.Send(c, bot.Message, message.Channel, item.getCurrentPage(p.maxLines))
return true return true
} else { } else {
fp := gofeed.NewParser() fp := gofeed.NewParser()
feed, err := fp.ParseURL(tokens[1]) feed, err := fp.ParseURL(tokens[1])
if err != nil { if err != nil {
p.Bot.Send(bot.Message, message.Channel, fmt.Sprintf("RSS error: %s", err.Error())) p.bot.Send(c, bot.Message, message.Channel, fmt.Sprintf("RSS error: %s", err.Error()))
return true return true
} }
item := &cacheItem{ item := &cacheItem{
@ -89,7 +89,7 @@ func (p *RSSPlugin) message(kind bot.Kind, message msg.Message, args ...interfac
p.cache[strings.ToLower(tokens[1])] = item p.cache[strings.ToLower(tokens[1])] = item
p.Bot.Send(bot.Message, message.Channel, item.getCurrentPage(p.maxLines)) p.bot.Send(c, bot.Message, message.Channel, item.getCurrentPage(p.maxLines))
return true return true
} }
} }
@ -98,7 +98,7 @@ func (p *RSSPlugin) message(kind bot.Kind, message msg.Message, args ...interfac
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.
func (p *RSSPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *RSSPlugin) help(c bot.Connector, 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'") p.bot.Send(c, bot.Message, message.Channel, "try '!rss http://rss.cnn.com/rss/edition.rss'")
return true return true
} }

View File

@ -2,6 +2,7 @@ package rss
import ( import (
"fmt" "fmt"
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -11,12 +12,12 @@ import (
"github.com/velour/catbase/bot/user" "github.com/velour/catbase/bot/user"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,

View File

@ -19,7 +19,7 @@ const (
) )
type SisyphusPlugin struct { type SisyphusPlugin struct {
Bot bot.Bot bot bot.Bot
listenFor map[string]*game listenFor map[string]*game
} }
@ -38,7 +38,7 @@ type game struct {
nextAns int nextAns int
} }
func NewRandomGame(b bot.Bot, channel, who string) *game { func NewRandomGame(c bot.Connector, b bot.Bot, channel, who string) *game {
size := rand.Intn(9) + 2 size := rand.Intn(9) + 2
g := game{ g := game{
channel: channel, channel: channel,
@ -48,32 +48,32 @@ func NewRandomGame(b bot.Bot, channel, who string) *game {
size: size, size: size,
current: size / 2, current: size / 2,
} }
g.id, _ = b.Send(bot.Message, channel, g.toMessageString()) g.id, _ = b.Send(c, bot.Message, channel, g.toMessageString())
g.schedulePush() g.schedulePush(c)
g.scheduleDecrement() g.scheduleDecrement(c)
return &g return &g
} }
func (g *game) scheduleDecrement() { func (g *game) scheduleDecrement(c bot.Connector) {
if g.timers[0] != nil { if g.timers[0] != nil {
g.timers[0].Stop() g.timers[0].Stop()
} }
minDec := g.bot.Config().GetInt("Sisyphus.MinDecrement", 10) minDec := g.bot.Config().GetInt("Sisyphus.MinDecrement", 10)
maxDec := g.bot.Config().GetInt("Sisyphus.MaxDecrement", 30) maxDec := g.bot.Config().GetInt("Sisyphus.MaxDecrement", 30)
g.nextDec = time.Now().Add(time.Duration((minDec + rand.Intn(maxDec))) * time.Minute) g.nextDec = time.Now().Add(time.Duration(minDec+rand.Intn(maxDec)) * time.Minute)
go func() { go func() {
t := time.NewTimer(g.nextDec.Sub(time.Now())) t := time.NewTimer(g.nextDec.Sub(time.Now()))
g.timers[0] = t g.timers[0] = t
select { select {
case <-t.C: case <-t.C:
g.handleDecrement() g.handleDecrement(c)
} }
}() }()
} }
func (g *game) schedulePush() { func (g *game) schedulePush(c bot.Connector) {
if g.timers[1] != nil { if g.timers[1] != nil {
g.timers[1].Stop() g.timers[1].Stop()
} }
@ -85,7 +85,7 @@ func (g *game) schedulePush() {
g.timers[1] = t g.timers[1] = t
select { select {
case <-t.C: case <-t.C:
g.handleNotify() g.handleNotify(c)
} }
}() }()
} }
@ -97,21 +97,21 @@ func (g *game) endGame() {
g.ended = true g.ended = true
} }
func (g *game) handleDecrement() { func (g *game) handleDecrement(c bot.Connector) {
g.current++ g.current++
g.bot.Send(bot.Edit, g.channel, g.toMessageString(), g.id) g.bot.Send(c, bot.Edit, g.channel, g.toMessageString(), g.id)
if g.current > g.size-2 { if g.current > g.size-2 {
g.bot.Send(bot.Reply, g.channel, "you lose", g.id) g.bot.Send(c, 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)) msg := fmt.Sprintf("%s just lost the game after %s", g.who, time.Now().Sub(g.start))
g.bot.Send(bot.Message, g.channel, msg) g.bot.Send(c, bot.Message, g.channel, msg)
g.endGame() g.endGame()
} else { } else {
g.scheduleDecrement() g.scheduleDecrement(c)
} }
} }
func (g *game) handleNotify() { func (g *game) handleNotify(c bot.Connector) {
g.bot.Send(bot.Reply, g.channel, "You can push now.\n"+g.generateQuestion(), g.id) g.bot.Send(c, bot.Reply, g.channel, "You can push now.\n"+g.generateQuestion(), g.id)
} }
func (g *game) generateQuestion() string { func (g *game) generateQuestion() string {
@ -164,7 +164,7 @@ func (g *game) toMessageString() string {
func New(b bot.Bot) *SisyphusPlugin { func New(b bot.Bot) *SisyphusPlugin {
sp := &SisyphusPlugin{ sp := &SisyphusPlugin{
Bot: b, bot: b,
listenFor: map[string]*game{}, listenFor: map[string]*game{},
} }
b.Register(sp, bot.Message, sp.message) b.Register(sp, bot.Message, sp.message)
@ -173,24 +173,24 @@ func New(b bot.Bot) *SisyphusPlugin {
return sp return sp
} }
func (p *SisyphusPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *SisyphusPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if strings.ToLower(message.Body) == "start sisyphus" { if strings.ToLower(message.Body) == "start sisyphus" {
b := NewRandomGame(p.Bot, message.Channel, message.User.Name) b := NewRandomGame(c, p.bot, message.Channel, message.User.Name)
p.listenFor[b.id] = b p.listenFor[b.id] = b
p.Bot.Send(bot.Reply, message.Channel, "Over here.", b.id) p.bot.Send(c, bot.Reply, message.Channel, "Over here.", b.id)
return true return true
} }
return false return false
} }
func (p *SisyphusPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *SisyphusPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.Bot.Send(bot.Message, message.Channel, "https://en.wikipedia.org/wiki/Sisyphus") p.bot.Send(c, bot.Message, message.Channel, "https://en.wikipedia.org/wiki/Sisyphus")
return true return true
} }
func (p *SisyphusPlugin) replyMessage(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *SisyphusPlugin) replyMessage(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
identifier := args[0].(string) identifier := args[0].(string)
if strings.ToLower(message.User.Name) != strings.ToLower(p.Bot.Config().Get("Nick", "bot")) { if strings.ToLower(message.User.Name) != strings.ToLower(p.bot.Config().Get("Nick", "bot")) {
if g, ok := p.listenFor[identifier]; ok { if g, ok := p.listenFor[identifier]; ok {
log.Debug().Msgf("got message on %s: %+v", identifier, message) log.Debug().Msgf("got message on %s: %+v", identifier, message)
@ -206,18 +206,18 @@ func (p *SisyphusPlugin) replyMessage(kind bot.Kind, message msg.Message, args .
if time.Now().After(g.nextPush) { if time.Now().After(g.nextPush) {
if g.checkAnswer(message.Body) { if g.checkAnswer(message.Body) {
p.Bot.Send(bot.Edit, message.Channel, g.toMessageString(), identifier) p.bot.Send(c, bot.Edit, message.Channel, g.toMessageString(), identifier)
g.schedulePush() g.schedulePush(c)
msg := fmt.Sprintf("Ok. You can push again in %s", g.nextPush.Sub(time.Now())) msg := fmt.Sprintf("Ok. You can push again in %s", g.nextPush.Sub(time.Now()))
p.Bot.Send(bot.Reply, message.Channel, msg, identifier) p.bot.Send(c, bot.Reply, message.Channel, msg, identifier)
} else { } else {
p.Bot.Send(bot.Reply, message.Channel, "you lose", identifier) p.bot.Send(c, 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)) msg := fmt.Sprintf("%s just lost the sisyphus game after %s", g.who, time.Now().Sub(g.start))
p.Bot.Send(bot.Message, message.Channel, msg) p.bot.Send(c, bot.Message, message.Channel, msg)
g.endGame() g.endGame()
} }
} else { } else {
p.Bot.Send(bot.Reply, message.Channel, "you cannot push yet", identifier) p.bot.Send(c, bot.Reply, message.Channel, "you cannot push yet", identifier)
} }
return true return true
} }

View File

@ -17,7 +17,7 @@ import (
"github.com/velour/catbase/config" "github.com/velour/catbase/config"
) )
var goatse []string = []string{ var goatse = []string{
"```* g o a t s e x * g o a t s e x * g o a t s e x *", "```* g o a t s e x * g o a t s e x * g o a t s e x *",
"g g", "g g",
"o / \\ \\ / \\ o", "o / \\ \\ / \\ o",
@ -46,23 +46,23 @@ var goatse []string = []string{
} }
type TalkerPlugin struct { type TalkerPlugin struct {
Bot bot.Bot bot bot.Bot
config *config.Config config *config.Config
sayings []string sayings []string
} }
func New(b bot.Bot) *TalkerPlugin { func New(b bot.Bot) *TalkerPlugin {
tp := &TalkerPlugin{ tp := &TalkerPlugin{
Bot: b, bot: b,
config: b.Config(), config: b.Config(),
} }
b.Register(tp, bot.Message, tp.message) b.Register(tp, bot.Message, tp.message)
b.Register(tp, bot.Help, tp.help) b.Register(tp, bot.Help, tp.help)
tp.registerWeb() tp.registerWeb(b.DefaultConnector())
return tp return tp
} }
func (p *TalkerPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *TalkerPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
channel := message.Channel channel := message.Channel
body := message.Body body := message.Body
lowermessage := strings.ToLower(body) lowermessage := strings.ToLower(body)
@ -70,24 +70,24 @@ func (p *TalkerPlugin) message(kind bot.Kind, message msg.Message, args ...inter
if message.Command && strings.HasPrefix(lowermessage, "cowsay") { if message.Command && strings.HasPrefix(lowermessage, "cowsay") {
msg, err := p.cowSay(strings.TrimPrefix(message.Body, "cowsay ")) msg, err := p.cowSay(strings.TrimPrefix(message.Body, "cowsay "))
if err != nil { if err != nil {
p.Bot.Send(bot.Message, channel, "Error running cowsay: %s", err) p.bot.Send(c, bot.Message, channel, "Error running cowsay: %s", err)
return true return true
} }
p.Bot.Send(bot.Message, channel, msg) p.bot.Send(c, bot.Message, channel, msg)
return true return true
} }
if message.Command && strings.HasPrefix(lowermessage, "list cows") { if message.Command && strings.HasPrefix(lowermessage, "list cows") {
cows := p.allCows() cows := p.allCows()
m := fmt.Sprintf("Cows: %s", strings.Join(cows, ", ")) m := fmt.Sprintf("Cows: %s", strings.Join(cows, ", "))
p.Bot.Send(bot.Message, channel, m) p.bot.Send(c, bot.Message, channel, m)
return true return true
} }
// TODO: This ought to be space split afterwards to remove any punctuation // TODO: This ought to be space split afterwards to remove any punctuation
if message.Command && strings.HasPrefix(lowermessage, "say") { if message.Command && strings.HasPrefix(lowermessage, "say") {
msg := strings.TrimSpace(body[3:]) msg := strings.TrimSpace(body[3:])
p.Bot.Send(bot.Message, channel, msg) p.bot.Send(c, bot.Message, channel, msg)
return true return true
} }
@ -103,15 +103,15 @@ func (p *TalkerPlugin) message(kind bot.Kind, message msg.Message, args ...inter
line = strings.Replace(line, "{nick}", nick, 1) line = strings.Replace(line, "{nick}", nick, 1)
output += line + "\n" output += line + "\n"
} }
p.Bot.Send(bot.Message, channel, output) p.bot.Send(c, bot.Message, channel, output)
return true return true
} }
return false return false
} }
func (p *TalkerPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *TalkerPlugin) help(c bot.Connector, 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!") p.bot.Send(c, bot.Message, message.Channel, "Hi, this is talker. I like to talk about FredFelps!")
return true return true
} }
@ -170,7 +170,7 @@ func (p *TalkerPlugin) allCows() []string {
return cows return cows
} }
func (p *TalkerPlugin) registerWeb() { func (p *TalkerPlugin) registerWeb(c bot.Connector) {
http.HandleFunc("/slash/cowsay", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/slash/cowsay", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
log.Debug().Msgf("Cowsay:\n%+v", r.PostForm.Get("text")) log.Debug().Msgf("Cowsay:\n%+v", r.PostForm.Get("text"))
@ -178,10 +178,10 @@ func (p *TalkerPlugin) registerWeb() {
log.Debug().Msgf("channel: %s", channel) log.Debug().Msgf("channel: %s", channel)
msg, err := p.cowSay(r.PostForm.Get("text")) msg, err := p.cowSay(r.PostForm.Get("text"))
if err != nil { if err != nil {
p.Bot.Send(bot.Message, channel, fmt.Sprintf("Error running cowsay: %s", err)) p.bot.Send(c, bot.Message, channel, fmt.Sprintf("Error running cowsay: %s", err))
return return
} }
p.Bot.Send(bot.Message, channel, msg) p.bot.Send(c, bot.Message, channel, msg)
w.WriteHeader(200) w.WriteHeader(200)
}) })
} }

View File

@ -3,6 +3,7 @@
package talker package talker
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -12,12 +13,12 @@ import (
"github.com/velour/catbase/bot/user" "github.com/velour/catbase/bot/user"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,
@ -78,6 +79,6 @@ func TestHelp(t *testing.T) {
mb := bot.NewMockBot() mb := bot.NewMockBot()
c := New(mb) c := New(mb)
assert.NotNil(t, c) assert.NotNil(t, c)
c.help(bot.Help, msg.Message{Channel: "channel"}, []string{}) c.help(&cli.CliPlugin{}, bot.Help, msg.Message{Channel: "channel"}, []string{})
assert.Len(t, mb.Messages, 1) assert.Len(t, mb.Messages, 1)
} }

View File

@ -21,20 +21,20 @@ func New(b bot.Bot) *TellPlugin {
return tp return tp
} }
func (t *TellPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (t *TellPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
if strings.HasPrefix(strings.ToLower(message.Body), "tell") { if strings.HasPrefix(strings.ToLower(message.Body), "tell") {
parts := strings.Split(message.Body, " ") parts := strings.Split(message.Body, " ")
target := strings.ToLower(parts[1]) target := strings.ToLower(parts[1])
newMessage := strings.Join(parts[2:], " ") newMessage := strings.Join(parts[2:], " ")
newMessage = fmt.Sprintf("Hey, %s. %s said: %s", target, message.User.Name, newMessage) newMessage = fmt.Sprintf("Hey, %s. %s said: %s", target, message.User.Name, newMessage)
t.users[target] = append(t.users[target], newMessage) t.users[target] = append(t.users[target], newMessage)
t.b.Send(bot.Message, message.Channel, fmt.Sprintf("Okay. I'll tell %s.", target)) t.b.Send(c, bot.Message, message.Channel, fmt.Sprintf("Okay. I'll tell %s.", target))
return true return true
} }
uname := strings.ToLower(message.User.Name) uname := strings.ToLower(message.User.Name)
if msg, ok := t.users[uname]; ok && len(msg) > 0 { if msg, ok := t.users[uname]; ok && len(msg) > 0 {
for _, m := range msg { for _, m := range msg {
t.b.Send(bot.Message, message.Channel, string(m)) t.b.Send(c, bot.Message, message.Channel, string(m))
} }
t.users[uname] = []string{} t.users[uname] = []string{}
return true return true

View File

@ -38,11 +38,11 @@ func New(b bot.Bot) *TLDRPlugin {
return plugin return plugin
} }
func (p *TLDRPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *TLDRPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
timeLimit := time.Duration(p.bot.Config().GetInt("TLDR.HourLimit", 1)) timeLimit := time.Duration(p.bot.Config().GetInt("TLDR.HourLimit", 1))
lowercaseMessage := strings.ToLower(message.Body) lowercaseMessage := strings.ToLower(message.Body)
if lowercaseMessage == "tl;dr" && p.lastRequest.After(time.Now().Add(-timeLimit*time.Hour)) { if lowercaseMessage == "tl;dr" && p.lastRequest.After(time.Now().Add(-timeLimit*time.Hour)) {
p.bot.Send(bot.Message, message.Channel, "Slow down, cowboy. Read that tiny backlog.") p.bot.Send(c, bot.Message, message.Channel, "Slow down, cowboy. Read that tiny backlog.")
return true return true
} else if lowercaseMessage == "tl;dr" { } else if lowercaseMessage == "tl;dr" {
p.lastRequest = time.Now() p.lastRequest = time.Now()
@ -114,7 +114,7 @@ func (p *TLDRPlugin) message(kind bot.Kind, message msg.Message, args ...interfa
} }
p.bot.Send(bot.Message, message.Channel, response) p.bot.Send(c, bot.Message, message.Channel, response)
return true return true
} }
@ -162,8 +162,8 @@ func (p *TLDRPlugin) getTopics() []string {
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.
func (p *TLDRPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *TLDRPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.bot.Send(bot.Message, message.Channel, "tl;dr") p.bot.Send(c, bot.Message, message.Channel, "tl;dr")
return true return true
} }

View File

@ -1,6 +1,7 @@
package tldr package tldr
import ( import (
"github.com/velour/catbase/plugins/cli"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@ -19,12 +20,12 @@ func init() {
log.Logger = log.Logger.Output(zerolog.ConsoleWriter{Out: os.Stderr}) log.Logger = log.Logger.Output(zerolog.ConsoleWriter{Out: os.Stderr})
} }
func makeMessageBy(payload, by string) (bot.Kind, msg.Message) { func makeMessageBy(payload, by string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: by}, User: &user.User{Name: by},
Channel: "test", Channel: "test",
Body: payload, Body: payload,
@ -32,7 +33,7 @@ func makeMessageBy(payload, by string) (bot.Kind, msg.Message) {
} }
} }
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
return makeMessageBy(payload, "tester") return makeMessageBy(payload, "tester")
} }

View File

@ -24,7 +24,7 @@ const (
) )
type TwitchPlugin struct { type TwitchPlugin struct {
Bot bot.Bot bot bot.Bot
config *config.Config config *config.Config
twitchList map[string]*Twitcher twitchList map[string]*Twitcher
} }
@ -60,7 +60,7 @@ type stream struct {
func New(b bot.Bot) *TwitchPlugin { func New(b bot.Bot) *TwitchPlugin {
p := &TwitchPlugin{ p := &TwitchPlugin{
Bot: b, bot: b,
config: b.Config(), config: b.Config(),
twitchList: map[string]*Twitcher{}, twitchList: map[string]*Twitcher{},
} }
@ -74,7 +74,7 @@ func New(b bot.Bot) *TwitchPlugin {
} }
} }
} }
go p.twitchLoop(ch) go p.twitchLoop(b.DefaultConnector(), ch)
} }
b.Register(p, bot.Message, p.message) b.Register(p, bot.Message, p.message)
@ -119,14 +119,14 @@ func (p *TwitchPlugin) serveStreaming(w http.ResponseWriter, r *http.Request) {
} }
} }
func (p *TwitchPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *TwitchPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
body := strings.ToLower(message.Body) body := strings.ToLower(message.Body)
if body == "twitch status" { if body == "twitch status" {
channel := message.Channel channel := message.Channel
if users := p.config.GetArray("Twitch."+channel+".Users", []string{}); len(users) > 0 { if users := p.config.GetArray("Twitch."+channel+".Users", []string{}); len(users) > 0 {
for _, twitcherName := range users { for _, twitcherName := range users {
if _, ok := p.twitchList[twitcherName]; ok { if _, ok := p.twitchList[twitcherName]; ok {
p.checkTwitch(channel, p.twitchList[twitcherName], true) p.checkTwitch(c, channel, p.twitchList[twitcherName], true)
} }
} }
} }
@ -140,18 +140,18 @@ func (p *TwitchPlugin) message(kind bot.Kind, message msg.Message, args ...inter
return false return false
} }
func (p *TwitchPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *TwitchPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
msg := "You can set the templates for streams with\n" msg := "You can set the templates for streams with\n"
msg += fmt.Sprintf("twitch.istpl (default: %s)\n", isStreamingTplFallback) msg += fmt.Sprintf("twitch.istpl (default: %s)\n", isStreamingTplFallback)
msg += fmt.Sprintf("twitch.nottpl (default: %s)\n", notStreamingTplFallback) msg += fmt.Sprintf("twitch.nottpl (default: %s)\n", notStreamingTplFallback)
msg += fmt.Sprintf("twitch.stoppedtpl (default: %s)\n", stoppedStreamingTplFallback) msg += fmt.Sprintf("twitch.stoppedtpl (default: %s)\n", stoppedStreamingTplFallback)
msg += "You can reset all messages with `!reset twitch`" msg += "You can reset all messages with `!reset twitch`"
msg += "And you can ask who is streaming with `!twitch status`" msg += "And you can ask who is streaming with `!twitch status`"
p.Bot.Send(bot.Message, message.Channel, msg) p.bot.Send(c, bot.Message, message.Channel, msg)
return true return true
} }
func (p *TwitchPlugin) twitchLoop(channel string) { func (p *TwitchPlugin) twitchLoop(c bot.Connector, channel string) {
frequency := p.config.GetInt("Twitch.Freq", 60) frequency := p.config.GetInt("Twitch.Freq", 60)
if p.config.Get("twitch.clientid", "") == "" || p.config.Get("twitch.authorization", "") == "" { if p.config.Get("twitch.clientid", "") == "" || p.config.Get("twitch.authorization", "") == "" {
log.Info().Msgf("Disabling twitch autochecking.") log.Info().Msgf("Disabling twitch autochecking.")
@ -164,7 +164,7 @@ func (p *TwitchPlugin) twitchLoop(channel string) {
time.Sleep(time.Duration(frequency) * time.Second) time.Sleep(time.Duration(frequency) * time.Second)
for _, twitcherName := range p.config.GetArray("Twitch."+channel+".Users", []string{}) { for _, twitcherName := range p.config.GetArray("Twitch."+channel+".Users", []string{}) {
p.checkTwitch(channel, p.twitchList[twitcherName], false) p.checkTwitch(c, channel, p.twitchList[twitcherName], false)
} }
} }
} }
@ -197,7 +197,7 @@ errCase:
return []byte{}, false return []byte{}, false
} }
func (p *TwitchPlugin) checkTwitch(channel string, twitcher *Twitcher, alwaysPrintStatus bool) { func (p *TwitchPlugin) checkTwitch(c bot.Connector, channel string, twitcher *Twitcher, alwaysPrintStatus bool) {
baseURL, err := url.Parse("https://api.twitch.tv/helix/streams") baseURL, err := url.Parse("https://api.twitch.tv/helix/streams")
if err != nil { if err != nil {
log.Error().Msg("Error parsing twitch stream URL") log.Error().Msg("Error parsing twitch stream URL")
@ -255,31 +255,31 @@ func (p *TwitchPlugin) checkTwitch(channel string, twitcher *Twitcher, alwaysPri
t, err := template.New("notStreaming").Parse(notStreamingTpl) t, err := template.New("notStreaming").Parse(notStreamingTpl)
if err != nil { if err != nil {
log.Error().Err(err) log.Error().Err(err)
p.Bot.Send(bot.Message, channel, err) p.bot.Send(c, bot.Message, channel, err)
t = template.Must(template.New("notStreaming").Parse(notStreamingTplFallback)) t = template.Must(template.New("notStreaming").Parse(notStreamingTplFallback))
} }
t.Execute(&buf, info) t.Execute(&buf, info)
p.Bot.Send(bot.Message, channel, buf.String()) p.bot.Send(c, bot.Message, channel, buf.String())
} else { } else {
t, err := template.New("isStreaming").Parse(isStreamingTpl) t, err := template.New("isStreaming").Parse(isStreamingTpl)
if err != nil { if err != nil {
log.Error().Err(err) log.Error().Err(err)
p.Bot.Send(bot.Message, channel, err) p.bot.Send(c, bot.Message, channel, err)
t = template.Must(template.New("isStreaming").Parse(isStreamingTplFallback)) t = template.Must(template.New("isStreaming").Parse(isStreamingTplFallback))
} }
t.Execute(&buf, info) t.Execute(&buf, info)
p.Bot.Send(bot.Message, channel, buf.String()) p.bot.Send(c, bot.Message, channel, buf.String())
} }
} else if gameID == "" { } else if gameID == "" {
if twitcher.gameID != "" { if twitcher.gameID != "" {
t, err := template.New("stoppedStreaming").Parse(stoppedStreamingTpl) t, err := template.New("stoppedStreaming").Parse(stoppedStreamingTpl)
if err != nil { if err != nil {
log.Error().Err(err) log.Error().Err(err)
p.Bot.Send(bot.Message, channel, err) p.bot.Send(c, bot.Message, channel, err)
t = template.Must(template.New("stoppedStreaming").Parse(stoppedStreamingTplFallback)) t = template.Must(template.New("stoppedStreaming").Parse(stoppedStreamingTplFallback))
} }
t.Execute(&buf, info) t.Execute(&buf, info)
p.Bot.Send(bot.Message, channel, buf.String()) p.bot.Send(c, bot.Message, channel, buf.String())
} }
twitcher.gameID = "" twitcher.gameID = ""
} else { } else {
@ -287,11 +287,11 @@ func (p *TwitchPlugin) checkTwitch(channel string, twitcher *Twitcher, alwaysPri
t, err := template.New("isStreaming").Parse(isStreamingTpl) t, err := template.New("isStreaming").Parse(isStreamingTpl)
if err != nil { if err != nil {
log.Error().Err(err) log.Error().Err(err)
p.Bot.Send(bot.Message, channel, err) p.bot.Send(c, bot.Message, channel, err)
t = template.Must(template.New("isStreaming").Parse(isStreamingTplFallback)) t = template.Must(template.New("isStreaming").Parse(isStreamingTplFallback))
} }
t.Execute(&buf, info) t.Execute(&buf, info)
p.Bot.Send(bot.Message, channel, buf.String()) p.bot.Send(c, bot.Message, channel, buf.String())
} }
twitcher.gameID = gameID twitcher.gameID = gameID
} }

View File

@ -3,6 +3,7 @@
package twitch package twitch
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -12,12 +13,12 @@ import (
"github.com/velour/catbase/bot/user" "github.com/velour/catbase/bot/user"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,

View File

@ -30,7 +30,7 @@ func New(b bot.Bot) *YourPlugin {
// Message responds to the bot hook on recieving messages. // 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. // 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. // Otherwise, the function returns false and the bot continues execution of other plugins.
func (p *YourPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *YourPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
maxLen := p.config.GetInt("your.maxlength", 140) maxLen := p.config.GetInt("your.maxlength", 140)
if len(message.Body) > maxLen { if len(message.Body) > maxLen {
return false return false
@ -46,14 +46,14 @@ func (p *YourPlugin) message(kind bot.Kind, message msg.Message, args ...interfa
} }
} }
if msg != message.Body { if msg != message.Body {
p.bot.Send(bot.Message, message.Channel, msg) p.bot.Send(c, bot.Message, message.Channel, msg)
return true return true
} }
return false return false
} }
// Help responds to help requests. Every plugin must implement a help function. // Help responds to help requests. Every plugin must implement a help function.
func (p *YourPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *YourPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.bot.Send(bot.Message, message.Channel, "Your corrects people's grammar.") p.bot.Send(c, bot.Message, message.Channel, "Your corrects people's grammar.")
return true return true
} }

View File

@ -3,6 +3,7 @@
package your package your
import ( import (
"github.com/velour/catbase/plugins/cli"
"strings" "strings"
"testing" "testing"
@ -12,12 +13,12 @@ import (
"github.com/velour/catbase/bot/user" "github.com/velour/catbase/bot/user"
) )
func makeMessage(payload string) (bot.Kind, msg.Message) { func makeMessage(payload string) (bot.Connector, bot.Kind, msg.Message) {
isCmd := strings.HasPrefix(payload, "!") isCmd := strings.HasPrefix(payload, "!")
if isCmd { if isCmd {
payload = payload[1:] payload = payload[1:]
} }
return bot.Message, msg.Message{ return &cli.CliPlugin{}, bot.Message, msg.Message{
User: &user.User{Name: "tester"}, User: &user.User{Name: "tester"},
Channel: "test", Channel: "test",
Body: payload, Body: payload,

View File

@ -37,7 +37,7 @@ func New(b bot.Bot) bot.Plugin {
return z return z
} }
func (p *ZorkPlugin) runZork(ch string) error { func (p *ZorkPlugin) runZork(c bot.Connector, ch string) error {
const importString = "github.com/velour/catbase/plugins/zork" const importString = "github.com/velour/catbase/plugins/zork"
pkg, err := build.Import(importString, "", build.FindOnly) pkg, err := build.Import(importString, "", build.FindOnly)
if err != nil { if err != nil {
@ -79,7 +79,7 @@ func (p *ZorkPlugin) runZork(ch string) error {
m := strings.Replace(s.Text(), ">", "", -1) m := strings.Replace(s.Text(), ">", "", -1)
m = strings.Replace(m, "\n", "\n>", -1) m = strings.Replace(m, "\n", "\n>", -1)
m = ">" + m + "\n" m = ">" + m + "\n"
p.bot.Send(bot.Message, ch, m) p.bot.Send(c, bot.Message, ch, m)
} }
}() }()
go func() { go func() {
@ -95,7 +95,7 @@ func (p *ZorkPlugin) runZork(ch string) error {
return nil return nil
} }
func (p *ZorkPlugin) message(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *ZorkPlugin) message(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
m := strings.ToLower(message.Body) m := strings.ToLower(message.Body)
log.Debug().Msgf("got message [%s]", m) log.Debug().Msgf("got message [%s]", m)
if ts := strings.Fields(m); len(ts) < 1 || ts[0] != "zork" { if ts := strings.Fields(m); len(ts) < 1 || ts[0] != "zork" {
@ -107,8 +107,8 @@ func (p *ZorkPlugin) message(kind bot.Kind, message msg.Message, args ...interfa
p.Lock() p.Lock()
defer p.Unlock() defer p.Unlock()
if p.zorks[ch] == nil { if p.zorks[ch] == nil {
if err := p.runZork(ch); err != nil { if err := p.runZork(c, ch); err != nil {
p.bot.Send(bot.Message, ch, "failed to run zork: "+err.Error()) p.bot.Send(c, bot.Message, ch, "failed to run zork: "+err.Error())
return true return true
} }
} }
@ -117,7 +117,7 @@ func (p *ZorkPlugin) message(kind bot.Kind, message msg.Message, args ...interfa
return true return true
} }
func (p *ZorkPlugin) help(kind bot.Kind, message msg.Message, args ...interface{}) bool { func (p *ZorkPlugin) help(c bot.Connector, kind bot.Kind, message msg.Message, args ...interface{}) bool {
p.bot.Send(bot.Message, message.Channel, "Play zork using 'zork <zork command>'.") p.bot.Send(c, bot.Message, message.Channel, "Play zork using 'zork <zork command>'.")
return true return true
} }