Pulling changes from joefitzgerald
Norberto Lopes
8 years ago
28 | 28 | return |
29 | 29 | } |
30 | 30 | for _, group := range groups { |
31 | fmt.Printf("Id: %s, Name: %s\n", group.Id, group.Name) | |
31 | fmt.Printf("ID: %s, Name: %s\n", group.ID, group.Name) | |
32 | 32 | } |
33 | 33 | } |
34 | 34 | |
47 | 47 | fmt.Printf("%s\n", err) |
48 | 48 | return |
49 | 49 | } |
50 | fmt.Printf("Id: %s, Fullname: %s, Email: %s\n", user.Id, user.Profile.RealName, user.Profile.Email) | |
50 | fmt.Printf("ID: %s, Fullname: %s, Email: %s\n", user.ID, user.Profile.RealName, user.Profile.Email) | |
51 | 51 | } |
52 | 52 | |
53 | 53 | ## Why? |
60 | 60 | Anyone is welcome to contribute. Either open a PR or create an issue. |
61 | 61 | |
62 | 62 | ## License |
63 | BSD 2 Clause license⏎ | |
63 | BSD 2 Clause license |
24 | 24 | return adminResponse, nil |
25 | 25 | } |
26 | 26 | |
27 | // DisableUser disabled a user account | |
28 | func (api *Slack) DisableUser(teamName string, user string) error { | |
29 | values := url.Values{ | |
30 | "user": {user}, | |
31 | "token": {api.config.token}, | |
32 | "set_active": {"true"}, | |
33 | "_attempts": {"1"}, | |
34 | } | |
35 | ||
36 | _, err := adminRequest("setInactive", teamName, values, api.debug) | |
37 | if err != nil { | |
38 | return fmt.Errorf("Failed to disable user (%s): %s", user, err) | |
39 | } | |
40 | ||
41 | return nil | |
42 | } | |
43 | ||
44 | // InviteGuest invites a user to Slack as a single-channel guest | |
27 | 45 | func (api *Slack) InviteGuest( |
28 | 46 | teamName string, |
29 | channelID string, | |
47 | channel string, | |
30 | 48 | firstName string, |
31 | 49 | lastName string, |
32 | 50 | emailAddress string, |
33 | 51 | ) error { |
34 | 52 | values := url.Values{ |
35 | 53 | "email": {emailAddress}, |
36 | "channels": {channelID}, | |
54 | "channels": {channel}, | |
37 | 55 | "first_name": {firstName}, |
38 | 56 | "last_name": {lastName}, |
39 | 57 | "ultra_restricted": {"1"}, |
50 | 68 | return nil |
51 | 69 | } |
52 | 70 | |
71 | // InviteRestricted invites a user to Slack as a restricted account | |
53 | 72 | func (api *Slack) InviteRestricted( |
54 | 73 | teamName string, |
55 | channelID string, | |
74 | channel string, | |
56 | 75 | firstName string, |
57 | 76 | lastName string, |
58 | 77 | emailAddress string, |
59 | 78 | ) error { |
60 | 79 | values := url.Values{ |
61 | 80 | "email": {emailAddress}, |
62 | "channels": {channelID}, | |
81 | "channels": {channel}, | |
63 | 82 | "first_name": {firstName}, |
64 | 83 | "last_name": {lastName}, |
65 | 84 | "restricted": {"1"}, |
75 | 94 | |
76 | 95 | return nil |
77 | 96 | } |
97 | ||
98 | // SetRegular enables the specified user | |
99 | func (api *Slack) SetRegular(teamName string, user string) error { | |
100 | values := url.Values{ | |
101 | "user": {user}, | |
102 | "token": {api.config.token}, | |
103 | "set_active": {"true"}, | |
104 | "_attempts": {"1"}, | |
105 | } | |
106 | ||
107 | _, err := adminRequest("setRegular", teamName, values, api.debug) | |
108 | if err != nil { | |
109 | return fmt.Errorf("Failed to change the user (%s) to a regular user: %s", user, err) | |
110 | } | |
111 | ||
112 | return nil | |
113 | } | |
114 | ||
115 | // SendSSOBindingEmail sends an SSO binding email to the specified user | |
116 | func (api *Slack) SendSSOBindingEmail(teamName string, user string) error { | |
117 | values := url.Values{ | |
118 | "user": {user}, | |
119 | "token": {api.config.token}, | |
120 | "set_active": {"true"}, | |
121 | "_attempts": {"1"}, | |
122 | } | |
123 | ||
124 | _, err := adminRequest("sendSSOBind", teamName, values, api.debug) | |
125 | if err != nil { | |
126 | return fmt.Errorf("Failed to send SSO binding email for user (%s): %s", user, err) | |
127 | } | |
128 | ||
129 | return nil | |
130 | } |
0 | package slack | |
1 | ||
2 | // AttachmentField contains information for an attachment field | |
3 | // An Attachment can contain multiple of these | |
4 | type AttachmentField struct { | |
5 | Title string `json:"title"` | |
6 | Value string `json:"value"` | |
7 | Short bool `json:"short"` | |
8 | } | |
9 | ||
10 | // Attachment contains all the information for an attachment | |
11 | type Attachment struct { | |
12 | Color string `json:"color,omitempty"` | |
13 | Fallback string `json:"fallback"` | |
14 | ||
15 | AuthorName string `json:"author_name,omitempty"` | |
16 | AuthorSubname string `json:"author_subname,omitempty"` | |
17 | AuthorLink string `json:"author_link,omitempty"` | |
18 | AuthorIcon string `json:"author_icon,omitempty"` | |
19 | ||
20 | Title string `json:"title,omitempty"` | |
21 | TitleLink string `json:"title_link,omitempty"` | |
22 | Pretext string `json:"pretext,omitempty"` | |
23 | Text string `json:"text"` | |
24 | ||
25 | ImageURL string `json:"image_url,omitempty"` | |
26 | ThumbURL string `json:"thumb_url,omitempty"` | |
27 | ||
28 | Fields []AttachmentField `json:"fields,omitempty"` | |
29 | MarkdownIn []string `json:"mrkdwn_in,omitempty"` | |
30 | } |
29 | 29 | LastSet JSONTime `json:"last_set"` |
30 | 30 | } |
31 | 31 | |
32 | type BaseChannel struct { | |
33 | Id string `json:"id"` | |
34 | Created JSONTime `json:"created"` | |
35 | IsOpen bool `json:"is_open"` | |
36 | LastRead string `json:"last_read,omitempty"` | |
37 | Latest Message `json:"latest,omitempty"` | |
38 | UnreadCount int `json:"unread_count,omitempty"` | |
39 | UnreadCountDisplay int `json:"unread_count_display,omitempty"` | |
32 | type baseChannel struct { | |
33 | ID string `json:"id"` | |
34 | Created JSONTime `json:"created"` | |
35 | IsOpen bool `json:"is_open"` | |
36 | LastRead string `json:"last_read,omitempty"` | |
37 | Latest Message `json:"latest,omitempty"` | |
38 | UnreadCount int `json:"unread_count,omitempty"` | |
39 | UnreadCountDisplay int `json:"unread_count_display,omitempty"` | |
40 | 40 | } |
41 | 41 | |
42 | 42 | // Channel contains information about the channel |
43 | 43 | type Channel struct { |
44 | BaseChannel | |
45 | Name string `json:"name"` | |
46 | IsChannel bool `json:"is_channel"` | |
47 | Creator string `json:"creator"` | |
48 | IsArchived bool `json:"is_archived"` | |
49 | IsGeneral bool `json:"is_general"` | |
50 | Members []string `json:"members"` | |
51 | Topic ChannelTopic `json:"topic"` | |
52 | Purpose ChannelPurpose `json:"purpose"` | |
53 | IsMember bool `json:"is_member"` | |
54 | LastRead string `json:"last_read,omitempty"` | |
55 | Latest *Message `json:"latest,omitempty"` | |
56 | UnreadCount int `json:"unread_count,omitempty"` | |
57 | NumMembers int `json:"num_members,omitempty"` | |
44 | baseChannel | |
45 | Name string `json:"name"` | |
46 | IsChannel bool `json:"is_channel"` | |
47 | Creator string `json:"creator"` | |
48 | IsArchived bool `json:"is_archived"` | |
49 | IsGeneral bool `json:"is_general"` | |
50 | Members []string `json:"members"` | |
51 | Topic ChannelTopic `json:"topic"` | |
52 | Purpose ChannelPurpose `json:"purpose"` | |
53 | IsMember bool `json:"is_member"` | |
54 | LastRead string `json:"last_read,omitempty"` | |
55 | Latest *Message `json:"latest,omitempty"` | |
56 | UnreadCount int `json:"unread_count,omitempty"` | |
57 | NumMembers int `json:"num_members,omitempty"` | |
58 | 58 | } |
59 | 59 | |
60 | 60 | func channelRequest(path string, values url.Values, debug bool) (*channelResponseFull, error) { |
70 | 70 | } |
71 | 71 | |
72 | 72 | // ArchiveChannel archives the given channel |
73 | func (api *Slack) ArchiveChannel(channelId string) error { | |
74 | values := url.Values{ | |
75 | "token": {api.config.token}, | |
76 | "channel": {channelId}, | |
73 | func (api *Slack) ArchiveChannel(channel string) error { | |
74 | values := url.Values{ | |
75 | "token": {api.config.token}, | |
76 | "channel": {channel}, | |
77 | 77 | } |
78 | 78 | _, err := channelRequest("channels.archive", values, api.debug) |
79 | 79 | if err != nil { |
83 | 83 | } |
84 | 84 | |
85 | 85 | // UnarchiveChannel unarchives the given channel |
86 | func (api *Slack) UnarchiveChannel(channelId string) error { | |
87 | values := url.Values{ | |
88 | "token": {api.config.token}, | |
89 | "channel": {channelId}, | |
86 | func (api *Slack) UnarchiveChannel(channel string) error { | |
87 | values := url.Values{ | |
88 | "token": {api.config.token}, | |
89 | "channel": {channel}, | |
90 | 90 | } |
91 | 91 | _, err := channelRequest("channels.unarchive", values, api.debug) |
92 | 92 | if err != nil { |
109 | 109 | } |
110 | 110 | |
111 | 111 | // GetChannelHistory retrieves the channel history |
112 | func (api *Slack) GetChannelHistory(channelId string, params HistoryParameters) (*History, error) { | |
113 | values := url.Values{ | |
114 | "token": {api.config.token}, | |
115 | "channel": {channelId}, | |
112 | func (api *Slack) GetChannelHistory(channel string, params HistoryParameters) (*History, error) { | |
113 | values := url.Values{ | |
114 | "token": {api.config.token}, | |
115 | "channel": {channel}, | |
116 | 116 | } |
117 | 117 | if params.Latest != DEFAULT_HISTORY_LATEST { |
118 | 118 | values.Add("latest", params.Latest) |
138 | 138 | } |
139 | 139 | |
140 | 140 | // GetChannelInfo retrieves the given channel |
141 | func (api *Slack) GetChannelInfo(channelId string) (*Channel, error) { | |
142 | values := url.Values{ | |
143 | "token": {api.config.token}, | |
144 | "channel": {channelId}, | |
141 | func (api *Slack) GetChannelInfo(channel string) (*Channel, error) { | |
142 | values := url.Values{ | |
143 | "token": {api.config.token}, | |
144 | "channel": {channel}, | |
145 | 145 | } |
146 | 146 | response, err := channelRequest("channels.info", values, api.debug) |
147 | 147 | if err != nil { |
151 | 151 | } |
152 | 152 | |
153 | 153 | // InviteUserToChannel invites a user to a given channel and returns a *Channel |
154 | func (api *Slack) InviteUserToChannel(channelId, userId string) (*Channel, error) { | |
155 | values := url.Values{ | |
156 | "token": {api.config.token}, | |
157 | "channel": {channelId}, | |
158 | "user": {userId}, | |
154 | func (api *Slack) InviteUserToChannel(channel, user string) (*Channel, error) { | |
155 | values := url.Values{ | |
156 | "token": {api.config.token}, | |
157 | "channel": {channel}, | |
158 | "user": {user}, | |
159 | 159 | } |
160 | 160 | response, err := channelRequest("channels.invite", values, api.debug) |
161 | 161 | if err != nil { |
178 | 178 | } |
179 | 179 | |
180 | 180 | // LeaveChannel makes the authenticated user leave the given channel |
181 | func (api *Slack) LeaveChannel(channelId string) (bool, error) { | |
182 | values := url.Values{ | |
183 | "token": {api.config.token}, | |
184 | "channel": {channelId}, | |
181 | func (api *Slack) LeaveChannel(channel string) (bool, error) { | |
182 | values := url.Values{ | |
183 | "token": {api.config.token}, | |
184 | "channel": {channel}, | |
185 | 185 | } |
186 | 186 | response, err := channelRequest("channels.leave", values, api.debug) |
187 | 187 | if err != nil { |
194 | 194 | } |
195 | 195 | |
196 | 196 | // KickUserFromChannel kicks a user from a given channel |
197 | func (api *Slack) KickUserFromChannel(channelId, userId string) error { | |
198 | values := url.Values{ | |
199 | "token": {api.config.token}, | |
200 | "channel": {channelId}, | |
201 | "user": {userId}, | |
197 | func (api *Slack) KickUserFromChannel(channel, user string) error { | |
198 | values := url.Values{ | |
199 | "token": {api.config.token}, | |
200 | "channel": {channel}, | |
201 | "user": {user}, | |
202 | 202 | } |
203 | 203 | _, err := channelRequest("channels.kick", values, api.debug) |
204 | 204 | if err != nil { |
227 | 227 | // timer before making the call. In this way, any further updates needed during the timeout will not generate extra calls |
228 | 228 | // (just one per channel). This is useful for when reading scroll-back history, or following a busy live channel. A |
229 | 229 | // timeout of 5 seconds is a good starting point. Be sure to flush these calls on shutdown/logout. |
230 | func (api *Slack) SetChannelReadMark(channelId, ts string) error { | |
231 | values := url.Values{ | |
232 | "token": {api.config.token}, | |
233 | "channel": {channelId}, | |
230 | func (api *Slack) SetChannelReadMark(channel, ts string) error { | |
231 | values := url.Values{ | |
232 | "token": {api.config.token}, | |
233 | "channel": {channel}, | |
234 | 234 | "ts": {ts}, |
235 | 235 | } |
236 | 236 | _, err := channelRequest("channels.mark", values, api.debug) |
241 | 241 | } |
242 | 242 | |
243 | 243 | // RenameChannel renames a given channel |
244 | func (api *Slack) RenameChannel(channelId, name string) (*Channel, error) { | |
245 | values := url.Values{ | |
246 | "token": {api.config.token}, | |
247 | "channel": {channelId}, | |
244 | func (api *Slack) RenameChannel(channel, name string) (*Channel, error) { | |
245 | values := url.Values{ | |
246 | "token": {api.config.token}, | |
247 | "channel": {channel}, | |
248 | 248 | "name": {name}, |
249 | 249 | } |
250 | 250 | // XXX: the created entry in this call returns a string instead of a number |
259 | 259 | |
260 | 260 | // SetChannelPurpose sets the channel purpose and returns the purpose that was |
261 | 261 | // successfully set |
262 | func (api *Slack) SetChannelPurpose(channelId, purpose string) (string, error) { | |
263 | values := url.Values{ | |
264 | "token": {api.config.token}, | |
265 | "channel": {channelId}, | |
262 | func (api *Slack) SetChannelPurpose(channel, purpose string) (string, error) { | |
263 | values := url.Values{ | |
264 | "token": {api.config.token}, | |
265 | "channel": {channel}, | |
266 | 266 | "purpose": {purpose}, |
267 | 267 | } |
268 | 268 | response, err := channelRequest("channels.setPurpose", values, api.debug) |
273 | 273 | } |
274 | 274 | |
275 | 275 | // SetChannelTopic sets the channel topic and returns the topic that was successfully set |
276 | func (api *Slack) SetChannelTopic(channelId, topic string) (string, error) { | |
277 | values := url.Values{ | |
278 | "token": {api.config.token}, | |
279 | "channel": {channelId}, | |
276 | func (api *Slack) SetChannelTopic(channel, topic string) (string, error) { | |
277 | values := url.Values{ | |
278 | "token": {api.config.token}, | |
279 | "channel": {channel}, | |
280 | 280 | "topic": {topic}, |
281 | 281 | } |
282 | 282 | response, err := channelRequest("channels.setTopic", values, api.debug) |
20 | 20 | ) |
21 | 21 | |
22 | 22 | type chatResponseFull struct { |
23 | ChannelId string `json:"channel"` | |
23 | Channel string `json:"channel"` | |
24 | 24 | Timestamp string `json:"ts"` |
25 | 25 | Text string `json:"text"` |
26 | 26 | SlackResponse |
27 | } | |
28 | ||
29 | // AttachmentField contains information for an attachment field | |
30 | // An Attachment can contain multiple of these | |
31 | type AttachmentField struct { | |
32 | Title string `json:"title"` | |
33 | Value string `json:"value"` | |
34 | Short bool `json:"short"` | |
35 | } | |
36 | ||
37 | // Attachment contains all the information for an attachment | |
38 | type Attachment struct { | |
39 | Fallback string `json:"fallback"` | |
40 | ||
41 | Color string `json:"color,omitempty"` | |
42 | ||
43 | Pretext string `json:"pretext,omitempty"` | |
44 | ||
45 | AuthorName string `json:"author_name,omitempty"` | |
46 | AuthorLink string `json:"author_link,omitempty"` | |
47 | AuthorIcon string `json:"author_icon,omitempty"` | |
48 | ||
49 | Title string `json:"title,omitempty"` | |
50 | TitleLink string `json:"title_link,omitempty"` | |
51 | ||
52 | Text string `json:"text"` | |
53 | ||
54 | ImageURL string `json:"image_url,omitempty"` | |
55 | ThumbURL string `json:"thumb_url,omitempty"` | |
56 | ||
57 | Fields []AttachmentField `json:"fields,omitempty"` | |
58 | ||
59 | MarkdownIn []string `json:"mrkdwn_in,omitempty"` | |
60 | 27 | } |
61 | 28 | |
62 | 29 | // PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request |
105 | 72 | } |
106 | 73 | |
107 | 74 | // DeleteMessage deletes a message in a channel |
108 | func (api *Slack) DeleteMessage(channelId, messageTimestamp string) (string, string, error) { | |
75 | func (api *Slack) DeleteMessage(channel, messageTimestamp string) (string, string, error) { | |
109 | 76 | values := url.Values{ |
110 | 77 | "token": {api.config.token}, |
111 | "channel": {channelId}, | |
78 | "channel": {channel}, | |
112 | 79 | "ts": {messageTimestamp}, |
113 | 80 | } |
114 | 81 | response, err := chatRequest("chat.delete", values, api.debug) |
115 | 82 | if err != nil { |
116 | 83 | return "", "", err |
117 | 84 | } |
118 | return response.ChannelId, response.Timestamp, nil | |
85 | return response.Channel, response.Timestamp, nil | |
119 | 86 | } |
120 | 87 | |
121 | 88 | func escapeMessage(message string) string { |
130 | 97 | |
131 | 98 | // PostMessage sends a message to a channel |
132 | 99 | // Message is escaped by default according to https://api.slack.com/docs/formatting |
133 | func (api *Slack) PostMessage(channelId string, text string, params PostMessageParameters) (channel string, timestamp string, err error) { | |
100 | func (api *Slack) PostMessage(channel string, text string, params PostMessageParameters) (string, string, error) { | |
134 | 101 | if params.EscapeText { |
135 | 102 | text = escapeMessage(text) |
136 | 103 | } |
137 | 104 | values := url.Values{ |
138 | 105 | "token": {api.config.token}, |
139 | "channel": {channelId}, | |
106 | "channel": {channel}, | |
140 | 107 | "text": {text}, |
141 | 108 | } |
142 | 109 | if params.Username != DEFAULT_MESSAGE_USERNAME { |
178 | 145 | if err != nil { |
179 | 146 | return "", "", err |
180 | 147 | } |
181 | return response.ChannelId, response.Timestamp, nil | |
148 | return response.Channel, response.Timestamp, nil | |
182 | 149 | } |
183 | 150 | |
184 | 151 | // UpdateMessage updates a message in a channel |
185 | func (api *Slack) UpdateMessage(channelId, timestamp, text string) (string, string, string, error) { | |
152 | func (api *Slack) UpdateMessage(channel, timestamp, text string) (string, string, string, error) { | |
186 | 153 | values := url.Values{ |
187 | 154 | "token": {api.config.token}, |
188 | "channel": {channelId}, | |
155 | "channel": {channel}, | |
189 | 156 | "text": {escapeMessage(text)}, |
190 | 157 | "ts": {timestamp}, |
191 | 158 | } |
193 | 160 | if err != nil { |
194 | 161 | return "", "", "", err |
195 | 162 | } |
196 | return response.ChannelId, response.Timestamp, response.Text, nil | |
163 | return response.Channel, response.Timestamp, response.Text, nil | |
197 | 164 | } |
0 | package slack | |
1 | ||
2 | // Comment contains all the information relative to a comment | |
3 | type Comment struct { | |
4 | ID string `json:"id,omitempty"` | |
5 | Created JSONTime `json:"created,omitempty"` | |
6 | Timestamp JSONTime `json:"timestamp,omitempty"` | |
7 | User string `json:"user,omitempty"` | |
8 | Comment string `json:"comment,omitempty"` | |
9 | } |
6 | 6 | ) |
7 | 7 | |
8 | 8 | type imChannel struct { |
9 | Id string `json:"id"` | |
9 | ID string `json:"id"` | |
10 | 10 | } |
11 | 11 | |
12 | 12 | type imResponseFull struct { |
21 | 21 | |
22 | 22 | // IM contains information related to the Direct Message channel |
23 | 23 | type IM struct { |
24 | BaseChannel | |
25 | IsIM bool `json:"is_im"` | |
26 | UserId string `json:"user"` | |
27 | IsUserDeleted bool `json:"is_user_deleted"` | |
24 | baseChannel | |
25 | IsIM bool `json:"is_im"` | |
26 | User string `json:"user"` | |
27 | IsUserDeleted bool `json:"is_user_deleted"` | |
28 | 28 | } |
29 | 29 | |
30 | 30 | func imRequest(path string, values url.Values, debug bool) (*imResponseFull, error) { |
40 | 40 | } |
41 | 41 | |
42 | 42 | // CloseIMChannel closes the direct message channel |
43 | func (api *Slack) CloseIMChannel(channelId string) (bool, bool, error) { | |
43 | func (api *Slack) CloseIMChannel(channel string) (bool, bool, error) { | |
44 | 44 | values := url.Values{ |
45 | 45 | "token": {api.config.token}, |
46 | "channel": {channelId}, | |
46 | "channel": {channel}, | |
47 | 47 | } |
48 | 48 | response, err := imRequest("im.close", values, api.debug) |
49 | 49 | if err != nil { |
53 | 53 | } |
54 | 54 | |
55 | 55 | // OpenIMChannel opens a direct message channel to the user provided as argument |
56 | // Returns some status and the channelId | |
57 | func (api *Slack) OpenIMChannel(userId string) (bool, bool, string, error) { | |
56 | // Returns some status and the channel | |
57 | func (api *Slack) OpenIMChannel(user string) (bool, bool, string, error) { | |
58 | 58 | values := url.Values{ |
59 | 59 | "token": {api.config.token}, |
60 | "user": {userId}, | |
60 | "user": {user}, | |
61 | 61 | } |
62 | 62 | response, err := imRequest("im.open", values, api.debug) |
63 | 63 | if err != nil { |
64 | 64 | return false, false, "", err |
65 | 65 | } |
66 | return response.NoOp, response.AlreadyOpen, response.Channel.Id, nil | |
66 | return response.NoOp, response.AlreadyOpen, response.Channel.ID, nil | |
67 | 67 | } |
68 | 68 | |
69 | 69 | // MarkIMChannel sets the read mark of a direct message channel to a specific point |
70 | func (api *Slack) MarkIMChannel(channelId, ts string) (err error) { | |
70 | func (api *Slack) MarkIMChannel(channel, ts string) (err error) { | |
71 | 71 | values := url.Values{ |
72 | 72 | "token": {api.config.token}, |
73 | "channel": {channelId}, | |
73 | "channel": {channel}, | |
74 | 74 | "ts": {ts}, |
75 | 75 | } |
76 | 76 | _, err = imRequest("im.mark", values, api.debug) |
81 | 81 | } |
82 | 82 | |
83 | 83 | // GetIMHistory retrieves the direct message channel history |
84 | func (api *Slack) GetIMHistory(channelId string, params HistoryParameters) (*History, error) { | |
84 | func (api *Slack) GetIMHistory(channel string, params HistoryParameters) (*History, error) { | |
85 | 85 | values := url.Values{ |
86 | 86 | "token": {api.config.token}, |
87 | "channel": {channelId}, | |
87 | "channel": {channel}, | |
88 | 88 | } |
89 | 89 | if params.Latest != DEFAULT_HISTORY_LATEST { |
90 | 90 | values.Add("latest", params.Latest) |
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 | err = api.DeleteFile(file.Id) | |
23 | err = api.DeleteFile(file.ID) | |
24 | 24 | if err != nil { |
25 | 25 | fmt.Printf("%s\n", err) |
26 | 26 | return |
16 | 16 | return |
17 | 17 | } |
18 | 18 | for _, group := range groups { |
19 | fmt.Printf("Id: %s, Name: %s\n", group.Id, group.Name) | |
19 | fmt.Printf("ID: %s, Name: %s\n", group.ID, group.Name) | |
20 | 20 | } |
21 | 21 | } |
22 | 22 | */ |
23 | 23 | } |
24 | 24 | params.Attachments = []slack.Attachment{attachment} |
25 | channelId, timestamp, err := api.PostMessage("CHANNEL_ID", "Some text", params) | |
25 | channelID, timestamp, err := api.PostMessage("CHANNEL_ID", "Some text", params) | |
26 | 26 | if err != nil { |
27 | 27 | fmt.Printf("%s\n", err) |
28 | 28 | return |
29 | 29 | } |
30 | fmt.Printf("Message successfully sent to channel %s at %s", channelId, timestamp) | |
30 | fmt.Printf("Message successfully sent to channel %s at %s", channelID, timestamp) | |
31 | 31 | } |
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) |
12 | 12 | fmt.Printf("%s\n", err) |
13 | 13 | return |
14 | 14 | } |
15 | fmt.Printf("Id: %s, Fullname: %s, Email: %s\n", user.Id, user.Profile.RealName, user.Profile.Email) | |
15 | fmt.Printf("ID: %s, Fullname: %s, Email: %s\n", user.ID, user.Profile.RealName, user.Profile.Email) | |
16 | 16 | } |
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) |
8 | 8 | |
9 | 9 | const ( |
10 | 10 | // Add here the defaults in the siten |
11 | DEFAULT_FILES_USERID = "" | |
11 | DEFAULT_FILES_USER = "" | |
12 | 12 | DEFAULT_FILES_TS_FROM = 0 |
13 | 13 | DEFAULT_FILES_TS_TO = -1 |
14 | 14 | DEFAULT_FILES_TYPES = "all" |
16 | 16 | DEFAULT_FILES_PAGE = 1 |
17 | 17 | ) |
18 | 18 | |
19 | // Comment contains all the information relative to a comment | |
20 | type Comment struct { | |
21 | Id string `json:"id"` | |
22 | Timestamp JSONTime `json:"timestamp"` | |
23 | UserId string `json:"user"` | |
24 | Comment string `json:"comment"` | |
25 | Created JSONTime `json:"created,omitempty"` | |
26 | } | |
27 | ||
28 | 19 | // File contains all the information for a file |
29 | 20 | type File struct { |
30 | Id string `json:"id"` | |
21 | ID string `json:"id"` | |
31 | 22 | Created JSONTime `json:"created"` |
32 | 23 | Timestamp JSONTime `json:"timestamp"` |
33 | 24 | |
36 | 27 | Mimetype string `json:"mimetype"` |
37 | 28 | Filetype string `json:"filetype"` |
38 | 29 | PrettyType string `json:"pretty_type"` |
39 | UserId string `json:"user"` | |
30 | User string `json:"user"` | |
40 | 31 | |
41 | 32 | Mode string `json:"mode"` |
42 | 33 | Editable bool `json:"editable"` |
86 | 77 | |
87 | 78 | // GetFilesParameters contains all the parameters necessary (including the optional ones) for a GetFiles() request |
88 | 79 | type GetFilesParameters struct { |
89 | UserId string | |
80 | User string | |
90 | 81 | TimestampFrom JSONTime |
91 | 82 | TimestampTo JSONTime |
92 | 83 | Types string |
106 | 97 | // NewGetFilesParameters provides an instance of GetFilesParameters with all the sane default values set |
107 | 98 | func NewGetFilesParameters() GetFilesParameters { |
108 | 99 | return GetFilesParameters{ |
109 | UserId: DEFAULT_FILES_USERID, | |
100 | User: DEFAULT_FILES_USER, | |
110 | 101 | TimestampFrom: DEFAULT_FILES_TS_FROM, |
111 | 102 | TimestampTo: DEFAULT_FILES_TS_TO, |
112 | 103 | Types: DEFAULT_FILES_TYPES, |
128 | 119 | } |
129 | 120 | |
130 | 121 | // GetFileInfo retrieves a file and related comments |
131 | func (api *Slack) GetFileInfo(fileId string, count, page int) (*File, []Comment, *Paging, error) { | |
132 | values := url.Values{ | |
133 | "token": {api.config.token}, | |
134 | "file": {fileId}, | |
122 | func (api *Slack) GetFileInfo(fileID string, count, page int) (*File, []Comment, *Paging, error) { | |
123 | values := url.Values{ | |
124 | "token": {api.config.token}, | |
125 | "file": {fileID}, | |
135 | 126 | "count": {strconv.Itoa(count)}, |
136 | 127 | "page": {strconv.Itoa(page)}, |
137 | 128 | } |
147 | 138 | values := url.Values{ |
148 | 139 | "token": {api.config.token}, |
149 | 140 | } |
150 | if params.UserId != DEFAULT_FILES_USERID { | |
151 | values.Add("user", params.UserId) | |
141 | if params.User != DEFAULT_FILES_USER { | |
142 | values.Add("user", params.User) | |
152 | 143 | } |
153 | 144 | // XXX: this is broken. fix it with a proper unix timestamp |
154 | 145 | if params.TimestampFrom != DEFAULT_FILES_TS_FROM { |
216 | 207 | } |
217 | 208 | |
218 | 209 | // DeleteFile deletes a file |
219 | func (api *Slack) DeleteFile(fileId string) error { | |
220 | values := url.Values{ | |
221 | "token": {api.config.token}, | |
222 | "file": {fileId}, | |
210 | func (api *Slack) DeleteFile(fileID string) error { | |
211 | values := url.Values{ | |
212 | "token": {api.config.token}, | |
213 | "file": {fileID}, | |
223 | 214 | } |
224 | 215 | _, err := fileRequest("files.delete", values, api.debug) |
225 | 216 | if err != nil { |
7 | 7 | |
8 | 8 | // Group contains all the information for a group |
9 | 9 | type Group struct { |
10 | BaseChannel | |
10 | baseChannel | |
11 | 11 | Name string `json:"name"` |
12 | 12 | IsGroup bool `json:"is_group"` |
13 | 13 | Creator string `json:"creator"` |
51 | 51 | } |
52 | 52 | |
53 | 53 | // ArchiveGroup archives a private group |
54 | func (api *Slack) ArchiveGroup(groupId string) error { | |
55 | values := url.Values{ | |
56 | "token": {api.config.token}, | |
57 | "channel": {groupId}, | |
54 | func (api *Slack) ArchiveGroup(group string) error { | |
55 | values := url.Values{ | |
56 | "token": {api.config.token}, | |
57 | "channel": {group}, | |
58 | 58 | } |
59 | 59 | _, err := groupRequest("groups.archive", values, api.debug) |
60 | 60 | if err != nil { |
64 | 64 | } |
65 | 65 | |
66 | 66 | // UnarchiveGroup unarchives a private group |
67 | func (api *Slack) UnarchiveGroup(groupId string) error { | |
68 | values := url.Values{ | |
69 | "token": {api.config.token}, | |
70 | "channel": {groupId}, | |
67 | func (api *Slack) UnarchiveGroup(group string) error { | |
68 | values := url.Values{ | |
69 | "token": {api.config.token}, | |
70 | "channel": {group}, | |
71 | 71 | } |
72 | 72 | _, err := groupRequest("groups.unarchive", values, api.debug) |
73 | 73 | if err != nil { |
95 | 95 | // 2. Archives the existing group. |
96 | 96 | // 3. Creates a new group with the name of the existing group. |
97 | 97 | // 4. Adds all members of the existing group to the new group. |
98 | func (api *Slack) CreateChildGroup(groupId string) (*Group, error) { | |
99 | values := url.Values{ | |
100 | "token": {api.config.token}, | |
101 | "channel": {groupId}, | |
98 | func (api *Slack) CreateChildGroup(group string) (*Group, error) { | |
99 | values := url.Values{ | |
100 | "token": {api.config.token}, | |
101 | "channel": {group}, | |
102 | 102 | } |
103 | 103 | response, err := groupRequest("groups.createChild", values, api.debug) |
104 | 104 | if err != nil { |
108 | 108 | } |
109 | 109 | |
110 | 110 | // CloseGroup closes a private group |
111 | func (api *Slack) CloseGroup(groupId string) (bool, bool, error) { | |
112 | values := url.Values{ | |
113 | "token": {api.config.token}, | |
114 | "channel": {groupId}, | |
111 | func (api *Slack) CloseGroup(group string) (bool, bool, error) { | |
112 | values := url.Values{ | |
113 | "token": {api.config.token}, | |
114 | "channel": {group}, | |
115 | 115 | } |
116 | 116 | response, err := imRequest("groups.close", values, api.debug) |
117 | 117 | if err != nil { |
121 | 121 | } |
122 | 122 | |
123 | 123 | // GetGroupHistory retrieves message history for a give group |
124 | func (api *Slack) GetGroupHistory(groupId string, params HistoryParameters) (*History, error) { | |
125 | values := url.Values{ | |
126 | "token": {api.config.token}, | |
127 | "channel": {groupId}, | |
124 | func (api *Slack) GetGroupHistory(group string, params HistoryParameters) (*History, error) { | |
125 | values := url.Values{ | |
126 | "token": {api.config.token}, | |
127 | "channel": {group}, | |
128 | 128 | } |
129 | 129 | if params.Latest != DEFAULT_HISTORY_LATEST { |
130 | 130 | values.Add("latest", params.Latest) |
150 | 150 | } |
151 | 151 | |
152 | 152 | // InviteUserToGroup invites a user to a group |
153 | func (api *Slack) InviteUserToGroup(groupId, userId string) (*Group, bool, error) { | |
154 | values := url.Values{ | |
155 | "token": {api.config.token}, | |
156 | "channel": {groupId}, | |
157 | "user": {userId}, | |
153 | func (api *Slack) InviteUserToGroup(group, user string) (*Group, bool, error) { | |
154 | values := url.Values{ | |
155 | "token": {api.config.token}, | |
156 | "channel": {group}, | |
157 | "user": {user}, | |
158 | 158 | } |
159 | 159 | response, err := groupRequest("groups.invite", values, api.debug) |
160 | 160 | if err != nil { |
164 | 164 | } |
165 | 165 | |
166 | 166 | // LeaveGroup makes authenticated user leave the group |
167 | func (api *Slack) LeaveGroup(groupId string) error { | |
168 | values := url.Values{ | |
169 | "token": {api.config.token}, | |
170 | "channel": {groupId}, | |
167 | func (api *Slack) LeaveGroup(group string) error { | |
168 | values := url.Values{ | |
169 | "token": {api.config.token}, | |
170 | "channel": {group}, | |
171 | 171 | } |
172 | 172 | _, err := groupRequest("groups.leave", values, api.debug) |
173 | 173 | if err != nil { |
177 | 177 | } |
178 | 178 | |
179 | 179 | // KickUserFromGroup kicks a user from a group |
180 | func (api *Slack) KickUserFromGroup(groupId, userId string) error { | |
181 | values := url.Values{ | |
182 | "token": {api.config.token}, | |
183 | "channel": {groupId}, | |
184 | "user": {userId}, | |
180 | func (api *Slack) KickUserFromGroup(group, user string) error { | |
181 | values := url.Values{ | |
182 | "token": {api.config.token}, | |
183 | "channel": {group}, | |
184 | "user": {user}, | |
185 | 185 | } |
186 | 186 | _, err := groupRequest("groups.kick", values, api.debug) |
187 | 187 | if err != nil { |
206 | 206 | } |
207 | 207 | |
208 | 208 | // GetGroupInfo retrieves the given group |
209 | func (api *Slack) GetGroupInfo(groupId string) (*Group, error) { | |
210 | values := url.Values{ | |
211 | "token": {api.config.token}, | |
212 | "channel": {groupId}, | |
209 | func (api *Slack) GetGroupInfo(group string) (*Group, error) { | |
210 | values := url.Values{ | |
211 | "token": {api.config.token}, | |
212 | "channel": {group}, | |
213 | 213 | } |
214 | 214 | response, err := groupRequest("groups.info", values, api.debug) |
215 | 215 | if err != nil { |
223 | 223 | // timer before making the call. In this way, any further updates needed during the timeout will not generate extra |
224 | 224 | // calls (just one per channel). This is useful for when reading scroll-back history, or following a busy live |
225 | 225 | // channel. A timeout of 5 seconds is a good starting point. Be sure to flush these calls on shutdown/logout. |
226 | func (api *Slack) SetGroupReadMark(groupId, ts string) error { | |
227 | values := url.Values{ | |
228 | "token": {api.config.token}, | |
229 | "channel": {groupId}, | |
226 | func (api *Slack) SetGroupReadMark(group, ts string) error { | |
227 | values := url.Values{ | |
228 | "token": {api.config.token}, | |
229 | "channel": {group}, | |
230 | 230 | "ts": {ts}, |
231 | 231 | } |
232 | 232 | _, err := groupRequest("groups.mark", values, api.debug) |
237 | 237 | } |
238 | 238 | |
239 | 239 | // OpenGroup opens a private group |
240 | func (api *Slack) OpenGroup(groupId string) (bool, bool, error) { | |
240 | func (api *Slack) OpenGroup(group string) (bool, bool, error) { | |
241 | 241 | values := url.Values{ |
242 | 242 | "token": {api.config.token}, |
243 | "user": {groupId}, | |
243 | "user": {group}, | |
244 | 244 | } |
245 | 245 | response, err := groupRequest("groups.open", values, api.debug) |
246 | 246 | if err != nil { |
252 | 252 | // RenameGroup renames a group |
253 | 253 | // XXX: They return a channel, not a group. What is this crap? :( |
254 | 254 | // Inconsistent api it seems. |
255 | func (api *Slack) RenameGroup(groupId, name string) (*Channel, error) { | |
256 | values := url.Values{ | |
257 | "token": {api.config.token}, | |
258 | "channel": {groupId}, | |
255 | func (api *Slack) RenameGroup(group, name string) (*Channel, error) { | |
256 | values := url.Values{ | |
257 | "token": {api.config.token}, | |
258 | "channel": {group}, | |
259 | 259 | "name": {name}, |
260 | 260 | } |
261 | 261 | // XXX: the created entry in this call returns a string instead of a number |
269 | 269 | } |
270 | 270 | |
271 | 271 | // SetGroupPurpose sets the group purpose |
272 | func (api *Slack) SetGroupPurpose(groupId, purpose string) (string, error) { | |
273 | values := url.Values{ | |
274 | "token": {api.config.token}, | |
275 | "channel": {groupId}, | |
272 | func (api *Slack) SetGroupPurpose(group, purpose string) (string, error) { | |
273 | values := url.Values{ | |
274 | "token": {api.config.token}, | |
275 | "channel": {group}, | |
276 | 276 | "purpose": {purpose}, |
277 | 277 | } |
278 | 278 | response, err := groupRequest("groups.setPurpose", values, api.debug) |
283 | 283 | } |
284 | 284 | |
285 | 285 | // SetGroupTopic sets the group topic |
286 | func (api *Slack) SetGroupTopic(groupId, topic string) (string, error) { | |
287 | values := url.Values{ | |
288 | "token": {api.config.token}, | |
289 | "channel": {groupId}, | |
286 | func (api *Slack) SetGroupTopic(group, topic string) (string, error) { | |
287 | values := url.Values{ | |
288 | "token": {api.config.token}, | |
289 | "channel": {group}, | |
290 | 290 | "topic": {topic}, |
291 | 291 | } |
292 | 292 | response, err := groupRequest("groups.setTopic", values, api.debug) |
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":"", |
105 | 105 | |
106 | 106 | // UserDetails contains user details coming in the initial response from StartRTM |
107 | 107 | type UserDetails struct { |
108 | Id string `json:"id"` | |
108 | ID string `json:"id"` | |
109 | 109 | Name string `json:"name"` |
110 | 110 | Created JSONTime `json:"created"` |
111 | 111 | ManualPresence string `json:"manual_presence"` |
123 | 123 | |
124 | 124 | // Team contains details about a team |
125 | 125 | type Team struct { |
126 | Id string `json:"id"` | |
126 | ID string `json:"id"` | |
127 | 127 | Name string `json:"name"` |
128 | 128 | Domain string `json:"name"` |
129 | 129 | } |
135 | 135 | |
136 | 136 | // Bot contains information about a bot |
137 | 137 | type Bot struct { |
138 | Id string `json:"id"` | |
138 | ID string `json:"id"` | |
139 | 139 | Name string `json:"name"` |
140 | 140 | Deleted bool `json:"deleted"` |
141 | 141 | Icons Icons `json:"icons"` |
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 | |
160 | } | |
161 | ||
162 | // GetBotById returns a bot given a bot id | |
163 | func (info Info) GetBotById(botId string) *Bot { | |
159 | WSResponse | |
160 | } | |
161 | ||
162 | // GetBotByID returns a bot given a bot id | |
163 | func (info Info) GetBotByID(botID string) *Bot { | |
164 | 164 | for _, bot := range info.Bots { |
165 | if bot.Id == botId { | |
165 | if bot.ID == botID { | |
166 | 166 | return &bot |
167 | 167 | } |
168 | 168 | } |
169 | 169 | return nil |
170 | 170 | } |
171 | 171 | |
172 | // GetUserById returns a user given a user id | |
173 | func (info Info) GetUserById(userId string) *User { | |
172 | // GetUserByID returns a user given a user id | |
173 | func (info Info) GetUserByID(userID string) *User { | |
174 | 174 | for _, user := range info.Users { |
175 | if user.Id == userId { | |
175 | if user.ID == userID { | |
176 | 176 | return &user |
177 | 177 | } |
178 | 178 | } |
179 | 179 | return nil |
180 | 180 | } |
181 | 181 | |
182 | // GetChannelById returns a channel given a channel id | |
183 | func (info Info) GetChannelById(channelId string) *Channel { | |
182 | // GetChannelByID returns a channel given a channel id | |
183 | func (info Info) GetChannelByID(channelID string) *Channel { | |
184 | 184 | for _, channel := range info.Channels { |
185 | if channel.Id == channelId { | |
185 | if channel.ID == channelID { | |
186 | 186 | return &channel |
187 | 187 | } |
188 | 188 | } |
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 | } |
52 | 52 | // CommentId, or the combination of ChannelId and Timestamp must be |
53 | 53 | // specified. |
54 | 54 | type ItemRef struct { |
55 | ChannelId string `json:"channel"` | |
55 | Channel string `json:"channel"` | |
56 | 56 | Timestamp string `json:"timestamp"` |
57 | FileId string `json:"file"` | |
58 | CommentId string `json:"file_comment"` | |
57 | File string `json:"file"` | |
58 | Comment string `json:"file_comment"` | |
59 | 59 | } |
60 | 60 | |
61 | 61 | // NewRefToMessage initializes a reference to to a message. |
62 | func NewRefToMessage(channelID, timestamp string) ItemRef { | |
63 | return ItemRef{ChannelId: channelID, Timestamp: timestamp} | |
62 | func NewRefToMessage(channel, timestamp string) ItemRef { | |
63 | return ItemRef{Channel: channel, Timestamp: timestamp} | |
64 | 64 | } |
65 | 65 | |
66 | 66 | // NewRefToFile initializes a reference to a file. |
67 | func NewRefToFile(fileID string) ItemRef { | |
68 | return ItemRef{FileId: fileID} | |
67 | func NewRefToFile(file string) ItemRef { | |
68 | return ItemRef{File: file} | |
69 | 69 | } |
70 | 70 | |
71 | 71 | // NewRefToComment initializes a reference to a file comment. |
72 | func NewRefToComment(commentID string) ItemRef { | |
73 | return ItemRef{CommentId: commentID} | |
72 | func NewRefToComment(comment string) ItemRef { | |
73 | return ItemRef{Comment: comment} | |
74 | 74 | } |
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 | } |
0 | 0 | package slack |
1 | 1 | |
2 | // OutgoingMessage is used for the realtime API, and seems incomplete. | |
2 | 3 | type OutgoingMessage struct { |
3 | Id int `json:"id"` | |
4 | ChannelId string `json:"channel,omitempty"` | |
5 | Text string `json:"text,omitempty"` | |
6 | Type string `json:"type,omitempty"` | |
4 | ID int `json:"id"` | |
5 | Channel string `json:"channel,omitempty"` | |
6 | Text string `json:"text,omitempty"` | |
7 | Type string `json:"type,omitempty"` | |
7 | 8 | } |
8 | 9 | |
9 | 10 | // Message is an auxiliary type to allow us to have a message containing sub messages |
14 | 15 | |
15 | 16 | // Msg contains information about a slack message |
16 | 17 | type Msg struct { |
17 | Id string `json:"id"` | |
18 | BotId string `json:"bot_id,omitempty"` | |
19 | UserId string `json:"user,omitempty"` | |
20 | Username string `json:"username,omitempty"` | |
21 | ChannelId string `json:"channel,omitempty"` | |
22 | Timestamp string `json:"ts,omitempty"` | |
23 | Text string `json:"text,omitempty"` | |
24 | Team string `json:"team,omitempty"` | |
25 | File *File `json:"file,omitempty"` | |
26 | // Type may come if it's part of a message list | |
27 | // e.g.: channel.history | |
28 | Type string `json:"type,omitempty"` | |
29 | IsStarred bool `json:"is_starred,omitempty"` | |
30 | // Submessage | |
31 | SubType string `json:"subtype,omitempty"` | |
32 | Hidden bool `json:"bool,omitempty"` | |
33 | DeletedTimestamp string `json:"deleted_ts,omitempty"` | |
34 | Attachments []Attachment `json:"attachments,omitempty"` | |
35 | ReplyTo int `json:"reply_to,omitempty"` | |
36 | Upload bool `json:"upload,omitempty"` | |
18 | // Basic Message | |
19 | Type string `json:"type,omitempty"` | |
20 | Channel string `json:"channel,omitempty"` | |
21 | User string `json:"user,omitempty"` | |
22 | Text string `json:"text,omitempty"` | |
23 | Timestamp string `json:"ts,omitempty"` | |
24 | IsStarred bool `json:"is_starred,omitempty"` | |
25 | Attachments []Attachment `json:"attachments,omitempty"` | |
26 | Edited *Edited `json:"edited,omitempty"` | |
27 | ||
28 | // Message Subtypes | |
29 | SubType string `json:"subtype,omitempty"` | |
30 | ||
31 | // Hidden Subtypes | |
32 | Hidden bool `json:"hidden,omitempty"` // message_changed, message_deleted, unpinned_item | |
33 | DeletedTimestamp string `json:"deleted_ts,omitempty"` // message_deleted | |
34 | EventTimestamp string `json:"event_ts,omitempty"` | |
35 | ||
36 | // bot_message (https://api.slack.com/events/message/bot_message) | |
37 | BotID string `json:"bot_id,omitempty"` | |
38 | Username string `json:"username,omitempty"` | |
39 | Icons *Icon `json:"icons,omitempty"` | |
40 | ||
41 | // channel_join, group_join | |
42 | Inviter string `json:"inviter,omitempty"` | |
43 | ||
44 | // channel_topic, group_topic | |
45 | Topic string `json:"topic,omitempty"` | |
46 | ||
47 | // channel_purpose, group_purpose | |
48 | Purpose string `json:"purpose,omitempty"` | |
49 | ||
50 | // channel_name, group_name | |
51 | Name string `json:"name,omitempty"` | |
52 | OldName string `json:"old_name,omitempty"` | |
53 | ||
54 | // channel_archive, group_archive | |
55 | Members []string `json:"members,omitempty"` | |
56 | ||
57 | // file_share, file_comment, file_mention | |
58 | File *File `json:"file,omitempty"` | |
59 | ||
60 | // file_share | |
61 | Upload bool `json:"upload,omitempty"` | |
62 | ||
63 | // file_comment | |
64 | Comment *Comment `json:"comment,omitempty"` | |
65 | ||
66 | // pinned_item | |
67 | ItemType string `json:"item_type,omitempty"` | |
68 | ||
69 | // https://api.slack.com/rtm | |
70 | ReplyTo int `json:"reply_to,omitempty"` | |
71 | Team string `json:"team,omitempty"` | |
37 | 72 | } |
38 | 73 | |
39 | // Presence XXX: not used yet | |
40 | type Presence struct { | |
41 | Presence string `json:"presence"` | |
42 | UserId string `json:"user"` | |
74 | // Icon is used for bot messages | |
75 | type Icon struct { | |
76 | IconURL string `json:"icon_url,omitempty"` | |
77 | IconEmoji string `json:"icon_emoji,omitempty"` | |
78 | } | |
79 | ||
80 | // Edited indicates that a message has been edited. | |
81 | type Edited struct { | |
82 | User string `json:"user,omitempty"` | |
83 | Timestamp string `json:"ts,omitempty"` | |
43 | 84 | } |
44 | 85 | |
45 | 86 | // Event contains the event type |
49 | 90 | |
50 | 91 | // Ping contains information about a Ping Event |
51 | 92 | type Ping struct { |
52 | Id int `json:"id"` | |
93 | ID int `json:"id"` | |
53 | 94 | Type string `json:"type"` |
54 | 95 | } |
55 | 96 | |
59 | 100 | ReplyTo int `json:"reply_to"` |
60 | 101 | } |
61 | 102 | |
62 | // NewOutGoingMessage prepares an OutgoingMessage that the user can use to send a message | |
63 | func (api *SlackWS) NewOutgoingMessage(text string, channel string) *OutgoingMessage { | |
103 | // NewOutgoingMessage prepares an OutgoingMessage that the user can use to send a message | |
104 | func (api *WS) NewOutgoingMessage(text string, channel string) *OutgoingMessage { | |
64 | 105 | api.mutex.Lock() |
65 | 106 | defer api.mutex.Unlock() |
66 | api.messageId++ | |
107 | api.messageID++ | |
67 | 108 | return &OutgoingMessage{ |
68 | Id: api.messageId, | |
69 | Type: "message", | |
70 | ChannelId: channel, | |
71 | Text: text, | |
109 | ID: api.messageID, | |
110 | Type: "message", | |
111 | Channel: channel, | |
112 | Text: text, | |
72 | 113 | } |
73 | 114 | } |
0 | package slack | |
1 | ||
2 | import ( | |
3 | "encoding/json" | |
4 | "fmt" | |
5 | "testing" | |
6 | ||
7 | "github.com/stretchr/testify/assert" | |
8 | ) | |
9 | ||
10 | var simpleMessage = `{ | |
11 | "type": "message", | |
12 | "channel": "C2147483705", | |
13 | "user": "U2147483697", | |
14 | "text": "Hello world", | |
15 | "ts": "1355517523.000005" | |
16 | }` | |
17 | ||
18 | func unmarshalMessage(j string) (*Message, error) { | |
19 | message := &Message{} | |
20 | if err := json.Unmarshal([]byte(j), &message); err != nil { | |
21 | return nil, err | |
22 | } | |
23 | return message, nil | |
24 | } | |
25 | ||
26 | func TestSimpleMessage(t *testing.T) { | |
27 | message, err := unmarshalMessage(simpleMessage) | |
28 | assert.Nil(t, err) | |
29 | assert.NotNil(t, message) | |
30 | assert.Equal(t, "message", message.Type) | |
31 | assert.Equal(t, "C2147483705", message.Channel) | |
32 | assert.Equal(t, "U2147483697", message.User) | |
33 | assert.Equal(t, "Hello world", message.Text) | |
34 | assert.Equal(t, "1355517523.000005", message.Timestamp) | |
35 | } | |
36 | ||
37 | var starredMessage = `{ | |
38 | "text": "is testing", | |
39 | "type": "message", | |
40 | "subtype": "me_message", | |
41 | "user": "U2147483697", | |
42 | "ts": "1433314126.000003", | |
43 | "is_starred": true | |
44 | }` | |
45 | ||
46 | func TestStarredMessage(t *testing.T) { | |
47 | message, err := unmarshalMessage(starredMessage) | |
48 | assert.Nil(t, err) | |
49 | assert.NotNil(t, message) | |
50 | assert.Equal(t, "is testing", message.Text) | |
51 | assert.Equal(t, "message", message.Type) | |
52 | assert.Equal(t, "me_message", message.SubType) | |
53 | assert.Equal(t, "U2147483697", message.User) | |
54 | assert.Equal(t, "1433314126.000003", message.Timestamp) | |
55 | assert.Equal(t, true, message.IsStarred) | |
56 | } | |
57 | ||
58 | var editedMessage = `{ | |
59 | "type": "message", | |
60 | "user": "U2147483697", | |
61 | "text": "hello edited", | |
62 | "edited": { | |
63 | "user": "U2147483697", | |
64 | "ts": "1433314416.000000" | |
65 | }, | |
66 | "ts": "1433314408.000004" | |
67 | }` | |
68 | ||
69 | func TestEditedMessage(t *testing.T) { | |
70 | message, err := unmarshalMessage(editedMessage) | |
71 | assert.Nil(t, err) | |
72 | assert.NotNil(t, message) | |
73 | assert.Equal(t, "message", message.Type) | |
74 | assert.Equal(t, "U2147483697", message.User) | |
75 | assert.Equal(t, "hello edited", message.Text) | |
76 | assert.NotNil(t, message.Edited) | |
77 | assert.Equal(t, "U2147483697", message.Edited.User) | |
78 | assert.Equal(t, "1433314416.000000", message.Edited.Timestamp) | |
79 | assert.Equal(t, "1433314408.000004", message.Timestamp) | |
80 | } | |
81 | ||
82 | var uploadedFile = `{ | |
83 | "type": "message", | |
84 | "subtype": "file_share", | |
85 | "text": "<@U2147483697|tester> uploaded a file: <https:\/\/test.slack.com\/files\/tester\/abc\/test.txt|test.txt> and commented: test comment here", | |
86 | "file": { | |
87 | "id": "abc", | |
88 | "created": 1433314757, | |
89 | "timestamp": 1433314757, | |
90 | "name": "test.txt", | |
91 | "title": "test.txt", | |
92 | "mimetype": "text\/plain", | |
93 | "filetype": "text", | |
94 | "pretty_type": "Plain Text", | |
95 | "user": "U2147483697", | |
96 | "editable": true, | |
97 | "size": 5, | |
98 | "mode": "snippet", | |
99 | "is_external": false, | |
100 | "external_type": "", | |
101 | "is_public": true, | |
102 | "public_url_shared": false, | |
103 | "url": "https:\/\/slack-files.com\/files-pub\/abc-def-ghi\/test.txt", | |
104 | "url_download": "https:\/\/slack-files.com\/files-pub\/abc-def-ghi\/download\/test.txt", | |
105 | "url_private": "https:\/\/files.slack.com\/files-pri\/abc-def\/test.txt", | |
106 | "url_private_download": "https:\/\/files.slack.com\/files-pri\/abc-def\/download\/test.txt", | |
107 | "permalink": "https:\/\/test.slack.com\/files\/tester\/abc\/test.txt", | |
108 | "permalink_public": "https:\/\/slack-files.com\/abc-def-ghi", | |
109 | "edit_link": "https:\/\/test.slack.com\/files\/tester\/abc\/test.txt\/edit", | |
110 | "preview": "test\n", | |
111 | "preview_highlight": "<div class=\"sssh-code\"><div class=\"sssh-line\"><pre>test<\/pre><\/div>\n<div class=\"sssh-line\"><pre><\/pre><\/div>\n<\/div>", | |
112 | "lines": 2, | |
113 | "lines_more": 0, | |
114 | "channels": [ | |
115 | "C2147483705" | |
116 | ], | |
117 | "groups": [], | |
118 | "ims": [], | |
119 | "comments_count": 1, | |
120 | "initial_comment": { | |
121 | "id": "Fc066YLGKH", | |
122 | "created": 1433314757, | |
123 | "timestamp": 1433314757, | |
124 | "user": "U2147483697", | |
125 | "comment": "test comment here" | |
126 | } | |
127 | }, | |
128 | "user": "U2147483697", | |
129 | "upload": true, | |
130 | "ts": "1433314757.000006" | |
131 | }` | |
132 | ||
133 | func TestUploadedFile(t *testing.T) { | |
134 | message, err := unmarshalMessage(uploadedFile) | |
135 | assert.Nil(t, err) | |
136 | assert.NotNil(t, message) | |
137 | assert.Equal(t, "message", message.Type) | |
138 | assert.Equal(t, "file_share", message.SubType) | |
139 | assert.Equal(t, "<@U2147483697|tester> uploaded a file: <https://test.slack.com/files/tester/abc/test.txt|test.txt> and commented: test comment here", message.Text) | |
140 | // TODO: Assert File | |
141 | assert.Equal(t, "U2147483697", message.User) | |
142 | assert.True(t, message.Upload) | |
143 | assert.Equal(t, "1433314757.000006", message.Timestamp) | |
144 | } | |
145 | ||
146 | var testPost = `{ | |
147 | "type": "message", | |
148 | "subtype": "file_share", | |
149 | "text": "<@U2147483697|tester> shared a file: <https:\/\/test.slack.com\/files\/tester\/abc\/test_post|test post>", | |
150 | "file": { | |
151 | "id": "abc", | |
152 | "created": 1433315398, | |
153 | "timestamp": 1433315398, | |
154 | "name": "test_post", | |
155 | "title": "test post", | |
156 | "mimetype": "text\/plain", | |
157 | "filetype": "post", | |
158 | "pretty_type": "Post", | |
159 | "user": "U2147483697", | |
160 | "editable": true, | |
161 | "size": 14, | |
162 | "mode": "post", | |
163 | "is_external": false, | |
164 | "external_type": "", | |
165 | "is_public": true, | |
166 | "public_url_shared": false, | |
167 | "url": "https:\/\/slack-files.com\/files-pub\/abc-def-ghi\/test_post", | |
168 | "url_download": "https:\/\/slack-files.com\/files-pub\/abc-def-ghi\/download\/test_post", | |
169 | "url_private": "https:\/\/files.slack.com\/files-pri\/abc-def\/test_post", | |
170 | "url_private_download": "https:\/\/files.slack.com\/files-pri\/abc-def\/download\/test_post", | |
171 | "permalink": "https:\/\/test.slack.com\/files\/tester\/abc\/test_post", | |
172 | "permalink_public": "https:\/\/slack-files.com\/abc-def-ghi", | |
173 | "edit_link": "https:\/\/test.slack.com\/files\/tester\/abc\/test_post\/edit", | |
174 | "preview": "test post body", | |
175 | "channels": [ | |
176 | "C2147483705" | |
177 | ], | |
178 | "groups": [], | |
179 | "ims": [], | |
180 | "comments_count": 1 | |
181 | }, | |
182 | "user": "U2147483697", | |
183 | "upload": false, | |
184 | "ts": "1433315416.000008" | |
185 | }` | |
186 | ||
187 | func TestPost(t *testing.T) { | |
188 | message, err := unmarshalMessage(testPost) | |
189 | assert.Nil(t, err) | |
190 | assert.NotNil(t, message) | |
191 | assert.Equal(t, "message", message.Type) | |
192 | assert.Equal(t, "file_share", message.SubType) | |
193 | assert.Equal(t, "<@U2147483697|tester> shared a file: <https://test.slack.com/files/tester/abc/test_post|test post>", message.Text) | |
194 | // TODO: Assert File | |
195 | assert.Equal(t, "U2147483697", message.User) | |
196 | assert.False(t, message.Upload) | |
197 | assert.Equal(t, "1433315416.000008", message.Timestamp) | |
198 | } | |
199 | ||
200 | var testComment = `{ | |
201 | "type": "message", | |
202 | "subtype": "file_comment", | |
203 | "text": "<@U2147483697|tester> commented on <@U2147483697|tester>'s file <https:\/\/test.slack.com\/files\/tester\/abc\/test_post|test post>: another comment", | |
204 | "file": { | |
205 | "id": "abc", | |
206 | "created": 1433315398, | |
207 | "timestamp": 1433315398, | |
208 | "name": "test_post", | |
209 | "title": "test post", | |
210 | "mimetype": "text\/plain", | |
211 | "filetype": "post", | |
212 | "pretty_type": "Post", | |
213 | "user": "U2147483697", | |
214 | "editable": true, | |
215 | "size": 14, | |
216 | "mode": "post", | |
217 | "is_external": false, | |
218 | "external_type": "", | |
219 | "is_public": true, | |
220 | "public_url_shared": false, | |
221 | "url": "https:\/\/slack-files.com\/files-pub\/abc-def-ghi\/test_post", | |
222 | "url_download": "https:\/\/slack-files.com\/files-pub\/abc-def-ghi\/download\/test_post", | |
223 | "url_private": "https:\/\/files.slack.com\/files-pri\/abc-def\/test_post", | |
224 | "url_private_download": "https:\/\/files.slack.com\/files-pri\/abc-def\/download\/test_post", | |
225 | "permalink": "https:\/\/test.slack.com\/files\/tester\/abc\/test_post", | |
226 | "permalink_public": "https:\/\/slack-files.com\/abc-def-ghi", | |
227 | "edit_link": "https:\/\/test.slack.com\/files\/tester\/abc\/test_post\/edit", | |
228 | "preview": "test post body", | |
229 | "channels": [ | |
230 | "C2147483705" | |
231 | ], | |
232 | "groups": [], | |
233 | "ims": [], | |
234 | "comments_count": 2 | |
235 | }, | |
236 | "comment": { | |
237 | "id": "xyz", | |
238 | "created": 1433316360, | |
239 | "timestamp": 1433316360, | |
240 | "user": "U2147483697", | |
241 | "comment": "another comment" | |
242 | }, | |
243 | "ts": "1433316360.000009" | |
244 | }` | |
245 | ||
246 | func TestComment(t *testing.T) { | |
247 | message, err := unmarshalMessage(testComment) | |
248 | fmt.Println(err) | |
249 | assert.Nil(t, err) | |
250 | assert.NotNil(t, message) | |
251 | assert.Equal(t, "message", message.Type) | |
252 | assert.Equal(t, "file_comment", message.SubType) | |
253 | assert.Equal(t, "<@U2147483697|tester> commented on <@U2147483697|tester>'s file <https://test.slack.com/files/tester/abc/test_post|test post>: another comment", message.Text) | |
254 | // TODO: Assert File | |
255 | // TODO: Assert Comment | |
256 | assert.Equal(t, "1433316360.000009", message.Timestamp) | |
257 | } | |
258 | ||
259 | var botMessage = `{ | |
260 | "type": "message", | |
261 | "subtype": "bot_message", | |
262 | "ts": "1358877455.000010", | |
263 | "text": "Pushing is the answer", | |
264 | "bot_id": "BB12033", | |
265 | "username": "github", | |
266 | "icons": {} | |
267 | }` | |
268 | ||
269 | func TestBotMessage(t *testing.T) { | |
270 | message, err := unmarshalMessage(botMessage) | |
271 | assert.Nil(t, err) | |
272 | assert.NotNil(t, message) | |
273 | assert.Equal(t, "message", message.Type) | |
274 | assert.Equal(t, "bot_message", message.SubType) | |
275 | assert.Equal(t, "1358877455.000010", message.Timestamp) | |
276 | assert.Equal(t, "Pushing is the answer", message.Text) | |
277 | assert.Equal(t, "BB12033", message.BotID) | |
278 | assert.Equal(t, "github", message.Username) | |
279 | assert.NotNil(t, message.Icons) | |
280 | assert.Empty(t, message.Icons.IconURL) | |
281 | assert.Empty(t, message.Icons.IconEmoji) | |
282 | } | |
283 | ||
284 | var meMessage = `{ | |
285 | "type": "message", | |
286 | "subtype": "me_message", | |
287 | "channel": "C2147483705", | |
288 | "user": "U2147483697", | |
289 | "text": "is doing that thing", | |
290 | "ts": "1355517523.000005" | |
291 | }` | |
292 | ||
293 | func TestMeMessage(t *testing.T) { | |
294 | message, err := unmarshalMessage(meMessage) | |
295 | assert.Nil(t, err) | |
296 | assert.NotNil(t, message) | |
297 | assert.Equal(t, "message", message.Type) | |
298 | assert.Equal(t, "me_message", message.SubType) | |
299 | assert.Equal(t, "C2147483705", message.Channel) | |
300 | assert.Equal(t, "U2147483697", message.User) | |
301 | assert.Equal(t, "is doing that thing", message.Text) | |
302 | assert.Equal(t, "1355517523.000005", message.Timestamp) | |
303 | } | |
304 | ||
305 | var messageChangedMessage = `{ | |
306 | "type": "message", | |
307 | "subtype": "message_changed", | |
308 | "hidden": true, | |
309 | "channel": "C2147483705", | |
310 | "ts": "1358878755.000001", | |
311 | "message": { | |
312 | "type": "message", | |
313 | "user": "U2147483697", | |
314 | "text": "Hello, world!", | |
315 | "ts": "1355517523.000005", | |
316 | "edited": { | |
317 | "user": "U2147483697", | |
318 | "ts": "1358878755.000001" | |
319 | } | |
320 | } | |
321 | }` | |
322 | ||
323 | func TestMessageChangedMessage(t *testing.T) { | |
324 | message, err := unmarshalMessage(messageChangedMessage) | |
325 | assert.Nil(t, err) | |
326 | assert.NotNil(t, message) | |
327 | assert.Equal(t, "message", message.Type) | |
328 | assert.Equal(t, "message_changed", message.SubType) | |
329 | assert.True(t, message.Hidden) | |
330 | assert.Equal(t, "C2147483705", message.Channel) | |
331 | assert.NotNil(t, message.SubMessage) | |
332 | assert.Equal(t, "message", message.SubMessage.Type) | |
333 | assert.Equal(t, "U2147483697", message.SubMessage.User) | |
334 | assert.Equal(t, "Hello, world!", message.SubMessage.Text) | |
335 | assert.Equal(t, "1355517523.000005", message.SubMessage.Timestamp) | |
336 | assert.NotNil(t, message.SubMessage.Edited) | |
337 | assert.Equal(t, "U2147483697", message.SubMessage.Edited.User) | |
338 | assert.Equal(t, "1358878755.000001", message.SubMessage.Edited.Timestamp) | |
339 | assert.Equal(t, "1358878755.000001", message.Timestamp) | |
340 | } | |
341 | ||
342 | var messageDeletedMessage = `{ | |
343 | "type": "message", | |
344 | "subtype": "message_deleted", | |
345 | "hidden": true, | |
346 | "channel": "C2147483705", | |
347 | "ts": "1358878755.000001", | |
348 | "deleted_ts": "1358878749.000002" | |
349 | }` | |
350 | ||
351 | func TestMessageDeletedMessage(t *testing.T) { | |
352 | message, err := unmarshalMessage(messageDeletedMessage) | |
353 | assert.Nil(t, err) | |
354 | assert.NotNil(t, message) | |
355 | assert.Equal(t, "message", message.Type) | |
356 | assert.Equal(t, "message_deleted", message.SubType) | |
357 | assert.True(t, message.Hidden) | |
358 | assert.Equal(t, "C2147483705", message.Channel) | |
359 | assert.Equal(t, "1358878755.000001", message.Timestamp) | |
360 | assert.Equal(t, "1358878749.000002", message.DeletedTimestamp) | |
361 | } | |
362 | ||
363 | var channelJoinMessage = `{ | |
364 | "type": "message", | |
365 | "subtype": "channel_join", | |
366 | "ts": "1358877458.000011", | |
367 | "user": "U2147483828", | |
368 | "text": "<@U2147483828|cal> has joined the channel" | |
369 | }` | |
370 | ||
371 | func TestChannelJoinMessage(t *testing.T) { | |
372 | message, err := unmarshalMessage(channelJoinMessage) | |
373 | assert.Nil(t, err) | |
374 | assert.NotNil(t, message) | |
375 | assert.Equal(t, "message", message.Type) | |
376 | assert.Equal(t, "channel_join", message.SubType) | |
377 | assert.Equal(t, "1358877458.000011", message.Timestamp) | |
378 | assert.Equal(t, "U2147483828", message.User) | |
379 | assert.Equal(t, "<@U2147483828|cal> has joined the channel", message.Text) | |
380 | } | |
381 | ||
382 | var channelJoinInvitedMessage = `{ | |
383 | "type": "message", | |
384 | "subtype": "channel_join", | |
385 | "ts": "1358877458.000011", | |
386 | "user": "U2147483828", | |
387 | "text": "<@U2147483828|cal> has joined the channel", | |
388 | "inviter": "U2147483829" | |
389 | }` | |
390 | ||
391 | func TestChannelJoinInvitedMessage(t *testing.T) { | |
392 | message, err := unmarshalMessage(channelJoinInvitedMessage) | |
393 | assert.Nil(t, err) | |
394 | assert.NotNil(t, message) | |
395 | assert.Equal(t, "message", message.Type) | |
396 | assert.Equal(t, "channel_join", message.SubType) | |
397 | assert.Equal(t, "1358877458.000011", message.Timestamp) | |
398 | assert.Equal(t, "U2147483828", message.User) | |
399 | assert.Equal(t, "<@U2147483828|cal> has joined the channel", message.Text) | |
400 | assert.Equal(t, "U2147483829", message.Inviter) | |
401 | } | |
402 | ||
403 | var channelLeaveMessage = `{ | |
404 | "type": "message", | |
405 | "subtype": "channel_leave", | |
406 | "ts": "1358877455.000010", | |
407 | "user": "U2147483828", | |
408 | "text": "<@U2147483828|cal> has left the channel" | |
409 | }` | |
410 | ||
411 | func TestChannelLeaveMessage(t *testing.T) { | |
412 | message, err := unmarshalMessage(channelLeaveMessage) | |
413 | assert.Nil(t, err) | |
414 | assert.NotNil(t, message) | |
415 | assert.Equal(t, "message", message.Type) | |
416 | assert.Equal(t, "channel_leave", message.SubType) | |
417 | assert.Equal(t, "1358877455.000010", message.Timestamp) | |
418 | assert.Equal(t, "U2147483828", message.User) | |
419 | assert.Equal(t, "<@U2147483828|cal> has left the channel", message.Text) | |
420 | } | |
421 | ||
422 | var channelTopicMessage = `{ | |
423 | "type": "message", | |
424 | "subtype": "channel_topic", | |
425 | "ts": "1358877455.000010", | |
426 | "user": "U2147483828", | |
427 | "topic": "hello world", | |
428 | "text": "<@U2147483828|cal> set the channel topic: hello world" | |
429 | }` | |
430 | ||
431 | func TestChannelTopicMessage(t *testing.T) { | |
432 | message, err := unmarshalMessage(channelTopicMessage) | |
433 | assert.Nil(t, err) | |
434 | assert.NotNil(t, message) | |
435 | assert.Equal(t, "message", message.Type) | |
436 | assert.Equal(t, "channel_topic", message.SubType) | |
437 | assert.Equal(t, "1358877455.000010", message.Timestamp) | |
438 | assert.Equal(t, "U2147483828", message.User) | |
439 | assert.Equal(t, "hello world", message.Topic) | |
440 | assert.Equal(t, "<@U2147483828|cal> set the channel topic: hello world", message.Text) | |
441 | } | |
442 | ||
443 | var channelPurposeMessage = `{ | |
444 | "type": "message", | |
445 | "subtype": "channel_purpose", | |
446 | "ts": "1358877455.000010", | |
447 | "user": "U2147483828", | |
448 | "purpose": "whatever", | |
449 | "text": "<@U2147483828|cal> set the channel purpose: whatever" | |
450 | }` | |
451 | ||
452 | func TestChannelPurposeMessage(t *testing.T) { | |
453 | message, err := unmarshalMessage(channelPurposeMessage) | |
454 | assert.Nil(t, err) | |
455 | assert.NotNil(t, message) | |
456 | assert.Equal(t, "message", message.Type) | |
457 | assert.Equal(t, "channel_purpose", message.SubType) | |
458 | assert.Equal(t, "1358877455.000010", message.Timestamp) | |
459 | assert.Equal(t, "U2147483828", message.User) | |
460 | assert.Equal(t, "whatever", message.Purpose) | |
461 | assert.Equal(t, "<@U2147483828|cal> set the channel purpose: whatever", message.Text) | |
462 | } | |
463 | ||
464 | var channelNameMessage = `{ | |
465 | "type": "message", | |
466 | "subtype": "channel_name", | |
467 | "ts": "1358877455.000010", | |
468 | "user": "U2147483828", | |
469 | "old_name": "random", | |
470 | "name": "watercooler", | |
471 | "text": "<@U2147483828|cal> has renamed the channel from \"random\" to \"watercooler\"" | |
472 | }` | |
473 | ||
474 | func TestChannelNameMessage(t *testing.T) { | |
475 | message, err := unmarshalMessage(channelNameMessage) | |
476 | assert.Nil(t, err) | |
477 | assert.NotNil(t, message) | |
478 | assert.Equal(t, "message", message.Type) | |
479 | assert.Equal(t, "channel_name", message.SubType) | |
480 | assert.Equal(t, "1358877455.000010", message.Timestamp) | |
481 | assert.Equal(t, "U2147483828", message.User) | |
482 | assert.Equal(t, "random", message.OldName) | |
483 | assert.Equal(t, "watercooler", message.Name) | |
484 | assert.Equal(t, "<@U2147483828|cal> has renamed the channel from \"random\" to \"watercooler\"", message.Text) | |
485 | } | |
486 | ||
487 | var channelArchiveMessage = `{ | |
488 | "type": "message", | |
489 | "subtype": "channel_archive", | |
490 | "ts": "1361482916.000003", | |
491 | "text": "<U1234|@cal> archived the channel", | |
492 | "user": "U1234", | |
493 | "members": ["U1234", "U5678"] | |
494 | }` | |
495 | ||
496 | func TestChannelArchiveMessage(t *testing.T) { | |
497 | message, err := unmarshalMessage(channelArchiveMessage) | |
498 | assert.Nil(t, err) | |
499 | assert.NotNil(t, message) | |
500 | assert.Equal(t, "message", message.Type) | |
501 | assert.Equal(t, "channel_archive", message.SubType) | |
502 | assert.Equal(t, "1361482916.000003", message.Timestamp) | |
503 | assert.Equal(t, "<U1234|@cal> archived the channel", message.Text) | |
504 | assert.Equal(t, "U1234", message.User) | |
505 | assert.NotNil(t, message.Members) | |
506 | assert.Equal(t, 2, len(message.Members)) | |
507 | } | |
508 | ||
509 | var channelUnarchiveMessage = `{ | |
510 | "type": "message", | |
511 | "subtype": "channel_unarchive", | |
512 | "ts": "1361482916.000003", | |
513 | "text": "<U1234|@cal> un-archived the channel", | |
514 | "user": "U1234" | |
515 | }` | |
516 | ||
517 | func TestChannelUnarchiveMessage(t *testing.T) { | |
518 | message, err := unmarshalMessage(channelUnarchiveMessage) | |
519 | assert.Nil(t, err) | |
520 | assert.NotNil(t, message) | |
521 | assert.Equal(t, "message", message.Type) | |
522 | assert.Equal(t, "channel_unarchive", message.SubType) | |
523 | assert.Equal(t, "1361482916.000003", message.Timestamp) | |
524 | assert.Equal(t, "<U1234|@cal> un-archived the channel", message.Text) | |
525 | assert.Equal(t, "U1234", message.User) | |
526 | } | |
527 | ||
528 | var groupJoinMessage = `{ | |
529 | "type": "message", | |
530 | "subtype": "group_join", | |
531 | "ts": "1358877458.000011", | |
532 | "user": "U2147483828", | |
533 | "text": "<@U2147483828|cal> has joined the group" | |
534 | }` | |
535 | ||
536 | func TestGroupJoinMessage(t *testing.T) { | |
537 | message, err := unmarshalMessage(groupJoinMessage) | |
538 | assert.Nil(t, err) | |
539 | assert.NotNil(t, message) | |
540 | assert.Equal(t, "message", message.Type) | |
541 | assert.Equal(t, "group_join", message.SubType) | |
542 | assert.Equal(t, "1358877458.000011", message.Timestamp) | |
543 | assert.Equal(t, "U2147483828", message.User) | |
544 | assert.Equal(t, "<@U2147483828|cal> has joined the group", message.Text) | |
545 | } | |
546 | ||
547 | var groupJoinInvitedMessage = `{ | |
548 | "type": "message", | |
549 | "subtype": "group_join", | |
550 | "ts": "1358877458.000011", | |
551 | "user": "U2147483828", | |
552 | "text": "<@U2147483828|cal> has joined the group", | |
553 | "inviter": "U2147483829" | |
554 | }` | |
555 | ||
556 | func TestGroupJoinInvitedMessage(t *testing.T) { | |
557 | message, err := unmarshalMessage(groupJoinInvitedMessage) | |
558 | assert.Nil(t, err) | |
559 | assert.NotNil(t, message) | |
560 | assert.Equal(t, "message", message.Type) | |
561 | assert.Equal(t, "group_join", message.SubType) | |
562 | assert.Equal(t, "1358877458.000011", message.Timestamp) | |
563 | assert.Equal(t, "U2147483828", message.User) | |
564 | assert.Equal(t, "<@U2147483828|cal> has joined the group", message.Text) | |
565 | assert.Equal(t, "U2147483829", message.Inviter) | |
566 | } | |
567 | ||
568 | var groupLeaveMessage = `{ | |
569 | "type": "message", | |
570 | "subtype": "group_leave", | |
571 | "ts": "1358877455.000010", | |
572 | "user": "U2147483828", | |
573 | "text": "<@U2147483828|cal> has left the group" | |
574 | }` | |
575 | ||
576 | func TestGroupLeaveMessage(t *testing.T) { | |
577 | message, err := unmarshalMessage(groupLeaveMessage) | |
578 | assert.Nil(t, err) | |
579 | assert.NotNil(t, message) | |
580 | assert.Equal(t, "message", message.Type) | |
581 | assert.Equal(t, "group_leave", message.SubType) | |
582 | assert.Equal(t, "1358877455.000010", message.Timestamp) | |
583 | assert.Equal(t, "U2147483828", message.User) | |
584 | assert.Equal(t, "<@U2147483828|cal> has left the group", message.Text) | |
585 | } | |
586 | ||
587 | var groupTopicMessage = `{ | |
588 | "type": "message", | |
589 | "subtype": "group_topic", | |
590 | "ts": "1358877455.000010", | |
591 | "user": "U2147483828", | |
592 | "topic": "hello world", | |
593 | "text": "<@U2147483828|cal> set the group topic: hello world" | |
594 | }` | |
595 | ||
596 | func TestGroupTopicMessage(t *testing.T) { | |
597 | message, err := unmarshalMessage(groupTopicMessage) | |
598 | assert.Nil(t, err) | |
599 | assert.NotNil(t, message) | |
600 | assert.Equal(t, "message", message.Type) | |
601 | assert.Equal(t, "group_topic", message.SubType) | |
602 | assert.Equal(t, "1358877455.000010", message.Timestamp) | |
603 | assert.Equal(t, "U2147483828", message.User) | |
604 | assert.Equal(t, "hello world", message.Topic) | |
605 | assert.Equal(t, "<@U2147483828|cal> set the group topic: hello world", message.Text) | |
606 | } | |
607 | ||
608 | var groupPurposeMessage = `{ | |
609 | "type": "message", | |
610 | "subtype": "group_purpose", | |
611 | "ts": "1358877455.000010", | |
612 | "user": "U2147483828", | |
613 | "purpose": "whatever", | |
614 | "text": "<@U2147483828|cal> set the group purpose: whatever" | |
615 | }` | |
616 | ||
617 | func TestGroupPurposeMessage(t *testing.T) { | |
618 | message, err := unmarshalMessage(groupPurposeMessage) | |
619 | assert.Nil(t, err) | |
620 | assert.NotNil(t, message) | |
621 | assert.Equal(t, "message", message.Type) | |
622 | assert.Equal(t, "group_purpose", message.SubType) | |
623 | assert.Equal(t, "1358877455.000010", message.Timestamp) | |
624 | assert.Equal(t, "U2147483828", message.User) | |
625 | assert.Equal(t, "whatever", message.Purpose) | |
626 | assert.Equal(t, "<@U2147483828|cal> set the group purpose: whatever", message.Text) | |
627 | } | |
628 | ||
629 | var groupNameMessage = `{ | |
630 | "type": "message", | |
631 | "subtype": "group_name", | |
632 | "ts": "1358877455.000010", | |
633 | "user": "U2147483828", | |
634 | "old_name": "random", | |
635 | "name": "watercooler", | |
636 | "text": "<@U2147483828|cal> has renamed the group from \"random\" to \"watercooler\"" | |
637 | }` | |
638 | ||
639 | func TestGroupNameMessage(t *testing.T) { | |
640 | message, err := unmarshalMessage(groupNameMessage) | |
641 | assert.Nil(t, err) | |
642 | assert.NotNil(t, message) | |
643 | assert.Equal(t, "message", message.Type) | |
644 | assert.Equal(t, "group_name", message.SubType) | |
645 | assert.Equal(t, "1358877455.000010", message.Timestamp) | |
646 | assert.Equal(t, "U2147483828", message.User) | |
647 | assert.Equal(t, "random", message.OldName) | |
648 | assert.Equal(t, "watercooler", message.Name) | |
649 | assert.Equal(t, "<@U2147483828|cal> has renamed the group from \"random\" to \"watercooler\"", message.Text) | |
650 | } | |
651 | ||
652 | var groupArchiveMessage = `{ | |
653 | "type": "message", | |
654 | "subtype": "group_archive", | |
655 | "ts": "1361482916.000003", | |
656 | "text": "<U1234|@cal> archived the group", | |
657 | "user": "U1234", | |
658 | "members": ["U1234", "U5678"] | |
659 | }` | |
660 | ||
661 | func TestGroupArchiveMessage(t *testing.T) { | |
662 | message, err := unmarshalMessage(groupArchiveMessage) | |
663 | assert.Nil(t, err) | |
664 | assert.NotNil(t, message) | |
665 | assert.Equal(t, "message", message.Type) | |
666 | assert.Equal(t, "group_archive", message.SubType) | |
667 | assert.Equal(t, "1361482916.000003", message.Timestamp) | |
668 | assert.Equal(t, "<U1234|@cal> archived the group", message.Text) | |
669 | assert.Equal(t, "U1234", message.User) | |
670 | assert.NotNil(t, message.Members) | |
671 | assert.Equal(t, 2, len(message.Members)) | |
672 | } | |
673 | ||
674 | var groupUnarchiveMessage = `{ | |
675 | "type": "message", | |
676 | "subtype": "group_unarchive", | |
677 | "ts": "1361482916.000003", | |
678 | "text": "<U1234|@cal> un-archived the group", | |
679 | "user": "U1234" | |
680 | }` | |
681 | ||
682 | func TestGroupUnarchiveMessage(t *testing.T) { | |
683 | message, err := unmarshalMessage(groupUnarchiveMessage) | |
684 | assert.Nil(t, err) | |
685 | assert.NotNil(t, message) | |
686 | assert.Equal(t, "message", message.Type) | |
687 | assert.Equal(t, "group_unarchive", message.SubType) | |
688 | assert.Equal(t, "1361482916.000003", message.Timestamp) | |
689 | assert.Equal(t, "<U1234|@cal> un-archived the group", message.Text) | |
690 | assert.Equal(t, "U1234", message.User) | |
691 | } | |
692 | ||
693 | var fileShareMessage = `{ | |
694 | "type": "message", | |
695 | "subtype": "file_share", | |
696 | "ts": "1358877455.000010", | |
697 | "text": "<@cal> uploaded a file: <https:...7.png|7.png>", | |
698 | "file": { | |
699 | "id" : "F2147483862", | |
700 | "created" : 1356032811, | |
701 | "timestamp" : 1356032811, | |
702 | "name" : "file.htm", | |
703 | "title" : "My HTML file", | |
704 | "mimetype" : "text\/plain", | |
705 | "filetype" : "text", | |
706 | "pretty_type": "Text", | |
707 | "user" : "U2147483697", | |
708 | "mode" : "hosted", | |
709 | "editable" : true, | |
710 | "is_external": false, | |
711 | "external_type": "", | |
712 | "size" : 12345, | |
713 | "url": "https:\/\/slack-files.com\/files-pub\/T024BE7LD-F024BERPE-09acb6\/1.png", | |
714 | "url_download": "https:\/\/slack-files.com\/files-pub\/T024BE7LD-F024BERPE-09acb6\/download\/1.png", | |
715 | "url_private": "https:\/\/slack.com\/files-pri\/T024BE7LD-F024BERPE\/1.png", | |
716 | "url_private_download": "https:\/\/slack.com\/files-pri\/T024BE7LD-F024BERPE\/download\/1.png", | |
717 | "thumb_64": "https:\/\/slack-files.com\/files-tmb\/T024BE7LD-F024BERPE-c66246\/1_64.png", | |
718 | "thumb_80": "https:\/\/slack-files.com\/files-tmb\/T024BE7LD-F024BERPE-c66246\/1_80.png", | |
719 | "thumb_360": "https:\/\/slack-files.com\/files-tmb\/T024BE7LD-F024BERPE-c66246\/1_360.png", | |
720 | "thumb_360_gif": "https:\/\/slack-files.com\/files-tmb\/T024BE7LD-F024BERPE-c66246\/1_360.gif", | |
721 | "thumb_360_w": 100, | |
722 | "thumb_360_h": 100, | |
723 | "permalink" : "https:\/\/tinyspeck.slack.com\/files\/cal\/F024BERPE\/1.png", | |
724 | "edit_link" : "https:\/\/tinyspeck.slack.com\/files\/cal\/F024BERPE\/1.png/edit", | |
725 | "preview" : "<!DOCTYPE html>\n<html>\n<meta charset='utf-8'>", | |
726 | "preview_highlight" : "<div class=\"sssh-code\"><div class=\"sssh-line\"><pre><!DOCTYPE html...", | |
727 | "lines" : 123, | |
728 | "lines_more": 118, | |
729 | "is_public": true, | |
730 | "public_url_shared": false, | |
731 | "channels": ["C024BE7LT"], | |
732 | "groups": ["G12345"], | |
733 | "ims": ["D12345"], | |
734 | "initial_comment": {}, | |
735 | "num_stars": 7, | |
736 | "is_starred": true | |
737 | }, | |
738 | "user": "U2147483697", | |
739 | "upload": true | |
740 | }` | |
741 | ||
742 | func TestFileShareMessage(t *testing.T) { | |
743 | message, err := unmarshalMessage(fileShareMessage) | |
744 | fmt.Println(err) | |
745 | assert.Nil(t, err) | |
746 | assert.NotNil(t, message) | |
747 | assert.Equal(t, "message", message.Type) | |
748 | assert.Equal(t, "file_share", message.SubType) | |
749 | assert.Equal(t, "1358877455.000010", message.Timestamp) | |
750 | assert.Equal(t, "<@cal> uploaded a file: <https:...7.png|7.png>", message.Text) | |
751 | assert.Equal(t, "U2147483697", message.User) | |
752 | assert.True(t, message.Upload) | |
753 | assert.NotNil(t, message.File) | |
754 | } |
11 | 11 | } |
12 | 12 | |
13 | 13 | // GetOAuthToken retrieves an AccessToken |
14 | func GetOAuthToken(clientId, clientSecret, code, redirectURI string, debug bool) (accessToken string, scope string, err error) { | |
14 | func GetOAuthToken(clientID, clientSecret, code, redirectURI string, debug bool) (accessToken string, scope string, err error) { | |
15 | 15 | values := url.Values{ |
16 | "client_id": {clientId}, | |
16 | "client_id": {clientID}, | |
17 | 17 | "client_secret": {clientSecret}, |
18 | 18 | "code": {code}, |
19 | 19 | "redirect_uri": {redirectURI}, |
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 |
22 | 22 | } |
23 | 23 | |
24 | 24 | type CtxChannel struct { |
25 | Id string `json:"id"` | |
25 | ID string `json:"id"` | |
26 | 26 | Name string `json:"name"` |
27 | 27 | } |
28 | 28 | |
29 | 29 | type CtxMessage struct { |
30 | UserId string `json:"user"` | |
30 | User string `json:"user"` | |
31 | 31 | Username string `json:"username"` |
32 | 32 | Text string `json:"text"` |
33 | 33 | Timestamp string `json:"ts"` |
37 | 37 | type SearchMessage struct { |
38 | 38 | Type string `json:"type"` |
39 | 39 | Channel CtxChannel `json:"channel"` |
40 | UserId string `json:"user"` | |
40 | User string `json:"user"` | |
41 | 41 | Username string `json:"username"` |
42 | 42 | Timestamp string `json:"ts"` |
43 | 43 | Text string `json:"text"` |
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 | TeamId string `json:"team_id"` | |
23 | UserId string `json:"user_id"` | |
22 | TeamID string `json:"team_id"` | |
23 | UserID string `json:"user_id"` | |
24 | 24 | } |
25 | 25 | |
26 | 26 | type authTestResponseFull struct { |
6 | 6 | ) |
7 | 7 | |
8 | 8 | const ( |
9 | DEFAULT_STARS_USERID = "" | |
10 | DEFAULT_STARS_COUNT = 100 | |
11 | DEFAULT_STARS_PAGE = 1 | |
9 | DEFAULT_STARS_USER = "" | |
10 | DEFAULT_STARS_COUNT = 100 | |
11 | DEFAULT_STARS_PAGE = 1 | |
12 | 12 | ) |
13 | 13 | |
14 | 14 | type StarsParameters struct { |
30 | 30 | |
31 | 31 | func NewStarsParameters() StarsParameters { |
32 | 32 | return StarsParameters{ |
33 | User: DEFAULT_STARS_USERID, | |
33 | User: DEFAULT_STARS_USER, | |
34 | 34 | Count: DEFAULT_STARS_COUNT, |
35 | 35 | Page: DEFAULT_STARS_PAGE, |
36 | 36 | } |
50 | 50 | values := url.Values{ |
51 | 51 | "token": {api.config.token}, |
52 | 52 | } |
53 | if params.User != DEFAULT_STARS_USERID { | |
53 | if params.User != DEFAULT_STARS_USER { | |
54 | 54 | values.Add("user", params.User) |
55 | 55 | } |
56 | 56 | if params.Count != DEFAULT_STARS_COUNT { |
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 | } |
24 | 24 | |
25 | 25 | // User contains all the information of a user |
26 | 26 | type User struct { |
27 | Id string `json:"id"` | |
27 | ID string `json:"id"` | |
28 | 28 | Name string `json:"name"` |
29 | 29 | Deleted bool `json:"deleted"` |
30 | 30 | Color string `json:"color"` |
74 | 74 | } |
75 | 75 | |
76 | 76 | // GetUserPresence will retrieve the current presence status of given user. |
77 | func (api *Slack) GetUserPresence(userId string) (*UserPresence, error) { | |
77 | func (api *Slack) GetUserPresence(user string) (*UserPresence, error) { | |
78 | 78 | values := url.Values{ |
79 | 79 | "token": {api.config.token}, |
80 | "user": {userId}, | |
80 | "user": {user}, | |
81 | 81 | } |
82 | 82 | response, err := userRequest("users.getPresence", values, api.debug) |
83 | 83 | if err != nil { |
87 | 87 | } |
88 | 88 | |
89 | 89 | // GetUserInfo will retrive the complete user information |
90 | func (api *Slack) GetUserInfo(userId string) (*User, error) { | |
90 | func (api *Slack) GetUserInfo(user string) (*User, error) { | |
91 | 91 | values := url.Values{ |
92 | 92 | "token": {api.config.token}, |
93 | "user": {userId}, | |
93 | "user": {user}, | |
94 | 94 | } |
95 | 95 | response, err := userRequest("users.info", values, api.debug) |
96 | 96 | if err != nil { |
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 { |
24 | 24 | // channel_archive |
25 | 25 | // channel_unarchive |
26 | 26 | Type string `json:"type"` |
27 | ChannelId string `json:"channel"` | |
28 | UserId string `json:"user,omitempty"` | |
27 | Channel string `json:"channel"` | |
28 | User string `json:"user,omitempty"` | |
29 | 29 | Timestamp *JSONTimeString `json:"ts,omitempty"` |
30 | 30 | } |
31 | 31 | |
35 | 35 | } |
36 | 36 | |
37 | 37 | type ChannelRenameInfo struct { |
38 | Id string `json:"id"` | |
38 | ID string `json:"id"` | |
39 | 39 | Name string `json:"name"` |
40 | 40 | Created JSONTimeString `json:"created"` |
41 | 41 | } |
1 | 1 | |
2 | 2 | type IMCreatedEvent struct { |
3 | 3 | Type string `json:"type"` |
4 | UserId string `json:"user"` | |
4 | User string `json:"user"` | |
5 | 5 | Channel ChannelCreatedInfo `json:"channel"` |
6 | 6 | } |
7 | 7 |
3 | 3 | Type string `json:"type"` |
4 | 4 | EventTimestamp JSONTimeString `json:"event_ts"` |
5 | 5 | File File `json:"file"` |
6 | // FileId is used for FileDeletedEvent | |
7 | FileId string `json:"file_id,omitempty"` | |
6 | // FileID is used for FileDeletedEvent | |
7 | FileID string `json:"file_id,omitempty"` | |
8 | 8 | } |
9 | 9 | |
10 | 10 | type FileCreatedEvent fileActionEvent |
27 | 27 | |
28 | 28 | type FileCommentDeletedEvent struct { |
29 | 29 | fileActionEvent |
30 | CommentId string `json:"comment"` | |
30 | Comment string `json:"comment"` | |
31 | 31 | } |
1 | 1 | |
2 | 2 | type GroupCreatedEvent struct { |
3 | 3 | Type string `json:"type"` |
4 | UserId string `json:"user"` | |
4 | User string `json:"user"` | |
5 | 5 | Channel ChannelCreatedInfo `json:"channel"` |
6 | 6 | } |
7 | 7 |
11 | 11 | type PresenceChangeEvent struct { |
12 | 12 | Type string `json:"type"` |
13 | 13 | Presence string `json:"presence"` |
14 | UserId string `json:"user"` | |
14 | User string `json:"user"` | |
15 | 15 | } |
16 | 16 | |
17 | 17 | type UserTypingEvent struct { |
18 | Type string `json:"type"` | |
19 | UserId string `json:"user"` | |
20 | ChannelId string `json:"channel"` | |
18 | Type string `json:"type"` | |
19 | User string `json:"user"` | |
20 | Channel string `json:"channel"` | |
21 | 21 | } |
22 | 22 | |
23 | 23 | type LatencyReport struct { |