Codebase list golang-github-nlopes-slack / fb34640
Merge pull request #121 from james-lawrence/feature/update-with-attachments refactor PostMessage/UpdateMessage, add SendMessage. Florin Pățan authored 7 years ago GitHub committed 7 years ago
2 changed file(s) with 220 addition(s) and 96 deletion(s). Raw diff Collapse all Expand all
6161 }
6262 }
6363
64 // DeleteMessage deletes a message in a channel
65 func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
66 respChannel, respTimestamp, _, err := api.SendMessage(channel, MsgOptionDelete(messageTimestamp))
67 return respChannel, respTimestamp, err
68 }
69
70 // PostMessage sends a message to a channel.
71 // Message is escaped by default according to https://api.slack.com/docs/formatting
72 // Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
73 func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
74 respChannel, respTimestamp, _, err := api.SendMessage(
75 channel,
76 MsgOptionText(text, params.EscapeText),
77 MsgOptionAttachments(params.Attachments...),
78 MsgOptionPostMessageParameters(params),
79 )
80 return respChannel, respTimestamp, err
81 }
82
83 // UpdateMessage updates a message in a channel
84 func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
85 return api.SendMessage(channel, MsgOptionUpdate(timestamp), MsgOptionText(text, true))
86 }
87
88 // SendMessage more flexible method for configuring messages.
89 func (api *Client) SendMessage(channel string, options ...MsgOption) (string, string, string, error) {
90 channel, values, err := ApplyMsgOptions(api.config.token, channel, options...)
91 if err != nil {
92 return "", "", "", err
93 }
94
95 response, err := chatRequest(channel, values, api.debug)
96 if err != nil {
97 return "", "", "", err
98 }
99
100 return response.Channel, response.Timestamp, response.Text, nil
101 }
102
103 // ApplyMsgOptions utility function for debugging/testing chat requests.
104 func ApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.Values, error) {
105 config := sendConfig{
106 mode: chatPostMessage,
107 values: url.Values{
108 "token": {token},
109 "channel": {channel},
110 },
111 }
112
113 for _, opt := range options {
114 if err := opt(&config); err != nil {
115 return string(config.mode), config.values, err
116 }
117 }
118
119 return string(config.mode), config.values, nil
120 }
121
122 func escapeMessage(message string) string {
123 replacer := strings.NewReplacer("&", "&amp;", "<", "&lt;", ">", "&gt;")
124 return replacer.Replace(message)
125 }
126
64127 func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
65128 response := &chatResponseFull{}
66129 err := post(path, values, response, debug)
73136 return response, nil
74137 }
75138
76 // DeleteMessage deletes a message in a channel
77 func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
78 values := url.Values{
79 "token": {api.config.token},
80 "channel": {channel},
81 "ts": {messageTimestamp},
82 }
83 response, err := chatRequest("chat.delete", values, api.debug)
84 if err != nil {
85 return "", "", err
86 }
87 return response.Channel, response.Timestamp, nil
88 }
89
90 func escapeMessage(message string) string {
91 replacer := strings.NewReplacer("&", "&amp;", "<", "&lt;", ">", "&gt;")
92 return replacer.Replace(message)
93 }
94
95 // PostMessage sends a message to a channel.
96 // Message is escaped by default according to https://api.slack.com/docs/formatting
97 // Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
98 func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
99 if params.EscapeText {
100 text = escapeMessage(text)
101 }
102 values := url.Values{
103 "token": {api.config.token},
104 "channel": {channel},
105 "text": {text},
106 }
107 if params.Username != DEFAULT_MESSAGE_USERNAME {
108 values.Set("username", string(params.Username))
109 }
110 if params.AsUser != DEFAULT_MESSAGE_ASUSER {
111 values.Set("as_user", "true")
112 }
113 if params.Parse != DEFAULT_MESSAGE_PARSE {
114 values.Set("parse", string(params.Parse))
115 }
116 if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
117 values.Set("link_names", "1")
118 }
119 if params.Attachments != nil {
120 attachments, err := json.Marshal(params.Attachments)
121 if err != nil {
122 return "", "", err
123 }
124 values.Set("attachments", string(attachments))
125 }
126 if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS {
127 values.Set("unfurl_links", "true")
128 }
129 // I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request.
130 // Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side.
131 if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
132 values.Set("unfurl_links", "false")
133 }
134 if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
135 values.Set("unfurl_media", "false")
136 }
137 if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
138 values.Set("icon_url", params.IconURL)
139 }
140 if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
141 values.Set("icon_emoji", params.IconEmoji)
142 }
143 if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
144 values.Set("mrkdwn", "false")
145 }
146 if params.ThreadTimestamp != DEFAULT_MESSAGE_THREAD_TIMESTAMP {
147 values.Set("thread_ts", params.ThreadTimestamp)
148 }
149
150 response, err := chatRequest("chat.postMessage", values, api.debug)
151 if err != nil {
152 return "", "", err
153 }
154 return response.Channel, response.Timestamp, nil
155 }
156
157 // UpdateMessage updates a message in a channel
158 func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
159 values := url.Values{
160 "token": {api.config.token},
161 "channel": {channel},
162 "text": {escapeMessage(text)},
163 "ts": {timestamp},
164 }
165 response, err := chatRequest("chat.update", values, api.debug)
166 if err != nil {
167 return "", "", "", err
168 }
169 return response.Channel, response.Timestamp, response.Text, nil
170 }
139 type sendMode string
140
141 const (
142 chatUpdate sendMode = "chat.update"
143 chatPostMessage sendMode = "chat.postMessage"
144 chatDelete sendMode = "chat.delete"
145 )
146
147 type sendConfig struct {
148 mode sendMode
149 values url.Values
150 }
151
152 // MsgOption option provided when sending a message.
153 type MsgOption func(*sendConfig) error
154
155 // MsgOptionPost posts a messages, this is the default.
156 func MsgOptionPost() MsgOption {
157 return func(config *sendConfig) error {
158 config.mode = chatPostMessage
159 config.values.Del("ts")
160 return nil
161 }
162 }
163
164 // MsgOptionUpdate updates a message based on the timestamp.
165 func MsgOptionUpdate(timestamp string) MsgOption {
166 return func(config *sendConfig) error {
167 config.mode = chatUpdate
168 config.values.Add("ts", timestamp)
169 return nil
170 }
171 }
172
173 // MsgOptionDelete deletes a message based on the timestamp.
174 func MsgOptionDelete(timestamp string) MsgOption {
175 return func(config *sendConfig) error {
176 config.mode = chatDelete
177 config.values.Add("ts", timestamp)
178 return nil
179 }
180 }
181
182 // MsgOptionAsUser whether or not to send the message as the user.
183 func MsgOptionAsUser(b bool) MsgOption {
184 return func(config *sendConfig) error {
185 if b != DEFAULT_MESSAGE_ASUSER {
186 config.values.Set("as_user", "true")
187 }
188 return nil
189 }
190 }
191
192 // MsgOptionText provide the text for the message, optionally escape the provided
193 // text.
194 func MsgOptionText(text string, escape bool) MsgOption {
195 return func(config *sendConfig) error {
196 if escape {
197 text = escapeMessage(text)
198 }
199 config.values.Add("text", text)
200 return nil
201 }
202 }
203
204 // MsgOptionAttachments provide attachments for the message.
205 func MsgOptionAttachments(attachments ...Attachment) MsgOption {
206 return func(config *sendConfig) error {
207 if attachments == nil {
208 return nil
209 }
210
211 attachments, err := json.Marshal(attachments)
212 if err == nil {
213 config.values.Set("attachments", string(attachments))
214 }
215 return err
216 }
217 }
218
219 // MsgOptionEnableLinkUnfurl enables link unfurling
220 func MsgOptionEnableLinkUnfurl() MsgOption {
221 return func(config *sendConfig) error {
222 config.values.Set("unfurl_links", "true")
223 return nil
224 }
225 }
226
227 // MsgOptionDisableMediaUnfurl disables media unfurling.
228 func MsgOptionDisableMediaUnfurl() MsgOption {
229 return func(config *sendConfig) error {
230 config.values.Set("unfurl_media", "false")
231 return nil
232 }
233 }
234
235 // MsgOptionDisableMarkdown disables markdown.
236 func MsgOptionDisableMarkdown() MsgOption {
237 return func(config *sendConfig) error {
238 config.values.Set("mrkdwn", "false")
239 return nil
240 }
241 }
242
243 // MsgOptionPostMessageParameters maintain backwards compatibility.
244 func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
245 return func(config *sendConfig) error {
246 if params.Username != DEFAULT_MESSAGE_USERNAME {
247 config.values.Set("username", string(params.Username))
248 }
249
250 // never generates an error.
251 _ = MsgOptionAsUser(params.AsUser)
252
253 if params.Parse != DEFAULT_MESSAGE_PARSE {
254 config.values.Set("parse", string(params.Parse))
255 }
256 if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
257 config.values.Set("link_names", "1")
258 }
259
260 if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS {
261 config.values.Set("unfurl_links", "true")
262 }
263
264 // I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request.
265 // Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side.
266 if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
267 config.values.Set("unfurl_links", "false")
268 }
269 if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
270 config.values.Set("unfurl_media", "false")
271 }
272 if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
273 config.values.Set("icon_url", params.IconURL)
274 }
275 if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
276 config.values.Set("icon_emoji", params.IconEmoji)
277 }
278 if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
279 config.values.Set("mrkdwn", "false")
280 }
281
282 if params.ThreadTimestamp != DEFAULT_MESSAGE_THREAD_TIMESTAMP {
283 config.values.Set("thread_ts", params.ThreadTimestamp)
284 }
285
286 return nil
287 }
288 }
113113 }
114114 defer resp.Body.Close()
115115
116 // Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
117 if resp.StatusCode != 200 {
118 logResponse(resp, debug)
119 return fmt.Errorf("Slack server error: %s.", resp.Status)
120 }
121
116122 return parseResponseBody(resp.Body, &intf, debug)
117123 }
118124
132138 return err
133139 }
134140
135 logger.Print(text)
141 logger.Print(string(text))
136142 }
137143
138144 return nil