Merge pull request #91 from velour/rpgORdie

RPG or DIEEEEEEEEEEEE
This commit is contained in:
Scott Kiesel 2017-11-09 06:14:00 -05:00 committed by GitHub
commit 695c749727
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 511 additions and 41 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -322,3 +322,5 @@ func reminderer(p *ReminderPlugin) {
p.queueUpNextReminder()
}
}
func (p *ReminderPlugin) ReplyMessage(message msg.Message, identifier string) bool { return false }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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