-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathrobot.go
More file actions
135 lines (116 loc) · 2.97 KB
/
robot.go
File metadata and controls
135 lines (116 loc) · 2.97 KB
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 bot
import (
"os"
"os/signal"
"syscall"
"time"
"github.com/gorilla/mux"
)
const timeout = 15
// Robot is the central structure for bot
type Robot struct {
// Chat adapter
Chat Chat
// Data store
Brain *Brain
// HTTP Router
Router *mux.Router
// Logger with levels
Logger Logger
plugins *pluginRegistry
internals *pluginRegistry
queue *responderQueue
}
// New creates an instance of a bot.Robot and loads in the chat adapter
// Typically you would install plugins before running the robot.
func New(c Chat, plugins ...Plugin) *Robot {
r := &Robot{
Chat: c,
Brain: NewBrain(),
Router: mux.NewRouter(),
Logger: defaultLogger,
plugins: newPluginRegistry(),
internals: newPluginRegistry(),
queue: newResponderQueue(10),
}
r.internals.Add(
c,
newServer(":"+os.Getenv("PORT")),
)
r.plugins.Add(plugins...)
return r
}
// Plugin allows you to fetch a loaded plugin for direct use
// See ExamplePluginUsage
func (r *Robot) Plugin(p Plugin) bool {
ok := r.plugins.Get(p)
if !ok {
return r.internals.Get(p)
}
return ok
}
// ListPlugins returns a slice of all loaded plugins.
func (r *Robot) ListPlugins() []Plugin {
return append(r.internals.List(), r.plugins.List()...)
}
// Run is responsible for:
// 1. Loading internals (chat, http)
// 2. Loading all plugins
// 3. Running RTM Slack until termination
// 4. Unloading all plugins in reverse order
// 5. Unloading internals in reverse order
func (r *Robot) Run() {
r.gracefulShutdown()
r.internals.Load(r)
r.plugins.Load(r)
r.queue.Forward(r, r.Chat.Messages())
r.stop()
}
func (r *Robot) gracefulShutdown() {
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
done := make(chan bool)
go func() {
r.stop()
done <- true
}()
select {
case <-time.After(timeout * time.Second):
r.Logger.Infof("Force quitting after %ds timeout", timeout)
os.Exit(1)
case <-done:
os.Exit(0)
}
}()
}
func (r *Robot) stop() {
r.Logger.Info("Shutting down...")
r.plugins.Unload(r)
r.internals.Unload(r)
}
func (r *Robot) onMessage(t messageType, m Matcher, h hook) {
if m == nil || h == nil {
return
}
for rs := range r.queue.On(t) {
if m(&rs) {
if err := h(rs); err != nil {
r.Logger.Errorf("Hook error: %s", err.Error())
}
}
}
}
// Hear is triggered on any message event.
func (r *Robot) Hear(m Matcher, h hook) { go r.onMessage(DefaultMessage, m, h) }
// Respond is triggered on messages to the bot
func (r *Robot) Respond(m Matcher, h hook) { go r.onMessage(Response, m, h) }
// Enter is triggered when someone enters a room
func (r *Robot) Enter(h hook) { go r.onMessage(Enter, nil, h) }
// Leave is triggered when someone leaves a room
func (r *Robot) Leave(h hook) { go r.onMessage(Leave, nil, h) }
// Topic is triggered when someone changes the topic
func (r *Robot) Topic(h hook) { go r.onMessage(Topic, nil, h) }
// Username provides the robot's username
func (r *Robot) Username() string { return r.Chat.Username() }