-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathannouncebot.go
135 lines (106 loc) · 3.3 KB
/
announcebot.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package announcebot
import (
"errors"
"fmt"
"net"
"net/http"
"strings"
"time"
"github.com/mattn/go-xmpp"
log "github.com/Sirupsen/logrus"
"github.com/tbruyelle/hipchat-go/hipchat"
"gopkg.in/redis.v3"
)
// ErrBotAlreadyStarted is returned when the bot has already been started
var ErrBotAlreadyStarted = errors.New("This bot has already been started")
// AnnounceBot represents a chat bot for making announcements
type AnnounceBot struct {
Config Configuration
MessageFactory func() (string, error)
commands map[string]ChatCommand
listener net.Listener
started bool
chatAPI *hipchat.Client
db *redis.Client
}
// NewAnnounceBot instantiates a new AnnounceBot
func NewAnnounceBot(config Configuration) *AnnounceBot {
bot := AnnounceBot{
Config: config,
MessageFactory: defaultMessageFactory,
}
bot.commands = make(map[string]ChatCommand)
bot.RegisterCommand("help", bot.helpCommand)
bot.RegisterCommand("subscribe", bot.subscribeCommand)
bot.RegisterCommand("unsubscribe", bot.unsubscribeCommand)
return &bot
}
// RegisterCommand binds a ChatCommand to a specific keyword
func (bot *AnnounceBot) RegisterCommand(key string, cmd ChatCommand) {
bot.commands[key] = cmd
}
func defaultMessageFactory() (string, error) {
return "Something important happened!", nil
}
func (bot *AnnounceBot) handleChatMessage(talk *xmpp.Client, user string, message string) {
message = strings.TrimSpace(message)
if message == "" {
return
}
userID := parseUser(user)
userlog := log.WithField("user", userID)
userlog.WithField("message", message).Info("Received message")
if !bot.lockMessage(user, message) {
userlog.Debug("Could not acquire lock... skipping")
return
}
reply := fmt.Sprintf("I'm sorry. I didn't understand what you said. Try 'help' if you need to see a list of commands.")
tokens := strings.Split(message, " ")
keyword := strings.ToLower(tokens[0])
handler, ok := bot.commands[keyword]
if ok {
reply = handler(userID, message, userlog)
}
if reply == "" {
return
}
defer func() {
userlog.WithField("message_reply", reply).Info("Replied to message")
}()
talk.Send(xmpp.Chat{Remote: user, Type: "chat", Text: reply})
}
func (bot *AnnounceBot) lockMessage(user string, message string) bool {
result := bot.db.SetNX(fmt.Sprintf("lock::%s::%s", user, message), 1, 4*time.Second)
if err := result.Err(); err != nil {
log.WithError(err).Error("Could not aquire lock because of an error")
}
return result.Val()
}
// Start begins serving API requests and processing chat messages
func (bot *AnnounceBot) Start(l net.Listener) error {
if bot.started {
return ErrBotAlreadyStarted
}
bot.started = true
bot.chatAPI = hipchat.NewClient(bot.Config.HipchatAPIToken)
bot.db = redis.NewClient(bot.Config.GetRedisOptions())
// Start the chat server
if bot.Config.HipchatUser != "" {
log.Info("Starting XMPP client")
bot.serveXMPP()
log.Info("XMPP client started succesfully")
}
// Start the API server
log.Info("Starting the API server")
bot.listener = l
r := bot.getRoutes()
return http.Serve(bot.listener, r)
}
// ListenAndStart starts the bot and begins listening for API requests
func (bot *AnnounceBot) ListenAndStart() error {
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", bot.Config.Port))
if err != nil {
return err
}
return bot.Start(listener)
}