slack-ruby-bot-boilerplate 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/README.md +24 -643
- data/lib/slack_respondent.rb +2 -1
- data/lib/slack_respondent/client_provider.rb +11 -0
- data/lib/slack_respondent/{client.rb → client_wrapper.rb} +2 -2
- data/lib/slack_respondent/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 774349454893d93e2351d0264773f5ee085c1240260049d903b2235eb748b213
|
4
|
+
data.tar.gz: e80103881da4e83f0b2d26571d7520436543581d0920a178956042f0a5c0922b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bce87c4ca5c655ca67e0cca712d23b41ea6dccb1468f7e35f2db89d8c7833e528f6cb45915feef606434418d3be8437d0890cab46342503995998f9b477d0606
|
7
|
+
data.tar.gz: 351034e3dd32c8c60f2db51676e92f9fccb1248a52d43e6cd2b5c88c20f815db7feba7267f59854f3f575b8fa9e9ef6de41d95802e817e0114a38ab344862dd6
|
data/README.md
CHANGED
@@ -1,23 +1,14 @@
|
|
1
1
|
Slack-Ruby-Bot-Boilerplate
|
2
2
|
==============
|
3
3
|
|
4
|
-
A generic Slack bot framework written in Ruby on top of [slack-ruby-client](https://github.com/slack-ruby/slack-ruby-client).
|
4
|
+
A generic Slack bot framework written in Ruby on top of [slack-ruby-client](https://github.com/slack-ruby/slack-ruby-client).
|
5
|
+
It is slack-ruby-bot but without server, only commands parsing part.
|
5
6
|
|
6
|
-
If you are not familiar with Slack bots or Slack API concepts, you might want to watch [this video](http://code.dblock.org/2016/03/11/your-first-slack-bot-service-video.html).
|
7
7
|
|
8
8
|

|
9
9
|
|
10
10
|
## Useful to Me?
|
11
|
-
|
12
|
-
* If you are just trying to send messages to Slack, use [slack-ruby-client](https://github.com/slack-ruby/slack-ruby-client), which this library is built on top of.
|
13
|
-
* If you're trying to roll out a full service with Slack button integration, check out [slack-ruby-bot-server](https://github.com/slack-ruby/slack-ruby-bot-server), which uses this library.
|
14
|
-
* Otherwise, this piece of the puzzle will help you create a single bot instance for one team.
|
15
|
-
|
16
|
-
## Stable Release
|
17
|
-
|
18
|
-
You're reading the documentation for the **next** release of slack-ruby-bot.
|
19
|
-
Please see the documentation for the [last stable release, v0.11.2](https://github.com/slack-ruby/slack-ruby-bot/tree/v0.11.2) unless you're integrating with HEAD.
|
20
|
-
See [CHANGELOG](CHANGELOG.md) for a history of changes and [UPGRADING](UPGRADING.md) for how to upgrade to more recent versions.
|
11
|
+
*
|
21
12
|
|
22
13
|
## Usage
|
23
14
|
|
@@ -28,661 +19,51 @@ See [CHANGELOG](CHANGELOG.md) for a history of changes and [UPGRADING](UPGRADING
|
|
28
19
|
```ruby
|
29
20
|
source 'https://rubygems.org'
|
30
21
|
|
31
|
-
gem 'slack-ruby-bot'
|
32
|
-
gem 'async-websocket'
|
22
|
+
gem 'slack-ruby-bot-boilerplate'
|
33
23
|
```
|
34
24
|
|
35
25
|
#### pongbot.rb
|
36
26
|
|
37
27
|
```ruby
|
38
|
-
require '
|
39
|
-
|
40
|
-
class PongBot < SlackRubyBot::Bot
|
41
|
-
command 'ping' do |client, data, match|
|
42
|
-
client.say(text: 'pong', channel: data.channel)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
PongBot.run
|
47
|
-
```
|
48
|
-
|
49
|
-
After [registering the bot](DEPLOYMENT.md), run with `SLACK_API_TOKEN=... bundle exec ruby pongbot.rb`. Have the bot join a channel and send it a ping.
|
50
|
-
|
51
|
-

|
52
|
-
|
53
|
-
### A Production Bot
|
54
|
-
|
55
|
-
A typical production Slack bot is a combination of a vanilla web server and a websocket application that talks to the Slack Real Time Messaging API. See our [Writing a Production Bot](TUTORIAL.md) tutorial for more information.
|
56
|
-
|
57
|
-
### More Involved Examples
|
58
|
-
|
59
|
-
The following examples of bots based on slack-ruby-bot are listed in growing order of complexity.
|
60
|
-
|
61
|
-
* [slack-bot-on-rails](https://github.com/dblock/slack-bot-on-rails): A bot running on Rails and using React to display Slack messages on a website.
|
62
|
-
* [slack-mathbot](https://github.com/dblock/slack-mathbot): Slack integration with math.
|
63
|
-
* [slack-google-bot](https://github.com/dblock/slack-google-bot): A Slack bot that searches Google, including CSE.
|
64
|
-
* [slack-aws](https://github.com/dblock/slack-aws): Slack integration with Amazon Web Services.
|
65
|
-
* [slack-deploy-bot](https://github.com/accessd/slack-deploy-bot): A Slack bot that helps you to deploy your apps.
|
66
|
-
* [slack-gamebot](https://github.com/dblock/slack-gamebot): A game bot service for ping pong, chess, etc, hosted at [playplay.io](http://playplay.io).
|
67
|
-
* [slack-victorbot](https://github.com/uShip/victorbot): A Slack bot to talk to the Victorops service.
|
68
|
-
|
69
|
-
### Commands and Operators
|
70
|
-
|
71
|
-
Bots are addressed by name, they respond to commands and operators. You can combine multiple commands.
|
72
|
-
|
73
|
-
```ruby
|
74
|
-
class CallBot < SlackRubyBot::Bot
|
75
|
-
command 'call', '呼び出し' do |client, data, match|
|
76
|
-
client.say(channel: data.channel, text: 'called')
|
77
|
-
end
|
78
|
-
end
|
79
|
-
```
|
80
|
-
|
81
|
-
Command match data includes `match['bot']`, `match['command']` and `match['expression']`. The `bot` match always checks against the `SlackRubyBot::Config.user` and `SlackRubyBot::Config.user_id` values obtained when the bot starts.
|
82
|
-
|
83
|
-
The `command` method can take strings, which will have to be escaped with `Regexp.escape`, and regular expressions.
|
84
|
-
|
85
|
-
```ruby
|
86
|
-
class CallBot < SlackRubyBot::Bot
|
87
|
-
command 'string with spaces', /some\s*regex+\?*/ do |client, data, match|
|
88
|
-
client.say(channel: data.channel, text: match['command'])
|
89
|
-
end
|
90
|
-
end
|
91
|
-
```
|
92
|
-
|
93
|
-
Operators are 1-letter long and are similar to commands. They don't require addressing a bot nor separating an operator from its arguments. The following class responds to `=2+2`.
|
94
|
-
|
95
|
-
```ruby
|
96
|
-
class MathBot < SlackRubyBot::Bot
|
97
|
-
operator '=' do |client, data, match|
|
98
|
-
# implementation detail
|
99
|
-
end
|
100
|
-
end
|
101
|
-
```
|
102
|
-
|
103
|
-
Operator match data includes `match['operator']` and `match['expression']`. The `bot` match always checks against the `SlackRubyBot::Config.user` setting.
|
104
|
-
|
105
|
-
### Threaded Messages
|
106
|
-
|
107
|
-
To reply to a message in a thread you must provide a reference to the first message that initiated the thread, which is available as either `data.ts` if no threaded messages have been sent, or `data.thread_ts` if the message being replied to is already in a thread. See [message-threading](https://api.slack.com/docs/message-threading) for more information.
|
108
|
-
|
109
|
-
```ruby
|
110
|
-
command 'reply in thread' do |client, data, match|
|
111
|
-
client.say(
|
112
|
-
channel: data.channel,
|
113
|
-
text: "let's avoid spamming everyone, I will tell you what you need in this thread",
|
114
|
-
thread_ts: data.thread_ts || data.ts
|
115
|
-
)
|
116
|
-
end
|
117
|
-
```
|
118
|
-
|
119
|
-
_Note that sending a message using only `thread_ts: data.ts` can cause some permanent issues where Slack will keep reporting inaccessible messages as unread. At the time of writing the slack team is still having problems clearing those notifications. As recommended by the slack documentation ..._
|
120
|
-
|
121
|
-
> A true parent's thread_ts should be used when replying. Providing a child's message ID will result in a new, detached thread breaking all context and sense.
|
122
|
-
|
123
|
-
_... the replies to a thread should always be sent to the message `ts` that started the thread, available as `thread_ts` for subsequent messages. Hence `data.thread_ts || data.ts`._
|
124
|
-
|
125
|
-
For additional options, including broadcasting, see [slack-ruby-client#chat_postMessage](https://github.com/slack-ruby/slack-ruby-client/blob/41539c647ac877400f20aa338aa42d2ebfd2866b/lib/slack/web/api/endpoints/chat.rb#L105).
|
126
|
-
|
127
|
-
### Bot Aliases
|
128
|
-
|
129
|
-
A bot will always respond to its name (eg. `rubybot`) and Slack ID (eg. `@rubybot`), but you can specify multiple aliases via the `SLACK_RUBY_BOT_ALIASES` environment variable or via an explicit configuration.
|
130
|
-
|
131
|
-
```
|
132
|
-
SLACK_RUBY_BOT_ALIASES=:pp: table-tennis
|
133
|
-
```
|
134
|
-
|
135
|
-
```ruby
|
136
|
-
SlackRubyBot.configure do |config|
|
137
|
-
config.aliases = [':pong:', 'pongbot']
|
138
|
-
end
|
139
|
-
```
|
140
|
-
|
141
|
-
This is particularly fun with emoji.
|
142
|
-
|
143
|
-

|
144
|
-
|
145
|
-
Bots will also respond to a direct message, with or without the bot name in the message itself.
|
146
|
-
|
147
|
-

|
148
|
-
|
149
|
-
### Generic Routing
|
150
|
-
|
151
|
-
Commands and operators are generic versions of bot routes. You can respond to just about anything by defining a custom route.
|
152
|
-
|
153
|
-
```ruby
|
154
|
-
class Weather < SlackRubyBot::Bot
|
155
|
-
match /^How is the weather in (?<location>\w*)\?$/ do |client, data, match|
|
156
|
-
client.say(channel: data.channel, text: "The weather in #{match[:location]} is nice.")
|
157
|
-
end
|
158
|
-
end
|
159
|
-
```
|
160
|
-
|
161
|
-

|
162
|
-
|
163
|
-
You can also capture multiple matchers with `scan`.
|
164
|
-
|
165
|
-
```ruby
|
166
|
-
class Market < SlackRubyBot::Bot
|
167
|
-
scan(/([A-Z]{2,5})/) do |client, data, stocks|
|
168
|
-
# lookup stock market price
|
169
|
-
end
|
170
|
-
end
|
171
|
-
```
|
172
|
-
|
173
|
-

|
174
|
-
|
175
|
-
See [examples/market](examples/market/marketbot.rb) for a working example.
|
176
|
-
|
177
|
-
### Matching text in message attachments
|
178
|
-
|
179
|
-
You can respond to text in [attachments](https://api.slack.com/docs/message-attachments) with
|
180
|
-
`attachment`. It will scan `text`, `pretext` and `title` fields in each attachment until a first
|
181
|
-
match is found.
|
182
|
-
|
183
|
-
For example you can match [this example attachment](http://goo.gl/K0cLkH)
|
184
|
-
by its `title` with the following bot:
|
185
|
-
|
186
|
-
```ruby
|
187
|
-
class Attachment < SlackRubyBot::Bot
|
188
|
-
attachment 'Slack API Documentation' do |client, data, match|
|
189
|
-
client.say(channel: data.channel, text: "Matched by #{match.attachment_field}.")
|
190
|
-
client.say(channel: data.channel, text: "The attachment's text: #{match.attachment.text}.")
|
191
|
-
end
|
192
|
-
end
|
193
|
-
```
|
194
|
-
|
195
|
-
You can also define which fields in attachment object should be scanned.
|
196
|
-
|
197
|
-
Scan only a single field:
|
198
|
-
|
199
|
-
```ruby
|
200
|
-
class Attachment < SlackRubyBot::Bot
|
201
|
-
attachment 'Slack API Documentation', :title do |client, data, match|
|
202
|
-
# implementation details
|
203
|
-
end
|
204
|
-
end
|
205
|
-
```
|
206
|
-
|
207
|
-
Scan multiple fields:
|
208
|
-
|
209
|
-
```ruby
|
210
|
-
class Attachment < SlackRubyBot::Bot
|
211
|
-
attachment 'Slack API Documentation', %i[text pretext author_name] do |client, data, match|
|
212
|
-
# implementation details
|
213
|
-
end
|
214
|
-
end
|
215
|
-
```
|
216
|
-
|
217
|
-
### Providing description for your bot and commands
|
218
|
-
|
219
|
-
You can specify help information for bot or commands with `help` block, for example:
|
220
|
-
|
221
|
-
in case of bot:
|
222
|
-
|
223
|
-
```ruby
|
224
|
-
class WeatherBot < SlackRubyBot::Bot
|
225
|
-
help do
|
226
|
-
title 'Weather Bot'
|
227
|
-
desc 'This bot tells you the weather.'
|
228
|
-
|
229
|
-
command 'clouds' do
|
230
|
-
desc 'Tells you how many clouds there\'re above you.'
|
231
|
-
end
|
232
|
-
|
233
|
-
command 'What\'s the weather in <city>?' do
|
234
|
-
desc 'Tells you the weather in a <city>.'
|
235
|
-
long_desc "Accurate 10 Day Weather Forecasts for thousands of places around the World.\n" \
|
236
|
-
'Bot provides detailed Weather Forecasts over a 10 day period updated four times a day.'
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
# commands implementation
|
241
|
-
end
|
242
|
-
```
|
243
|
-
|
244
|
-

|
245
|
-
|
246
|
-
in case of your own command:
|
247
|
-
|
248
|
-
```ruby
|
249
|
-
class Deploy < SlackRubyBot::Commands::Base
|
250
|
-
help do
|
251
|
-
title 'deploy'
|
252
|
-
desc 'deploys your app'
|
253
|
-
long_desc 'command format: *deploy <branch> to <env>* where <env> is production or staging'
|
254
|
-
end
|
255
|
-
end
|
256
|
-
```
|
257
|
-
|
258
|
-
### Customize your command help output
|
259
|
-
|
260
|
-
If you've used the `help` block described above to document your
|
261
|
-
commands, you can provide your own implementation of outputting help
|
262
|
-
for commands like so:
|
263
|
-
|
264
|
-
```ruby
|
265
|
-
class Market < SlackRubyBot::Bot
|
266
|
-
command 'help' do |client, data, match|
|
267
|
-
user_command = match[:expression]
|
268
|
-
help_attrs = SlackRubyBot::Commands::Support::Help.instance.find_command_help_attrs(user_command)
|
269
|
-
client.say(channel: data.channel, text: "#{help_attrs.command_desc}\n\n#{help_attrs.command_long_desc}"
|
270
|
-
end
|
271
|
-
end
|
272
|
-
```
|
273
|
-
|
274
|
-
### SlackRubyBot::Commands::Base
|
275
|
-
|
276
|
-
The `SlackRubyBot::Bot` class is DSL sugar deriving from `SlackRubyBot::Commands::Base`. For more involved bots you can organize the bot implementation into subclasses of `SlackRubyBot::Commands::Base` manually. By default a command class responds, case-insensitively, to its name. A class called `Phone` that inherits from `SlackRubyBot::Commands::Base` responds to `phone` and `Phone` and calls the `call` method when implemented.
|
277
|
-
|
278
|
-
```ruby
|
279
|
-
class Phone < SlackRubyBot::Commands::Base
|
280
|
-
command 'call'
|
281
|
-
|
282
|
-
def self.call(client, data, match)
|
283
|
-
client.say(channel: data.channel, text: 'called')
|
284
|
-
end
|
285
|
-
end
|
286
|
-
```
|
287
|
-
|
288
|
-
To respond to custom commands and to disable automatic class name matching, use the `command` keyword. The following command responds to `call` and `呼び出し` (call in Japanese).
|
289
|
-
|
290
|
-
```ruby
|
291
|
-
class Phone < SlackRubyBot::Commands::Base
|
292
|
-
command 'call'
|
293
|
-
command '呼び出し'
|
294
|
-
|
295
|
-
def self.call(client, data, match)
|
296
|
-
client.say(channel: data.channel, text: 'called')
|
297
|
-
end
|
298
|
-
end
|
299
|
-
```
|
300
|
-
|
301
|
-
### Authorization
|
302
|
-
|
303
|
-
The framework does not provide any user authentication or command authorization capability out of the box. However, the `SlackRubyBot::Commands::Base` class does check every command invocation for permission prior to executing the command. The default method always returns true.
|
304
|
-
|
305
|
-
Therefore, subclasses of `SlackRubyBot::Commands::Base` can override the `permitted?` private method to provide its own authorization logic. This method is intended to be exploited by user code or external gems that want to provide custom authorization logic for command execution.
|
306
|
-
|
307
|
-
```ruby
|
308
|
-
class AuthorizedBot < SlackRubyBot::Commands::Base
|
309
|
-
command 'phone home' do |client, data, match|
|
310
|
-
client.say(channel: data.channel, text: 'Elliot!')
|
311
|
-
end
|
312
|
-
|
313
|
-
# Only allow user 'Uxyzabc' to run this command
|
314
|
-
def self.permitted?(client, data, match)
|
315
|
-
data && data.user && data.user == 'Uxyzabc'
|
316
|
-
end
|
317
|
-
end
|
318
|
-
```
|
319
|
-
|
320
|
-
### Animated GIFs
|
321
|
-
|
322
|
-
The `SlackRubyBot::Client` implementation comes with GIF support. To enable it add `gem GiphyClient` (official Giphy SDK) or `gem giphy` (older SDK, deprecated) to your **Gemfile** and set a Giphy key via `ENV['GIPHY_API_KEY']`. Obtain one from [developers.giphy.com](https://developers.giphy.com).
|
323
|
-
|
324
|
-
**Note:** Bots send animated GIFs in default commands and errors.
|
325
|
-
|
326
|
-
```ruby
|
327
|
-
class Phone < SlackRubyBot::Commands::Base
|
328
|
-
command 'call'
|
329
|
-
|
330
|
-
def self.call(client, data, match)
|
331
|
-
client.say(channel: data.channel, text: 'called', gif: 'phone')
|
332
|
-
# Sends the text 'called' and a random GIF that matches the keyword 'phone'.
|
333
|
-
end
|
334
|
-
end
|
335
|
-
```
|
336
|
-
|
337
|
-
Giphy API key is set automatically via `ENV['GIPHY_API_KEY']`. You can override this manually.
|
338
|
-
|
339
|
-
```ruby
|
340
|
-
Giphy.configure do |config|
|
341
|
-
config.api_key = 'key'
|
342
|
-
end
|
343
|
-
```
|
344
|
-
|
345
|
-
With `GiphyClient` you can configure the default GIF rating, which supports Y, G, PG, PG-13, and R. The default value is `G`.
|
346
|
-
|
347
|
-
```ruby
|
348
|
-
Giphy.configure do |config|
|
349
|
-
config.rating = 'Y' # illustrated content only, i.e. cartoons
|
350
|
-
end
|
351
|
-
```
|
352
|
-
|
353
|
-
If you use giphy for something else but don't want your bots to send GIFs you can set `ENV['SLACK_RUBY_BOT_SEND_GIFS']` or `SlackRubyBot::Config.send_gifs` to `false`. The latter takes precedence.
|
354
|
-
|
355
|
-
```ruby
|
356
|
-
SlackRubyBot.configure do |config|
|
357
|
-
config.send_gifs = false
|
358
|
-
end
|
359
|
-
```
|
360
|
-
|
361
|
-
### Built-In Commands
|
362
|
-
|
363
|
-
Slack-ruby-bot comes with several built-in commands. You can re-define built-in commands, normally, as described above.
|
364
|
-
|
365
|
-
#### [bot name]
|
366
|
-
|
367
|
-
This is also known as the `default` command. Shows bot version and links.
|
368
|
-
|
369
|
-
#### [bot name] hi
|
370
|
-
|
371
|
-
Politely says 'hi' back.
|
372
|
-
|
373
|
-
#### [bot name] help
|
374
|
-
|
375
|
-
Get help.
|
376
|
-
|
377
|
-
### Hooks
|
378
|
-
|
379
|
-
Hooks are event handlers and respond to Slack RTM API [events](https://api.slack.com/events), such as [hello](lib/slack-ruby-bot/hooks/hello.rb) or [message](lib/slack-ruby-bot/hooks/message.rb). You can implement your own in a couple of ways:
|
380
|
-
|
381
|
-
#### Implement and register a Hook Handler
|
382
|
-
|
383
|
-
A Hook Handler is any object that respond to a `call` message, like a proc, instance of an object, class with a `call` class method, etc.
|
384
|
-
|
385
|
-
Hooks can be registered using different methods based on user preference / use case.
|
386
|
-
Currently someone can use one of the following methods:
|
387
|
-
|
388
|
-
* Pass `hooks` in `SlackRubyBot::Server` initialization.
|
389
|
-
* Register `hooks` on `SlackRubyBot::Server` using `on` class method.
|
390
|
-
* Register `hooks` on `SlackRubyBot::Server` using `on` instance method.
|
391
|
-
|
392
|
-
|
393
|
-
##### Hooks registration on `SlackRubyBot::Server` initialization
|
394
|
-
|
395
|
-
```ruby
|
396
|
-
SlackRubyBot::Server.new(hook_handlers: {
|
397
|
-
hello: MyBot::Hooks::UserChange.new,
|
398
|
-
user_change: [->(client, data) { }, ->(client, data) {}]
|
399
|
-
})
|
400
|
-
```
|
401
|
-
|
402
|
-
##### Hooks registration on a `SlackRubyBot::Server` instance
|
403
|
-
|
404
|
-
```ruby
|
405
|
-
# Register an object that implements `call` method
|
406
|
-
class MyBot::Hooks::Hello
|
407
|
-
def call(client, data)
|
408
|
-
puts "Hello"
|
409
|
-
end
|
410
|
-
end
|
411
|
-
|
412
|
-
server.on(:hello, MyBot::Hooks::Hello.new)
|
413
|
-
|
414
|
-
# or register a lambda function to handle the event
|
415
|
-
server.on(:hello, ->(client, data) { puts "Hello!" })
|
416
|
-
```
|
417
|
-
|
418
|
-
For example, the following hook handles [user_change](https://api.slack.com/events/user_change), an event sent when a team member updates their profile or data. This can be useful to update the local user cache when a user is renamed.
|
419
|
-
|
420
|
-
```ruby
|
421
|
-
module MyBot
|
422
|
-
module Hooks
|
423
|
-
class UserChange
|
424
|
-
def call(client, data)
|
425
|
-
# data['user']['id'] contains the user ID
|
426
|
-
# data['user']['name'] contains the new user name
|
427
|
-
# ...
|
428
|
-
end
|
429
|
-
end
|
430
|
-
end
|
431
|
-
end
|
432
|
-
```
|
433
|
-
|
434
|
-
##### Hooks registration on `SlackRubyBot::Server` class
|
435
|
-
|
436
|
-
Example:
|
437
|
-
|
438
|
-
```ruby
|
439
|
-
module MyBot
|
440
|
-
class MyServer < SlackRubyBot::Server
|
441
|
-
on 'hello' do |client, data|
|
442
|
-
# data['user']['id'] contains the user ID
|
443
|
-
# data['user']['name'] contains the new user name
|
444
|
-
end
|
445
|
-
|
446
|
-
on 'user_change', ->(client, data) {
|
447
|
-
# data['user']['id'] contains the user ID
|
448
|
-
# data['user']['name'] contains the new user name
|
449
|
-
}
|
450
|
-
end
|
451
|
-
end
|
452
|
-
```
|
453
|
-
|
454
|
-
These will get pushed into the hook set on initialization.
|
455
|
-
|
456
|
-
Either by configuration, explicit assignment or hook blocks, multiple handlers can exist for the same event type.
|
457
|
-
|
458
|
-
|
459
|
-
#### Deprecated hook registration
|
460
|
-
|
461
|
-
Registering a hook method using `hooks.add` is considered deprecated and
|
462
|
-
will be removed on future versions.
|
463
|
-
|
464
|
-
```ruby
|
465
|
-
# [DEPRECATED]
|
466
|
-
server.hooks.add(:hello, MyBot::Hooks::UserChange.new)
|
467
|
-
server.hooks.add(:hello, ->(client, data) { puts "Hello!" })
|
468
|
-
|
469
|
-
```
|
470
|
-
|
471
|
-
### Message Loop Protection
|
472
|
-
|
473
|
-
By default bots do not respond to their own messages. If you wish to change that behavior, set `allow_message_loops` to `true`.
|
474
|
-
|
475
|
-
```ruby
|
476
|
-
SlackRubyBot.configure do |config|
|
477
|
-
config.allow_message_loops = true
|
478
|
-
end
|
479
|
-
```
|
480
|
-
|
481
|
-
### Logging
|
482
|
-
|
483
|
-
By default bots set a logger to `$stdout` with `DEBUG` level. The logger is used in both the RealTime and Web clients.
|
484
|
-
|
485
|
-
Silence logger as follows.
|
486
|
-
|
487
|
-
```ruby
|
488
|
-
SlackRubyBot::Client.logger.level = Logger::WARN
|
489
|
-
```
|
490
|
-
|
491
|
-
If you wish to customize logger, set `logger` to your logger.
|
492
|
-
|
493
|
-
```ruby
|
494
|
-
SlackRubyBot.configure do |config|
|
495
|
-
config.logger = Logger.new("slack-ruby-bot.log", "daily")
|
496
|
-
end
|
497
|
-
```
|
498
|
-
|
499
|
-
### Advanced Integration
|
500
|
-
|
501
|
-
You may want to integrate a bot or multiple bots into other systems, in which case a globally configured bot may not work for you. You may create instances of [SlackRubyBot::Server](lib/slack-ruby-bot/server.rb) which accepts `token`, `aliases` and `send_gifs`.
|
502
|
-
|
503
|
-
```ruby
|
504
|
-
EM.run do
|
505
|
-
bot1 = SlackRubyBot::Server.new(token: token1, aliases: ['bot1'])
|
506
|
-
bot1.start_async
|
507
|
-
|
508
|
-
bot2 = SlackRubyBot::Server.new(token: token2, send_gifs: false, aliases: ['bot2'])
|
509
|
-
bot2.start_async
|
510
|
-
end
|
511
|
-
```
|
512
|
-
|
513
|
-
For an example of advanced integration that supports multiple teams, see [slack-gamebot](https://github.com/dblock/slack-gamebot) and [playplay.io](http://playplay.io) that is built on top of it.
|
514
|
-
|
515
|
-
### Proxy Configuration
|
516
|
-
|
517
|
-
There are [several proxy options](https://github.com/slack-ruby/slack-ruby-client#web-client-options) that can be configured on `Slack::Web::Client`. You can also control what proxy options are used by modifying the `http_proxy` environment variable per [Net::HTTP's documentation](https://docs.ruby-lang.org/en/2.0.0/Net/HTTP.html#class-Net::HTTP-label-Proxies).
|
518
|
-
|
519
|
-
Note that Docker on OSX seems to incorrectly set the proxy, [causing `Faraday::ConnectionFailed`](https://github.com/slack-ruby/slack-ruby-bot/issues/155), `ERROR -- : Failed to open TCP connection to : (getaddrinfo: Name or service not known)`. You might need to manually unset `http_proxy` in that case, eg. `http_proxy="" bundle exec ruby ./my_bot.rb`.
|
520
|
-
|
521
|
-
### Model-View-Controller Design
|
522
|
-
|
523
|
-
The `command` method is essentially a controller method that receives input from the outside and acts upon it. Complex behaviors could lead to a long and difficult-to-understand `command` block. A complex `command` block is a candidate for separation into classes conforming to the Model-View-Controller pattern popularized by Rails.
|
524
|
-
|
525
|
-
The library provides three helpful base classes named `SlackRubyBot::MVC::Model::Base`, `SlackRubyBot::MVC::View::Base`, and `SlackRubyBot::MVC::Controller::Base`.
|
526
|
-
|
527
|
-
Testing a `command` block is difficult. As separate classes, the Model/View/Controller's behavior can be tested via `rspec` or a similar tool.
|
528
|
-
|
529
|
-
#### Controller
|
530
|
-
|
531
|
-
The Controller is the focal point of the bot behavior. Typically the code that would go into the `command` block will now go into an instance method in a Controller subclass. The instance method name should match the command name exactly (case sensitive).
|
532
|
-
|
533
|
-
As an example, these two classes are functionally equivalent.
|
534
|
-
|
535
|
-
Consider the following `Agent` class which is the simplest default approach to take.
|
536
|
-
|
537
|
-
```ruby
|
538
|
-
class Agent < SlackRubyBot::Bot
|
539
|
-
command 'sayhello', 'alternate way to call hello' do |client, data, match|
|
540
|
-
client.say(channel: data.channel, text: "Received command #{match[:command]} with args #{match[:expression]}")
|
541
|
-
end
|
542
|
-
end
|
543
|
-
```
|
544
|
-
|
545
|
-
Using the MVC functionality, we would create a controller instead to encapsulate this function.
|
546
|
-
```ruby
|
547
|
-
class MyController < SlackRubyBot::MVC::Controller::Base
|
548
|
-
def sayhello
|
549
|
-
client.say(channel: data.channel, text: "Received command #{match[:command]} with args #{match[:expression]}")
|
550
|
-
end
|
551
|
-
alternate_name :sayhello, :alternate_way_to_call_hello
|
552
|
-
end
|
553
|
-
MyController.new(MyModel.new, MyView.new)
|
554
|
-
```
|
555
|
-
Note in the above example that the Controller instance method `sayhello` does not receive any arguments. When the instance method is called, the Controller class sets up some accessor methods to provide the normal `client`, `data`, and `match` objects. These are the same objects passed to the `command` block.
|
556
|
-
|
557
|
-
However, the Controller anticipates that the model and view objects should contain business logic that will also operate on the `client`, `data`, and `match` objects. The controller provides access to the model and view via the `model` and `view` accessor methods. The [inventory example](examples/inventory/inventorybot.rb) provides a full example of a Model, View, and Controller working together.
|
558
|
-
|
559
|
-
A Controller may need helper methods for certain work. To prevent the helper method from creating a route that the bot will respond to directly, the instance method name should begin with an underscore (e.g. `_my_helper_method`). When building the bot routes, these methods will be skipped.
|
560
|
-
|
561
|
-
Calling `alternate_name` after the method definition allows for method aliases similar to the regular `command` structure. When commands can be triggered by multiple text strings it's useful to have that ability map to the controller methods too.
|
562
|
-
|
563
|
-
Lastly, the Controller class includes `ActiveSupport::Callbacks` which allows for full flexibility in creating `before`, `after`, and `around` hooks for all methods. Again, see the [inventory example](examples/inventory/inventorybot.rb) for more information.
|
564
|
-
|
565
|
-
#### Model
|
566
|
-
|
567
|
-
A complex bot may need to read or write data from a database or other network resource. Setting up and tearing down these connections can be costly, so the model can do it once upon instantiation.
|
568
|
-
|
569
|
-
The Model also includes `ActiveSupport::Callbacks`.
|
570
|
-
|
571
|
-
```ruby
|
572
|
-
class MyModel < SlackRubyBot::MVC::Model::Base
|
573
|
-
define_callbacks :sanitize
|
574
|
-
set_callback :sanitize, :around, :sanitize_resource
|
575
|
-
attr_accessor :_resource
|
28
|
+
require 'slack_respondent'
|
29
|
+
require 'pong_command'
|
576
30
|
|
31
|
+
class PongBot
|
32
|
+
include SlackRespondent::Reactions
|
33
|
+
|
577
34
|
def initialize
|
578
|
-
|
35
|
+
on("app_mention", PongCommand)
|
579
36
|
end
|
580
37
|
|
581
|
-
def
|
582
|
-
|
583
|
-
run_callbacks :sanitize do
|
584
|
-
@db.select(:column1 => resource)
|
585
|
-
# ... do some expensive work
|
586
|
-
end
|
38
|
+
def call(request)
|
39
|
+
react(request["event"]["type"], request["event"])
|
587
40
|
end
|
588
41
|
|
589
|
-
private
|
590
|
-
|
591
|
-
def sanitize_resource
|
592
|
-
self._resource.downcase
|
593
|
-
result = yield
|
594
|
-
puts "After read, result is #{result.inspect}"
|
595
|
-
end
|
596
42
|
end
|
597
43
|
```
|
598
|
-
|
599
|
-
Like Controllers, the Model is automatically loaded with the latest version of the `client`, `data`, and `match` objects each time the controller method is called. Therefore the model will always have access to the latest objects when doing its work. It will typically only use the `data` and `match` objects.
|
600
|
-
|
601
|
-
Model methods are not matched to routes, so there is no restriction on how to name methods as there is in Controllers.
|
602
|
-
|
603
|
-
#### View
|
604
|
-
|
605
|
-
A typical bot just writes to a channel or uses the web client to react/unreact to a message. More complex bots will probably require more complex behaviors. These should be stored in a `SlackRubyBot::MVC::View::Base` subclass.
|
606
|
-
|
44
|
+
#### pong_command.rb
|
607
45
|
```ruby
|
608
|
-
class
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
def initialize
|
613
|
-
@mailer = setup_mailer
|
614
|
-
@ftp = setup_ftp_handler
|
615
|
-
end
|
616
|
-
|
617
|
-
def email_admin(message)
|
618
|
-
run_callbacks :logit do
|
619
|
-
@mailer.send(:administrator, message)
|
620
|
-
end
|
621
|
-
end
|
622
|
-
|
623
|
-
def react_thumbsup
|
624
|
-
client.web_client.reactions_add(
|
625
|
-
name: :thumbs_up,
|
626
|
-
channel: data.channel,
|
627
|
-
timestamp: data.ts,
|
628
|
-
as_user: true)
|
46
|
+
class PongCommand
|
47
|
+
help do
|
48
|
+
title 'candidate import'
|
49
|
+
desc 'Import new candidate to HR app'
|
629
50
|
end
|
630
51
|
|
631
|
-
def
|
632
|
-
client.
|
633
|
-
name: :thumbs_up,
|
634
|
-
channel: data.channel,
|
635
|
-
timestamp: data.ts,
|
636
|
-
as_user: true)
|
637
|
-
end
|
638
|
-
|
639
|
-
private
|
640
|
-
|
641
|
-
def audit_trail
|
642
|
-
Logger.audit("Sending email at [#{Time.now}]")
|
643
|
-
yield
|
644
|
-
Logger.audit("Email sent by [#{Time.now}]")
|
52
|
+
def self.call(client, data, _match)
|
53
|
+
client.say(channel: data.channel, text: "Pong")
|
645
54
|
end
|
646
55
|
end
|
647
56
|
```
|
648
|
-
Again, the View will have access to the most up to date `client`, `data`, and `match` objects. It will typically only use the `client` and `data` objects.
|
649
|
-
|
650
|
-
View methods are not matched to routes, so there is no restriction on how to name methods as there is in Controllers.
|
651
57
|
|
652
|
-
|
653
|
-
|
654
|
-
Slack-ruby-bot ships with a number of shared RSpec behaviors that can be used in your RSpec tests.
|
655
|
-
|
656
|
-
* [behaves like a slack bot](lib/slack-ruby-bot/rspec/support/slack-ruby-bot/it_behaves_like_a_slack_bot.rb): A bot quacks like a Slack Ruby bot.
|
657
|
-
* [respond with slack message](lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_slack_message.rb): The bot responds with a message.
|
658
|
-
* [respond with slack messages](lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_slack_messages.rb): The bot responds with a multiple messages.
|
659
|
-
* [respond with error](lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_error.rb): An exception is raised inside a bot command.
|
660
|
-
|
661
|
-
Require `slack-ruby-bot/rspec` in your `spec_helper.rb` along with the following dependencies in Gemfile.
|
662
|
-
|
663
|
-
```ruby
|
664
|
-
group :development, :test do
|
665
|
-
gem 'rack-test'
|
666
|
-
gem 'rspec'
|
667
|
-
gem 'vcr'
|
668
|
-
gem 'webmock'
|
669
|
-
end
|
670
|
-
```
|
671
|
-
|
672
|
-
### Useful Libraries
|
673
|
-
|
674
|
-
* [newrelic-slack-ruby-bot](https://github.com/dblock/newrelic-slack-ruby-bot): NewRelic instrumentation for slack-ruby-bot.
|
58
|
+
After [registering the bot](DEPLOYMENT.md), run with `SLACK_API_TOKEN=... bundle exec ruby pongbot.rb`. Have the bot join a channel and send it a ping.
|
675
59
|
|
676
|
-
|
60
|
+

|
677
61
|
|
678
|
-
|
62
|
+
### A Production Bot
|
679
63
|
|
680
|
-
## Upgrading
|
681
64
|
|
682
|
-
|
65
|
+
### More Involved Examples
|
683
66
|
|
684
|
-
## Copyright and License
|
685
67
|
|
686
|
-
|
68
|
+
### Commands and Operators
|
687
69
|
|
688
|
-
This project is licensed under the [MIT License](LICENSE.md).
|
data/lib/slack_respondent.rb
CHANGED
@@ -5,7 +5,8 @@ require 'active_support/core_ext'
|
|
5
5
|
require_relative 'slack_respondent/support/loggable'
|
6
6
|
require_relative 'slack_respondent/commands'
|
7
7
|
require_relative 'slack_respondent/reactions'
|
8
|
-
require_relative 'slack_respondent/
|
8
|
+
require_relative 'slack_respondent/client_wrapper'
|
9
|
+
require_relative 'slack_respondent/client_provider'
|
9
10
|
require_relative 'slack_respondent/config'
|
10
11
|
|
11
12
|
module SlackRespondent
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module SlackRespondent
|
2
2
|
class ClientWrapper
|
3
3
|
include Loggable
|
4
|
+
|
4
5
|
attr_accessor :aliases
|
5
|
-
attr_accessor :send_gifs
|
6
6
|
attr_accessor :names
|
7
7
|
attr_reader :client
|
8
8
|
|
@@ -55,7 +55,7 @@ module SlackRespondent
|
|
55
55
|
private
|
56
56
|
|
57
57
|
def client
|
58
|
-
@client ||=
|
58
|
+
@client ||= ClientProvider.client.new
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slack-ruby-bot-boilerplate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilyas Valiullov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hashie
|
@@ -146,7 +146,8 @@ files:
|
|
146
146
|
- lib/config/boot.rb
|
147
147
|
- lib/config/environment.rb
|
148
148
|
- lib/slack_respondent.rb
|
149
|
-
- lib/slack_respondent/
|
149
|
+
- lib/slack_respondent/client_provider.rb
|
150
|
+
- lib/slack_respondent/client_wrapper.rb
|
150
151
|
- lib/slack_respondent/commands.rb
|
151
152
|
- lib/slack_respondent/commands/about.rb
|
152
153
|
- lib/slack_respondent/commands/base.rb
|