Codebase list golang-github-nlopes-slack / 7cff306
Add option to connect using rtm.connect API Florin Patan 6 years ago
3 changed file(s) with 69 addition(s) and 30 deletion(s). Raw diff Collapse all Expand all
00 package slack
11
22 import (
3 "encoding/json"
34 "fmt"
45 "net/url"
6 "time"
57 )
68
79 // StartRTM calls the "rtm.start" endpoint and returns the provided URL and the full Info
3638 //
3739 // To have a fully managed Websocket connection, use `NewRTM`, and call `ManageConnection()`
3840 // on it.
39 func (api *Client) ConnectRTM() (info *Info, websocketURL string, slackResponse WebResponse, err error) {
41 func (api *Client) ConnectRTM() (info *Info, websocketURL string, err error) {
4042 response := &infoResponseFull{}
4143 err = post("rtm.connect", url.Values{"token": {api.config.token}}, response, api.debug)
4244 if err != nil {
43 return nil, "", response.WebResponse, fmt.Errorf("post: %s", err)
45 return nil, "", fmt.Errorf("post: %s", err)
4446 }
4547 if !response.Ok {
46 return nil, "", response.WebResponse, response.Error
48 return nil, "", response.Error
4749 }
4850
4951 // websocket.Dial does not accept url without the port (yet)
5254 api.Debugln("Using URL:", response.Info.URL)
5355 websocketURL, err = websocketizeURLPort(response.Info.URL)
5456 if err != nil {
55 return nil, "", response.WebResponse, fmt.Errorf("parsing response URL: %s", err)
57 return nil, "", fmt.Errorf("parsing response URL: %s", err)
5658 }
5759
58 return &response.Info, websocketURL, response.WebResponse, nil
60 return &response.Info, websocketURL, nil
5961 }
6062
6163 // NewRTM returns a RTM, which provides a fully managed connection to
62 // Slack's websocket-based Real-Time Messaging protocol./
64 // Slack's websocket-based Real-Time Messaging protocol.
6365 func (api *Client) NewRTM() *RTM {
64 return newRTM(api)
66 return api.NewRTMWithOptions(nil)
6567 }
68
69 // NewRTMWithOptions returns a RTM, which provides a fully managed connection to
70 // Slack's websocket-based Real-Time Messaging protocol.
71 // This also allows to configure various options available for RTM API.
72 func (api *Client) NewRTMWithOptions(options *RTMOptions) *RTM {
73 result := &RTM{
74 Client: *api,
75 IncomingEvents: make(chan RTMEvent, 50),
76 outgoingMessages: make(chan OutgoingMessage, 20),
77 pings: make(map[int]time.Time),
78 isConnected: false,
79 wasIntentional: true,
80 killChannel: make(chan bool),
81 forcePing: make(chan bool),
82 rawEvents: make(chan json.RawMessage),
83 idGen: NewSafeID(1),
84 }
85
86 if options != nil {
87 result.useRTMStart = options.UseRTMStart
88 } else {
89 result.useRTMStart = true
90 }
91
92 return result
93 }
1616 // RTM represents a managed websocket connection. It also supports
1717 // all the methods of the `Client` type.
1818 //
19 // Create this element with Client's NewRTM().
19 // Create this element with Client's NewRTM() or NewRTMWithOptions(*RTMOptions)
2020 type RTM struct {
2121 idGen IDGenerator
2222 pings map[int]time.Time
3737
3838 // UserDetails upon connection
3939 info *Info
40
41 // useRTMStart should be set to true if you want to use
42 // rtm.start to connect to Slack, otherwise it will use
43 // rtm.connect
44 useRTMStart bool
4045 }
4146
42 // NewRTM returns a RTM, which provides a fully managed connection to
43 // Slack's websocket-based Real-Time Messaging protocol.
44 func newRTM(api *Client) *RTM {
45 return &RTM{
46 Client: *api,
47 IncomingEvents: make(chan RTMEvent, 50),
48 outgoingMessages: make(chan OutgoingMessage, 20),
49 pings: make(map[int]time.Time),
50 isConnected: false,
51 wasIntentional: true,
52 killChannel: make(chan bool),
53 forcePing: make(chan bool),
54 rawEvents: make(chan json.RawMessage),
55 idGen: NewSafeID(1),
56 }
47 // RTMOptions allows configuration of various options available for RTM messaging
48 //
49 // This structure will evolve in time so please make sure you are always using the
50 // named keys for every entry available as per Go 1 compatibility promise adding fields
51 // to this structure should not be considered a breaking change.
52 type RTMOptions struct {
53 // UseRTMStart set to true in order to use rtm.start or false to use rtm.connect
54 // As of 11th July 2017 you should prefer setting this to false, see:
55 // https://api.slack.com/changelog/2017-04-start-using-rtm-connect-and-stop-using-rtm-start
56 UseRTMStart bool
5757 }
5858
5959 // Disconnect and wait, blocking until a successful disconnection.
2828 connectionCount++
2929 // start trying to connect
3030 // the returned err is already passed onto the IncomingEvents channel
31 info, conn, err := rtm.connect(connectionCount)
31 info, conn, err := rtm.connect(connectionCount, rtm.useRTMStart)
3232 // if err != nil then the connection is sucessful - otherwise it is
3333 // fatal
3434 if err != nil {
6363 // connect attempts to connect to the slack websocket API. It handles any
6464 // errors that occur while connecting and will return once a connection
6565 // has been successfully opened.
66 func (rtm *RTM) connect(connectionCount int) (*Info, *websocket.Conn, error) {
66 // If useRTMStart is false then it uses rtm.connect to create the connection,
67 // otherwise it uses rtm.start.
68 func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocket.Conn, error) {
6769 // used to provide exponential backoff wait time with jitter before trying
6870 // to connect to slack again
6971 boff := &backoff{
8082 ConnectionCount: connectionCount,
8183 }}
8284 // attempt to start the connection
83 info, conn, err := rtm.startRTMAndDial()
85 info, conn, err := rtm.startRTMAndDial(useRTMStart)
8486 if err == nil {
8587 return info, conn, nil
8688 }
104106 }
105107 }
106108
107 // startRTMAndDial attemps to connect to the slack websocket. It returns the
108 // full information returned by the "rtm.start" method on the slack API.
109 func (rtm *RTM) startRTMAndDial() (*Info, *websocket.Conn, error) {
110 info, url, err := rtm.StartRTM()
109 // startRTMAndDial attempts to connect to the slack websocket. If useRTMStart is true,
110 // then it returns the full information returned by the "rtm.start" method on the
111 // slack API. Else it uses the "rtm.connect" method to connect
112 func (rtm *RTM) startRTMAndDial(useRTMStart bool) (*Info, *websocket.Conn, error) {
113 var info *Info
114 var url string
115 var err error
116
117 if useRTMStart {
118 info, url, err = rtm.StartRTM()
119 } else {
120 info, url, err = rtm.ConnectRTM()
121 }
111122 if err != nil {
112123 return nil, nil, err
113124 }