1package slacktest 2 3import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 "net/http" 8 "net/http/httptest" 9 "time" 10 11 "github.com/nlopes/slack" 12) 13 14func newMessageChannels() *messageChannels { 15 sent := make(chan (string)) 16 seen := make(chan (string)) 17 mc := messageChannels{ 18 seen: seen, 19 sent: sent, 20 } 21 return &mc 22} 23 24// Customize the server's responses. 25type Customize interface { 26 Handle(pattern string, handler http.HandlerFunc) 27} 28 29type binder func(Customize) 30 31// NewTestServer returns a slacktest.Server ready to be started 32func NewTestServer(custom ...binder) *Server { 33 serverChans := newMessageChannels() 34 35 channels := &serverChannels{} 36 groups := &serverGroups{} 37 s := &Server{ 38 registered: map[string]struct{}{}, 39 mux: http.NewServeMux(), 40 seenInboundMessages: &messageCollection{}, 41 seenOutboundMessages: &messageCollection{}, 42 } 43 44 for _, c := range custom { 45 c(s) 46 } 47 48 s.Handle("/ws", s.wsHandler) 49 s.Handle("/rtm.start", rtmStartHandler) 50 s.Handle("/rtm.connect", rtmConnectHandler) 51 s.Handle("/chat.postMessage", s.postMessageHandler) 52 s.Handle("/channels.list", listChannelsHandler) 53 s.Handle("/groups.list", listGroupsHandler) 54 s.Handle("/users.info", usersInfoHandler) 55 s.Handle("/bots.info", botsInfoHandler) 56 s.Handle("/auth.test", authTestHandler) 57 58 httpserver := httptest.NewUnstartedServer(s.mux) 59 addr := httpserver.Listener.Addr().String() 60 61 s.ServerAddr = addr 62 s.server = httpserver 63 s.BotName = defaultBotName 64 s.BotID = defaultBotID 65 s.SeenFeed = serverChans.seen 66 s.channels = channels 67 s.groups = groups 68 69 addErr := addServerToHub(s, serverChans) 70 if addErr != nil { 71 log.Printf("Unable to add server to hub: %s", addErr.Error()) 72 } 73 74 return s 75} 76 77// Handle allow for customizing endpoints 78func (sts *Server) Handle(pattern string, handler http.HandlerFunc) { 79 if _, found := sts.registered[pattern]; found { 80 log.Printf("route already registered: %s\n", pattern) 81 return 82 } 83 84 sts.registered[pattern] = struct{}{} 85 sts.mux.Handle(pattern, contextHandler(sts, handler)) 86} 87 88// GetChannels returns all the fake channels registered 89func (sts *Server) GetChannels() []slack.Channel { 90 sts.channels.RLock() 91 defer sts.channels.RUnlock() 92 return sts.channels.channels 93} 94 95// GetGroups returns all the fake groups registered 96func (sts *Server) GetGroups() []slack.Group { 97 return sts.groups.channels 98} 99 100// GetSeenInboundMessages returns all messages seen via websocket excluding pings 101func (sts *Server) GetSeenInboundMessages() []string { 102 sts.seenInboundMessages.RLock() 103 m := sts.seenInboundMessages.messages 104 sts.seenInboundMessages.RUnlock() 105 return m 106} 107 108// GetSeenOutboundMessages returns all messages seen via websocket excluding pings 109func (sts *Server) GetSeenOutboundMessages() []string { 110 sts.seenOutboundMessages.RLock() 111 m := sts.seenOutboundMessages.messages 112 sts.seenOutboundMessages.RUnlock() 113 return m 114} 115 116// SawOutgoingMessage checks if a message was sent to connected websocket clients 117func (sts *Server) SawOutgoingMessage(msg string) bool { 118 sts.seenOutboundMessages.RLock() 119 defer sts.seenOutboundMessages.RUnlock() 120 for _, m := range sts.seenOutboundMessages.messages { 121 evt := &slack.MessageEvent{} 122 jErr := json.Unmarshal([]byte(m), evt) 123 if jErr != nil { 124 continue 125 } 126 127 if evt.Text == msg { 128 return true 129 } 130 } 131 return false 132} 133 134// SawMessage checks if an incoming message was seen 135func (sts *Server) SawMessage(msg string) bool { 136 sts.seenInboundMessages.RLock() 137 defer sts.seenInboundMessages.RUnlock() 138 for _, m := range sts.seenInboundMessages.messages { 139 evt := &slack.MessageEvent{} 140 jErr := json.Unmarshal([]byte(m), evt) 141 if jErr != nil { 142 // This event isn't a message event so we'll skip it 143 continue 144 } 145 if evt.Text == msg { 146 return true 147 } 148 } 149 return false 150} 151 152// GetAPIURL returns the api url you can pass to slack.SLACK_API 153func (sts *Server) GetAPIURL() string { 154 return "http://" + sts.ServerAddr + "/" 155} 156 157// GetWSURL returns the websocket url 158func (sts *Server) GetWSURL() string { 159 return "ws://" + sts.ServerAddr + "/ws" 160} 161 162// Stop stops the test server 163func (sts *Server) Stop() { 164 sts.server.Close() 165} 166 167// Start starts the test server 168func (sts *Server) Start() { 169 log.Print("starting server") 170 sts.server.Start() 171} 172 173// SendMessageToBot sends a message addressed to the Bot 174func (sts *Server) SendMessageToBot(channel, msg string) { 175 m := slack.Message{} 176 m.Type = slack.TYPE_MESSAGE 177 m.Channel = channel 178 m.User = defaultNonBotUserID 179 m.Text = fmt.Sprintf("<@%s> %s", sts.BotID, msg) 180 m.Timestamp = fmt.Sprintf("%d", time.Now().Unix()) 181 j, jErr := json.Marshal(m) 182 if jErr != nil { 183 log.Printf("Unable to marshal message for bot: %s", jErr.Error()) 184 return 185 } 186 go sts.queueForWebsocket(string(j), sts.ServerAddr) 187} 188 189// SendDirectMessageToBot sends a direct message to the bot 190func (sts *Server) SendDirectMessageToBot(msg string) { 191 m := slack.Message{} 192 m.Type = slack.TYPE_MESSAGE 193 m.Channel = "D024BE91L" 194 m.User = defaultNonBotUserID 195 m.Text = msg 196 m.Timestamp = fmt.Sprintf("%d", time.Now().Unix()) 197 j, jErr := json.Marshal(m) 198 if jErr != nil { 199 log.Printf("Unable to marshal private message for bot: %s", jErr.Error()) 200 return 201 } 202 go sts.queueForWebsocket(string(j), sts.ServerAddr) 203} 204 205// SendMessageToChannel sends a message to a channel 206func (sts *Server) SendMessageToChannel(channel, msg string) { 207 m := slack.Message{} 208 m.Type = slack.TYPE_MESSAGE 209 m.Channel = channel 210 m.Text = msg 211 m.User = defaultNonBotUserID 212 m.Timestamp = fmt.Sprintf("%d", time.Now().Unix()) 213 j, jErr := json.Marshal(m) 214 if jErr != nil { 215 log.Printf("Unable to marshal message for channel: %s", jErr.Error()) 216 return 217 } 218 stringMsg := string(j) 219 go sts.queueForWebsocket(stringMsg, sts.ServerAddr) 220} 221 222// SendToWebsocket send `s` as is to connected clients. 223// This is useful for sending your own custom json to the websocket 224func (sts *Server) SendToWebsocket(s string) { 225 go sts.queueForWebsocket(s, sts.ServerAddr) 226} 227 228// SetBotName sets a custom botname 229func (sts *Server) SetBotName(b string) { 230 sts.BotName = b 231} 232 233// SendBotChannelInvite invites the bot to a channel 234func (sts *Server) SendBotChannelInvite() { 235 joinMsg := ` 236 { 237 "type":"channel_joined", 238 "channel": 239 { 240 "id": "C024BE92L", 241 "name": "bot-playground", 242 "is_channel": true, 243 "created": 1360782804, 244 "creator": "W012A3CDE", 245 "is_archived": false, 246 "is_general": true, 247 "members": [ 248 "W012A3CDE" 249 ], 250 "topic": { 251 "value": "Fun times", 252 "creator": "W012A3CDE", 253 "last_set": 1360782804 254 }, 255 "purpose": { 256 "value": "This channel is for fun", 257 "creator": "W012A3CDE", 258 "last_set": 1360782804 259 }, 260 "is_member": true 261 } 262 }` 263 sts.SendToWebsocket(joinMsg) 264} 265 266// SendBotGroupInvite invites the bot to a channel 267func (sts *Server) SendBotGroupInvite() { 268 joinMsg := ` 269 { 270 "type":"group_joined", 271 "channel": 272 { 273 "id": "G024BE91L", 274 "name": "secretplans", 275 "is_group": true, 276 "created": 1360782804, 277 "creator": "W012A3CDE", 278 "is_archived": false, 279 "members": [ 280 "W012A3CDE" 281 ], 282 "topic": { 283 "value": "Secret plans on hold", 284 "creator": "W012A3CDE", 285 "last_set": 1360782804 286 }, 287 "purpose": { 288 "value": "Discuss secret plans that no-one else should know", 289 "creator": "W012A3CDE", 290 "last_set": 1360782804 291 } 292 } 293 }` 294 sts.SendToWebsocket(joinMsg) 295} 296 297// GetTestRTMInstance will give you an RTM instance in the context of the current fake server 298func (sts *Server) GetTestRTMInstance() *slack.RTM { 299 api := slack.New("ABCEFG", slack.OptionAPIURL(sts.GetAPIURL())) 300 rtm := api.NewRTM() 301 return rtm 302} 303