slack_sender 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +57 -1
- data/README.md +7 -6
- data/lib/slack_sender/configuration.rb +56 -26
- data/lib/slack_sender/delivery_axn/async_configuration.rb +26 -8
- data/lib/slack_sender/delivery_axn/validation.rb +9 -3
- data/lib/slack_sender/delivery_axn.rb +54 -5
- data/lib/slack_sender/error_messages.rb +7 -3
- data/lib/slack_sender/file_wrapper.rb +3 -2
- data/lib/slack_sender/notifier/notification_definition.rb +5 -2
- data/lib/slack_sender/notifier/notification_dsl.rb +5 -1
- data/lib/slack_sender/profile.rb +26 -16
- data/lib/slack_sender/strategy.rb +9 -2
- data/lib/slack_sender/util.rb +13 -1
- data/lib/slack_sender/version.rb +1 -1
- data/lib/slack_sender.rb +14 -16
- metadata +12 -13
- data/.husky/pre-commit +0 -1
- data/.lintstagedrc +0 -1
- data/Rakefile +0 -15
- data/docs/axn_integration.md +0 -168
- data/docs/configuration.md +0 -252
- data/docs/troubleshooting.md +0 -160
- data/docs/usage.md +0 -382
data/docs/troubleshooting.md
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# Troubleshooting & FAQ
|
|
2
|
-
|
|
3
|
-
[← Back to README](../README.md)
|
|
4
|
-
|
|
5
|
-
## Common Issues
|
|
6
|
-
|
|
7
|
-
### Messages Not Being Sent
|
|
8
|
-
|
|
9
|
-
**Check the following:**
|
|
10
|
-
|
|
11
|
-
1. Ensure `SlackSender.config.enabled` is `true` (default)
|
|
12
|
-
2. Verify your profile is registered: `SlackSender.profile(:default)`
|
|
13
|
-
3. Check `SlackSender.config.sandbox_mode?` -- if `true`, check configured [`sandbox.behavior`](./configuration.md#sandbox-mode)
|
|
14
|
-
4. Check that an async backend is available if using `call` (not necessary for `call!`)
|
|
15
|
-
5. Verify your Slack token is valid and has the required scopes
|
|
16
|
-
|
|
17
|
-
### Messages Work in Production but Not in Development
|
|
18
|
-
|
|
19
|
-
When `SlackSender.config.sandbox_mode?` is enabled (by default, if `Rails.env.production?` is false), the configured [`sandbox.behavior`](./configuration.md#sandbox-mode) will apply. This likely means logging but skipping send (if `:noop`) or redirecting to a specific channel.
|
|
20
|
-
|
|
21
|
-
**If messages are not received in the sandbox channel, check:**
|
|
22
|
-
|
|
23
|
-
1. `SlackSender.config.sandbox_mode?` — should be `true` in development
|
|
24
|
-
2. Your `sandbox.channel.replace_with` channel ID is correct
|
|
25
|
-
3. The bot is invited to the sandbox channel
|
|
26
|
-
|
|
27
|
-
### "NotInChannel" Errors
|
|
28
|
-
|
|
29
|
-
The bot must be invited to the channel.
|
|
30
|
-
|
|
31
|
-
**Resolution:**
|
|
32
|
-
|
|
33
|
-
* Invite the bot to the channel (see [stackoverflow](https://stackoverflow.com/a/68475477))
|
|
34
|
-
|
|
35
|
-
### `missing_scope` Errors
|
|
36
|
-
|
|
37
|
-
Your Slack app is missing required OAuth scopes. The error message will tell you which scope is needed:
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
Slack API missing_scope error: required scope 'files:write' is not granted.
|
|
41
|
-
Add this scope to your Slack app at https://api.slack.com/apps and reinstall the app.
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
**To fix:**
|
|
45
|
-
|
|
46
|
-
1. Go to https://api.slack.com/apps and select your app
|
|
47
|
-
2. Navigate to **OAuth & Permissions** → **Bot Token Scopes**
|
|
48
|
-
3. Add the missing scope (e.g., `files:write`)
|
|
49
|
-
4. Reinstall the app to your workspace
|
|
50
|
-
|
|
51
|
-
See [Required Slack Scopes](configuration.md#required-slack-scopes) for a complete list.
|
|
52
|
-
|
|
53
|
-
### File Uploads Fail with "channel ID required" Error
|
|
54
|
-
|
|
55
|
-
Slack's file upload APIs require channel IDs, not usernames or channel names:
|
|
56
|
-
|
|
57
|
-
```ruby
|
|
58
|
-
# These don't work for file uploads
|
|
59
|
-
SlackSender.call!(channel: "@username", file: file)
|
|
60
|
-
SlackSender.call!(channel: "#general", file: file)
|
|
61
|
-
|
|
62
|
-
# Use channel IDs instead
|
|
63
|
-
SlackSender.call!(channel: "C024BE91L", file: file) # Public channel
|
|
64
|
-
SlackSender.call!(channel: "D032AC32T", file: file) # DM channel
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
For DMs, find the DM channel ID (starts with `D`) from Slack's URL when viewing the conversation.
|
|
68
|
-
|
|
69
|
-
### File Uploads Fail with Async Delivery
|
|
70
|
-
|
|
71
|
-
File uploads with async delivery (`call`) are supported. Files are uploaded to Slack synchronously, then shared via the background job. Total file size cannot exceed `max_async_file_upload_size` (default 25 MB).
|
|
72
|
-
|
|
73
|
-
**If you're hitting the async size limit:**
|
|
74
|
-
|
|
75
|
-
```ruby
|
|
76
|
-
# Try increasing the async limit
|
|
77
|
-
SlackSender.config.max_async_file_upload_size = 100_000_000 # 100 MB
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## FAQ
|
|
83
|
-
|
|
84
|
-
### How do I disable SlackSender temporarily?
|
|
85
|
-
|
|
86
|
-
Set `SlackSender.config.enabled = false`. All `call` and `call!` methods will return `false` without sending messages.
|
|
87
|
-
|
|
88
|
-
### Can I send to multiple channels at once?
|
|
89
|
-
|
|
90
|
-
Yes, use `channels:` (plural) with async delivery:
|
|
91
|
-
|
|
92
|
-
```ruby
|
|
93
|
-
SlackSender.call(channels: [:alerts, :ops], text: "Broadcast message")
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Multi-channel is only supported for async (`call`). Sync (`call!`) requires sending to each channel individually (to avoid confusion about how to report on partial failures). Files are uploaded once and shared to all channels efficiently.
|
|
97
|
-
|
|
98
|
-
### Can I use multiple Slack workspaces?
|
|
99
|
-
|
|
100
|
-
Yes, register multiple profiles:
|
|
101
|
-
|
|
102
|
-
```ruby
|
|
103
|
-
SlackSender.register(:workspace1, token: TOKEN1, channels: {...})
|
|
104
|
-
SlackSender.register(:workspace2, token: TOKEN2, channels: {...})
|
|
105
|
-
|
|
106
|
-
SlackSender.profile(:workspace1).call(...)
|
|
107
|
-
SlackSender.profile(:workspace2).call(...)
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### How are rate limits handled?
|
|
111
|
-
|
|
112
|
-
When sent async, SlackSender automatically detects rate limit errors and retries with the delay specified in Slack's `Retry-After` header. Retries happen up to 5 times before giving up.
|
|
113
|
-
|
|
114
|
-
### What errors are retried vs discarded?
|
|
115
|
-
|
|
116
|
-
**Retried:**
|
|
117
|
-
- Rate limit errors (with `Retry-After` delay)
|
|
118
|
-
|
|
119
|
-
**Discarded immediately (no retry):**
|
|
120
|
-
- `NotInChannel` — Bot not in channel
|
|
121
|
-
- `ChannelNotFound` — Channel doesn't exist
|
|
122
|
-
- `IsArchived` — Channel is archived
|
|
123
|
-
|
|
124
|
-
### How do I silence archived channel exceptions?
|
|
125
|
-
|
|
126
|
-
```ruby
|
|
127
|
-
SlackSender.config.silence_archived_channel_exceptions = true
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
This will log the error but not raise an exception.
|
|
131
|
-
|
|
132
|
-
### What's the difference between `call` and `call!`?
|
|
133
|
-
|
|
134
|
-
| Method | Delivery | Return Value | Retries |
|
|
135
|
-
|--------|----------|--------------|---------|
|
|
136
|
-
| `call` | Async (background job) | `true` or `false` | Yes (automatic) |
|
|
137
|
-
| `call!` | Sync (immediate) | Thread timestamp or `false` | No |
|
|
138
|
-
|
|
139
|
-
Use `call` by default. Use `call!` when you need the `thread_ts` for threading.
|
|
140
|
-
|
|
141
|
-
### How do I test SlackSender in my specs?
|
|
142
|
-
|
|
143
|
-
Stub at the profile level:
|
|
144
|
-
|
|
145
|
-
```ruby
|
|
146
|
-
allow(SlackSender.profile(:default)).to receive(:call).and_return(true)
|
|
147
|
-
allow(SlackSender.profile(:default)).to receive(:call!).and_return("1234567890.123456")
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Compatibility
|
|
153
|
-
|
|
154
|
-
- **Ruby**: >= 3.2.1
|
|
155
|
-
- **Dependencies**:
|
|
156
|
-
- `axn` (>= 0.1.0-alpha.4.1, < 0.2.0)
|
|
157
|
-
- `slack-ruby-client` (latest)
|
|
158
|
-
- **Optional dependencies**:
|
|
159
|
-
- `sidekiq` or `active_job` (for async delivery)
|
|
160
|
-
- `active_storage` (for ActiveStorage::Attachment file support)
|
data/docs/usage.md
DELETED
|
@@ -1,382 +0,0 @@
|
|
|
1
|
-
# Usage Guide
|
|
2
|
-
|
|
3
|
-
[← Back to README](../README.md)
|
|
4
|
-
|
|
5
|
-
This guide covers sending messages, file uploads, threading, and advanced delivery patterns.
|
|
6
|
-
|
|
7
|
-
## Basic Messages
|
|
8
|
-
|
|
9
|
-
```ruby
|
|
10
|
-
# Simple text message
|
|
11
|
-
SlackSender.call(
|
|
12
|
-
channel: :ops_alerts,
|
|
13
|
-
text: ":warning: Redis latency is *elevated*"
|
|
14
|
-
)
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Text is parsed as [Slack mrkdwn](https://api.slack.com/reference/surfaces/formatting) by default.
|
|
18
|
-
|
|
19
|
-
### Sync vs Async Delivery
|
|
20
|
-
|
|
21
|
-
| Method | Delivery | Return Value | Use When |
|
|
22
|
-
|--------|----------|--------------|----------|
|
|
23
|
-
| `call(...)` | Async (background job) | `true` or `false` | Default; enables auto-retry for rate limits |
|
|
24
|
-
| `call!(...)` | Sync (immediate) | Thread timestamp or `false` | You need the `thread_ts` return value |
|
|
25
|
-
|
|
26
|
-
```ruby
|
|
27
|
-
# Async delivery (recommended) - uses Sidekiq or ActiveJob
|
|
28
|
-
SlackSender.call(channel: :ops_alerts, text: "Alert")
|
|
29
|
-
|
|
30
|
-
# Synchronous delivery (returns thread timestamp)
|
|
31
|
-
thread_ts = SlackSender.call!(channel: :deployments, text: "Deploy finished")
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
**Note:** If `text:` is explicitly provided but blank (and you did not provide `blocks`, `attachments`, or `files`), SlackSender treats it as a no-op and returns `false`.
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## Channel Resolution
|
|
39
|
-
|
|
40
|
-
Channels can be specified as symbols (resolved from profile config) or channel IDs:
|
|
41
|
-
|
|
42
|
-
```ruby
|
|
43
|
-
# Using symbol (resolved from channels hash)
|
|
44
|
-
SlackSender.call(channel: :ops_alerts, text: "Alert")
|
|
45
|
-
|
|
46
|
-
# Using channel ID directly
|
|
47
|
-
SlackSender.call(channel: "C1234567890", text: "Alert")
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Default Channel
|
|
51
|
-
|
|
52
|
-
Configure a default channel for a profile to avoid passing `channel:` on every call:
|
|
53
|
-
|
|
54
|
-
```ruby
|
|
55
|
-
SlackSender.register(
|
|
56
|
-
token: ENV['SLACK_BOT_TOKEN'],
|
|
57
|
-
default_channel: :ops_alerts,
|
|
58
|
-
channels: {
|
|
59
|
-
ops_alerts: 'C1111111111',
|
|
60
|
-
deployments: 'C2222222222',
|
|
61
|
-
}
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
# These are equivalent:
|
|
65
|
-
SlackSender.call(text: "Alert!") # Uses default_channel
|
|
66
|
-
SlackSender.call(channel: :ops_alerts, text: "Alert!") # Explicit channel
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Sandbox Mode
|
|
70
|
-
|
|
71
|
-
To prevent accidental notifications in development or staging environments, you can enable sandbox mode. When active, all messages—regardless of their target channel—are redirected to a single "sandbox" channel. This ensures you can test notifications safely without spamming real users.
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
## Rich Messages
|
|
76
|
-
|
|
77
|
-
SlackSender passes blocks, attachments, and icon_emoji through [directly to slack-ruby-client](https://www.rubydoc.info/gems/slack-ruby-client/Slack/Web/Api/Endpoints/Chat#chat_postMessage-instance_method).
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
### Blocks
|
|
81
|
-
|
|
82
|
-
```ruby
|
|
83
|
-
SlackSender.call(
|
|
84
|
-
channel: :deployments,
|
|
85
|
-
blocks: [
|
|
86
|
-
{
|
|
87
|
-
type: "section",
|
|
88
|
-
text: { type: "mrkdwn", text: ":rocket: *Deploy finished* for `my-app`" }
|
|
89
|
-
}
|
|
90
|
-
]
|
|
91
|
-
)
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Attachments
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
```ruby
|
|
98
|
-
SlackSender.call(
|
|
99
|
-
channel: :ops_alerts,
|
|
100
|
-
attachments: [
|
|
101
|
-
{
|
|
102
|
-
color: "good",
|
|
103
|
-
text: "Autoscaling event completed successfully"
|
|
104
|
-
}
|
|
105
|
-
]
|
|
106
|
-
)
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Custom Emoji
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
```ruby
|
|
113
|
-
SlackSender.call(
|
|
114
|
-
channel: :ops_alerts,
|
|
115
|
-
text: "Background job queue is healthy",
|
|
116
|
-
icon_emoji: "robot"
|
|
117
|
-
)
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
## File Uploads
|
|
123
|
-
|
|
124
|
-
File uploads are supported with both synchronous (`call!`) and async (`call`) delivery.
|
|
125
|
-
|
|
126
|
-
```ruby
|
|
127
|
-
# Single file - use file: (singular)
|
|
128
|
-
SlackSender.call!(
|
|
129
|
-
channel: :reports,
|
|
130
|
-
text: "Daily ops report attached",
|
|
131
|
-
file: File.open("report.pdf")
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
# Multiple files - use files: (plural)
|
|
135
|
-
SlackSender.call!(
|
|
136
|
-
channel: :reports,
|
|
137
|
-
text: "Daily ops report (details + raw export)",
|
|
138
|
-
files: [
|
|
139
|
-
File.open("report.pdf"),
|
|
140
|
-
File.open("data.csv")
|
|
141
|
-
]
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
# Async delivery (background job handles sharing)
|
|
145
|
-
SlackSender.call(
|
|
146
|
-
channel: :alerts,
|
|
147
|
-
text: "Multiple files attached",
|
|
148
|
-
files: [File.open("report.pdf"), File.open("data.csv")]
|
|
149
|
-
)
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### ⚠️ Channel ID Required
|
|
153
|
-
|
|
154
|
-
Unlike normal message sending, Slack's `files_upload_v2` API requires channel _IDs_ (e.g., `C024BE91L`, `D032AC32T`) and does _not_ support usernames or channel names:
|
|
155
|
-
|
|
156
|
-
```ruby
|
|
157
|
-
# Works - using channel ID from profile
|
|
158
|
-
SlackSender.call!(channel: :alerts, file: file)
|
|
159
|
-
|
|
160
|
-
# Works - using channel ID directly
|
|
161
|
-
SlackSender.call!(channel: "C024BE91L", file: file)
|
|
162
|
-
|
|
163
|
-
# Fails - @username not supported for file uploads
|
|
164
|
-
SlackSender.call!(channel: "@username", file: file)
|
|
165
|
-
|
|
166
|
-
# Fails - #channel-name not supported for file uploads
|
|
167
|
-
SlackSender.call!(channel: "#general", file: file)
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
To send files as a DM, use the DM channel ID (starts with `D`) from Slack's URL.
|
|
171
|
-
|
|
172
|
-
### Async File Upload Behavior
|
|
173
|
-
|
|
174
|
-
Files are uploaded to Slack's servers synchronously before the background job is enqueued. The job then shares the uploaded files to the channel. This means `call` with files may block briefly during the upload phase.
|
|
175
|
-
|
|
176
|
-
### Size Limits
|
|
177
|
-
|
|
178
|
-
- Individual files cannot exceed **1 GB** (Slack's hard limit)
|
|
179
|
-
- Total file size for async uploads is limited by `max_async_file_upload_size` (default 25 MB)
|
|
180
|
-
- Use `call!` for synchronous upload when you need to upload files larger than `max_async_file_upload_size`
|
|
181
|
-
|
|
182
|
-
### Supported File Types
|
|
183
|
-
|
|
184
|
-
- `File` objects
|
|
185
|
-
- `Tempfile` objects
|
|
186
|
-
- `StringIO` objects
|
|
187
|
-
- `ActiveStorage::Attachment` objects (if ActiveStorage is available)
|
|
188
|
-
- String file paths (will be opened automatically)
|
|
189
|
-
- Any object that responds to `read` and has `original_filename` or `path`
|
|
190
|
-
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
## Threading
|
|
194
|
-
|
|
195
|
-
```ruby
|
|
196
|
-
# Get thread timestamp from initial message
|
|
197
|
-
thread_ts = SlackSender.call!(
|
|
198
|
-
channel: :ops_alerts,
|
|
199
|
-
text: ":rotating_light: Elevated 500s detected on /checkout"
|
|
200
|
-
)
|
|
201
|
-
# thread_ts => "1234567890.123456"
|
|
202
|
-
|
|
203
|
-
# Reply to a thread
|
|
204
|
-
SlackSender.call(
|
|
205
|
-
channel: :ops_alerts,
|
|
206
|
-
text: "Mitigation: rolled back to previous release",
|
|
207
|
-
thread_ts: thread_ts
|
|
208
|
-
)
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
---
|
|
212
|
-
|
|
213
|
-
## Multi-Channel Delivery
|
|
214
|
-
|
|
215
|
-
Send the same message to multiple channels with a single call using `channels:` (plural):
|
|
216
|
-
|
|
217
|
-
```ruby
|
|
218
|
-
# Async delivery to multiple channels
|
|
219
|
-
SlackSender.call(
|
|
220
|
-
channels: [:ops_alerts, :deployments],
|
|
221
|
-
text: ":rocket: Deploy finished for my-app"
|
|
222
|
-
)
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
**Key behaviors:**
|
|
226
|
-
- Multi-channel delivery is only supported via async (`call`). Using `call!` with `channels:` raises an error (due to complexity in handling partial failures)
|
|
227
|
-
- Files are uploaded once and shared to all channels efficiently
|
|
228
|
-
- Each channel receives a separate background job with independent retry handling
|
|
229
|
-
- Single-element arrays (e.g., `channels: [:ops_alerts]`) are normalized to `channel:`
|
|
230
|
-
|
|
231
|
-
```ruby
|
|
232
|
-
# Sync multi-channel not supported
|
|
233
|
-
SlackSender.call!(channels: [:a, :b], text: "...") # Raises ArgumentError
|
|
234
|
-
|
|
235
|
-
# Use async instead
|
|
236
|
-
SlackSender.call(channels: [:a, :b], text: "...")
|
|
237
|
-
|
|
238
|
-
# Or send individually if you need sync (but beware :b may fail with e.g. a ratelimit error after :a has already been delivered)
|
|
239
|
-
[:a, :b].each { |ch| SlackSender.call!(channel: ch, text: "...") }
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
---
|
|
243
|
-
|
|
244
|
-
## User Group Mentions
|
|
245
|
-
|
|
246
|
-
Format user group mentions with `SlackSender.group_link`. This is sandbox-aware and supports symbol keys from your profile's `user_groups` registry:
|
|
247
|
-
|
|
248
|
-
```ruby
|
|
249
|
-
SlackSender.group_link(:on_call)
|
|
250
|
-
# => "<!subteam^S1234567890>"
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
If `sandbox.user_group.replace_with` is configured and the app is in sandbox mode, `group_link` will replace the requested group with the sandbox user_group instead:
|
|
254
|
-
|
|
255
|
-
```ruby
|
|
256
|
-
SlackSender.register(
|
|
257
|
-
token: ENV['SLACK_BOT_TOKEN'],
|
|
258
|
-
user_groups: { engineers: 'S1234567890' },
|
|
259
|
-
sandbox: {
|
|
260
|
-
user_group: { replace_with: 'S_DEV_GROUP' }
|
|
261
|
-
}
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
# In sandbox mode, this returns the sandbox user_group mention
|
|
265
|
-
SlackSender.group_link(:engineers)
|
|
266
|
-
# => "<!subteam^S_DEV_GROUP>"
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### Other Formatting Helpers
|
|
270
|
-
|
|
271
|
-
For user mentions, channels, links, and other formatting, use the `Slack::Messages::Formatting` helpers provided by the underlying [slack-ruby-client](https://github.com/slack-ruby/slack-ruby-client#message-formatting):
|
|
272
|
-
|
|
273
|
-
```ruby
|
|
274
|
-
SlackSender.call(
|
|
275
|
-
channel: :ops_alerts,
|
|
276
|
-
text: [
|
|
277
|
-
":rotating_light: Incident acknowledged by #{Slack::Messages::Formatting.user_link(user.slack_id)}",
|
|
278
|
-
Slack::Messages::Formatting.url_link('Incident timeline', 'https://status.example.com/incidents/123'),
|
|
279
|
-
].join("\n")
|
|
280
|
-
)
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
---
|
|
284
|
-
|
|
285
|
-
## Rate Limiting & Retries
|
|
286
|
-
|
|
287
|
-
When using async delivery, SlackSender automatically:
|
|
288
|
-
|
|
289
|
-
- Detects rate limit errors from Slack API responses
|
|
290
|
-
- Extracts `Retry-After` header value
|
|
291
|
-
- Schedules retry with appropriate delay (+ jitter)
|
|
292
|
-
- Retries up to 5 times before giving up
|
|
293
|
-
|
|
294
|
-
Rate limit handling works with both Sidekiq and ActiveJob backends.
|
|
295
|
-
|
|
296
|
-
---
|
|
297
|
-
|
|
298
|
-
## Error Handling
|
|
299
|
-
|
|
300
|
-
The following errors will log warning but are _not_ retried (discarded immediately):
|
|
301
|
-
- `NotInChannel` - Bot not in channel
|
|
302
|
-
- `ChannelNotFound` - Channel doesn't exist
|
|
303
|
-
- `IsArchived` - Channel is archived (NOTE: skips warning if `config.silence_archived_channel_exceptions = true`)
|
|
304
|
-
|
|
305
|
-
Other Slack API errors will be re-raised (so will retry if sent via background processor).
|
|
306
|
-
|
|
307
|
-
---
|
|
308
|
-
|
|
309
|
-
## Examples
|
|
310
|
-
|
|
311
|
-
### Deployment Notifications
|
|
312
|
-
|
|
313
|
-
```ruby
|
|
314
|
-
SlackSender.call(
|
|
315
|
-
channel: :deployments,
|
|
316
|
-
text: ":rocket: Deploy finished for `my-app` (#{Rails.env})",
|
|
317
|
-
blocks: [
|
|
318
|
-
{
|
|
319
|
-
type: "section",
|
|
320
|
-
fields: [
|
|
321
|
-
{ type: "mrkdwn", text: "*Environment:*\n#{Rails.env}" },
|
|
322
|
-
{ type: "mrkdwn", text: "*Version:*\n#{ENV['APP_VERSION']}" }
|
|
323
|
-
]
|
|
324
|
-
}
|
|
325
|
-
]
|
|
326
|
-
)
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
### Error Alerts
|
|
330
|
-
|
|
331
|
-
```ruby
|
|
332
|
-
SlackSender.call(
|
|
333
|
-
channel: :ops_alerts,
|
|
334
|
-
text: ":rotating_light: Payment processing error",
|
|
335
|
-
attachments: [
|
|
336
|
-
{
|
|
337
|
-
color: "danger",
|
|
338
|
-
fields: [
|
|
339
|
-
{ title: "Error", value: error.message, short: false },
|
|
340
|
-
{ title: "User", value: user.email, short: true }
|
|
341
|
-
]
|
|
342
|
-
}
|
|
343
|
-
]
|
|
344
|
-
)
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
### Scheduled Reports with File Upload
|
|
348
|
-
|
|
349
|
-
```ruby
|
|
350
|
-
# Generate and send report (synchronous for thread_ts)
|
|
351
|
-
report = generate_daily_report
|
|
352
|
-
thread_ts = SlackSender.call!(
|
|
353
|
-
channel: :reports,
|
|
354
|
-
text: "Daily Report - #{Date.today}",
|
|
355
|
-
file: report.to_file
|
|
356
|
-
)
|
|
357
|
-
|
|
358
|
-
# Follow up in thread
|
|
359
|
-
SlackSender.call(
|
|
360
|
-
channel: :reports,
|
|
361
|
-
text: "Summary: no SEV incidents; deploys are healthy",
|
|
362
|
-
thread_ts: thread_ts
|
|
363
|
-
)
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
### Dedicated Notifiers
|
|
367
|
-
|
|
368
|
-
For complex notifications, you can simplify your code by creating dedicated notifier classes using `SlackSender::Notifier`. This allows you to encapsulate logic, use callbacks, and keep your business logic clean. See documentation on [SlackSender::Notifier Base Class](axn_integration.md#slacksendernotifier-base-class) for details.
|
|
369
|
-
|
|
370
|
-
```ruby
|
|
371
|
-
class DeployNotifier < SlackSender::Notifier
|
|
372
|
-
expects :service_name
|
|
373
|
-
|
|
374
|
-
notify do
|
|
375
|
-
channel :deployments
|
|
376
|
-
text { ":rocket: #{service_name} deployed successfully!" }
|
|
377
|
-
end
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
# Usage
|
|
381
|
-
DeployNotifier.call(service_name: "payment-service")
|
|
382
|
-
```
|