mirror of https://github.com/velour/catbase.git
commit
695c749727
|
@ -93,6 +93,7 @@ func New(config *config.Config, connector Connector) Bot {
|
|||
|
||||
connector.RegisterMessageReceived(bot.MsgReceived)
|
||||
connector.RegisterEventReceived(bot.EventReceived)
|
||||
connector.RegisterReplyMessageReceived(bot.ReplyMsgReceived)
|
||||
|
||||
return bot
|
||||
}
|
||||
|
@ -145,7 +146,7 @@ func (b *bot) migrateDB() {
|
|||
|
||||
// Adds a constructed handler to the bots handlers list
|
||||
func (b *bot) AddHandler(name string, h Handler) {
|
||||
b.plugins[strings.ToLower(name)] = h
|
||||
b.plugins[name] = h
|
||||
b.pluginOrdering = append(b.pluginOrdering, name)
|
||||
if entry := h.RegisterWeb(); entry != nil {
|
||||
b.httpEndPoints[name] = *entry
|
||||
|
|
|
@ -22,7 +22,6 @@ func (b *bot) MsgReceived(msg msg.Message) {
|
|||
|
||||
// msg := b.buildMessage(client, inMsg)
|
||||
// do need to look up user and fix it
|
||||
|
||||
if strings.HasPrefix(msg.Body, "help ") && msg.Command {
|
||||
parts := strings.Fields(strings.ToLower(msg.Body))
|
||||
b.checkHelp(msg.Channel, parts)
|
||||
|
@ -53,16 +52,40 @@ func (b *bot) EventReceived(msg msg.Message) {
|
|||
}
|
||||
}
|
||||
|
||||
func (b *bot) SendMessage(channel, message string) {
|
||||
b.conn.SendMessage(channel, message)
|
||||
// Handle incoming replys
|
||||
func (b *bot) ReplyMsgReceived(msg msg.Message, identifier string) {
|
||||
log.Println("Received message: ", msg)
|
||||
|
||||
for _, name := range b.pluginOrdering {
|
||||
p := b.plugins[name]
|
||||
if p.ReplyMessage(msg, identifier) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bot) SendAction(channel, message string) {
|
||||
b.conn.SendAction(channel, message)
|
||||
func (b *bot) SendMessage(channel, message string) string {
|
||||
return b.conn.SendMessage(channel, message)
|
||||
}
|
||||
|
||||
func (b *bot) React(channel, reaction string, message msg.Message) {
|
||||
b.conn.React(channel, reaction, message)
|
||||
func (b *bot) SendAction(channel, message string) string {
|
||||
return b.conn.SendAction(channel, message)
|
||||
}
|
||||
|
||||
func (b *bot) ReplyToMessageIdentifier(channel, message, identifier string) (string, bool) {
|
||||
return b.conn.ReplyToMessageIdentifier(channel, message, identifier)
|
||||
}
|
||||
|
||||
func (b *bot) ReplyToMessage(channel, message string, replyTo msg.Message) (string, bool) {
|
||||
return b.conn.ReplyToMessage(channel, message, replyTo)
|
||||
}
|
||||
|
||||
func (b *bot) React(channel, reaction string, message msg.Message) bool {
|
||||
return b.conn.React(channel, reaction, message)
|
||||
}
|
||||
|
||||
func (b *bot) Edit(channel, newMessage, identifier string) bool {
|
||||
return b.conn.Edit(channel, newMessage, identifier)
|
||||
}
|
||||
|
||||
func (b *bot) GetEmojiList() map[string]string {
|
||||
|
|
|
@ -15,10 +15,14 @@ type Bot interface {
|
|||
DB() *sqlx.DB
|
||||
Who(string) []user.User
|
||||
AddHandler(string, Handler)
|
||||
SendMessage(string, string)
|
||||
SendAction(string, string)
|
||||
React(string, string, msg.Message)
|
||||
SendMessage(string, string) string
|
||||
SendAction(string, string) string
|
||||
ReplyToMessageIdentifier(string, string, string) (string, bool)
|
||||
ReplyToMessage(string, string, msg.Message) (string, bool)
|
||||
React(string, string, msg.Message) bool
|
||||
Edit(string, string, string) bool
|
||||
MsgReceived(msg.Message)
|
||||
ReplyMsgReceived(msg.Message, string)
|
||||
EventReceived(msg.Message)
|
||||
Filter(msg.Message, string) string
|
||||
LastMessage(string) (msg.Message, error)
|
||||
|
@ -30,10 +34,14 @@ type Bot interface {
|
|||
type Connector interface {
|
||||
RegisterEventReceived(func(message msg.Message))
|
||||
RegisterMessageReceived(func(message msg.Message))
|
||||
RegisterReplyMessageReceived(func(msg.Message, string))
|
||||
|
||||
SendMessage(channel, message string)
|
||||
SendAction(channel, message string)
|
||||
React(string, string, msg.Message)
|
||||
SendMessage(channel, message string) string
|
||||
SendAction(channel, message string) string
|
||||
ReplyToMessageIdentifier(string, string, string) (string, bool)
|
||||
ReplyToMessage(string, string, msg.Message) (string, bool)
|
||||
React(string, string, msg.Message) bool
|
||||
Edit(string, string, string) bool
|
||||
GetEmojiList() map[string]string
|
||||
Serve() error
|
||||
|
||||
|
@ -44,6 +52,7 @@ type Connector interface {
|
|||
type Handler interface {
|
||||
Message(message msg.Message) bool
|
||||
Event(kind string, message msg.Message) bool
|
||||
ReplyMessage(msg.Message, string) bool
|
||||
BotMessage(message msg.Message) bool
|
||||
Help(channel string, parts []string)
|
||||
RegisterWeb() *string
|
||||
|
|
56
bot/mock.go
56
bot/mock.go
|
@ -3,7 +3,10 @@
|
|||
package bot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
@ -25,13 +28,22 @@ type MockBot struct {
|
|||
func (mb *MockBot) Config() *config.Config { return &mb.Cfg }
|
||||
func (mb *MockBot) DBVersion() int64 { return 1 }
|
||||
func (mb *MockBot) DB() *sqlx.DB { return mb.db }
|
||||
func (mb *MockBot) Conn() Connector { return nil }
|
||||
func (mb *MockBot) Who(string) []user.User { return []user.User{} }
|
||||
func (mb *MockBot) AddHandler(name string, f Handler) {}
|
||||
func (mb *MockBot) SendMessage(ch string, msg string) {
|
||||
func (mb *MockBot) SendMessage(ch string, msg string) string {
|
||||
mb.Messages = append(mb.Messages, msg)
|
||||
return fmt.Sprintf("m-%d", len(mb.Actions)-1)
|
||||
}
|
||||
func (mb *MockBot) SendAction(ch string, msg string) {
|
||||
func (mb *MockBot) SendAction(ch string, msg string) string {
|
||||
mb.Actions = append(mb.Actions, msg)
|
||||
return fmt.Sprintf("a-%d", len(mb.Actions)-1)
|
||||
}
|
||||
func (mb *MockBot) ReplyToMessageIdentifier(channel, message, identifier string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
func (mb *MockBot) ReplyToMessage(channel, message string, replyTo msg.Message) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
func (mb *MockBot) MsgReceived(msg msg.Message) {}
|
||||
func (mb *MockBot) EventReceived(msg msg.Message) {}
|
||||
|
@ -39,9 +51,43 @@ func (mb *MockBot) Filter(msg msg.Message, s string) string { return "" }
|
|||
func (mb *MockBot) LastMessage(ch string) (msg.Message, error) { return msg.Message{}, nil }
|
||||
func (mb *MockBot) CheckAdmin(nick string) bool { return false }
|
||||
|
||||
func (mb *MockBot) React(channel, reaction string, message msg.Message) {}
|
||||
func (mb *MockBot) GetEmojiList() map[string]string { return make(map[string]string) }
|
||||
func (mb *MockBot) RegisterFilter(s string, f func(string) string) {}
|
||||
func (mb *MockBot) React(channel, reaction string, message msg.Message) bool { return false }
|
||||
|
||||
func (mb *MockBot) Edit(channel, newMessage, identifier string) bool {
|
||||
isMessage := identifier[0] == 'm'
|
||||
if !isMessage && identifier[0] != 'a' {
|
||||
log.Printf("failed to parse identifier: %s", identifier)
|
||||
return false
|
||||
}
|
||||
|
||||
index, err := strconv.Atoi(strings.Split(identifier, "-")[1])
|
||||
if err != nil {
|
||||
log.Printf("failed to parse identifier: %s", identifier)
|
||||
return false
|
||||
}
|
||||
|
||||
if isMessage {
|
||||
if index < len(mb.Messages) {
|
||||
mb.Messages[index] = newMessage
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if index < len(mb.Actions) {
|
||||
mb.Actions[index] = newMessage
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (mb *MockBot) ReplyMsgReceived(msg.Message, string) {
|
||||
|
||||
}
|
||||
|
||||
func (mb *MockBot) GetEmojiList() map[string]string { return make(map[string]string) }
|
||||
func (mb *MockBot) RegisterFilter(s string, f func(string) string) {}
|
||||
|
||||
func NewMockBot() *MockBot {
|
||||
db, err := sqlx.Open("sqlite3_custom", ":memory:")
|
||||
|
|
27
irc/irc.go
27
irc/irc.go
|
@ -44,6 +44,7 @@ type Irc struct {
|
|||
|
||||
eventReceived func(msg.Message)
|
||||
messageReceived func(msg.Message)
|
||||
replyMessageReceived func(msg.Message, string)
|
||||
}
|
||||
|
||||
func New(c *config.Config) *Irc {
|
||||
|
@ -61,12 +62,16 @@ func (i *Irc) RegisterMessageReceived(f func(msg.Message)) {
|
|||
i.messageReceived = f
|
||||
}
|
||||
|
||||
func (i *Irc) RegisterReplyMessageReceived(f func(msg.Message, string)) {
|
||||
i.replyMessageReceived = f
|
||||
}
|
||||
|
||||
func (i *Irc) JoinChannel(channel string) {
|
||||
log.Printf("Joining channel: %s", channel)
|
||||
i.Client.Out <- irc.Msg{Cmd: irc.JOIN, Args: []string{channel}}
|
||||
}
|
||||
|
||||
func (i *Irc) SendMessage(channel, message string) {
|
||||
func (i *Irc) SendMessage(channel, message string) string {
|
||||
for len(message) > 0 {
|
||||
m := irc.Msg{
|
||||
Cmd: "PRIVMSG",
|
||||
|
@ -90,17 +95,33 @@ func (i *Irc) SendMessage(channel, message string) {
|
|||
|
||||
i.Client.Out <- m
|
||||
}
|
||||
return "NO_IRC_IDENTIFIERS"
|
||||
}
|
||||
|
||||
// Sends action to channel
|
||||
func (i *Irc) SendAction(channel, message string) {
|
||||
func (i *Irc) SendAction(channel, message string) string {
|
||||
message = actionPrefix + " " + message + "\x01"
|
||||
|
||||
i.SendMessage(channel, message)
|
||||
return "NO_IRC_IDENTIFIERS"
|
||||
}
|
||||
|
||||
func (i *Irc) React(channel, reaction string, message msg.Message) {
|
||||
func (i *Irc) ReplyToMessageIdentifier(channel, message, identifier string) (string, bool) {
|
||||
return "NO_IRC_IDENTIFIERS", false
|
||||
}
|
||||
|
||||
func (i *Irc) ReplyToMessage(channel, message string, replyTo msg.Message) (string, bool) {
|
||||
return "NO_IRC_IDENTIFIERS", false
|
||||
}
|
||||
|
||||
func (i *Irc) React(channel, reaction string, message msg.Message) bool {
|
||||
//we're not goign to do anything because it's IRC
|
||||
return false
|
||||
}
|
||||
|
||||
func (i *Irc) Edit(channel, newMessage, identifier string) bool {
|
||||
//we're not goign to do anything because it's IRC
|
||||
return false
|
||||
}
|
||||
|
||||
func (i *Irc) GetEmojiList() map[string]string {
|
||||
|
|
2
main.go
2
main.go
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/velour/catbase/plugins/leftpad"
|
||||
"github.com/velour/catbase/plugins/reaction"
|
||||
"github.com/velour/catbase/plugins/reminder"
|
||||
"github.com/velour/catbase/plugins/rpgORdie"
|
||||
"github.com/velour/catbase/plugins/rss"
|
||||
"github.com/velour/catbase/plugins/stats"
|
||||
"github.com/velour/catbase/plugins/talker"
|
||||
|
@ -69,6 +70,7 @@ func main() {
|
|||
b.AddHandler("emojifyme", emojifyme.New(b))
|
||||
b.AddHandler("twitch", twitch.New(b))
|
||||
b.AddHandler("inventory", inventory.New(b))
|
||||
b.AddHandler("rpgORdie", rpgORdie.New(b))
|
||||
// catches anything left, will always return true
|
||||
b.AddHandler("factoid", fact.New(b))
|
||||
|
||||
|
|
|
@ -117,3 +117,5 @@ func (p *AdminPlugin) BotMessage(message msg.Message) bool {
|
|||
func (p *AdminPlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AdminPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -935,3 +935,5 @@ func (p *BabblerPlugin) babbleSeedBookends(babblerName string, start, end []stri
|
|||
|
||||
return strings.Join(words, " "), nil
|
||||
}
|
||||
|
||||
func (p *BabblerPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -461,3 +461,5 @@ func (p *BeersPlugin) BotMessage(message msg.Message) bool {
|
|||
func (p *BeersPlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *BeersPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -364,3 +364,5 @@ func (p *CounterPlugin) BotMessage(message msg.Message) bool {
|
|||
func (p *CounterPlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *CounterPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -100,3 +100,5 @@ func (p *DicePlugin) BotMessage(message msg.Message) bool {
|
|||
func (p *DicePlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *DicePlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -231,3 +231,5 @@ func (p *DowntimePlugin) BotMessage(message msg.Message) bool {
|
|||
func (p *DowntimePlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *DowntimePlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -112,3 +112,5 @@ func (p *EmojifyMePlugin) BotMessage(message msg.Message) bool {
|
|||
func (p *EmojifyMePlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *EmojifyMePlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -764,3 +764,5 @@ func (p *Factoid) serveQuery(w http.ResponseWriter, r *http.Request) {
|
|||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Factoid) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -170,3 +170,5 @@ func (p *RememberPlugin) recordMsg(message msg.Message) {
|
|||
log.Printf("Logging message: %s: %s", message.User.Name, message.Body)
|
||||
p.Log[message.Channel] = append(p.Log[message.Channel], message)
|
||||
}
|
||||
|
||||
func (p *RememberPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -228,3 +228,5 @@ func (p *FirstPlugin) BotMessage(message msg.Message) bool {
|
|||
func (p *FirstPlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *FirstPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -236,3 +236,5 @@ func (p *InventoryPlugin) RegisterWeb() *string {
|
|||
// nothing to register
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *InventoryPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -76,3 +76,5 @@ func (p *LeftpadPlugin) RegisterWeb() *string {
|
|||
// nothing to register
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *LeftpadPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -80,3 +80,5 @@ func (p *ReactionPlugin) BotMessage(message msg.Message) bool {
|
|||
func (p *ReactionPlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ReactionPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -322,3 +322,5 @@ func reminderer(p *ReminderPlugin) {
|
|||
p.queueUpNextReminder()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ReminderPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
package rpgORdie
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/velour/catbase/bot"
|
||||
"github.com/velour/catbase/bot/msg"
|
||||
)
|
||||
|
||||
const (
|
||||
DUDE = ":lion_face:"
|
||||
BOULDER = ":full_moon:"
|
||||
HOLE = ":new_moon:"
|
||||
EMPTY = ":white_large_square:"
|
||||
|
||||
OK = iota
|
||||
INVALID = iota
|
||||
WIN = iota
|
||||
)
|
||||
|
||||
type RPGPlugin struct {
|
||||
Bot bot.Bot
|
||||
listenFor map[string]*board
|
||||
}
|
||||
|
||||
type board struct {
|
||||
state [][]string
|
||||
x, y int
|
||||
}
|
||||
|
||||
func NewRandomBoard() *board {
|
||||
boardSize := 5
|
||||
b := board{
|
||||
state: make([][]string, boardSize),
|
||||
x: boardSize - 1,
|
||||
y: boardSize - 1,
|
||||
}
|
||||
for i := 0; i < boardSize; i++ {
|
||||
b.state[i] = make([]string, boardSize)
|
||||
for j := 0; j < boardSize; j++ {
|
||||
b.state[i][j] = ":white_large_square:"
|
||||
}
|
||||
}
|
||||
|
||||
b.state[boardSize-1][boardSize-1] = DUDE
|
||||
b.state[boardSize/2][boardSize/2] = BOULDER
|
||||
b.state[0][0] = HOLE
|
||||
|
||||
return &b
|
||||
}
|
||||
|
||||
func (b *board) toMessageString() string {
|
||||
lines := make([]string, len(b.state))
|
||||
for i := 0; i < len(b.state); i++ {
|
||||
lines[i] = strings.Join(b.state[i], "")
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func (b *board) checkAndMove(dx, dy int) int {
|
||||
newX := b.x + dx
|
||||
newY := b.y + dy
|
||||
|
||||
if newX < 0 || newY < 0 || newX >= len(b.state) || newY >= len(b.state) {
|
||||
return INVALID
|
||||
}
|
||||
|
||||
if b.state[newY][newX] == HOLE {
|
||||
return INVALID
|
||||
}
|
||||
|
||||
win := false
|
||||
if b.state[newY][newX] == BOULDER {
|
||||
newBoulderX := newX + dx
|
||||
newBoulderY := newY + dy
|
||||
|
||||
if newBoulderX < 0 || newBoulderY < 0 || newBoulderX >= len(b.state) || newBoulderY >= len(b.state) {
|
||||
return INVALID
|
||||
}
|
||||
|
||||
if b.state[newBoulderY][newBoulderX] != HOLE {
|
||||
b.state[newBoulderY][newBoulderX] = BOULDER
|
||||
} else {
|
||||
win = true
|
||||
}
|
||||
}
|
||||
|
||||
b.state[newY][newX] = DUDE
|
||||
b.state[b.y][b.x] = EMPTY
|
||||
b.x = newX
|
||||
b.y = newY
|
||||
|
||||
if win {
|
||||
return WIN
|
||||
}
|
||||
return OK
|
||||
}
|
||||
|
||||
func New(b bot.Bot) *RPGPlugin {
|
||||
return &RPGPlugin{
|
||||
Bot: b,
|
||||
listenFor: map[string]*board{},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *RPGPlugin) Message(message msg.Message) bool {
|
||||
if strings.ToLower(message.Body) == "start rpg" {
|
||||
b := NewRandomBoard()
|
||||
ts := p.Bot.SendMessage(message.Channel, b.toMessageString())
|
||||
p.listenFor[ts] = b
|
||||
p.Bot.ReplyToMessageIdentifier(message.Channel, "Over here.", ts)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *RPGPlugin) LoadData() {
|
||||
|
||||
}
|
||||
|
||||
func (p *RPGPlugin) Help(channel string, parts []string) {
|
||||
p.Bot.SendMessage(channel, "Go find a walkthrough or something.")
|
||||
}
|
||||
|
||||
func (p *RPGPlugin) Event(kind string, message msg.Message) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *RPGPlugin) BotMessage(message msg.Message) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *RPGPlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *RPGPlugin) ReplyMessage(message msg.Message, identifier string) bool {
|
||||
if strings.ToLower(message.User.Name) != strings.ToLower(p.Bot.Config().Nick) {
|
||||
if b, ok := p.listenFor[identifier]; ok {
|
||||
|
||||
var res int
|
||||
|
||||
if message.Body == "left" {
|
||||
res = b.checkAndMove(-1, 0)
|
||||
} else if message.Body == "right" {
|
||||
res = b.checkAndMove(1, 0)
|
||||
} else if message.Body == "up" {
|
||||
res = b.checkAndMove(0, -1)
|
||||
} else if message.Body == "down" {
|
||||
res = b.checkAndMove(0, 1)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
switch res {
|
||||
case OK:
|
||||
p.Bot.Edit(message.Channel, b.toMessageString(), identifier)
|
||||
case WIN:
|
||||
p.Bot.Edit(message.Channel, b.toMessageString(), identifier)
|
||||
p.Bot.ReplyToMessageIdentifier(message.Channel, "congratulations, you beat the easiest level imaginable.", identifier)
|
||||
case INVALID:
|
||||
p.Bot.ReplyToMessageIdentifier(message.Channel, fmt.Sprintf("you can't move %s", message.Body), identifier)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package rpgORdie
|
||||
|
||||
import ()
|
|
@ -117,3 +117,5 @@ func (p *RSSPlugin) BotMessage(message msg.Message) bool {
|
|||
func (p *RSSPlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *RSSPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -275,3 +275,5 @@ func (p *StatsPlugin) mkSightingStat(message msg.Message) stats {
|
|||
func (p *StatsPlugin) mkChannelStat(message msg.Message) stats {
|
||||
return stats{stat{mkDay(), "channel", message.Channel, 1}}
|
||||
}
|
||||
|
||||
func (p *StatsPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -119,3 +119,5 @@ func (p *TalkerPlugin) BotMessage(message msg.Message) bool {
|
|||
func (p *TalkerPlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TalkerPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -238,3 +238,5 @@ func (p *TwitchPlugin) checkTwitch(channel string, twitcher *Twitcher, alwaysPri
|
|||
twitcher.game = game
|
||||
}
|
||||
}
|
||||
|
||||
func (p *TwitchPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -66,3 +66,5 @@ func (p *YourPlugin) BotMessage(message msg.Message) bool {
|
|||
func (p *YourPlugin) RegisterWeb() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *YourPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
|
@ -122,3 +122,5 @@ func (p *ZorkPlugin) Help(ch string, _ []string) {
|
|||
}
|
||||
|
||||
func (p *ZorkPlugin) RegisterWeb() *string { return nil }
|
||||
|
||||
func (p *ZorkPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }
|
||||
|
|
194
slack/slack.go
194
slack/slack.go
|
@ -5,6 +5,7 @@ package slack
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
|
@ -15,7 +16,7 @@ import (
|
|||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
// "sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/velour/catbase/bot"
|
||||
|
@ -40,6 +41,7 @@ type Slack struct {
|
|||
|
||||
eventReceived func(msg.Message)
|
||||
messageReceived func(msg.Message)
|
||||
replyMessageReceived func(msg.Message, string)
|
||||
}
|
||||
|
||||
var idCounter uint64
|
||||
|
@ -133,6 +135,7 @@ type slackMessage struct {
|
|||
User string `json:"user"`
|
||||
Username string `json:"username"`
|
||||
Ts string `json:"ts"`
|
||||
ThreadTs string `json:"thread_ts"`
|
||||
Error struct {
|
||||
Code uint64 `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
|
@ -163,6 +166,27 @@ func New(c *config.Config) *Slack {
|
|||
}
|
||||
}
|
||||
|
||||
func checkReturnStatus(response *http.Response) bool {
|
||||
type Response struct {
|
||||
OK bool `json:"ok"`
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
response.Body.Close()
|
||||
if err != nil {
|
||||
log.Printf("Error reading Slack API body: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
var resp Response
|
||||
err = json.Unmarshal(body, &resp)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing message response: %s", err)
|
||||
return false
|
||||
}
|
||||
return resp.OK
|
||||
}
|
||||
|
||||
func (s *Slack) RegisterEventReceived(f func(msg.Message)) {
|
||||
s.eventReceived = f
|
||||
}
|
||||
|
@ -171,32 +195,112 @@ func (s *Slack) RegisterMessageReceived(f func(msg.Message)) {
|
|||
s.messageReceived = f
|
||||
}
|
||||
|
||||
func (s *Slack) SendMessageType(channel, messageType, subType, message string) error {
|
||||
m := slackMessage{
|
||||
ID: atomic.AddUint64(&idCounter, 1),
|
||||
Type: messageType,
|
||||
SubType: subType,
|
||||
Channel: channel,
|
||||
Text: message,
|
||||
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"
|
||||
if meMessage {
|
||||
postUrl = "https://slack.com/api/chat.meMessage"
|
||||
}
|
||||
err := websocket.JSON.Send(s.ws, m)
|
||||
|
||||
resp, err := http.PostForm(postUrl,
|
||||
url.Values{"token": {s.config.Slack.Token},
|
||||
"channel": {channel},
|
||||
"text": {message},
|
||||
"as_user": {"true"},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error sending Slack message: %s", err)
|
||||
}
|
||||
return err
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
log.Fatalf("Error reading Slack API body: %s", err)
|
||||
}
|
||||
|
||||
log.Println(string(body))
|
||||
|
||||
type MessageResponse struct {
|
||||
OK bool `json:"ok"`
|
||||
Timestamp string `json:"ts"`
|
||||
}
|
||||
|
||||
var mr MessageResponse
|
||||
err = json.Unmarshal(body, &mr)
|
||||
if err != nil {
|
||||
log.Fatalf("Error parsing message response: %s", err)
|
||||
}
|
||||
|
||||
if !mr.OK {
|
||||
return "", errors.New("failure response received")
|
||||
}
|
||||
|
||||
return mr.Timestamp, err
|
||||
}
|
||||
|
||||
func (s *Slack) SendMessage(channel, message string) {
|
||||
func (s *Slack) SendMessage(channel, message string) string {
|
||||
log.Printf("Sending message to %s: %s", channel, message)
|
||||
s.SendMessageType(channel, "message", "", message)
|
||||
identifier, _ := s.SendMessageType(channel, message, false)
|
||||
return identifier
|
||||
}
|
||||
|
||||
func (s *Slack) SendAction(channel, message string) {
|
||||
func (s *Slack) SendAction(channel, message string) string {
|
||||
log.Printf("Sending action to %s: %s", channel, message)
|
||||
s.SendMessageType(channel, "message", "me_message", "_"+message+"_")
|
||||
identifier, _ := s.SendMessageType(channel, "_"+message+"_", true)
|
||||
return identifier
|
||||
}
|
||||
|
||||
func (s *Slack) React(channel, reaction string, message msg.Message) {
|
||||
func (s *Slack) ReplyToMessageIdentifier(channel, message, identifier string) (string, bool) {
|
||||
resp, err := http.PostForm("https://slack.com/api/chat.postMessage",
|
||||
url.Values{"token": {s.config.Slack.Token},
|
||||
"channel": {channel},
|
||||
"text": {message},
|
||||
"as_user": {"true"},
|
||||
"thread_ts": {identifier},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error sending Slack reply: %s", err)
|
||||
return "", false
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
log.Printf("Error reading Slack API body: %s", err)
|
||||
return "", false
|
||||
}
|
||||
|
||||
log.Println(string(body))
|
||||
|
||||
type MessageResponse struct {
|
||||
OK bool `json:"ok"`
|
||||
Timestamp string `json:"ts"`
|
||||
}
|
||||
|
||||
var mr MessageResponse
|
||||
err = json.Unmarshal(body, &mr)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing message response: %s", err)
|
||||
return "", false
|
||||
}
|
||||
|
||||
if !mr.OK {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return mr.Timestamp, err == nil
|
||||
}
|
||||
|
||||
func (s *Slack) ReplyToMessage(channel, message string, replyTo msg.Message) (string, bool) {
|
||||
return s.ReplyToMessageIdentifier(channel, message, replyTo.AdditionalData["RAW_SLACK_TIMESTAMP"])
|
||||
}
|
||||
|
||||
func (s *Slack) React(channel, reaction string, message msg.Message) bool {
|
||||
log.Printf("Reacting in %s: %s", channel, reaction)
|
||||
resp, err := http.PostForm("https://slack.com/api/reactions.add",
|
||||
url.Values{"token": {s.config.Slack.Token},
|
||||
|
@ -204,9 +308,24 @@ func (s *Slack) React(channel, reaction string, message msg.Message) {
|
|||
"channel": {channel},
|
||||
"timestamp": {message.AdditionalData["RAW_SLACK_TIMESTAMP"]}})
|
||||
if err != nil {
|
||||
log.Printf("Error sending Slack reaction: %s", err)
|
||||
log.Println("reaction failed: %s", err)
|
||||
return false
|
||||
}
|
||||
log.Print(resp)
|
||||
return checkReturnStatus(resp)
|
||||
}
|
||||
|
||||
func (s *Slack) Edit(channel, newMessage, identifier string) bool {
|
||||
log.Printf("Editing in (%s) %s: %s", identifier, channel, newMessage)
|
||||
resp, err := http.PostForm("https://slack.com/api/chat.update",
|
||||
url.Values{"token": {s.config.Slack.Token},
|
||||
"channel": {channel},
|
||||
"text": {newMessage},
|
||||
"ts": {identifier}})
|
||||
if err != nil {
|
||||
log.Println("edit failed: %s", err)
|
||||
return false
|
||||
}
|
||||
return checkReturnStatus(resp)
|
||||
}
|
||||
|
||||
func (s *Slack) GetEmojiList() map[string]string {
|
||||
|
@ -273,14 +392,17 @@ func (s *Slack) Serve() error {
|
|||
}
|
||||
switch msg.Type {
|
||||
case "message":
|
||||
if !msg.Hidden {
|
||||
if !msg.Hidden && msg.ThreadTs == "" {
|
||||
m := s.buildMessage(msg)
|
||||
if m.Time.Before(s.lastRecieved) {
|
||||
log.Printf("Ignoring message: %+v\nlastRecieved: %v msg: %v", msg.ID, s.lastRecieved, m.Time)
|
||||
} else {
|
||||
s.lastRecieved = m.Time
|
||||
s.messageReceived(s.buildMessage(msg))
|
||||
s.messageReceived(m)
|
||||
}
|
||||
} else if msg.ThreadTs != "" {
|
||||
//we're throwing away some information here by not parsing the correct reply object type, but that's okay
|
||||
s.replyMessageReceived(s.buildLightReplyMessage(msg), msg.ThreadTs)
|
||||
} else {
|
||||
log.Printf("THAT MESSAGE WAS HIDDEN: %+v", msg.ID)
|
||||
}
|
||||
|
@ -337,6 +459,40 @@ func (s *Slack) buildMessage(m slackMessage) msg.Message {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Slack) buildLightReplyMessage(m slackMessage) msg.Message {
|
||||
text := html.UnescapeString(m.Text)
|
||||
|
||||
text = fixText(s.getUser, text)
|
||||
|
||||
isCmd, text := bot.IsCmd(s.config, text)
|
||||
|
||||
isAction := m.SubType == "me_message"
|
||||
|
||||
u, _ := s.getUser(m.User)
|
||||
if m.Username != "" {
|
||||
u = m.Username
|
||||
}
|
||||
|
||||
tstamp := slackTStoTime(m.Ts)
|
||||
|
||||
return msg.Message{
|
||||
User: &user.User{
|
||||
ID: m.User,
|
||||
Name: u,
|
||||
},
|
||||
Body: text,
|
||||
Raw: m.Text,
|
||||
Channel: m.Channel,
|
||||
Command: isCmd,
|
||||
Action: isAction,
|
||||
Host: string(m.ID),
|
||||
Time: tstamp,
|
||||
AdditionalData: map[string]string{
|
||||
"RAW_SLACK_TIMESTAMP": m.Ts,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// markAllChannelsRead gets a list of all channels and marks each as read
|
||||
func (s *Slack) markAllChannelsRead() {
|
||||
chs := s.getAllChannels()
|
||||
|
|
Loading…
Reference in New Issue