slackify 0.1.4 โ 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -3
- data/README.md +111 -46
- data/app/controllers/slackify/slack_controller.rb +24 -7
- data/lib/slackify.rb +2 -4
- data/lib/slackify/configuration.rb +45 -7
- data/lib/slackify/engine.rb +1 -0
- data/lib/slackify/exceptions.rb +4 -0
- data/lib/slackify/handlers.rb +1 -1
- data/lib/slackify/handlers/base.rb +14 -1
- data/lib/slackify/handlers/factory.rb +29 -0
- data/lib/slackify/handlers/unhandled_handler.rb +4 -0
- data/lib/slackify/handlers/validator.rb +69 -19
- data/lib/slackify/parameter.rb +22 -0
- data/lib/slackify/router.rb +102 -0
- metadata +20 -5
- data/lib/slackify/handlers/configuration.rb +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3b94c8d8ee9aadee9d6c12cfa3e84ebd938e3b34908315e6add11a60d4ddca8
|
4
|
+
data.tar.gz: 5d04503efbf517d256d55bdb49977ef0e13b79d01615b34c7dd6df44c3fd6806
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5cc3b8a43401712f68b2b818f7d5f8c3339a8c0cee57aed7bb2907d2553d8066ca7f8c27cd63744468b2b4c659d71719b2bf9ecba816f0947fcf00c8c7d95e0
|
7
|
+
data.tar.gz: d5c1bc32e1ec8c054d059418e7137b83420b0117835ba629b21f325943c8dafed174215922ea11c4d430ebdaa078340b3e327f62a7c370c98689e7bbe700205a
|
data/CHANGELOG.md
CHANGED
@@ -1,15 +1,45 @@
|
|
1
|
+
## V0.4.0
|
2
|
+
|
3
|
+
- **BREAKING CHANGE:** Use `approved_bot_ids` instead of `whitelisted_bot_ids` ([6243c11](https://github.com/jusleg/slackify/commit'6243c11c3b5e49fa31feb2ebfd9394c362509524)) by [@DougEdey](https://github.com/DougEdey)
|
4
|
+
- Add support for named parameters in commands ([PR #12](https://github.com/jusleg/slackify/pull/12)) by [@DougEdey](https://github.com/DougEdey)
|
5
|
+
- Add support for custom parameters in commands ([PR #17](https://github.com/jusleg/slackify/pull/17)) by [@DougEdey](https://github.com/DougEdey)
|
6
|
+
- Tidy up some tests
|
7
|
+
- Force slack-ruby-client to be version 0.15.1 or above to allow for pagination of Conversations
|
8
|
+
|
9
|
+
## V0.3.2
|
10
|
+
|
11
|
+
- Add support for interactive block payloads ([b6cf1db](https://github.com/jusleg/slackify/commit/b6cf1dbb47b832037ebff56054efa27c9e3251dc)) by [@drose-shopify](https://github.com/drose-shopify)
|
12
|
+
|
13
|
+
## V0.3.0
|
14
|
+
|
15
|
+
- Add code documentation and improve exception message
|
16
|
+
- Add approval of bot ids in the configuration through `allowed_bot_ids=`
|
17
|
+
- Refactored Handler configuration into `Slackify::Router` and `Slackify::Handlers::Factory`
|
18
|
+
- Improved testing
|
19
|
+
- Remove the need to perform `Slackify.load_handler`
|
20
|
+
- **Breaking change:** Given that we now load the handlers on `Slack.configure`, the configuration step done in `config/application.rb` will have to be done in an initializer to have all the handler class loaded.
|
21
|
+
|
22
|
+
## V0.2.0
|
23
|
+
|
24
|
+
Update `custom_event_subtype_handlers` to `custom_message_subtype_handlers` and add support for `custom_event_type_handlers`. This is a breaking change since we rename the field that was previously used. To fix, update any calls from `custom_event_subtype_handlers` to `custom_message_subtype_handlers` and you should be good to go.
|
25
|
+
|
26
|
+
## V0.1.5
|
27
|
+
|
28
|
+
Update how the `bot_id` is set in the handler configuration. You can disable the slack auth test (which is used to obtain the bot_id) by setting `SLACK_AUTH_SKIP=1` in your environment variables. If you are running in a Rails environment other than production, development or staging and would like to use the bot for real requests, you can trigger a manual auth test by calling `Slackify.configuration.handlers.bot_auth_test`. Gemfile.lock was removed.
|
29
|
+
|
1
30
|
## V0.1.4
|
2
31
|
|
3
32
|
Custom unhandled_handler configuration fix. It wouldn't let you set a custom one as the validation was checking for `is_a?` instead of `<`
|
4
33
|
|
5
34
|
## V0.1.3
|
35
|
+
|
6
36
|
Added `remove_unhandled_handler` as a configuration option to disable the unhandled handler.
|
7
37
|
|
8
38
|
## V0.1.2
|
9
39
|
|
10
|
-
|
11
|
-
|
12
|
-
|
40
|
+
- Renaming the gem from toddlerbot to slackify.
|
41
|
+
- Cleanup of `lib/slackify` folder.
|
42
|
+
- Added a new configuration: unhandled_handler. You can specify a subclass of `Slackify::Handlers::Base` in the config. This class will be called with `#unhandled` when a message has no regex match.
|
13
43
|
|
14
44
|
## V0.1.1
|
15
45
|
|
data/README.md
CHANGED
@@ -1,24 +1,27 @@
|
|
1
|
-
# Slackify [![
|
1
|
+
# Slackify [![Tests ๐งช](https://github.com/jusleg/slackify/workflows/Tests%20%F0%9F%A7%AA/badge.svg)](https://github.com/jusleg/slackify/actions?query=workflow%3A%22Tests+%F0%9F%A7%AA%22) [![Lint ๐](https://github.com/jusleg/slackify/workflows/Lint%20%F0%9F%92%8E/badge.svg)](https://github.com/jusleg/slackify/actions?query=workflow%3A%22Lint+%F0%9F%92%8E%22) [![Gem Version](https://badge.fury.io/rb/slackify.svg)](https://badge.fury.io/rb/slackify) ![Gem downloads](https://img.shields.io/gem/dt/slackify)
|
2
2
|
|
3
3
|
Slackify is a gem that allows to build slackbots on Rails using the [Event API](https://api.slack.com/events-api) from Slack.
|
4
4
|
|
5
5
|
## Table of Contents
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
6
|
+
|
7
|
+
- [How does it work](#how-does-it-work)
|
8
|
+
- [Handlers](#handlers)
|
9
|
+
- [Plain messages](#handling-plain-messages)
|
10
|
+
- [Interactive messages](#handling-interactive-messages)
|
11
|
+
- [Slash Command](#handling-slash-commands)
|
12
|
+
- [Custom handler for message subtypes](#custom-handler-for-message-subtypes)
|
13
|
+
- [Custom handler for event types](#custom-handler-for-event-types)
|
14
|
+
- [Custom unhandled handler](#custom-unhandled-handler)
|
15
|
+
- [Slack client](#slack-client)
|
16
|
+
- [Sending a simple message](#sending-a-simple-message)
|
17
|
+
- [Sending an interactive message](#sending-an-interactive-message)
|
18
|
+
- [Slack 3 second reply window](#slack-3-seconds-reply-window)
|
19
|
+
- [How to run your own slackify](#how-to-run-your-own-slackify)
|
20
|
+
- [Initial Setup](#initial-setup)
|
21
|
+
- [Slack Setup](#slack-setup)
|
20
22
|
|
21
23
|
# How does it work
|
24
|
+
|
22
25
|
The core logic of the bot resides in its handlers. When the app starts, a list of handler gets initialized from a config file (`config/handlers.yml`). This initializes all the plain message handlers. Out of the box, the application supports three types of events
|
23
26
|
|
24
27
|
1. [Plain messages](#handling-plain-messages)
|
@@ -26,19 +29,19 @@ The core logic of the bot resides in its handlers. When the app starts, a list o
|
|
26
29
|
3. [Slash Command](#handling-slash-commands)
|
27
30
|
|
28
31
|
## Handlers
|
32
|
+
|
29
33
|
### Handling plain messages
|
34
|
+
|
30
35
|
These are the basic handlers. They use a regex to identify if they should be called. When a message event gets sent to the bots, the slack controller sends the message to the list of handlers. The message will be checked against the regex of every handler until there is a match. When there is a match, the handler will get called with all the parameters provided by slack. If no handler matches the command, the unhandled handler will be called instead.
|
31
36
|
|
32
37
|
Those handlers are configured via the `config/handlers.yml` configuration file. Let's dissect the configuration of a handler.
|
33
38
|
|
34
39
|
```yaml
|
35
|
-
-
|
36
|
-
repeat_handler:
|
40
|
+
- repeat_handler:
|
37
41
|
commands:
|
38
|
-
-
|
39
|
-
name: Repeat
|
42
|
+
- name: Repeat
|
40
43
|
description: "`repeat [sentence]`: Repeats the sentence you wrote"
|
41
|
-
regex: !ruby/regexp
|
44
|
+
regex: !ruby/regexp "/^repeat (?<sentence>.+)/i"
|
42
45
|
action: repeat
|
43
46
|
```
|
44
47
|
|
@@ -64,20 +67,48 @@ To add a new handler, you can add a new file under `app/handlers/` and start add
|
|
64
67
|
|
65
68
|
**Note:** The regex supports [named capture](https://www.regular-expressions.info/named.html). In this example, we have a name example of `sentence`. When the handler command will be called, a key in the parameter hash will be added: `command_arguments`. This key will point to a hash of the capture name and value. In this case, `command_arguments => {sentence: "the sentence you wrote"}`
|
66
69
|
|
70
|
+
### Handling messages with defined parameters
|
71
|
+
|
72
|
+
The regular expression matching in the previous example provides a mechanism to supply named parameters to a method. However, many handlers may want to use named & typed parameters. This can be done using the `base_command` and `parameters` options.
|
73
|
+
|
74
|
+
We can rewrite the command above to
|
75
|
+
|
76
|
+
```yaml
|
77
|
+
- repeat_handler:
|
78
|
+
commands:
|
79
|
+
- name: Repeat
|
80
|
+
description: "`repeat [sentence]`: Repeats the sentence you wrote"
|
81
|
+
base_command: "repeat"
|
82
|
+
parameters:
|
83
|
+
- sentence: string
|
84
|
+
- times: 10
|
85
|
+
action: repeat
|
86
|
+
```
|
87
|
+
|
88
|
+
And call the command with
|
89
|
+
|
90
|
+
`slackify repeat sentence="Why hullo there" times=10`
|
91
|
+
|
92
|
+
This will supply a command arguments that are typed and coerced to the defined types:
|
93
|
+
|
94
|
+
`command_arguments => {sentence: "Why hullo there", time: 10}`
|
95
|
+
|
67
96
|
### Handling interactive messages
|
97
|
+
|
68
98
|
When sending an interactive message to a user, slack let's you define the `callback_id`. The app uses the callback id to select the proper handler for the message that was sent. The callback id must follow the given format: `class_name#method_name`. For instance if you set the callback id to `repeat_handler#repeat`, then `RepeatHandler#repeat` will be called. Adding new handlers does not require to update the `config/handlers.yml` configuration. You only need to update the callback id to define the proper handler to be used when you send an interactive message.
|
69
99
|
|
70
100
|
### Handling slash commands
|
101
|
+
|
71
102
|
The code also has an example of a slash command and its handler (`slash_handler.rb`). To add a command on the bot, head to you app configuration on https://api.slack.com/apps and navigate to Slack Commands using the sidebar. Create a new one. The important part is to set the path properly. To bind with the demo handler, you would need to setup the path like this: `/slackify/slash/slash_handler/example_slash`. The format is `/slackify/slash/[handler_name]/[action_name]`. An app shouldn't have many slash commands. Keep in mind that adding a slash command means that the whole organization will see it.
|
72
103
|
|
73
|
-
You will need to
|
104
|
+
You will need to allow the method in the handler to indicate it can be used as a slash command using `allow_slash_method`
|
74
105
|
|
75
106
|
```ruby
|
76
107
|
class DummyHandler < Slackify::Handlers::Base
|
77
108
|
allow_slash_method :slash_command
|
78
109
|
|
79
110
|
class << self
|
80
|
-
|
111
|
+
|
81
112
|
def slash_command(_params)
|
82
113
|
"dummy_handler slash_command() was called"
|
83
114
|
end
|
@@ -85,17 +116,29 @@ class DummyHandler < Slackify::Handlers::Base
|
|
85
116
|
end
|
86
117
|
```
|
87
118
|
|
88
|
-
### Custom handler for
|
119
|
+
### Custom handler for message subtypes
|
89
120
|
|
90
|
-
If you wish to add more functionalities to your bot, you can specify define new behaviours for different
|
121
|
+
If you wish to add more functionalities to your bot, you can specify define new behaviours for different message subtypes. You can specify a hash with the event subtype as a key and the handler class as the value. Slackify will call `.handle_event` on your class and pass the controller params as parameters.
|
91
122
|
|
92
123
|
```ruby
|
93
|
-
Slackify.configuration.
|
124
|
+
Slackify.configuration.custom_message_subtype_handlers = {
|
125
|
+
file_share: MessageImageHandler
|
126
|
+
}
|
127
|
+
```
|
128
|
+
|
129
|
+
In this example, all message of subtype `file_share` will be sent to the `MessageImageHandler` class.
|
130
|
+
|
131
|
+
### Custom handler for event types
|
132
|
+
|
133
|
+
If you wish to add more functionalities to your bot, you can specify define new behaviours for different event types. You can specify a hash with the event type as a key and the handler class as the value. Slackify will call `.handle_event` on your class and pass the controller params as parameters.
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
Slackify.configuration.custom_event_type_handlers = {
|
94
137
|
file_share: ImageHandler
|
95
138
|
}
|
96
139
|
```
|
97
140
|
|
98
|
-
In this example, all events of
|
141
|
+
In this example, all events of type `file_share` will be sent to the `ImageHandler` class.
|
99
142
|
|
100
143
|
### Custom unhandled handler
|
101
144
|
|
@@ -121,18 +164,21 @@ end
|
|
121
164
|
```
|
122
165
|
|
123
166
|
## Slack client
|
167
|
+
|
124
168
|
In order to send messages, the [slack ruby client gem](https://github.com/slack-ruby/slack-ruby-client) was used. You can send plain text messages, images and interactive messages. Since the bot was envisioned being more repsonsive than proactive, the client was made available for handlers to call using the `slack_client` method. If you wish to send messages outside of handlers, you can get the slack client by calling `Slackify.configuration.slack_client`
|
125
169
|
|
126
170
|
### Sending a simple message
|
171
|
+
|
127
172
|
```ruby
|
128
173
|
slack_client.chat_postMessage(channel: 'MEMBER ID OR CHANNEL ID', text: 'Hello World', as_user: true)
|
129
174
|
```
|
130
175
|
|
131
176
|
### Sending an interactive message
|
177
|
+
|
132
178
|
```ruby
|
133
179
|
slack_client.chat_postMessage(
|
134
|
-
channel: 'MEMBER ID OR CHANNEL ID',
|
135
|
-
as_user: true,
|
180
|
+
channel: 'MEMBER ID OR CHANNEL ID',
|
181
|
+
as_user: true,
|
136
182
|
attachments: [{
|
137
183
|
"fallback": "Would you recommend it to customers?",
|
138
184
|
"title": "Would you recommend it to customers?",
|
@@ -158,10 +204,13 @@ slack_client.chat_postMessage(
|
|
158
204
|
```
|
159
205
|
|
160
206
|
## Slack 3 seconds reply window
|
207
|
+
|
161
208
|
Slack introduced a [3 seconds reply window](https://api.slack.com/messaging/interactivity#response) for interactive messages. That means that if you reply to an interactive message or slash command event with a json, slack will show either update the attachment or send a new one without having to use `chat_postMessage`. If you wish to use this feature with Slackify, you only need to return either a json of an attachment or a plain text string when you handler method is called. **Your method should always return `nil` otherwise**.
|
162
209
|
|
163
210
|
# How to run your own slackify
|
211
|
+
|
164
212
|
## Initial Setup
|
213
|
+
|
165
214
|
1. Install slackify in your app by adding the following line in your `Gemfile`:
|
166
215
|
|
167
216
|
```ruby
|
@@ -180,58 +229,74 @@ bundle install
|
|
180
229
|
|
181
230
|
5. [Proceed to connect your bot to slack](#slack-setup)
|
182
231
|
|
183
|
-
|
184
232
|
## Slack Setup
|
233
|
+
|
185
234
|
First, you'll need to create a new app on slack. Head over to [slack api](https://api.slack.com/apps) and create a new app.
|
186
235
|
|
187
236
|
1. **Set Slack Secret Token**
|
188
237
|
|
189
|
-
|
238
|
+
In order to verify that the requets are coming from slack, we'll need to set the slack secret token in slackify. This value can be found as the signing secret in the app credentials section of the basic information page.
|
190
239
|
|
191
240
|
2. **Add a bot user**
|
192
|
-
|
193
|
-
|
241
|
+
|
242
|
+
Under the feature section, click on "bot users". Pick a name for you slack bot and toggle on "Always Show My Bot as Online". Save the setting.
|
194
243
|
|
195
244
|
3. **Enable events subscription**
|
196
245
|
|
197
|
-
|
246
|
+
Under the feature section, click "Events subscription". Turn the feature on and use your app url followed by `/slackify/event`. [Ngrok](https://ngrok.com/) can easily get you a public url if you are developing locally. The app needs to be running when you configure this url. After the url is configured, under the section "Subscribe to Bot Events", add the bot user event `message.im`.
|
198
247
|
|
199
248
|
4. **Activate the interactive components**
|
200
|
-
|
201
|
-
|
249
|
+
|
250
|
+
Under the feature section, click "interactive components". Turn the feature on and use your ngrok url followed by `/slackify/interactive`. Save the setting.
|
202
251
|
|
203
252
|
5. **Install the App**
|
204
253
|
|
205
|
-
|
254
|
+
Under the setting section, click "install app" and proceed to install the app to the workspace. Once the app is installed, go back to the "install app" page and copy the Bot User OAuth Access Token.
|
206
255
|
|
207
256
|
6. **Configure Slackify**
|
257
|
+
Add a new initializer with the following code
|
258
|
+
|
208
259
|
```ruby
|
260
|
+
# config/initializers/slackify.rb
|
209
261
|
Slackify.configure do |config|
|
210
262
|
config.slack_bot_token = "xoxb-sdkjlkjsdflsd..."
|
211
263
|
config.slack_secret_token = "1234dummysecret"
|
212
264
|
end
|
213
265
|
```
|
214
266
|
|
215
|
-
7. **
|
267
|
+
7. **Define handlers specific subtypes** (Optional)
|
268
|
+
You can set custom [message subtype](https://api.slack.com/events/message) handlers or custom [event type](https://api.slack.com/events) handlers inside your configuration
|
269
|
+
|
216
270
|
```ruby
|
217
271
|
# config/initializers/slackify.rb
|
218
|
-
Slackify.
|
272
|
+
Slackify.configure do |config|
|
273
|
+
config.slack_bot_token = "xoxb-sdkjlkjsdflsd..."
|
274
|
+
config.slack_secret_token = "1234dummysecret"
|
275
|
+
config.custom_message_subtype_handlers = {
|
276
|
+
file_share: ImageHandler,
|
277
|
+
channel_join: JoinHandler,
|
278
|
+
...
|
279
|
+
}
|
280
|
+
config.custom_event_type_handlers = {
|
281
|
+
app_mention: ...
|
282
|
+
}
|
283
|
+
end
|
219
284
|
```
|
220
285
|
|
221
|
-
8. **
|
286
|
+
8. **Handle bot messages** (Highly optional)
|
287
|
+
If you want your bot to accept other bot messages (which you probably should not do), you can. In the configuration step, you can set an array of approved bot ids.
|
222
288
|
|
223
289
|
```ruby
|
224
290
|
# config/initializers/slackify.rb
|
225
|
-
Slackify.
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
}
|
291
|
+
Slackify.configure do |config|
|
292
|
+
...
|
293
|
+
config.approved_bot_ids = ['abc123', 'def456']
|
294
|
+
...
|
295
|
+
end
|
231
296
|
```
|
232
297
|
|
233
298
|
**At this point, you are ready to go ๐**
|
234
299
|
|
235
|
-
|
236
300
|
# LICENSE
|
301
|
+
|
237
302
|
Copyright (c) 2019 Justin Lรฉger, Michel Chatmajian. See [LICENSE](https://github.com/jusleg/slackify/blob/master/LICENSE) for further details.
|
@@ -9,6 +9,8 @@ module Slackify
|
|
9
9
|
def event_callback
|
10
10
|
if params[:type] == "url_verification"
|
11
11
|
render plain: params["challenge"]
|
12
|
+
elsif Slackify.configuration.custom_event_type_handlers[params[:event][:type]]
|
13
|
+
handle_custom_event_type
|
12
14
|
elsif params[:event][:type] == "message"
|
13
15
|
handle_direct_message_event
|
14
16
|
head :ok
|
@@ -19,7 +21,14 @@ module Slackify
|
|
19
21
|
|
20
22
|
def interactive_callback
|
21
23
|
parsed_payload = JSON.parse(params[:payload])
|
22
|
-
|
24
|
+
|
25
|
+
callback_id = if parsed_payload.key?('view')
|
26
|
+
parsed_payload.dig('view', 'callback_id')
|
27
|
+
else
|
28
|
+
parsed_payload['callback_id']
|
29
|
+
end
|
30
|
+
|
31
|
+
response = handler_from_callback_id(callback_id).call(parsed_payload)
|
23
32
|
if !response.nil?
|
24
33
|
Timeout.timeout(SLACK_TIMEOUT_SECONDS) do
|
25
34
|
render json: response
|
@@ -28,7 +37,7 @@ module Slackify
|
|
28
37
|
head :ok
|
29
38
|
end
|
30
39
|
rescue Timeout::Error
|
31
|
-
raise Timeout::Error, "Slack interactive callback timed out for #{
|
40
|
+
raise Timeout::Error, "Slack interactive callback timed out for #{callback_id}"
|
32
41
|
end
|
33
42
|
|
34
43
|
def slash_command_callback
|
@@ -51,25 +60,33 @@ module Slackify
|
|
51
60
|
private
|
52
61
|
|
53
62
|
def handle_direct_message_event
|
54
|
-
if handler = Slackify.configuration.
|
63
|
+
if handler = Slackify.configuration.custom_message_subtype_handlers[params[:event][:subtype]]
|
55
64
|
handler.handle_event(params[:slack])
|
56
65
|
head :ok
|
57
66
|
return
|
58
67
|
end
|
59
68
|
|
60
|
-
return if params[:event][:subtype] == "bot_message" ||
|
69
|
+
return if (params[:event][:subtype] == "bot_message" ||
|
70
|
+
params[:event].key?(:bot_id) ||
|
71
|
+
params[:event][:hidden]) &&
|
72
|
+
Slackify.configuration.approved_bot_ids.exclude?(params.dig(:event, :bot_id))
|
61
73
|
|
62
74
|
command = params[:event][:text]
|
63
|
-
Slackify.
|
75
|
+
Slackify::Router.call_command(command, params[:slack])
|
64
76
|
rescue RuntimeError => e
|
65
77
|
raise e unless e.message == "Component not found for a command message"
|
66
78
|
end
|
67
79
|
|
80
|
+
def handle_custom_event_type
|
81
|
+
Slackify.configuration.custom_event_type_handlers[params[:event][:type]].handle_event(params[:slack])
|
82
|
+
head :ok
|
83
|
+
end
|
84
|
+
|
68
85
|
def handler_from_callback_id(callback_id)
|
69
86
|
class_name, method_name = callback_id.split('#')
|
70
87
|
class_name = class_name.camelize
|
71
88
|
|
72
|
-
raise Exceptions::HandlerNotSupported, class_name unless
|
89
|
+
raise Exceptions::HandlerNotSupported, "#{class_name} is not a subclass of Slackify::Handlers::Base" unless
|
73
90
|
Handlers::Base.supported_handlers.include?(class_name)
|
74
91
|
|
75
92
|
class_name.constantize.method(method_name)
|
@@ -78,7 +95,7 @@ module Slackify
|
|
78
95
|
def verify_handler_slash_permission(handler_class, handler_method)
|
79
96
|
handler_class = handler_class.camelize
|
80
97
|
|
81
|
-
raise Exceptions::HandlerNotSupported, handler_class unless
|
98
|
+
raise Exceptions::HandlerNotSupported, "#{handler_class} is not a subclass of Slackify::Handlers::Base" unless
|
82
99
|
Handlers::Base.supported_handlers.include?(handler_class)
|
83
100
|
|
84
101
|
handler = handler_class.constantize
|
data/lib/slackify.rb
CHANGED
@@ -4,6 +4,8 @@ require 'slackify/configuration'
|
|
4
4
|
require 'slackify/engine'
|
5
5
|
require 'slackify/exceptions'
|
6
6
|
require 'slackify/handlers'
|
7
|
+
require 'slackify/parameter'
|
8
|
+
require 'slackify/router'
|
7
9
|
|
8
10
|
module Slackify
|
9
11
|
class << self
|
@@ -19,9 +21,5 @@ module Slackify
|
|
19
21
|
def configure
|
20
22
|
yield(configuration)
|
21
23
|
end
|
22
|
-
|
23
|
-
def load_handlers
|
24
|
-
@configuration.handlers = Handlers::Configuration.new
|
25
|
-
end
|
26
24
|
end
|
27
25
|
end
|
@@ -3,37 +3,75 @@
|
|
3
3
|
require 'slack'
|
4
4
|
|
5
5
|
module Slackify
|
6
|
+
# Where the configuration for Slackify lives
|
6
7
|
class Configuration
|
7
|
-
attr_reader :
|
8
|
-
attr_accessor :handlers, :slack_secret_token, :slack_client
|
8
|
+
attr_reader :custom_message_subtype_handlers, :slack_bot_token, :unhandled_handler, :custom_event_type_handlers
|
9
|
+
attr_accessor :handlers, :slack_secret_token, :slack_client, :approved_bot_ids
|
9
10
|
|
10
11
|
def initialize
|
11
12
|
@slack_bot_token = nil
|
12
13
|
@slack_secret_token = nil
|
13
|
-
@handlers =
|
14
|
+
@handlers = generate_handlers
|
14
15
|
@slack_client = nil
|
15
|
-
@
|
16
|
+
@custom_message_subtype_handlers = {}
|
17
|
+
@custom_event_type_handlers = {}
|
16
18
|
@unhandled_handler = Handlers::UnhandledHandler
|
19
|
+
@approved_bot_ids = []
|
17
20
|
end
|
18
21
|
|
22
|
+
# Set your own unhandled handler
|
19
23
|
def unhandled_handler=(handler)
|
20
|
-
raise
|
24
|
+
raise HandlerNotSupported, "#{handler.class} is not a subclass of Slackify::Handlers::Base" unless
|
21
25
|
handler < Handlers::Base
|
22
26
|
|
23
27
|
@unhandled_handler = handler
|
24
28
|
end
|
25
29
|
|
30
|
+
# Remove unhandled handler. The bot will not reply if the message doesn't
|
31
|
+
# match any regex
|
26
32
|
def remove_unhandled_handler
|
27
33
|
@unhandled_handler = nil
|
28
34
|
end
|
29
35
|
|
36
|
+
# Set the token that we will use to connect to slack
|
30
37
|
def slack_bot_token=(token)
|
31
38
|
@slack_bot_token = token
|
32
39
|
@slack_client = Slack::Web::Client.new(token: token).freeze
|
33
40
|
end
|
34
41
|
|
35
|
-
|
36
|
-
|
42
|
+
# Set a handler for a specific message subtype
|
43
|
+
# That handler will have to implement `self.handle_event(params)`
|
44
|
+
# see https://api.slack.com/events/message
|
45
|
+
def custom_message_subtype_handlers=(event_subtype_hash)
|
46
|
+
@custom_message_subtype_handlers = event_subtype_hash.with_indifferent_access
|
47
|
+
end
|
48
|
+
|
49
|
+
# Set a handler for a event type
|
50
|
+
# That handler will have to implement `self.handle_event(params)`
|
51
|
+
# see https://api.slack.com/events
|
52
|
+
def custom_event_type_handlers=(event_type_hash)
|
53
|
+
@custom_event_type_handlers = event_type_hash.with_indifferent_access
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Convert a hash to a list of lambda functions that will be called to handle
|
59
|
+
# the user messages
|
60
|
+
def generate_handlers
|
61
|
+
generated_handlers = []
|
62
|
+
read_handlers_yaml.each do |handler_hash|
|
63
|
+
handler = Handlers::Factory.for(handler_hash)
|
64
|
+
generated_handlers << handler
|
65
|
+
end
|
66
|
+
|
67
|
+
generated_handlers
|
68
|
+
end
|
69
|
+
|
70
|
+
# Reads the config/handlers.yml configuration
|
71
|
+
def read_handlers_yaml
|
72
|
+
raise 'config/handlers.yml does not exist' unless File.exist?("#{Rails.root}/config/handlers.yml")
|
73
|
+
|
74
|
+
YAML.load_file("#{Rails.root}/config/handlers.yml") || []
|
37
75
|
end
|
38
76
|
end
|
39
77
|
end
|
data/lib/slackify/engine.rb
CHANGED
data/lib/slackify/exceptions.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Slackify
|
4
|
+
# When things go boom, we need these.
|
4
5
|
module Exceptions
|
6
|
+
# You tried to call a class that was not extending the base handler
|
5
7
|
class HandlerNotSupported < StandardError; end
|
8
|
+
# You handler is failing validations
|
6
9
|
class InvalidHandler < StandardError; end
|
10
|
+
# The handler method was not approved to be a slash command
|
7
11
|
class MissingSlashPermission < StandardError; end
|
8
12
|
end
|
9
13
|
end
|
data/lib/slackify/handlers.rb
CHANGED
@@ -2,28 +2,41 @@
|
|
2
2
|
|
3
3
|
module Slackify
|
4
4
|
module Handlers
|
5
|
+
# Base handler class that any user defined handlers must inherit from
|
5
6
|
class Base
|
6
7
|
@@supported_handlers = []
|
7
8
|
|
8
9
|
class << self
|
9
10
|
attr_reader :allowed_slash_methods
|
10
11
|
|
12
|
+
# Get the slack client that you can use to perform slack api calls
|
13
|
+
# @see https://github.com/slack-ruby/slack-ruby-client
|
11
14
|
def slack_client
|
12
15
|
Slackify.configuration.slack_client
|
13
16
|
end
|
14
17
|
|
18
|
+
# Enables a method to be called for a slash command.
|
19
|
+
#
|
20
|
+
# More context:
|
21
|
+
# Slash commands have extra validations. To call a slash command, it
|
22
|
+
# needs to call a method on a supported handler and that handler needs
|
23
|
+
# to explicitly specify which methods are for slash command.
|
15
24
|
def allow_slash_method(element)
|
16
|
-
if @allowed_slash_methods
|
25
|
+
if defined?(@allowed_slash_methods) && @allowed_slash_methods
|
17
26
|
@allowed_slash_methods.push(*element)
|
18
27
|
else
|
19
28
|
@allowed_slash_methods = Array(element)
|
20
29
|
end
|
21
30
|
end
|
22
31
|
|
32
|
+
# Any class inheriting from Slackify::Handler::Base will be added to
|
33
|
+
# the list of supported handlers
|
23
34
|
def inherited(subclass)
|
24
35
|
@@supported_handlers.push(subclass.to_s)
|
25
36
|
end
|
26
37
|
|
38
|
+
# Show a list of the handlers supported by the app. Since we do
|
39
|
+
# metaprogramming, we want to ensure we can only call defined handlers
|
27
40
|
def supported_handlers
|
28
41
|
@@supported_handlers
|
29
42
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slackify
|
4
|
+
module Handlers
|
5
|
+
# Creates the handler structs
|
6
|
+
class Factory
|
7
|
+
def self.for(configuration)
|
8
|
+
Validator.verify_handler_integrity(configuration)
|
9
|
+
|
10
|
+
handler = OpenStruct.new
|
11
|
+
handler.name = configuration.keys.first
|
12
|
+
handler.commands = []
|
13
|
+
|
14
|
+
configuration[handler.name]['commands']&.each do |command|
|
15
|
+
built_command = OpenStruct.new
|
16
|
+
built_command.regex = command['regex']
|
17
|
+
built_command.handler = handler.name.camelize.constantize.method(command['action'])
|
18
|
+
built_command.description = command['description']
|
19
|
+
built_command.parameters = command['parameters']
|
20
|
+
built_command.base_command = command['base_command']
|
21
|
+
handler.commands << built_command.freeze
|
22
|
+
end
|
23
|
+
|
24
|
+
handler.commands.freeze
|
25
|
+
handler.freeze
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
module Slackify
|
4
4
|
module Handlers
|
5
|
+
# Default handler for any text message that is not handler by other handlers
|
6
|
+
# This can easily be replaced by setting you own unhandled handler when
|
7
|
+
# configuring slackify. Use `#unhandled_handler=` to set it. Your handler
|
8
|
+
# must implement a `self.unhandled` method.
|
5
9
|
class UnhandledHandler < Base
|
6
10
|
def self.unhandled(params)
|
7
11
|
slack_client.chat_postMessage(
|
@@ -2,36 +2,86 @@
|
|
2
2
|
|
3
3
|
module Slackify
|
4
4
|
module Handlers
|
5
|
+
# Simple validator for handlers. It will blow your app up on the
|
6
|
+
# configuration step instead of crashing when handling production requests.
|
5
7
|
class Validator
|
6
|
-
|
7
|
-
handler_name = handler.keys.first
|
8
|
-
handler_class = handler_name.camelize.constantize
|
8
|
+
VALID_PARAMETER_TYPES = [:string, :int, :boolean, :float].freeze
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
class << self
|
11
|
+
# Checks if your handler hash is valid. It's pass or raise ๐งจ๐ฅ
|
12
|
+
def verify_handler_integrity(handler)
|
13
|
+
handler_name = handler.keys.first
|
14
|
+
handler_class = handler_name.camelize.constantize
|
15
|
+
|
16
|
+
unless handler[handler_name].key?('commands') && handler.dig(handler_name, 'commands')&.any?
|
17
|
+
raise Exceptions::InvalidHandler, "#{handler_name} doesn't have any command specified"
|
18
|
+
end
|
13
19
|
|
14
|
-
|
20
|
+
handler_errors = []
|
15
21
|
|
16
|
-
|
17
|
-
|
22
|
+
handler.dig(handler_name, 'commands').each do |command|
|
23
|
+
command_errors = []
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
|
25
|
+
unless command['regex'] || command['base_command']
|
26
|
+
command_errors.append('No regex or base command was provided.')
|
27
|
+
end
|
28
|
+
|
29
|
+
if command['regex'].present?
|
30
|
+
if command['base_command'].present?
|
31
|
+
command_errors.append('Regex and base_command cannot be used in the same handler.')
|
32
|
+
end
|
33
|
+
|
34
|
+
if command['parameters'].present?
|
35
|
+
command_errors.append('Regex and parameters cannot be used in the same handler.')
|
36
|
+
end
|
37
|
+
|
38
|
+
unless command['regex'].is_a?(Regexp)
|
39
|
+
command_errors.append('No regex was provided.')
|
40
|
+
end
|
41
|
+
end
|
22
42
|
|
23
|
-
|
24
|
-
|
43
|
+
if command['base_command']
|
44
|
+
unless command['base_command'].is_a?(String)
|
45
|
+
command_errors.append('Invalid base command provided, it must be a string.')
|
46
|
+
end
|
47
|
+
|
48
|
+
if command['parameters'].present?
|
49
|
+
command_errors << validate_parameters(command['parameters'])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
unless !command['action'].to_s.strip.empty? && handler_class.respond_to?(command['action'])
|
54
|
+
command_errors.append('No valid action was provided.')
|
55
|
+
end
|
56
|
+
command_errors = command_errors.flatten.compact
|
57
|
+
handler_errors.append("[#{command['name']}]: #{command_errors.join(' ')}") unless command_errors.empty?
|
25
58
|
end
|
26
59
|
|
27
|
-
|
60
|
+
unless handler_errors.empty?
|
61
|
+
raise Exceptions::InvalidHandler, "#{handler_name} is not valid: #{handler_errors.join(' ')}"
|
62
|
+
end
|
63
|
+
rescue NameError
|
64
|
+
raise Exceptions::InvalidHandler, "#{handler_name} is not defined"
|
28
65
|
end
|
29
66
|
|
30
|
-
|
31
|
-
|
67
|
+
def validate_parameters(parameters)
|
68
|
+
errors = []
|
69
|
+
parameters.each do |parameter|
|
70
|
+
key = parameter.keys[0]
|
71
|
+
type = parameter.values[0]
|
72
|
+
|
73
|
+
next if VALID_PARAMETER_TYPES.include?(type.to_sym)
|
74
|
+
|
75
|
+
type.constantize
|
76
|
+
next if Slackify::Parameter.supported_parameters.include?(type)
|
77
|
+
|
78
|
+
errors << "Invalid parameter type for: #{key}, '#{type}'.\n"\
|
79
|
+
"If this is a custom parameter, make sure it inherits from Slackify::Parameter"
|
80
|
+
rescue NameError
|
81
|
+
errors << "Failed to find the custom class for: #{key}, '#{type}'."
|
82
|
+
end
|
83
|
+
errors
|
32
84
|
end
|
33
|
-
rescue NameError
|
34
|
-
raise Exceptions::InvalidHandler, "#{handler_name} is not defined"
|
35
85
|
end
|
36
86
|
end
|
37
87
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slackify
|
4
|
+
# Base parameter class that any user defined parameters must inherit from
|
5
|
+
class Parameter
|
6
|
+
@@supported_parameters = []
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Any class inheriting from Slackify::Parameter will be added to
|
10
|
+
# the list of supported parameters
|
11
|
+
def inherited(subclass)
|
12
|
+
@@supported_parameters.push(subclass.to_s)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Show a list of the parameters supported by the app. Since we do
|
16
|
+
# metaprogramming, we want to ensure we can only call defined parameter
|
17
|
+
def supported_parameters
|
18
|
+
@@supported_parameters
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slackify
|
4
|
+
# In charge of routing a message to its proper handler
|
5
|
+
class Router
|
6
|
+
class << self
|
7
|
+
# List all available commands
|
8
|
+
def all_commands
|
9
|
+
Slackify.configuration.handlers.collect(&:commands).flatten
|
10
|
+
end
|
11
|
+
|
12
|
+
# Find the matching command based on the message string
|
13
|
+
def matching_command(message)
|
14
|
+
all_commands.each do |command|
|
15
|
+
return command if command.regex.present? && command.regex.match?(message)
|
16
|
+
return command if command.base_command.present? && message.start_with?(command.base_command)
|
17
|
+
end
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Call command based on message string
|
22
|
+
def call_command(message, params)
|
23
|
+
command = matching_command(message)
|
24
|
+
if command.nil?
|
25
|
+
return unless Slackify.configuration.unhandled_handler
|
26
|
+
|
27
|
+
Slackify.configuration.unhandled_handler.unhandled(params)
|
28
|
+
else
|
29
|
+
new_params = params.merge( command_arguments: extract_arguments(message, command) )
|
30
|
+
|
31
|
+
command.handler.call(new_params)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def extract_arguments(message, command)
|
36
|
+
if command.regex
|
37
|
+
command.regex.match(message).named_captures
|
38
|
+
else
|
39
|
+
raw_arguments = message.sub(/^#{command.base_command}/, '').strip
|
40
|
+
spec = {}
|
41
|
+
command.parameters.each do |parameter|
|
42
|
+
spec[parameter.keys[0].to_sym] = { type: parameter.values[0].to_sym }
|
43
|
+
end
|
44
|
+
parse_by_spec(spec, raw_arguments)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_by_spec(spec, raw_arguments)
|
49
|
+
processed_args = {}
|
50
|
+
|
51
|
+
s = StringScanner.new(raw_arguments)
|
52
|
+
until s.eos?
|
53
|
+
# get the key, remove '=' and extra whitespace
|
54
|
+
current_key = s.scan_until(/=/)
|
55
|
+
break if current_key.nil?
|
56
|
+
|
57
|
+
current_key = current_key[0..-2].strip
|
58
|
+
|
59
|
+
# grab value accounting for any quotes
|
60
|
+
terminating_string = case s.peek(1)
|
61
|
+
when "'"
|
62
|
+
s.skip(/'/)
|
63
|
+
/'/
|
64
|
+
when '"'
|
65
|
+
s.skip(/"/)
|
66
|
+
/"/
|
67
|
+
else
|
68
|
+
/ /
|
69
|
+
end
|
70
|
+
processed_args[current_key.to_sym] = if s.exist?(terminating_string)
|
71
|
+
# grab everything before the next instance of the terminating character
|
72
|
+
s.scan_until(terminating_string)[0..-2]
|
73
|
+
else
|
74
|
+
# this is probably wrong unless we were expecting a space, but hit eos
|
75
|
+
s.rest
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# only pass on expected parameters for now.
|
80
|
+
processed_spec = {}
|
81
|
+
spec.each do |key, value|
|
82
|
+
# coerce to the expected type
|
83
|
+
type = value.fetch(:type, 'string')
|
84
|
+
processed_spec[key] = case type
|
85
|
+
when :int
|
86
|
+
processed_args[key].to_i
|
87
|
+
when :float
|
88
|
+
processed_args[key].to_f
|
89
|
+
when :boolean
|
90
|
+
ActiveModel::Type::Boolean.new.cast(processed_args[key])
|
91
|
+
when :string
|
92
|
+
processed_args[key]
|
93
|
+
else
|
94
|
+
Object.const_get(type).new(processed_args[key]).parse
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
processed_spec
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slackify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Leger
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-
|
12
|
+
date: 2019-12-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -27,6 +27,20 @@ dependencies:
|
|
27
27
|
version: '0'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: slack-ruby-client
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.15.1
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.15.1
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: strscan
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
31
45
|
requirements:
|
32
46
|
- - ">="
|
@@ -114,9 +128,11 @@ files:
|
|
114
128
|
- lib/slackify/exceptions.rb
|
115
129
|
- lib/slackify/handlers.rb
|
116
130
|
- lib/slackify/handlers/base.rb
|
117
|
-
- lib/slackify/handlers/
|
131
|
+
- lib/slackify/handlers/factory.rb
|
118
132
|
- lib/slackify/handlers/unhandled_handler.rb
|
119
133
|
- lib/slackify/handlers/validator.rb
|
134
|
+
- lib/slackify/parameter.rb
|
135
|
+
- lib/slackify/router.rb
|
120
136
|
homepage: https://github.com/jusleg/slackify
|
121
137
|
licenses:
|
122
138
|
- MIT
|
@@ -138,8 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
154
|
- !ruby/object:Gem::Version
|
139
155
|
version: '0'
|
140
156
|
requirements: []
|
141
|
-
|
142
|
-
rubygems_version: 2.7.3
|
157
|
+
rubygems_version: 3.0.3
|
143
158
|
signing_key:
|
144
159
|
specification_version: 4
|
145
160
|
summary: Slackbot framework for Rails using the Events API
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'ostruct'
|
4
|
-
|
5
|
-
module Slackify
|
6
|
-
module Handlers
|
7
|
-
class Configuration
|
8
|
-
attr_reader :bot_id
|
9
|
-
attr_accessor :handlers
|
10
|
-
delegate :each, to: :@handlers
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
@handlers = []
|
14
|
-
|
15
|
-
read_handlers_yaml.each do |handler_yaml|
|
16
|
-
handler = generate_handler_from_yaml(handler_yaml)
|
17
|
-
@handlers << handler
|
18
|
-
end
|
19
|
-
|
20
|
-
environment_configurations
|
21
|
-
end
|
22
|
-
|
23
|
-
def all_commands
|
24
|
-
@handlers.collect(&:commands).flatten
|
25
|
-
end
|
26
|
-
|
27
|
-
def matching_command(message)
|
28
|
-
all_commands.each { |command| return command if command.regex.match? message }
|
29
|
-
nil
|
30
|
-
end
|
31
|
-
|
32
|
-
def call_command(message, params)
|
33
|
-
return if params.dig(:event, :user) == @bot_id || params.dig(:event, :message, :user) == @bot_id
|
34
|
-
|
35
|
-
command = matching_command(message)
|
36
|
-
if command.nil?
|
37
|
-
return unless Slackify.configuration.unhandled_handler
|
38
|
-
|
39
|
-
Slackify.configuration.unhandled_handler.unhandled(params)
|
40
|
-
else
|
41
|
-
new_params = params.merge(command_arguments: command.regex.match(message).named_captures)
|
42
|
-
command.handler.call(new_params)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def environment_configurations
|
49
|
-
@bot_id =
|
50
|
-
case Rails.env
|
51
|
-
when 'production', 'staging', 'development'
|
52
|
-
Slackify.configuration.slack_client.auth_test['user_id']
|
53
|
-
when 'test'
|
54
|
-
''
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def read_handlers_yaml
|
59
|
-
raise 'config/handlers.yml does not exist' unless File.exist?("#{Rails.root}/config/handlers.yml")
|
60
|
-
|
61
|
-
YAML.load_file("#{Rails.root}/config/handlers.yml") || []
|
62
|
-
end
|
63
|
-
|
64
|
-
def generate_handler_from_yaml(handler_yaml)
|
65
|
-
Validator.verify_handler_integrity(handler_yaml)
|
66
|
-
|
67
|
-
handler = OpenStruct.new
|
68
|
-
handler.name = handler_yaml.keys.first
|
69
|
-
handler.commands = []
|
70
|
-
|
71
|
-
handler_yaml[handler.name]['commands']&.each do |command|
|
72
|
-
built_command = OpenStruct.new
|
73
|
-
built_command.regex = command['regex']
|
74
|
-
built_command.handler = handler.name.camelize.constantize.method(command['action'])
|
75
|
-
built_command.description = command['description']
|
76
|
-
handler.commands << built_command.freeze
|
77
|
-
end
|
78
|
-
|
79
|
-
handler.commands.freeze
|
80
|
-
handler.freeze
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|