slack_message 2.3.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,326 @@
1
+ ### The Message DSL
2
+
3
+ A pretty good number of the elements available in BlockKit are usable in SlackMessage. There are also a few elements that haven't been implemented in the official API, but are too useful to be missing.
4
+
5
+ #### Basic Text
6
+
7
+ While BlockKit officially requires that any elements are contained within a section element, that requirement is relaxed in SlackMessage. If you don't specify a section, one will silently be created to encapsulate your code. That's the secret behind the most basic messages in these docs.
8
+
9
+ ```ruby
10
+ SlackMessage.build do
11
+ text "couldn't be easier"
12
+ end
13
+
14
+ # => [{:type=>"section",
15
+ # :text=>{:type=>"mrkdwn", :text=>"couldn't be easier"}
16
+ # }]
17
+ ```
18
+
19
+ This is equivalent to the more verbose version with a declared section.
20
+
21
+ ```ruby
22
+ SlackMessage.build do
23
+ section do
24
+ text "could be easier"
25
+ end
26
+ end
27
+
28
+ # => [{:type=>"section",
29
+ # :text=>{:type=>"mrkdwn", :text=>"could be easier"}
30
+ # }]
31
+ ```
32
+
33
+ Text elements are the most basic type of element. Adding multiple text calls
34
+ will add a newline between text calls, which will cause a line break
35
+ appropriately.
36
+
37
+ ```ruby
38
+ SlackMessage.build do
39
+ text "one fish, two fish"
40
+ text "red fish, blue fish"
41
+ end
42
+
43
+ # => [{:type=>"section",
44
+ # :text=>{:type=>"mrkdwn", :text=>"one fish, two fish\nred fish, blue fish"}
45
+ # }]
46
+ ```
47
+
48
+ Slack uses a [faux-markdown syntax called
49
+ mrkdwn](https://api.slack.com/reference/surfaces/formatting#basics), which you
50
+ may be familiar with by typing in the Slack app itself. The API will
51
+ automatically render mrkdwn appropriately.
52
+
53
+ ```ruby
54
+ SlackMessage.build do
55
+ text "*Favorite Colors*"
56
+ text "_John_: ~red~ actually blue."
57
+ end
58
+
59
+ # => [{:type=>"section",
60
+ # :text=>{:type=>"mrkdwn", :text=>"*Favorite Colors*\n_John_: ~red~ actually blue."}}]
61
+ ```
62
+
63
+ Rendering emoji in messages is possible using either a) real unicode emoji in
64
+ your message, or b) using the `:emojiname:` syntax, which supports any emoji
65
+ that would work in your Slack app itself, including custom emoji.
66
+
67
+ ```ruby
68
+ SlackMessage.build do
69
+ text ":shipit_squirrel:🚀 time to gooo :tada:"
70
+ end
71
+
72
+ # => [{:type=>"section",
73
+ # :text=>{:type=>"mrkdwn", :text=>":shipit_squirrel:🚀 time to gooo :tada:"}
74
+ # }]
75
+ ```
76
+
77
+ To add a link using Slack's non-markdown link syntax, use the `link` helper
78
+ method interpolated into a text element. Using the `link` helper as its own
79
+ element won't work, as the method simply returns a string that has to be
80
+ included into a text element specifically.
81
+
82
+ ```ruby
83
+ SlackMessage.build do
84
+ text "Your #{link('build', 'https://google.com')} is ready."
85
+ end
86
+
87
+ # => [{:type=>"section",
88
+ # :text=>{:type=>"mrkdwn", :text=>"Your <https://google.com|build> is ready."}
89
+ # }]
90
+ ```
91
+
92
+ While the API squishes whitespace (much in the same way HTML does), it may
93
+ sometimes be useful to add a blank line between text _without_ adding a new
94
+ section to your message. To do so, use the pseudo-element `blank_line`.
95
+
96
+ ```ruby
97
+ SlackMessage.build do
98
+ text "don't let this line"
99
+ blank_line
100
+ text "touch this line."
101
+ end
102
+
103
+ # => => [{:type=>"section",
104
+ # :text=>{:type=>"mrkdwn", :text=>"don't let this line\n \ntouch this line."}
105
+ # }]
106
+ ```
107
+
108
+ Note that between the two newlines in the above example is a unicode emspace,
109
+ which the API will respect as a line worth rendering.
110
+
111
+ #### Buttons
112
+
113
+ BlockKit allows you to specify a button to the right of a section / block. That
114
+ button will be aligned outside the normal space for a section, and is meant to
115
+ link out of the app. To create one of these, use the `link_button` helper.
116
+
117
+ ```ruby
118
+ SlackMessage.build do
119
+ text "Your daily stats are ready @here"
120
+ link_button "Stats Dashboard", stats_dashboard_url
121
+ end
122
+
123
+ # => [{:type=>"section",
124
+ # :text=>{:type=>"mrkdwn", :text=>"Your daily stats are ready @here"},
125
+ # :accessory=>
126
+ # {:type=>"button",
127
+ # :url=>"http://yoursite.com/stats_dashboard",
128
+ # :text=>{:type=>"plain_text", :text=>"Stats Dashboard", :emoji=>true},
129
+ # :style=>:primary}}]
130
+ ```
131
+
132
+ Slack allows three styles for buttons: `default`, `primary`, and `danger`.
133
+ These correspond to gray, green and red buttons respectively. If not specified,
134
+ SlackMessage will use the `primary` style for buttons. I get that this could be
135
+ confusing when there is a default style, but in my experience, a colorful button
136
+ is way more common.
137
+
138
+ You can override the button style by specifying the style with your link button.
139
+
140
+ ```ruby
141
+ SlackMessage.build do
142
+ text "A job has failed catastrophically!"
143
+ link_button "Sidekiq Dashboard", sidekiq_dashboard_url, style: :danger
144
+ end
145
+
146
+ # => [{:type=>"section",
147
+ # :text=>{:type=>"mrkdwn", :text=>"A job has failed catastrophically!"},
148
+ # :accessory=>
149
+ # {:type=>"button",
150
+ # :url=>"https://yoursite.com/sidekiq",
151
+ # :text=>{:type=>"plain_text", :text=>"Sidekiq Dashboard", :emoji=>true},
152
+ # :style=>:danger}}]
153
+ ```
154
+
155
+ #### Ordered and Unordered Lists
156
+
157
+ The Slack API doesn't have native support for HTML-style ordered and unordered
158
+ lists, but there are convenience methods in SlackMessage to render a close
159
+ approximation.
160
+
161
+ ```ruby
162
+ SlackMessage.build do
163
+ section do
164
+ text '*Pet Goodness Tiers*'
165
+
166
+ ol([
167
+ 'tiny pigs',
168
+ 'reptiles',
169
+ 'dogs',
170
+ 'cats',
171
+ ])
172
+ end
173
+
174
+ section do
175
+ text '_voted by_'
176
+ ul(['Joe', 'Emily', 'Sophia', 'Matt'])
177
+ end
178
+ end
179
+ ```
180
+
181
+ Because Slack automatically collapses leading whitespace, indention of lists is
182
+ handled using unicode emspaces. Bullets for unordered lists are also unicode
183
+ characters to avoid being read as markdown.
184
+
185
+ #### List Items (e.g. HTML dt & dd)
186
+
187
+ When trying to represent title / value lists, you can use the "list item" block
188
+ type to pass a set of values. Slack does not allow you to customize how many
189
+ items are shown per line, so you'll just have to work with it.
190
+
191
+ ```ruby
192
+ SlackMessage.build do
193
+ text 'Import results are available!'
194
+
195
+ list_item 'Import Date', Date.today.to_s
196
+ list_item 'Items Imported', 55_000
197
+ list_item 'Errors', 23
198
+ list_item 'Bad Values', errors.map(&:to_s)
199
+ end
200
+ ```
201
+
202
+ #### Including Multiple Sections
203
+
204
+ Adding more sections is trivial. Simply declare each section and it will be
205
+ separated in the rendered message. This can often occur when looping.
206
+
207
+ ```ruby
208
+ SlackMessage.build do
209
+ pet_types.each do |type, breeds|
210
+ section do
211
+ text "*#{type}:* #{breeds.join(", ")}"
212
+ end
213
+ end
214
+ end
215
+ ```
216
+
217
+ It can also be useful to add a visual divider (similar to a `hr` in HTML)
218
+ between sections. To add one of these, use the `divider` helper. You can also
219
+ add a divider at the end of all the sections, but it often looks silly.
220
+
221
+ ```ruby
222
+ SlackMessage.build do
223
+ section do
224
+ text "*Topsiders:* Emily, Elsie, Derick"
225
+ end
226
+
227
+ divider
228
+
229
+ section do
230
+ text "*Undergrounders:* Kristina, Lauren, Different Emily"
231
+ end
232
+ end
233
+
234
+ # => [
235
+ # {:type=>"section", :text=>{:type=>"mrkdwn", :text=>"*Topsiders:* Emily, Elsie, Derick"}},
236
+ # {:type=>"divider"},
237
+ # {:type=>"section", :text=>{:type=>"mrkdwn", :text=>"*Undergrounders:* Kristina, Lauren, Different Emily"}}
238
+ # ]
239
+ ```
240
+
241
+ Note that a divider can only occur between sections, not within a single
242
+ section. Because of how implicit sections are built, it may look like this works
243
+ for simple messages. You may have troubles when you start adding more
244
+ complicated elements to your messages.
245
+
246
+ #### Images
247
+ TODO: image, accessory_image
248
+
249
+ #### Footers (Context)
250
+
251
+ Slack allows you to add a small additional piece of text to your message, which
252
+ will be rendered in italics and small text. It can support both links and emoji,
253
+ and is useful for providing minor details for your message.
254
+
255
+ ```ruby
256
+ SlackMessage.build do
257
+ text "New coffee complaints have been added."
258
+ context "this complaint added by #{link('Joe Mastey', 'hello@joemastey.com')}."
259
+ end
260
+
261
+ # => [{:type=>"section",
262
+ # :text=>{:type=>"mrkdwn", :text=>"New coffee complaints have been added."}
263
+ # },
264
+ # {:type=>"context", :elements=>
265
+ # [{:type=>"mrkdwn",
266
+ # :text=>"this complaint added by <hello@joemastey.com|Joe Mastey>."
267
+ # }]
268
+ # }]
269
+ ```
270
+
271
+ Context does not belong to a section, and is per-message, not per-section.
272
+ Specifying more than one context will simply overwrite previous calls.
273
+
274
+ #### Bot Customization
275
+
276
+ By default - and with scheduled messages - Slack will use the name and icon of
277
+ the Slack app whose API key you configured. As seen before, it's
278
+ possible to override those default names and icons in configuration. However, it
279
+ can also be customized per-message.
280
+
281
+ ```ruby
282
+ SlackMessage.build do
283
+ bot_icon ":sad_robot:"
284
+ bot_name "BadNewsBuildBot"
285
+
286
+ text "The build is broken. @here"
287
+ end
288
+
289
+ # => [{:type=>"section", :text=>{:type=>"mrkdwn", :text=>"The build is broken. @here"}}]
290
+ ```
291
+
292
+ Notice that the bot details aren't shown in the output of the `build` command.
293
+ To view the changes these methods cause, use `debug` mode.
294
+
295
+ The `bot_icon` can be specified as either an emoji (`:example:`), or a URL
296
+ pointing to an image (`http://mysite.com/shipit.png`). Any other value seems to
297
+ cause an error.
298
+
299
+ #### Custom Notification Text
300
+
301
+ For users who have notifications turned on, Slack will provide a small message
302
+ preview when you send them a message. By default, this preview will take the
303
+ first several words from your message.
304
+
305
+ However, you can specify some custom notification text to be shown to the user.
306
+ This text supports basic emoji and formatting, but nothing complicated.
307
+
308
+ ```ruby
309
+ SlackMessage.build do
310
+ notification_text "Having issues with the build. :ohnoes:"
311
+
312
+ text "The build is broken. The error message was 'undefined method round for NilClass'"
313
+
314
+ # => [{:type=>"section",
315
+ # :text=>
316
+ # {:type=>"mrkdwn",
317
+ # :text=>"The build is broken. The error message was 'undefined method round for NilClass'"}}]
318
+ end
319
+ ```
320
+
321
+ Again notice that notification text is not set within the blocks themselves, so
322
+ you will need to enable debugging to see how it changes what is sent to the API.
323
+
324
+ ---
325
+
326
+ Next: [Editing Messages](https://jmmastey.github.io/slack_message/04_editing_messages)
@@ -0,0 +1,88 @@
1
+ ### Updating a Previous Message
2
+
3
+ After you've posted a message, you may want to edit it later. Interactive bots,
4
+ for instance, may want to repeatedly update a message.
5
+
6
+ Posting will always return an object representing your posted message.
7
+
8
+ ```ruby
9
+ message = SlackMessage.post_to('#general') do
10
+ text "Getting ready..."
11
+ end
12
+ ```
13
+
14
+ Then, you can use that response object to go back and rewrite the message a
15
+ little or a lot.
16
+
17
+ ```ruby
18
+ SlackMessage.update(message) do
19
+ text "Done!"
20
+ end
21
+ ```
22
+
23
+ The new message contents will be built and updated via the API. To give an
24
+ example, you could alert slack to a job status by updating your original
25
+ message.
26
+
27
+
28
+ ```ruby
29
+ class SomeWorker < ApplicationWorker
30
+ def perform
31
+ post_started_status
32
+
33
+ # ... perform work here
34
+
35
+ post_finished_status
36
+ end
37
+
38
+ private
39
+
40
+ def post_started_status
41
+ @message = SlackMessage.post_as(:job_worker) do
42
+ text "Beginning upload."
43
+ end
44
+ end
45
+
46
+ def post_finished_status
47
+ SlackMessage.update(@message) do
48
+ text "Finished upload! @here come and get it."
49
+ link_button "See Results", uploaded_data_url
50
+ end
51
+ end
52
+ end
53
+ ```
54
+
55
+ #### Storing Response Objects for Later
56
+
57
+ Since updates are likely to occur after you've long since finished posting the
58
+ original message, you'll need to persist the message response somehow until you
59
+ need to update it later. As one option, you could serialize the response object
60
+ for later.
61
+
62
+ ```ruby
63
+ # initially
64
+ message = SlackMessage.post_to('#general') do
65
+ text "Starting..."
66
+ end
67
+ redis_connection.set(self.message_cache_key, Marshal.dump(message))
68
+
69
+
70
+ # later
71
+ message = Marshal.load(redis_connection.get(self.message_cache_key))
72
+ SlackMessage.update(message) do
73
+ text "Finished!"
74
+ end
75
+ ```
76
+
77
+ #### Updating Scheduled Messages
78
+
79
+ Sadly, there's currently no way to edit a scheduled message. You'll receive an
80
+ error if you attempt to call `update` on a scheduled message.
81
+
82
+ See the [API documentation for
83
+ chat.update](https://api.slack.com/methods/chat.update) for more information on
84
+ updating messages.
85
+
86
+ ---
87
+
88
+ Next: [Deleting Messages](https://jmmastey.github.io/slack_message/05_deleting_messages)
@@ -0,0 +1,45 @@
1
+ ### Deleting Messages
2
+
3
+ Deleting a message is much like editing a message, only simpler. Just like when
4
+ you edit a message, you'll need a reference to the message you posted.
5
+
6
+ *Important Note: It's not possible to delete a message sent directly to a user.
7
+ It's also not possible to delete a scheduled message once it's already posted.
8
+ Don't send anything you don't want your boss to read.*
9
+
10
+ ```ruby
11
+ message = SlackMessage.post_to('#general') do
12
+ text "Testing: #{SLACK_SECRET_KEY}"
13
+ end
14
+ ```
15
+
16
+ Now you can simply call the `delete` method to make up for your mistakes.
17
+
18
+ ```ruby
19
+ SlackMessage.delete(message)
20
+ ```
21
+
22
+ As with editing a message, it's possible to persist messages to redis / your
23
+ database and remove them using the timestamp and channel of your message.
24
+
25
+ ```ruby
26
+ # initially
27
+ message = SlackMessage.post_to('#general') do
28
+ text "Testing: #{SLACK_SECRET_KEY}"
29
+ end
30
+ redis_connection.set(self.message_cache_key, Marshal.dump(message))
31
+
32
+
33
+ # later
34
+ message = Marshal.load(redis_connection.get(self.message_cache_key))
35
+ SlackMessage.delete(message)
36
+ ```
37
+
38
+ See the [API documentation for
39
+ chat.delete](https://api.slack.com/methods/chat.delete) or
40
+ [chat.deleteScheduledMessage](https://api.slack.com/methods/chat.deleteScheduledMessage)
41
+ for more information on deleting messages.
42
+
43
+ ---
44
+
45
+ Next: [Mentions / Notifying Users](https://jmmastey.github.io/slack_message/06_notifying_users)
@@ -0,0 +1,62 @@
1
+ ## Mentions / Notifying Users
2
+
3
+ There are several supported ways to tag and notify users. As mentioned
4
+ initially, it's possible to DM a user by their account email.
5
+
6
+ ```ruby
7
+ SlackMessage.post_to('hello@joemastey.com') do
8
+ text "Hi there!"
9
+ end
10
+ ```
11
+
12
+ You can also mention a user by email within a channel by wrapping their name in
13
+ tags.
14
+
15
+ ```ruby
16
+ SlackMessage.post_to('#general') do
17
+ bot_name "CoffeeBot"
18
+ bot_icon ":coffee:"
19
+
20
+ text ":coffee: It's your turn to make coffee <hello@joemastey.com>."
21
+ end
22
+ ```
23
+
24
+ Emails that are not wrapped in tags will be rendered as normal email addresses.
25
+ Additionally, Slack will automatically convert a number of channel names and
26
+ tags you're probably already used to.
27
+
28
+ ```ruby
29
+ SlackMessage.post_to('#general') do
30
+ bot_name "CoffeeBot"
31
+ bot_icon ":coffee:"
32
+
33
+ text "@here There's no coffee left! Let #general know when you fix it."
34
+ end
35
+ ```
36
+
37
+ By default, the desktop notification for a message will be the text of the
38
+ message itself. However, you can customize desktop notifications if you prefer.
39
+
40
+ ```ruby
41
+ SlackMessage.post_to('hello@joemastey.com') do
42
+ bot_name "CoffeeBot"
43
+ bot_icon ":coffee:"
44
+
45
+ notification_text "It's a coffee emergency!"
46
+ text "There's no coffee left!"
47
+ end
48
+ ```
49
+
50
+ #### Using @channel or @here
51
+
52
+ Not really a feature, but Slack will respect usage of `@here` and `@channel`.
53
+
54
+ ```ruby
55
+ SlackMessage.post_to('#general') do
56
+ text "Hey @channel, don't forget to submit your drink requests."
57
+ end
58
+ ```
59
+
60
+ ---
61
+
62
+ Next: [Testing](https://jmmastey.github.io/slack_message/07_testing)
@@ -0,0 +1,49 @@
1
+ ## Testing
2
+
3
+ You can do some basic testing against SlackMessage, at least if you use RSpec!
4
+ You'll need to require and include the testing behavior in your spec_helper
5
+ file.
6
+
7
+ ```ruby
8
+ require 'slack_message/rspec'
9
+
10
+ RSpec.configure do |config|
11
+ include SlackMessage::RSpec
12
+
13
+ # your other spec_helper config
14
+ end
15
+ ```
16
+
17
+ This will prevent API calls from leaking in your tests, and will allow you
18
+ access to some custom matchers.
19
+
20
+ ```ruby
21
+ expect {
22
+ SlackMessage.post_to('#general') { text "foo" }
23
+ }.to post_slack_message_to('#general').with_content_matching(/foo/)
24
+
25
+ expect {
26
+ SlackMessage.post_as(:schmoebot) { text "foo" }
27
+ }.to post_slack_message_as(:schmoebot)
28
+
29
+ expect {
30
+ SlackMessage.post_as(:schmoebot) { text "foo" }
31
+ }.to post_slack_message_as('Schmoe Bot')
32
+
33
+ expect {
34
+ SlackMessage.post_as(:schmoebot) { text "foo" }
35
+ }.to post_slack_message_with_icon(':schmoebot:')
36
+
37
+ expect {
38
+ SlackMessage.post_as(:schmoebot) { text "foo" }
39
+ }.to post_slack_message_with_icon_matching(/gravatar/)
40
+
41
+ expect {
42
+ SlackMessage.post_to('#general') { text "foo" }
43
+ }.to post_to_slack
44
+ ```
45
+
46
+ Be forewarned, I'm frankly not that great at more complicated RSpec matchers,
47
+ so I'm guessing there are some bugs. Also, because the content of a message
48
+ gets turned into a complex JSON object, matching against content isn't capable
49
+ of very complicated regexes.
data/docs/_config.yml ADDED
@@ -0,0 +1,6 @@
1
+ plugins:
2
+ - jekyll-relative-links
3
+ relative_links:
4
+ enabled: true
5
+ collections: true
6
+ theme: jekyll-theme-midnight
data/docs/index.md ADDED
@@ -0,0 +1,49 @@
1
+ * [Getting Started / Configuration](https://jmmastey.github.io/slack_message/01_configuration)
2
+ * [Posting a Message](https://jmmastey.github.io/slack_message/02_posting_a_message)
3
+ * [The SlackMessage DSL](https://jmmastey.github.io/slack_message/03_message_dsl)
4
+ * [Editing Messages](https://jmmastey.github.io/slack_message/04_editing_messages)
5
+ * [Deleting Messages](https://jmmastey.github.io/slack_message/05_deleting_messages)
6
+ * [Mentions / Notifying Users](https://jmmastey.github.io/slack_message/06_notifying_users)
7
+ * [Testing](https://jmmastey.github.io/slack_message/07_testing)
8
+
9
+ SlackMessage is a gem that makes it easy to send messages to Slack from your
10
+ application. _Really_ easy.
11
+
12
+ ```ruby
13
+ SlackMessage.post_to('#general') do
14
+ text "We did it @here! :thumbsup:"
15
+ end
16
+ ```
17
+
18
+ And not just simple messages. You can compose complicated messages quickly in a
19
+ DSL that's focused on usability and maintainability. It can be tough to
20
+ maintain code in other similar gems, but not here.
21
+
22
+ ```ruby
23
+ SlackMessage.post_to('hello@joemastey.com') do
24
+ section do
25
+ text "A job has generated some output for you to review."
26
+ text 'And More' * 10
27
+ link_button "See Results", "https://google.com"
28
+ end
29
+
30
+ section do
31
+ text ":unlock-new: New Data Summary"
32
+
33
+ list_item "Date", "09/05/2021"
34
+ list_item "Total Imported", 45_004
35
+ list_item "Total Errors", 5
36
+ end
37
+
38
+ divider
39
+
40
+ section do
41
+ text "See more here: #{link('result', 'https://google.com')}"
42
+ end
43
+
44
+ context "Kicked off by <hello@joemastey.com> at **9:05am**"
45
+ end
46
+ ```
47
+
48
+ It has no dependencies and minimal configuration needs, so you can get up and
49
+ running quickly.