-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathskit.go
189 lines (156 loc) · 4.37 KB
/
skit.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
package skit
import (
"context"
"os"
"reflect"
"strings"
"text/template"
"github.com/slack-go/slack"
)
var defaultNoHandler = template.Must(template.New("simple").Parse("I don't know what to say :neutral_face:"))
// New initializes an instance of skit with default event handlers.
func New(token string, logger Logger) *Skit {
sk := &Skit{}
sk.Logger = logger
sk.token = token
sk.connected = false
sk.NoHandler = *defaultNoHandler
return sk
}
// Skit represents an instance of skit.
type Skit struct {
Logger
NoHandler template.Template
RouteGroupMessages bool
Client *slack.Client
// internal states
self string
selfName string
token string
connected bool
handlers []registeredHandler
}
type registeredHandler struct {
name string
handler Handler
}
// Register a handler to handle message events. Handlers will be executed
// in the order they are registered in.
func (sk *Skit) Register(name string, handler Handler) {
sk.handlers = append(sk.handlers, registeredHandler{
name: name,
handler: handler,
})
}
// SendText sends the given message to the channel.
func (sk *Skit) SendText(ctx context.Context, msg string, channel string) error {
_, _, _, err := sk.Client.SendMessageContext(ctx, channel,
slack.MsgOptionText(msg, false),
slack.MsgOptionAsUser(true),
)
return err
}
// Listen connects to slack with the given configurations and starts
// the event loop
func (sk *Skit) Listen(ctx context.Context) error {
sk.Client = slack.New(sk.token)
rtm := sk.Client.NewRTM()
go rtm.ManageConnection()
for {
select {
case <-ctx.Done():
return ctx.Err()
case ev := <-rtm.IncomingEvents:
if err := sk.routeEvent(ev); err != nil {
return err
}
}
}
}
func (sk *Skit) routeEvent(rtmEv slack.RTMEvent) error {
switch ev := rtmEv.Data.(type) {
case *slack.HelloEvent:
sk.Debugf("HelloEvent received")
case *slack.ConnectingEvent:
sk.connected = false
sk.Infof("connecting to slack: attempt=%d", ev.Attempt)
if ev.Attempt >= 10 {
sk.Errorf("failed to connect to slack in 10 attempts, exiting")
os.Exit(1)
}
case *slack.ConnectedEvent:
sk.connected = true
sk.self = ev.Info.User.ID
sk.selfName = ev.Info.User.Name
sk.Infof("connected to slack: %s", ev.Info.User.ID)
case *slack.MessageEvent:
if ev.Msg.User == sk.self {
return nil
}
_, err := sk.Client.GetGroupInfo(ev.Channel)
if err == nil && !sk.RouteGroupMessages {
if !sk.isAddressedToMe(ev) {
return nil
}
}
sk.Debugf("message received: channel=%s", ev.Channel)
sk.handleMessageEvent(ev)
case *slack.UserTypingEvent:
sk.Debugf("ignoring user typing event")
case *slack.RTMError:
sk.Errorf("rtm error received: %s", ev)
return ev
case *slack.LatencyReport:
sk.Infof("latency received: %s", ev.Value)
case *slack.UserChangeEvent:
sk.Debugf("user change event: %s", ev.User.Name)
case *slack.InvalidAuthEvent:
sk.Errorf("received authentication failure, exiting..")
os.Exit(1)
default:
sk.Warnf("unknown event: %s", reflect.TypeOf(ev))
}
return nil
}
func (sk *Skit) handleMessageEvent(sme *slack.MessageEvent) {
ev := MessageEvent(*sme)
for _, reg := range sk.handlers {
if reg.handler.Handle(context.Background(), sk, &ev) {
sk.Debugf("handled by '%s'", reg.name)
return
}
}
sk.Debugf("no handler found to handle '%s'", ev.Text)
msg, err := Render(sk.NoHandler, ev)
if err != nil {
sk.Errorf("failed to render NoHandler template: %v", err)
sk.SendText(context.Background(), "Oops! something went terribly wrong :sob:", ev.Channel)
return
}
sk.SendText(context.Background(), msg, ev.Channel)
}
func (sk *Skit) isAddressedToMe(ev *slack.MessageEvent) bool {
var prefixes = []string{
"<@" + sk.self + ">",
"<@" + sk.self + "|" + sk.selfName + ">:",
}
sk.Debugf("received message: %v", ev.Msg.Text)
msgText := ev.Msg.Text
for _, prefix := range prefixes {
if strings.HasPrefix(ev.Msg.Text, prefix) {
msgText = strings.TrimSpace(strings.Replace(ev.Msg.Text, prefix, "", -1))
ev.Text = msgText
return true
}
}
return false
}
// Logger implementation is responsible for providing logging functions.
type Logger interface {
Debugf(msg string, args ...interface{})
Infof(msg string, args ...interface{})
Warnf(msg string, args ...interface{})
Errorf(msg string, args ...interface{})
}
// MessageEvent represents a slack message event.
type MessageEvent slack.MessageEvent