:shirt: Fix Lint and Vet Errors
Joe Fitzgerald
8 years ago
18 | 18 | fmt.Printf("%s\n", err) |
19 | 19 | return |
20 | 20 | } |
21 | fmt.Printf("Name: %s, Url: %s\n", file.Name, file.URL) | |
21 | fmt.Printf("Name: %s, URL: %s\n", file.Name, file.URL) | |
22 | 22 | |
23 | 23 | err = api.DeleteFile(file.ID) |
24 | 24 | if err != nil { |
38 | 38 | |
39 | 39 | // Post as the authenticated user. |
40 | 40 | postAsUserName = authTest.User |
41 | postAsUserID = authTest.UserId | |
41 | postAsUserID = authTest.UserID | |
42 | 42 | |
43 | 43 | // Posting to DM with self causes a conversation with slackbot. |
44 | 44 | postToUserName = authTest.User |
45 | postToUserID = authTest.UserId | |
45 | postToUserID = authTest.UserID | |
46 | 46 | |
47 | 47 | // Find the channel. |
48 | 48 | _, _, chanID, err := api.OpenIMChannel(postToUserID) |
18 | 18 | } |
19 | 19 | go wsAPI.HandleIncomingEvents(chReceiver) |
20 | 20 | go wsAPI.Keepalive(20 * time.Second) |
21 | go func(wsAPI *slack.SlackWS, chSender chan slack.OutgoingMessage) { | |
21 | go func(wsAPI *slack.WS, chSender chan slack.OutgoingMessage) { | |
22 | 22 | for { |
23 | 23 | select { |
24 | 24 | case msg := <-chSender: |
42 | 42 | case slack.LatencyReport: |
43 | 43 | a := msg.Data.(slack.LatencyReport) |
44 | 44 | fmt.Printf("Current latency: %v\n", a.Value) |
45 | case *slack.SlackWSError: | |
46 | error := msg.Data.(*slack.SlackWSError) | |
45 | case *slack.WSError: | |
46 | error := msg.Data.(*slack.WSError) | |
47 | 47 | fmt.Printf("Error: %d - %s\n", error.Code, error.Msg) |
48 | 48 | default: |
49 | 49 | fmt.Printf("Unexpected: %v\n", msg.Data) |
4 | 4 | "time" |
5 | 5 | ) |
6 | 6 | |
7 | // XXX: Need to implement | |
7 | // UserPrefs needs to be implemented | |
8 | 8 | type UserPrefs struct { |
9 | 9 | // "highlight_words":"", |
10 | 10 | // "user_colors":"", |
144 | 144 | // Info contains various details about Users, Channels, Bots and the authenticated user |
145 | 145 | // It is returned by StartRTM |
146 | 146 | type Info struct { |
147 | Url string `json:"url,omitempty"` | |
147 | URL string `json:"url,omitempty"` | |
148 | 148 | User *UserDetails `json:"self,omitempty"` |
149 | 149 | Team *Team `json:"team,omitempty"` |
150 | 150 | Users []User `json:"users,omitempty"` |
156 | 156 | |
157 | 157 | type infoResponseFull struct { |
158 | 158 | Info |
159 | SlackWSResponse | |
159 | WSResponse | |
160 | 160 | } |
161 | 161 | |
162 | 162 | // GetBotByID returns a bot given a bot id |
189 | 189 | return nil |
190 | 190 | } |
191 | 191 | |
192 | // GetGroupById returns a group given a group id | |
193 | func (info Info) GetGroupById(groupId string) *Group { | |
192 | // GetGroupByID returns a group given a group id | |
193 | func (info Info) GetGroupByID(groupID string) *Group { | |
194 | 194 | for _, group := range info.Groups { |
195 | if group.Id == groupId { | |
195 | if group.ID == groupID { | |
196 | 196 | return &group |
197 | 197 | } |
198 | 198 | } |
51 | 51 | // CommentId, or the combination of ChannelId and Timestamp must be |
52 | 52 | // specified. |
53 | 53 | type ItemRef struct { |
54 | ChannelId string `json:"channel"` | |
54 | Channel string `json:"channel"` | |
55 | 55 | Timestamp string `json:"timestamp"` |
56 | FileId string `json:"file"` | |
57 | CommentId string `json:"file_comment"` | |
56 | File string `json:"file"` | |
57 | Comment string `json:"file_comment"` | |
58 | 58 | } |
59 | 59 | |
60 | 60 | // NewRefToMessage initializes a reference to to a message. |
61 | func NewRefToMessage(channelID, timestamp string) ItemRef { | |
62 | return ItemRef{ChannelId: channelID, Timestamp: timestamp} | |
61 | func NewRefToMessage(channel, timestamp string) ItemRef { | |
62 | return ItemRef{Channel: channel, Timestamp: timestamp} | |
63 | 63 | } |
64 | 64 | |
65 | 65 | // NewRefToFile initializes a reference to a file. |
66 | func NewRefToFile(fileID string) ItemRef { | |
67 | return ItemRef{FileId: fileID} | |
66 | func NewRefToFile(file string) ItemRef { | |
67 | return ItemRef{File: file} | |
68 | 68 | } |
69 | 69 | |
70 | 70 | // NewRefToComment initializes a reference to a file comment. |
71 | func NewRefToComment(commentID string) ItemRef { | |
72 | return ItemRef{CommentId: commentID} | |
71 | func NewRefToComment(comment string) ItemRef { | |
72 | return ItemRef{Comment: comment} | |
73 | 73 | } |
77 | 77 | |
78 | 78 | func TestNewRefToMessage(t *testing.T) { |
79 | 79 | ref := NewRefToMessage("chan", "ts") |
80 | if got, want := ref.ChannelId, "chan"; got != want { | |
81 | t.Errorf("ChannelId got %s, want %s", got, want) | |
80 | if got, want := ref.Channel, "chan"; got != want { | |
81 | t.Errorf("Channel got %s, want %s", got, want) | |
82 | 82 | } |
83 | 83 | if got, want := ref.Timestamp, "ts"; got != want { |
84 | 84 | t.Errorf("Timestamp got %s, want %s", got, want) |
85 | 85 | } |
86 | if got, want := ref.FileId, ""; got != want { | |
87 | t.Errorf("FileId got %s, want %s", got, want) | |
86 | if got, want := ref.File, ""; got != want { | |
87 | t.Errorf("File got %s, want %s", got, want) | |
88 | 88 | } |
89 | if got, want := ref.CommentId, ""; got != want { | |
90 | t.Errorf("CommentId got %s, want %s", got, want) | |
89 | if got, want := ref.Comment, ""; got != want { | |
90 | t.Errorf("Comment got %s, want %s", got, want) | |
91 | 91 | } |
92 | 92 | } |
93 | 93 | |
94 | 94 | func TestNewRefToFile(t *testing.T) { |
95 | 95 | ref := NewRefToFile("file") |
96 | if got, want := ref.ChannelId, ""; got != want { | |
97 | t.Errorf("ChannelId got %s, want %s", got, want) | |
96 | if got, want := ref.Channel, ""; got != want { | |
97 | t.Errorf("Channel got %s, want %s", got, want) | |
98 | 98 | } |
99 | 99 | if got, want := ref.Timestamp, ""; got != want { |
100 | 100 | t.Errorf("Timestamp got %s, want %s", got, want) |
101 | 101 | } |
102 | if got, want := ref.FileId, "file"; got != want { | |
103 | t.Errorf("FileId got %s, want %s", got, want) | |
102 | if got, want := ref.File, "file"; got != want { | |
103 | t.Errorf("File got %s, want %s", got, want) | |
104 | 104 | } |
105 | if got, want := ref.CommentId, ""; got != want { | |
106 | t.Errorf("CommentId got %s, want %s", got, want) | |
105 | if got, want := ref.Comment, ""; got != want { | |
106 | t.Errorf("Comment got %s, want %s", got, want) | |
107 | 107 | } |
108 | 108 | } |
109 | 109 | |
110 | 110 | func TestNewRefToComment(t *testing.T) { |
111 | 111 | ref := NewRefToComment("file_comment") |
112 | if got, want := ref.ChannelId, ""; got != want { | |
113 | t.Errorf("ChannelId got %s, want %s", got, want) | |
112 | if got, want := ref.Channel, ""; got != want { | |
113 | t.Errorf("Channel got %s, want %s", got, want) | |
114 | 114 | } |
115 | 115 | if got, want := ref.Timestamp, ""; got != want { |
116 | 116 | t.Errorf("Timestamp got %s, want %s", got, want) |
117 | 117 | } |
118 | if got, want := ref.FileId, ""; got != want { | |
119 | t.Errorf("FileId got %s, want %s", got, want) | |
118 | if got, want := ref.File, ""; got != want { | |
119 | t.Errorf("File got %s, want %s", got, want) | |
120 | 120 | } |
121 | if got, want := ref.CommentId, "file_comment"; got != want { | |
122 | t.Errorf("CommentId got %s, want %s", got, want) | |
121 | if got, want := ref.Comment, "file_comment"; got != want { | |
122 | t.Errorf("Comment got %s, want %s", got, want) | |
123 | 123 | } |
124 | 124 | } |
101 | 101 | } |
102 | 102 | |
103 | 103 | // NewOutgoingMessage prepares an OutgoingMessage that the user can use to send a message |
104 | func (api *SlackWS) NewOutgoingMessage(text string, channel string) *OutgoingMessage { | |
104 | func (api *WS) NewOutgoingMessage(text string, channel string) *OutgoingMessage { | |
105 | 105 | api.mutex.Lock() |
106 | 106 | defer api.mutex.Unlock() |
107 | api.messageId++ | |
107 | api.messageID++ | |
108 | 108 | return &OutgoingMessage{ |
109 | ID: api.messageId, | |
109 | ID: api.messageID, | |
110 | 110 | Type: "message", |
111 | 111 | Channel: channel, |
112 | 112 | Text: text, |
58 | 58 | } |
59 | 59 | |
60 | 60 | const ( |
61 | DEFAULT_REACTIONS_USERID = "" | |
62 | DEFAULT_REACTIONS_COUNT = 100 | |
63 | DEFAULT_REACTIONS_PAGE = 1 | |
64 | DEFAULT_REACTIONS_FULL = false | |
61 | DEFAULT_REACTIONS_USER = "" | |
62 | DEFAULT_REACTIONS_COUNT = 100 | |
63 | DEFAULT_REACTIONS_PAGE = 1 | |
64 | DEFAULT_REACTIONS_FULL = false | |
65 | 65 | ) |
66 | 66 | |
67 | 67 | // ListReactionsParameters is the inputs to find all reactions by a user. |
68 | 68 | type ListReactionsParameters struct { |
69 | UserId string | |
70 | Count int | |
71 | Page int | |
72 | Full bool | |
69 | User string | |
70 | Count int | |
71 | Page int | |
72 | Full bool | |
73 | 73 | } |
74 | 74 | |
75 | 75 | // NewListReactionsParameters initializes the inputs to find all reactions |
76 | 76 | // performed by a user. |
77 | 77 | func NewListReactionsParameters() ListReactionsParameters { |
78 | 78 | return ListReactionsParameters{ |
79 | UserId: DEFAULT_REACTIONS_USERID, | |
80 | Count: DEFAULT_REACTIONS_COUNT, | |
81 | Page: DEFAULT_REACTIONS_PAGE, | |
82 | Full: DEFAULT_REACTIONS_FULL, | |
79 | User: DEFAULT_REACTIONS_USER, | |
80 | Count: DEFAULT_REACTIONS_COUNT, | |
81 | Page: DEFAULT_REACTIONS_PAGE, | |
82 | Full: DEFAULT_REACTIONS_FULL, | |
83 | 83 | } |
84 | 84 | } |
85 | 85 | |
135 | 135 | if name != "" { |
136 | 136 | values.Set("name", name) |
137 | 137 | } |
138 | if item.ChannelId != "" { | |
139 | values.Set("channel", string(item.ChannelId)) | |
138 | if item.Channel != "" { | |
139 | values.Set("channel", string(item.Channel)) | |
140 | 140 | } |
141 | 141 | if item.Timestamp != "" { |
142 | 142 | values.Set("timestamp", string(item.Timestamp)) |
143 | 143 | } |
144 | if item.FileId != "" { | |
145 | values.Set("file", string(item.FileId)) | |
146 | } | |
147 | if item.CommentId != "" { | |
148 | values.Set("file_comment", string(item.CommentId)) | |
144 | if item.File != "" { | |
145 | values.Set("file", string(item.File)) | |
146 | } | |
147 | if item.Comment != "" { | |
148 | values.Set("file_comment", string(item.Comment)) | |
149 | 149 | } |
150 | 150 | response := &SlackResponse{} |
151 | 151 | if err := parseResponse("reactions.add", values, response, api.debug); err != nil { |
165 | 165 | if name != "" { |
166 | 166 | values.Set("name", name) |
167 | 167 | } |
168 | if item.ChannelId != "" { | |
169 | values.Set("channel", string(item.ChannelId)) | |
168 | if item.Channel != "" { | |
169 | values.Set("channel", string(item.Channel)) | |
170 | 170 | } |
171 | 171 | if item.Timestamp != "" { |
172 | 172 | values.Set("timestamp", string(item.Timestamp)) |
173 | 173 | } |
174 | if item.FileId != "" { | |
175 | values.Set("file", string(item.FileId)) | |
176 | } | |
177 | if item.CommentId != "" { | |
178 | values.Set("file_comment", string(item.CommentId)) | |
174 | if item.File != "" { | |
175 | values.Set("file", string(item.File)) | |
176 | } | |
177 | if item.Comment != "" { | |
178 | values.Set("file_comment", string(item.Comment)) | |
179 | 179 | } |
180 | 180 | response := &SlackResponse{} |
181 | 181 | if err := parseResponse("reactions.remove", values, response, api.debug); err != nil { |
192 | 192 | values := url.Values{ |
193 | 193 | "token": {api.config.token}, |
194 | 194 | } |
195 | if item.ChannelId != "" { | |
196 | values.Set("channel", string(item.ChannelId)) | |
195 | if item.Channel != "" { | |
196 | values.Set("channel", string(item.Channel)) | |
197 | 197 | } |
198 | 198 | if item.Timestamp != "" { |
199 | 199 | values.Set("timestamp", string(item.Timestamp)) |
200 | 200 | } |
201 | if item.FileId != "" { | |
202 | values.Set("file", string(item.FileId)) | |
203 | } | |
204 | if item.CommentId != "" { | |
205 | values.Set("file_comment", string(item.CommentId)) | |
201 | if item.File != "" { | |
202 | values.Set("file", string(item.File)) | |
203 | } | |
204 | if item.Comment != "" { | |
205 | values.Set("file_comment", string(item.Comment)) | |
206 | 206 | } |
207 | 207 | if params.Full != DEFAULT_REACTIONS_FULL { |
208 | 208 | values.Set("full", strconv.FormatBool(params.Full)) |
222 | 222 | values := url.Values{ |
223 | 223 | "token": {api.config.token}, |
224 | 224 | } |
225 | if params.UserId != DEFAULT_REACTIONS_USERID { | |
226 | values.Add("user", params.UserId) | |
225 | if params.User != DEFAULT_REACTIONS_USER { | |
226 | values.Add("user", params.User) | |
227 | 227 | } |
228 | 228 | if params.Count != DEFAULT_REACTIONS_COUNT { |
229 | 229 | values.Add("count", strconv.Itoa(params.Count)) |
336 | 336 | }, |
337 | 337 | } |
338 | 338 | wantParams := map[string]string{ |
339 | "user": "UserID", | |
339 | "user": "User", | |
340 | 340 | "count": "200", |
341 | 341 | "page": "2", |
342 | 342 | "full": "true", |
343 | 343 | } |
344 | 344 | params := NewListReactionsParameters() |
345 | params.UserId = "UserID" | |
345 | params.User = "User" | |
346 | 346 | params.Count = 200 |
347 | 347 | params.Page = 2 |
348 | 348 | params.Full = true |
16 | 16 | } |
17 | 17 | |
18 | 18 | type AuthTestResponse struct { |
19 | Url string `json:"url"` | |
19 | URL string `json:"url"` | |
20 | 20 | Team string `json:"team"` |
21 | 21 | User string `json:"user"` |
22 | 22 | TeamID string `json:"team_id"` |
117 | 117 | for i, test := range tests { |
118 | 118 | sh = newStarsHandler() |
119 | 119 | sh.response = test.json |
120 | response_items, response_paging, err := api.GetStarred(test.params) | |
120 | responseItems, responsePaging, err := api.GetStarred(test.params) | |
121 | 121 | if err != nil { |
122 | 122 | t.Fatalf("%d Unexpected error: %s", i, err) |
123 | 123 | } |
124 | 124 | if !reflect.DeepEqual(sh.gotParams, test.wantParams) { |
125 | 125 | t.Errorf("%d got %v; want %v", i, sh.gotParams, test.wantParams) |
126 | 126 | } |
127 | if !reflect.DeepEqual(response_items, test.starredItems) { | |
128 | t.Errorf("%d got %v; want %v", i, response_items, test.starredItems) | |
127 | if !reflect.DeepEqual(responseItems, test.starredItems) { | |
128 | t.Errorf("%d got %v; want %v", i, responseItems, test.starredItems) | |
129 | 129 | } |
130 | if !reflect.DeepEqual(response_paging, test.paging) { | |
131 | t.Errorf("%d got %v; want %v", i, response_paging, test.paging) | |
130 | if !reflect.DeepEqual(responsePaging, test.paging) { | |
131 | t.Errorf("%d got %v; want %v", i, responsePaging, test.paging) | |
132 | 132 | } |
133 | 133 | } |
134 | 134 | } |
15 | 15 | |
16 | 16 | type MessageEvent Message |
17 | 17 | |
18 | type SlackWS struct { | |
18 | type WS struct { | |
19 | 19 | conn *websocket.Conn |
20 | messageId int | |
20 | messageID int | |
21 | 21 | mutex sync.Mutex |
22 | 22 | pings map[int]time.Time |
23 | 23 | Slack |
28 | 28 | ReplyTo int `json:"reply_to"` |
29 | 29 | Timestamp string `json:"ts"` |
30 | 30 | Text string `json:"text"` |
31 | SlackWSResponse | |
32 | } | |
33 | ||
34 | type SlackWSResponse struct { | |
35 | Ok bool `json:"ok"` | |
36 | Error *SlackWSError `json:"error"` | |
37 | } | |
38 | ||
39 | type SlackWSError struct { | |
31 | WSResponse | |
32 | } | |
33 | ||
34 | type WSResponse struct { | |
35 | Ok bool `json:"ok"` | |
36 | Error *WSError `json:"error"` | |
37 | } | |
38 | ||
39 | type WSError struct { | |
40 | 40 | Code int |
41 | 41 | Msg string |
42 | 42 | } |
63 | 63 | return fmt.Sprintf("\"%s\"", tm.Format("Mon Jan _2")) |
64 | 64 | } |
65 | 65 | |
66 | func (s SlackWSError) Error() string { | |
66 | func (s WSError) Error() string { | |
67 | 67 | return s.Msg |
68 | 68 | } |
69 | 69 | |
70 | 70 | var portMapping = map[string]string{"ws": "80", "wss": "443"} |
71 | 71 | |
72 | func fixUrlPort(orig string) (string, error) { | |
72 | func fixURLPort(orig string) (string, error) { | |
73 | 73 | urlObj, err := url.ParseRequestURI(orig) |
74 | 74 | if err != nil { |
75 | 75 | return "", err |
81 | 81 | return orig, nil |
82 | 82 | } |
83 | 83 | |
84 | func (api *Slack) StartRTM(protocol, origin string) (*SlackWS, error) { | |
84 | func (api *Slack) StartRTM(protocol, origin string) (*WS, error) { | |
85 | 85 | response := &infoResponseFull{} |
86 | 86 | err := parseResponse("rtm.start", url.Values{"token": {api.config.token}}, response, api.debug) |
87 | 87 | if err != nil { |
94 | 94 | // websocket.Dial does not accept url without the port (yet) |
95 | 95 | // Fixed by: https://github.com/golang/net/commit/5058c78c3627b31e484a81463acd51c7cecc06f3 |
96 | 96 | // but slack returns the address with no port, so we have to fix it |
97 | api.info.Url, err = fixUrlPort(api.info.Url) | |
97 | api.info.URL, err = fixURLPort(api.info.URL) | |
98 | 98 | if err != nil { |
99 | 99 | return nil, err |
100 | 100 | } |
101 | 101 | api.config.protocol, api.config.origin = protocol, origin |
102 | wsApi := &SlackWS{Slack: *api} | |
103 | wsApi.conn, err = websocket.Dial(api.info.Url, api.config.protocol, api.config.origin) | |
102 | wsAPI := &WS{Slack: *api} | |
103 | wsAPI.conn, err = websocket.Dial(api.info.URL, api.config.protocol, api.config.origin) | |
104 | 104 | if err != nil { |
105 | 105 | return nil, err |
106 | 106 | } |
107 | wsApi.pings = make(map[int]time.Time) | |
108 | return wsApi, nil | |
109 | } | |
110 | ||
111 | func (api *SlackWS) Ping() error { | |
107 | wsAPI.pings = make(map[int]time.Time) | |
108 | return wsAPI, nil | |
109 | } | |
110 | ||
111 | func (api *WS) Ping() error { | |
112 | 112 | api.mutex.Lock() |
113 | 113 | defer api.mutex.Unlock() |
114 | api.messageId++ | |
115 | msg := &Ping{ID: api.messageId, Type: "ping"} | |
114 | api.messageID++ | |
115 | msg := &Ping{ID: api.messageID, Type: "ping"} | |
116 | 116 | if err := websocket.JSON.Send(api.conn, msg); err != nil { |
117 | 117 | return err |
118 | 118 | } |
119 | 119 | // TODO: What happens if we already have this id? |
120 | api.pings[api.messageId] = time.Now() | |
120 | api.pings[api.messageID] = time.Now() | |
121 | 121 | return nil |
122 | 122 | } |
123 | 123 | |
124 | func (api *SlackWS) Keepalive(interval time.Duration) { | |
124 | func (api *WS) Keepalive(interval time.Duration) { | |
125 | 125 | ticker := time.NewTicker(interval) |
126 | 126 | defer ticker.Stop() |
127 | 127 | |
135 | 135 | } |
136 | 136 | } |
137 | 137 | |
138 | func (api *SlackWS) SendMessage(msg *OutgoingMessage) error { | |
138 | func (api *WS) SendMessage(msg *OutgoingMessage) error { | |
139 | 139 | if msg == nil { |
140 | 140 | return fmt.Errorf("Can't send a nil message") |
141 | 141 | } |
146 | 146 | return nil |
147 | 147 | } |
148 | 148 | |
149 | func (api *SlackWS) HandleIncomingEvents(ch chan SlackEvent) { | |
149 | func (api *WS) HandleIncomingEvents(ch chan SlackEvent) { | |
150 | 150 | for { |
151 | 151 | event := json.RawMessage{} |
152 | 152 | if err := websocket.JSON.Receive(api.conn, &event); err == io.EOF { |
156 | 156 | //} |
157 | 157 | // should we reconnect here? |
158 | 158 | if !api.conn.IsClientConn() { |
159 | api.conn, err = websocket.Dial(api.info.Url, api.config.protocol, api.config.origin) | |
159 | api.conn, err = websocket.Dial(api.info.URL, api.config.protocol, api.config.origin) | |
160 | 160 | if err != nil { |
161 | 161 | log.Panic(err) |
162 | 162 | } |
179 | 179 | } |
180 | 180 | } |
181 | 181 | |
182 | func (api *SlackWS) handleEvent(ch chan SlackEvent, event json.RawMessage) { | |
182 | func (api *WS) handleEvent(ch chan SlackEvent, event json.RawMessage) { | |
183 | 183 | em := Event{} |
184 | 184 | err := json.Unmarshal(event, &em) |
185 | 185 | if err != nil { |