mirror of https://github.com/velour/catbase.git
bot: hook connectors up to events
This includes a full test of `admin`
This commit is contained in:
parent
d85c855d47
commit
82dcf410f2
14
bot/bot.go
14
bot/bot.go
|
@ -83,9 +83,7 @@ func New(config *config.Config, connector Connector) Bot {
|
||||||
addr := config.Get("HttpAddr", "127.0.0.1:1337")
|
addr := config.Get("HttpAddr", "127.0.0.1:1337")
|
||||||
go http.ListenAndServe(addr, nil)
|
go http.ListenAndServe(addr, nil)
|
||||||
|
|
||||||
connector.RegisterMessageReceived(bot.MsgReceived)
|
connector.RegisterEvent(bot.Receive)
|
||||||
connector.RegisterEventReceived(bot.EventReceived)
|
|
||||||
connector.RegisterReplyMessageReceived(bot.ReplyMsgReceived)
|
|
||||||
|
|
||||||
return bot
|
return bot
|
||||||
}
|
}
|
||||||
|
@ -249,10 +247,14 @@ func (b *bot) RegisterFilter(name string, f func(string) string) {
|
||||||
b.filters[name] = f
|
b.filters[name] = f
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a message to the connection
|
|
||||||
func (b *bot) Send(kind Kind, args ...interface{}) (error, string) { return nil, "" }
|
|
||||||
|
|
||||||
// Register a callback
|
// Register a callback
|
||||||
func (b *bot) Register(name string, kind Kind, cb Callback) {
|
func (b *bot) Register(name string, kind Kind, cb Callback) {
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
if _, ok := b.callbacks[name]; !ok {
|
||||||
|
b.callbacks[name] = make(map[Kind][]Callback)
|
||||||
|
}
|
||||||
|
if _, ok := b.callbacks[name][kind]; !ok {
|
||||||
|
b.callbacks[name][kind] = []Callback{}
|
||||||
|
}
|
||||||
b.callbacks[name][kind] = append(b.callbacks[name][kind], cb)
|
b.callbacks[name][kind] = append(b.callbacks[name][kind], cb)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,23 +17,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *bot) Receive(kind Kind, msg msg.Message, args ...interface{}) {
|
func (b *bot) Receive(kind Kind, msg msg.Message, args ...interface{}) {
|
||||||
panic("I don't know what to do here yet")
|
log.Println("Received event: ", msg)
|
||||||
}
|
|
||||||
|
|
||||||
// Handles incomming PRIVMSG requests
|
|
||||||
func (b *bot) MsgReceived(msg msg.Message) {
|
|
||||||
log.Println("Received message: ", msg)
|
|
||||||
|
|
||||||
// msg := b.buildMessage(client, inMsg)
|
// msg := b.buildMessage(client, inMsg)
|
||||||
// do need to look up user and fix it
|
// do need to look up user and fix it
|
||||||
if strings.HasPrefix(msg.Body, "help ") && msg.Command {
|
if kind == Message && strings.HasPrefix(msg.Body, "help ") && msg.Command {
|
||||||
parts := strings.Fields(strings.ToLower(msg.Body))
|
parts := strings.Fields(strings.ToLower(msg.Body))
|
||||||
b.checkHelp(msg.Channel, parts)
|
b.checkHelp(msg.Channel, parts)
|
||||||
goto RET
|
goto RET
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range b.pluginOrdering {
|
for _, name := range b.pluginOrdering {
|
||||||
if b.runCallback(name, Message, msg) {
|
if b.runCallback(name, kind, msg, args) {
|
||||||
goto RET
|
goto RET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,59 +38,18 @@ RET:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle incoming events
|
|
||||||
func (b *bot) EventReceived(msg msg.Message) {
|
|
||||||
log.Println("Received event: ", msg)
|
|
||||||
//msg := b.buildMessage(conn, inMsg)
|
|
||||||
for _, name := range b.pluginOrdering {
|
|
||||||
if b.runCallback(name, Event, msg) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bot) runCallback(plugin string, evt Kind, message msg.Message, args ...interface{}) bool {
|
func (b *bot) runCallback(plugin string, evt Kind, message msg.Message, args ...interface{}) bool {
|
||||||
for _, cb := range b.callbacks[plugin][evt] {
|
for _, cb := range b.callbacks[plugin][evt] {
|
||||||
if cb(evt, message) {
|
if cb(evt, message, args) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle incoming replys
|
// Send a message to the connection
|
||||||
func (b *bot) ReplyMsgReceived(msg msg.Message, identifier string) {
|
func (b *bot) Send(kind Kind, args ...interface{}) (string, error) {
|
||||||
log.Println("Received message: ", msg)
|
return b.conn.Send(kind, args...)
|
||||||
|
|
||||||
for _, name := range b.pluginOrdering {
|
|
||||||
if b.runCallback(name, Reply, msg, identifier) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bot) SendMessage(channel, message string) (error, string) {
|
|
||||||
return b.conn.Send(Message, channel, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bot) SendAction(channel, message string) (error, string) {
|
|
||||||
return b.conn.Send(Action, channel, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bot) ReplyToMessageIdentifier(channel, message, identifier string) (error, string) {
|
|
||||||
return b.conn.Send(Reply, channel, message, identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bot) ReplyToMessage(channel, message string, replyTo msg.Message) (error, string) {
|
|
||||||
return b.conn.Send(Reply, channel, message, replyTo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bot) React(channel, reaction string, message msg.Message) (error, string) {
|
|
||||||
return b.conn.Send(Reaction, channel, reaction, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *bot) Edit(channel, newMessage, identifier string) (error, string) {
|
|
||||||
return b.conn.Send(Edit, channel, newMessage, identifier)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bot) GetEmojiList() map[string]string {
|
func (b *bot) GetEmojiList() map[string]string {
|
||||||
|
@ -110,7 +64,7 @@ func (b *bot) checkHelp(channel string, parts []string) {
|
||||||
for name, _ := range b.plugins {
|
for name, _ := range b.plugins {
|
||||||
topics = fmt.Sprintf("%s, %s", topics, name)
|
topics = fmt.Sprintf("%s, %s", topics, name)
|
||||||
}
|
}
|
||||||
b.SendMessage(channel, topics)
|
b.Send(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" {
|
||||||
|
@ -127,7 +81,7 @@ func (b *bot) checkHelp(channel string, parts []string) {
|
||||||
b.runCallback(parts[1], Help, msg.Message{Channel: channel}, channel, parts)
|
b.runCallback(parts[1], Help, msg.Message{Channel: channel}, channel, parts)
|
||||||
} else {
|
} else {
|
||||||
msg := fmt.Sprintf("I'm sorry, I don't know what %s is!", parts[1])
|
msg := fmt.Sprintf("I'm sorry, I don't know what %s is!", parts[1])
|
||||||
b.SendMessage(channel, msg)
|
b.Send(Message, channel, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,14 +177,14 @@ 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.SendMessage(channel, msg)
|
b.Send(Message, channel, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bot) Help(channel string, parts []string) {
|
func (b *bot) Help(channel string, parts []string) {
|
||||||
msg := fmt.Sprintf("Hi, I'm based on godeepintir version %s. I'm written in Go, and you "+
|
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.SendMessage(channel, msg)
|
b.Send(Message, channel, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send our own musings to the plugins
|
// Send our own musings to the plugins
|
||||||
|
|
|
@ -45,7 +45,7 @@ type Bot interface {
|
||||||
// AddPlugin registers a new plugin handler
|
// AddPlugin registers a new plugin handler
|
||||||
AddPlugin(string, Plugin)
|
AddPlugin(string, 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{}) (error, string)
|
Send(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{})
|
Receive(Kind, msg.Message, ...interface{})
|
||||||
// Register a callback
|
// Register a callback
|
||||||
|
@ -61,11 +61,9 @@ type Bot interface {
|
||||||
|
|
||||||
// Connector represents a server connection to a chat service
|
// Connector represents a server connection to a chat service
|
||||||
type Connector interface {
|
type Connector interface {
|
||||||
RegisterEventReceived(func(message msg.Message))
|
RegisterEvent(func(Kind, msg.Message, ...interface{}))
|
||||||
RegisterMessageReceived(func(message msg.Message))
|
|
||||||
RegisterReplyMessageReceived(func(msg.Message, string))
|
|
||||||
|
|
||||||
Send(Kind, ...interface{}) (error, string)
|
Send(Kind, ...interface{}) (string, error)
|
||||||
|
|
||||||
GetEmojiList() map[string]string
|
GetEmojiList() map[string]string
|
||||||
Serve() error
|
Serve() error
|
||||||
|
|
24
bot/mock.go
24
bot/mock.go
|
@ -29,14 +29,14 @@ 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{}) (error, string) {
|
func (mb *MockBot) Send(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))
|
||||||
return nil, fmt.Sprintf("m-%d", len(mb.Actions)-1)
|
return fmt.Sprintf("m-%d", len(mb.Actions)-1), nil
|
||||||
case Action:
|
case Action:
|
||||||
mb.Actions = append(mb.Actions, args[1].(string))
|
mb.Actions = append(mb.Actions, args[1].(string))
|
||||||
return nil, fmt.Sprintf("a-%d", len(mb.Actions)-1)
|
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(ch, m, id)
|
||||||
|
@ -44,7 +44,7 @@ func (mb *MockBot) Send(kind Kind, args ...interface{}) (error, string) {
|
||||||
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(ch, re, msg)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Mesasge type unhandled"), "ERROR"
|
return "ERR", fmt.Errorf("Mesasge type unhandled")
|
||||||
}
|
}
|
||||||
func (mb *MockBot) AddPlugin(name string, f Plugin) {}
|
func (mb *MockBot) AddPlugin(name string, f Plugin) {}
|
||||||
func (mb *MockBot) Register(name string, kind Kind, cb Callback) {}
|
func (mb *MockBot) Register(name string, kind Kind, cb Callback) {}
|
||||||
|
@ -53,40 +53,40 @@ func (mb *MockBot) Filter(msg msg.Message, s string) string { re
|
||||||
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) (error, string) {
|
func (mb *MockBot) react(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) (error, string) {
|
func (mb *MockBot) edit(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)
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return err, ""
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
index, err := strconv.Atoi(strings.Split(identifier, "-")[1])
|
index, err := strconv.Atoi(strings.Split(identifier, "-")[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("failed to parse identifier: %s", identifier)
|
err := fmt.Errorf("failed to parse identifier: %s", identifier)
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return err, ""
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if isMessage {
|
if isMessage {
|
||||||
if index < len(mb.Messages) {
|
if index < len(mb.Messages) {
|
||||||
mb.Messages[index] = newMessage
|
mb.Messages[index] = newMessage
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("No message"), ""
|
return "", fmt.Errorf("No message")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if index < len(mb.Actions) {
|
if index < len(mb.Actions) {
|
||||||
mb.Actions[index] = newMessage
|
mb.Actions[index] = newMessage
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("No action"), ""
|
return "", fmt.Errorf("No action")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, ""
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mb *MockBot) GetEmojiList() map[string]string { return make(map[string]string) }
|
func (mb *MockBot) GetEmojiList() map[string]string { return make(map[string]string) }
|
||||||
|
|
78
irc/irc.go
78
irc/irc.go
|
@ -42,9 +42,7 @@ type Irc struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
quit chan bool
|
quit chan bool
|
||||||
|
|
||||||
eventReceived func(msg.Message)
|
event func(bot.Kind, msg.Message, ...interface{})
|
||||||
messageReceived func(msg.Message)
|
|
||||||
replyMessageReceived func(msg.Message, string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(c *config.Config) *Irc {
|
func New(c *config.Config) *Irc {
|
||||||
|
@ -54,19 +52,11 @@ func New(c *config.Config) *Irc {
|
||||||
return &i
|
return &i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Irc) RegisterEventReceived(f func(msg.Message)) {
|
func (i *Irc) RegisterEvent(f func(bot.Kind, msg.Message, ...interface{})) {
|
||||||
i.eventReceived = f
|
i.event = f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Irc) RegisterMessageReceived(f func(msg.Message)) {
|
func (i *Irc) Send(kind bot.Kind, args ...interface{}) (string, error) {
|
||||||
i.messageReceived = f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Irc) RegisterReplyMessageReceived(f func(msg.Message, string)) {
|
|
||||||
i.replyMessageReceived = f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Irc) Send(kind bot.Kind, args ...interface{}) (error, string) {
|
|
||||||
switch kind {
|
switch kind {
|
||||||
case bot.Reply:
|
case bot.Reply:
|
||||||
case bot.Message:
|
case bot.Message:
|
||||||
|
@ -75,7 +65,7 @@ func (i *Irc) Send(kind bot.Kind, args ...interface{}) (error, string) {
|
||||||
return i.sendAction(args[0].(string), args[1].(string))
|
return i.sendAction(args[0].(string), args[1].(string))
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
return nil, ""
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Irc) JoinChannel(channel string) {
|
func (i *Irc) JoinChannel(channel string) {
|
||||||
|
@ -83,7 +73,7 @@ func (i *Irc) JoinChannel(channel string) {
|
||||||
i.Client.Out <- irc.Msg{Cmd: irc.JOIN, Args: []string{channel}}
|
i.Client.Out <- irc.Msg{Cmd: irc.JOIN, Args: []string{channel}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Irc) sendMessage(channel, message string) (error, string) {
|
func (i *Irc) sendMessage(channel, message string) (string, error) {
|
||||||
for len(message) > 0 {
|
for len(message) > 0 {
|
||||||
m := irc.Msg{
|
m := irc.Msg{
|
||||||
Cmd: "PRIVMSG",
|
Cmd: "PRIVMSG",
|
||||||
|
@ -107,11 +97,11 @@ func (i *Irc) sendMessage(channel, message string) (error, string) {
|
||||||
|
|
||||||
i.Client.Out <- m
|
i.Client.Out <- m
|
||||||
}
|
}
|
||||||
return nil, "NO_IRC_IDENTIFIERS"
|
return "NO_IRC_IDENTIFIERS", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sends action to channel
|
// Sends action to channel
|
||||||
func (i *Irc) sendAction(channel, message string) (error, string) {
|
func (i *Irc) sendAction(channel, message string) (string, error) {
|
||||||
message = actionPrefix + " " + message + "\x01"
|
message = actionPrefix + " " + message + "\x01"
|
||||||
|
|
||||||
return i.sendMessage(channel, message)
|
return i.sendMessage(channel, message)
|
||||||
|
@ -123,7 +113,7 @@ func (i *Irc) GetEmojiList() map[string]string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Irc) Serve() error {
|
func (i *Irc) Serve() error {
|
||||||
if i.eventReceived == nil || i.messageReceived == nil {
|
if i.event == nil {
|
||||||
return fmt.Errorf("Missing an event handler")
|
return fmt.Errorf("Missing an event handler")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,53 +192,53 @@ func (i *Irc) handleMsg(msg irc.Msg) {
|
||||||
// OK, ignore
|
// OK, ignore
|
||||||
|
|
||||||
case irc.ERR_NOSUCHNICK:
|
case irc.ERR_NOSUCHNICK:
|
||||||
i.eventReceived(botMsg)
|
fallthrough
|
||||||
|
|
||||||
case irc.ERR_NOSUCHCHANNEL:
|
case irc.ERR_NOSUCHCHANNEL:
|
||||||
i.eventReceived(botMsg)
|
fallthrough
|
||||||
|
|
||||||
case irc.RPL_MOTD:
|
case irc.RPL_MOTD:
|
||||||
i.eventReceived(botMsg)
|
fallthrough
|
||||||
|
|
||||||
case irc.RPL_NAMREPLY:
|
case irc.RPL_NAMREPLY:
|
||||||
i.eventReceived(botMsg)
|
fallthrough
|
||||||
|
|
||||||
case irc.RPL_TOPIC:
|
case irc.RPL_TOPIC:
|
||||||
i.eventReceived(botMsg)
|
fallthrough
|
||||||
|
|
||||||
case irc.KICK:
|
case irc.KICK:
|
||||||
i.eventReceived(botMsg)
|
fallthrough
|
||||||
|
|
||||||
case irc.TOPIC:
|
case irc.TOPIC:
|
||||||
i.eventReceived(botMsg)
|
fallthrough
|
||||||
|
|
||||||
case irc.MODE:
|
case irc.MODE:
|
||||||
i.eventReceived(botMsg)
|
fallthrough
|
||||||
|
|
||||||
case irc.JOIN:
|
case irc.JOIN:
|
||||||
i.eventReceived(botMsg)
|
fallthrough
|
||||||
|
|
||||||
case irc.PART:
|
case irc.PART:
|
||||||
i.eventReceived(botMsg)
|
fallthrough
|
||||||
|
|
||||||
|
case irc.NOTICE:
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case irc.NICK:
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case irc.RPL_WHOREPLY:
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case irc.RPL_ENDOFWHO:
|
||||||
|
i.event(bot.Event, botMsg)
|
||||||
|
|
||||||
|
case irc.PRIVMSG:
|
||||||
|
i.event(bot.Message, botMsg)
|
||||||
|
|
||||||
case irc.QUIT:
|
case irc.QUIT:
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
||||||
case irc.NOTICE:
|
|
||||||
i.eventReceived(botMsg)
|
|
||||||
|
|
||||||
case irc.PRIVMSG:
|
|
||||||
i.messageReceived(botMsg)
|
|
||||||
|
|
||||||
case irc.NICK:
|
|
||||||
i.eventReceived(botMsg)
|
|
||||||
|
|
||||||
case irc.RPL_WHOREPLY:
|
|
||||||
i.eventReceived(botMsg)
|
|
||||||
|
|
||||||
case irc.RPL_ENDOFWHO:
|
|
||||||
i.eventReceived(botMsg)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
cmd := irc.CmdNames[msg.Cmd]
|
cmd := irc.CmdNames[msg.Cmd]
|
||||||
log.Println("(" + cmd + ") " + msg.Raw)
|
log.Println("(" + cmd + ") " + msg.Raw)
|
||||||
|
|
3
main.go
3
main.go
|
@ -35,6 +35,7 @@ import (
|
||||||
"github.com/velour/catbase/plugins/twitch"
|
"github.com/velour/catbase/plugins/twitch"
|
||||||
"github.com/velour/catbase/plugins/your"
|
"github.com/velour/catbase/plugins/your"
|
||||||
"github.com/velour/catbase/plugins/zork"
|
"github.com/velour/catbase/plugins/zork"
|
||||||
|
"github.com/velour/catbase/slack"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -70,7 +71,7 @@ func main() {
|
||||||
case "irc":
|
case "irc":
|
||||||
client = irc.New(c)
|
client = irc.New(c)
|
||||||
case "slack":
|
case "slack":
|
||||||
//client = slack.New(c)
|
client = slack.New(c)
|
||||||
default:
|
default:
|
||||||
log.Fatalf("Unknown connection type: %s", c.Get("type", "UNSET"))
|
log.Fatalf("Unknown connection type: %s", c.Get("type", "UNSET"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,12 +25,14 @@ type AdminPlugin struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAdminPlugin creates a new AdminPlugin with the Plugin interface
|
// NewAdminPlugin creates a new AdminPlugin with the Plugin interface
|
||||||
func New(bot bot.Bot) *AdminPlugin {
|
func New(b bot.Bot) *AdminPlugin {
|
||||||
p := &AdminPlugin{
|
p := &AdminPlugin{
|
||||||
Bot: bot,
|
Bot: b,
|
||||||
db: bot.DB(),
|
db: b.DB(),
|
||||||
cfg: bot.Config(),
|
cfg: b.Config(),
|
||||||
}
|
}
|
||||||
|
b.Register("admin", bot.Message, p.message)
|
||||||
|
b.Register("admin", bot.Help, p.help)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +46,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(message msg.Message) bool {
|
func (p *AdminPlugin) message(k bot.Kind, message msg.Message, args ...interface{}) bool {
|
||||||
body := message.Body
|
body := message.Body
|
||||||
|
|
||||||
if p.quiet {
|
if p.quiet {
|
||||||
|
@ -143,23 +145,12 @@ func (p *AdminPlugin) handleVariables(message msg.Message) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(channel string, parts []string) {
|
func (p *AdminPlugin) help(kind bot.Kind, m msg.Message, args ...interface{}) bool {
|
||||||
p.Bot.Send(bot.Message, channel, "This does super secret things that you're not allowed to know about.")
|
p.Bot.Send(bot.Message, m.Channel, "This does super secret things that you're not allowed to know about.")
|
||||||
}
|
return true
|
||||||
|
|
||||||
// Empty event handler because this plugin does not do anything on event recv
|
|
||||||
func (p *AdminPlugin) Event(kind string, message msg.Message) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handler for bot's own messages
|
|
||||||
func (p *AdminPlugin) BotMessage(message msg.Message) bool {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register any web URLs desired
|
// Register any web URLs desired
|
||||||
func (p *AdminPlugin) RegisterWeb() *string {
|
func (p *AdminPlugin) RegisterWeb() *string {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *AdminPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
|
||||||
|
|
|
@ -22,12 +22,12 @@ func setup(t *testing.T) (*AdminPlugin, *bot.MockBot) {
|
||||||
return a, mb
|
return a, mb
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMessage(payload string) msg.Message {
|
func makeMessage(payload string) (bot.Kind, msg.Message) {
|
||||||
isCmd := strings.HasPrefix(payload, "!")
|
isCmd := strings.HasPrefix(payload, "!")
|
||||||
if isCmd {
|
if isCmd {
|
||||||
payload = payload[1:]
|
payload = payload[1:]
|
||||||
}
|
}
|
||||||
return msg.Message{
|
return bot.Message, msg.Message{
|
||||||
User: &user.User{Name: "tester"},
|
User: &user.User{Name: "tester"},
|
||||||
Channel: "test",
|
Channel: "test",
|
||||||
Body: payload,
|
Body: payload,
|
||||||
|
@ -38,7 +38,7 @@ func makeMessage(payload string) msg.Message {
|
||||||
func TestSet(t *testing.T) {
|
func TestSet(t *testing.T) {
|
||||||
a, mb := setup(t)
|
a, mb := setup(t)
|
||||||
expected := "test value"
|
expected := "test value"
|
||||||
a.Message(makeMessage("!set test.key " + expected))
|
a.message(makeMessage("!set test.key " + expected))
|
||||||
actual := mb.Config().Get("test.key", "ERR")
|
actual := mb.Config().Get("test.key", "ERR")
|
||||||
assert.Equal(t, expected, actual)
|
assert.Equal(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ func TestGetValue(t *testing.T) {
|
||||||
a, mb := setup(t)
|
a, mb := setup(t)
|
||||||
expected := "value"
|
expected := "value"
|
||||||
mb.Config().Set("test.key", "value")
|
mb.Config().Set("test.key", "value")
|
||||||
a.Message(makeMessage("!get test.key"))
|
a.message(makeMessage("!get test.key"))
|
||||||
assert.Len(t, mb.Messages, 1)
|
assert.Len(t, mb.Messages, 1)
|
||||||
assert.Contains(t, mb.Messages[0], expected)
|
assert.Contains(t, mb.Messages[0], expected)
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ func TestGetValue(t *testing.T) {
|
||||||
func TestGetEmpty(t *testing.T) {
|
func TestGetEmpty(t *testing.T) {
|
||||||
a, mb := setup(t)
|
a, mb := setup(t)
|
||||||
expected := "test.key: <unknown>"
|
expected := "test.key: <unknown>"
|
||||||
a.Message(makeMessage("!get test.key"))
|
a.message(makeMessage("!get test.key"))
|
||||||
assert.Len(t, mb.Messages, 1)
|
assert.Len(t, mb.Messages, 1)
|
||||||
assert.Equal(t, expected, mb.Messages[0])
|
assert.Equal(t, expected, mb.Messages[0])
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ func TestGetEmpty(t *testing.T) {
|
||||||
func TestGetForbidden(t *testing.T) {
|
func TestGetForbidden(t *testing.T) {
|
||||||
a, mb := setup(t)
|
a, mb := setup(t)
|
||||||
expected := "cannot access"
|
expected := "cannot access"
|
||||||
a.Message(makeMessage("!get slack.token"))
|
a.message(makeMessage("!get slack.token"))
|
||||||
assert.Len(t, mb.Messages, 1)
|
assert.Len(t, mb.Messages, 1)
|
||||||
assert.Contains(t, mb.Messages[0], expected)
|
assert.Contains(t, mb.Messages[0], expected)
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,7 @@ func New(b bot.Bot) *RPGPlugin {
|
||||||
func (p *RPGPlugin) Message(message msg.Message) bool {
|
func (p *RPGPlugin) Message(message msg.Message) 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(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(bot.Reply, message.Channel, "Over here.", ts)
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -47,7 +47,7 @@ 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(bot.Message, channel, g.toMessageString())
|
||||||
|
|
||||||
g.schedulePush()
|
g.schedulePush()
|
||||||
g.scheduleDecrement()
|
g.scheduleDecrement()
|
||||||
|
|
108
slack/slack.go
108
slack/slack.go
|
@ -44,9 +44,7 @@ type Slack struct {
|
||||||
|
|
||||||
emoji map[string]string
|
emoji map[string]string
|
||||||
|
|
||||||
eventReceived func(msg.Message)
|
event func(bot.Kind, msg.Message, ...interface{})
|
||||||
messageReceived func(msg.Message)
|
|
||||||
replyMessageReceived func(msg.Message, string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var idCounter uint64
|
var idCounter uint64
|
||||||
|
@ -177,7 +175,31 @@ func New(c *config.Config) *Slack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkReturnStatus(response *http.Response) bool {
|
func (s *Slack) Send(kind bot.Kind, args ...interface{}) (string, error) {
|
||||||
|
switch kind {
|
||||||
|
case bot.Message:
|
||||||
|
return s.sendMessage(args[0].(string), args[1].(string))
|
||||||
|
case bot.Action:
|
||||||
|
return s.sendAction(args[0].(string), args[1].(string))
|
||||||
|
case bot.Edit:
|
||||||
|
return s.edit(args[0].(string), args[1].(string), args[2].(string))
|
||||||
|
case bot.Reply:
|
||||||
|
switch args[2].(type) {
|
||||||
|
case msg.Message:
|
||||||
|
return s.replyToMessage(args[0].(string), args[1].(string), args[2].(msg.Message))
|
||||||
|
case string:
|
||||||
|
return s.replyToMessageIdentifier(args[0].(string), args[1].(string), args[2].(string))
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Invalid types given to Reply")
|
||||||
|
}
|
||||||
|
case bot.Reaction:
|
||||||
|
return s.react(args[0].(string), args[1].(string), args[2].(msg.Message))
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("No handler for message type %d", kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkReturnStatus(response *http.Response) error {
|
||||||
type Response struct {
|
type Response struct {
|
||||||
OK bool `json:"ok"`
|
OK bool `json:"ok"`
|
||||||
}
|
}
|
||||||
|
@ -185,32 +207,24 @@ func checkReturnStatus(response *http.Response) bool {
|
||||||
body, err := ioutil.ReadAll(response.Body)
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
response.Body.Close()
|
response.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error reading Slack API body: %s", err)
|
err := fmt.Errorf("Error reading Slack API body: %s", err)
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp Response
|
var resp Response
|
||||||
err = json.Unmarshal(body, &resp)
|
err = json.Unmarshal(body, &resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error parsing message response: %s", err)
|
err := fmt.Errorf("Error parsing message response: %s", err)
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
return resp.OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) RegisterEventReceived(f func(msg.Message)) {
|
func (s *Slack) RegisterEvent(f func(bot.Kind, msg.Message, ...interface{})) {
|
||||||
s.eventReceived = f
|
s.event = f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) RegisterMessageReceived(f func(msg.Message)) {
|
func (s *Slack) sendMessageType(channel, message string, meMessage bool) (string, error) {
|
||||||
s.messageReceived = f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Slack) RegisterReplyMessageReceived(f func(msg.Message, string)) {
|
|
||||||
s.replyMessageReceived = f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Slack) SendMessageType(channel, message string, meMessage bool) (string, error) {
|
|
||||||
postUrl := "https://slack.com/api/chat.postMessage"
|
postUrl := "https://slack.com/api/chat.postMessage"
|
||||||
if meMessage {
|
if meMessage {
|
||||||
postUrl = "https://slack.com/api/chat.meMessage"
|
postUrl = "https://slack.com/api/chat.meMessage"
|
||||||
|
@ -262,19 +276,19 @@ func (s *Slack) SendMessageType(channel, message string, meMessage bool) (string
|
||||||
return mr.Timestamp, err
|
return mr.Timestamp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) SendMessage(channel, message string) string {
|
func (s *Slack) sendMessage(channel, message string) (string, error) {
|
||||||
log.Printf("Sending message to %s: %s", channel, message)
|
log.Printf("Sending message to %s: %s", channel, message)
|
||||||
identifier, _ := s.SendMessageType(channel, message, false)
|
identifier, err := s.sendMessageType(channel, message, false)
|
||||||
return identifier
|
return identifier, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) SendAction(channel, message string) string {
|
func (s *Slack) sendAction(channel, message string) (string, error) {
|
||||||
log.Printf("Sending action to %s: %s", channel, message)
|
log.Printf("Sending action to %s: %s", channel, message)
|
||||||
identifier, _ := s.SendMessageType(channel, "_"+message+"_", true)
|
identifier, err := s.sendMessageType(channel, "_"+message+"_", true)
|
||||||
return identifier
|
return identifier, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) ReplyToMessageIdentifier(channel, message, identifier string) (string, bool) {
|
func (s *Slack) replyToMessageIdentifier(channel, message, identifier string) (string, error) {
|
||||||
nick := s.config.Get("Nick", "bot")
|
nick := s.config.Get("Nick", "bot")
|
||||||
icon := s.config.Get("IconURL", "https://placekitten.com/128/128")
|
icon := s.config.Get("IconURL", "https://placekitten.com/128/128")
|
||||||
|
|
||||||
|
@ -288,15 +302,15 @@ func (s *Slack) ReplyToMessageIdentifier(channel, message, identifier string) (s
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error sending Slack reply: %s", err)
|
err := fmt.Errorf("Error sending Slack reply: %s", err)
|
||||||
return "", false
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error reading Slack API body: %s", err)
|
err := fmt.Errorf("Error reading Slack API body: %s", err)
|
||||||
return "", false
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println(string(body))
|
log.Println(string(body))
|
||||||
|
@ -309,22 +323,22 @@ func (s *Slack) ReplyToMessageIdentifier(channel, message, identifier string) (s
|
||||||
var mr MessageResponse
|
var mr MessageResponse
|
||||||
err = json.Unmarshal(body, &mr)
|
err = json.Unmarshal(body, &mr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error parsing message response: %s", err)
|
err := fmt.Errorf("Error parsing message response: %s", err)
|
||||||
return "", false
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !mr.OK {
|
if !mr.OK {
|
||||||
return "", false
|
return "", fmt.Errorf("Got !OK from slack message response")
|
||||||
}
|
}
|
||||||
|
|
||||||
return mr.Timestamp, err == nil
|
return mr.Timestamp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) ReplyToMessage(channel, message string, replyTo msg.Message) (string, bool) {
|
func (s *Slack) replyToMessage(channel, message string, replyTo msg.Message) (string, error) {
|
||||||
return s.ReplyToMessageIdentifier(channel, message, replyTo.AdditionalData["RAW_SLACK_TIMESTAMP"])
|
return s.replyToMessageIdentifier(channel, message, replyTo.AdditionalData["RAW_SLACK_TIMESTAMP"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) React(channel, reaction string, message msg.Message) bool {
|
func (s *Slack) react(channel, reaction string, message msg.Message) (string, error) {
|
||||||
log.Printf("Reacting in %s: %s", channel, reaction)
|
log.Printf("Reacting in %s: %s", channel, reaction)
|
||||||
resp, err := http.PostForm("https://slack.com/api/reactions.add",
|
resp, err := http.PostForm("https://slack.com/api/reactions.add",
|
||||||
url.Values{"token": {s.token},
|
url.Values{"token": {s.token},
|
||||||
|
@ -332,13 +346,13 @@ func (s *Slack) React(channel, reaction string, message msg.Message) bool {
|
||||||
"channel": {channel},
|
"channel": {channel},
|
||||||
"timestamp": {message.AdditionalData["RAW_SLACK_TIMESTAMP"]}})
|
"timestamp": {message.AdditionalData["RAW_SLACK_TIMESTAMP"]}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("reaction failed: %s", err)
|
err := fmt.Errorf("reaction failed: %s", err)
|
||||||
return false
|
return "", err
|
||||||
}
|
}
|
||||||
return checkReturnStatus(resp)
|
return "", checkReturnStatus(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) Edit(channel, newMessage, identifier string) bool {
|
func (s *Slack) edit(channel, newMessage, identifier string) (string, error) {
|
||||||
log.Printf("Editing in (%s) %s: %s", identifier, channel, newMessage)
|
log.Printf("Editing in (%s) %s: %s", identifier, channel, newMessage)
|
||||||
resp, err := http.PostForm("https://slack.com/api/chat.update",
|
resp, err := http.PostForm("https://slack.com/api/chat.update",
|
||||||
url.Values{"token": {s.token},
|
url.Values{"token": {s.token},
|
||||||
|
@ -346,10 +360,10 @@ func (s *Slack) Edit(channel, newMessage, identifier string) bool {
|
||||||
"text": {newMessage},
|
"text": {newMessage},
|
||||||
"ts": {identifier}})
|
"ts": {identifier}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("edit failed: %s", err)
|
err := fmt.Errorf("edit failed: %s", err)
|
||||||
return false
|
return "", err
|
||||||
}
|
}
|
||||||
return checkReturnStatus(resp)
|
return "", checkReturnStatus(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) GetEmojiList() map[string]string {
|
func (s *Slack) GetEmojiList() map[string]string {
|
||||||
|
@ -441,11 +455,11 @@ func (s *Slack) Serve() error {
|
||||||
log.Printf("Ignoring message: %+v\nlastRecieved: %v msg: %v", msg.ID, s.lastRecieved, m.Time)
|
log.Printf("Ignoring message: %+v\nlastRecieved: %v msg: %v", msg.ID, s.lastRecieved, m.Time)
|
||||||
} else {
|
} else {
|
||||||
s.lastRecieved = m.Time
|
s.lastRecieved = m.Time
|
||||||
s.messageReceived(m)
|
s.event(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.replyMessageReceived(s.buildLightReplyMessage(msg), msg.ThreadTs)
|
s.event(bot.Reply, s.buildLightReplyMessage(msg), msg.ThreadTs)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("THAT MESSAGE WAS HIDDEN: %+v", msg.ID)
|
log.Printf("THAT MESSAGE WAS HIDDEN: %+v", msg.ID)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue