slack-ruby-bot 0.4.5 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +16 -11
- data/CHANGELOG.md +11 -0
- data/README.md +18 -2
- data/TUTORIAL.md +1 -1
- data/examples/weather/Procfile +1 -0
- data/lib/initializers/giphy.rb +2 -0
- data/lib/slack-ruby-bot.rb +3 -0
- data/lib/slack-ruby-bot/app.rb +8 -74
- data/lib/slack-ruby-bot/client.rb +31 -0
- data/lib/slack-ruby-bot/commands/base.rb +10 -9
- data/lib/slack-ruby-bot/commands/unknown.rb +1 -1
- data/lib/slack-ruby-bot/config.rb +22 -4
- data/lib/slack-ruby-bot/hooks/hello.rb +2 -2
- data/lib/slack-ruby-bot/hooks/message.rb +1 -1
- data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/it_behaves_like_a_slack_bot.rb +0 -3
- data/lib/slack-ruby-bot/server.rb +101 -0
- data/lib/slack-ruby-bot/version.rb +1 -1
- data/slack-ruby-bot.gemspec +2 -2
- data/spec/slack-ruby-bot/app_spec.rb +0 -23
- data/spec/slack-ruby-bot/commands/commands_regexp_escape_spec.rb +17 -0
- data/spec/slack-ruby-bot/commands/send_message_with_gif_spec.rb +28 -0
- data/spec/slack-ruby-bot/commands/unknown_spec.rb +3 -0
- data/spec/slack-ruby-bot/config_spec.rb +55 -0
- data/spec/slack-ruby-bot/server_spec.rb +56 -0
- metadata +22 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b416684447c6fb8687ce155f3c86527b82f8e007
|
4
|
+
data.tar.gz: d7d4d84f063816712410b913187089d6fda8a62f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eed29c1effba3bc4d3c27a1dada0665cddf57fe785487c45c418f7e7ce3cc2af7c9b4b78ae4a2ac2f1f70875c73f7a6ebf27ccd9249d0f8c2c5674828c687e8e
|
7
|
+
data.tar.gz: cbbc3f361dc13b115a348d2817f62b9702baeac5888398e799b300875d45124d248c2fac142730363fc5011ce48e78f1dd093c538a159157c7d062ace6a6391b
|
data/.rubocop_todo.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# This configuration was generated by `rubocop --auto-gen-config`
|
2
|
-
# on 2015-
|
2
|
+
# on 2015-11-26 14:43:46 +0900 using RuboCop version 0.32.1.
|
3
3
|
# The point is for the user to remove these configuration records
|
4
4
|
# one by one as the offenses are removed from the code base.
|
5
5
|
# Note that changes in the inspected code, or installation of new
|
@@ -13,29 +13,34 @@ Lint/HandleExceptions:
|
|
13
13
|
Lint/UselessAccessModifier:
|
14
14
|
Enabled: false
|
15
15
|
|
16
|
-
# Offense count:
|
16
|
+
# Offense count: 8
|
17
17
|
Metrics/AbcSize:
|
18
|
-
Max:
|
18
|
+
Max: 27
|
19
19
|
|
20
|
-
# Offense count:
|
20
|
+
# Offense count: 1
|
21
|
+
# Configuration parameters: CountComments.
|
22
|
+
Metrics/ClassLength:
|
23
|
+
Max: 101
|
24
|
+
|
25
|
+
# Offense count: 3
|
21
26
|
Metrics/CyclomaticComplexity:
|
22
|
-
Max:
|
27
|
+
Max: 8
|
23
28
|
|
24
|
-
# Offense count:
|
29
|
+
# Offense count: 96
|
25
30
|
# Configuration parameters: AllowURI, URISchemes.
|
26
31
|
Metrics/LineLength:
|
27
|
-
Max:
|
32
|
+
Max: 145
|
28
33
|
|
29
|
-
# Offense count:
|
34
|
+
# Offense count: 5
|
30
35
|
# Configuration parameters: CountComments.
|
31
36
|
Metrics/MethodLength:
|
32
|
-
Max:
|
37
|
+
Max: 22
|
33
38
|
|
34
|
-
# Offense count:
|
39
|
+
# Offense count: 2
|
35
40
|
Metrics/PerceivedComplexity:
|
36
41
|
Max: 8
|
37
42
|
|
38
|
-
# Offense count:
|
43
|
+
# Offense count: 17
|
39
44
|
Style/Documentation:
|
40
45
|
Enabled: false
|
41
46
|
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
### 0.5.0 (12/7/2015)
|
2
|
+
|
3
|
+
* Disable animated GIFs via `SlackRubyBot::Config.send_gifs` or ENV['SLACK_RUBY_BOT_SEND_GIFS'] - [@dblock](https://github.com/dblock).
|
4
|
+
* `SlackRubyBot::Server` supports `restart!` with retry - [@dblock](https://github.com/dblock).
|
5
|
+
* `SlackRubyBot::Server` publicly supports `auth!`, `start!` and `start_async` that make up a `run` loop - [@dblock](https://github.com/dblock).
|
6
|
+
* Extracted `SlackRubyBot::Server` from `SlackRubyBot::App` - [@dblock](https://github.com/dblock).
|
7
|
+
* Fix: explicitly require 'giphy' - [@dblock](https://github.com/dblock).
|
8
|
+
* Fix: undefined method `stop` for `Slack::RealTime::Client` - [@dblock](https://github.com/dblock).
|
9
|
+
* [#29](https://github.com/dblock/slack-ruby-bot/pull/29): Fixed bot failing to correctly respond to unknown commands when queried with format `@botname` - [@crayment](https://github.com/crayment).
|
10
|
+
* [#30](https://github.com/dblock/slack-ruby-bot/pull/30): Fix RegexpError when parsing command - [@kuboshizuma](https://github.com/kuboshizuma).
|
11
|
+
|
1
12
|
### 0.4.5 (10/29/2015)
|
2
13
|
|
3
14
|
* [#23](https://github.com/dblock/slack-ruby-bot/pull/23): Fixed `match` that forced bot name into the expression being evaluated - [@dblock](https://github.com/dblock).
|
data/README.md
CHANGED
@@ -2,12 +2,17 @@ Slack-Ruby-Bot
|
|
2
2
|
==============
|
3
3
|
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/slack-ruby-bot.svg)](http://badge.fury.io/rb/slack-ruby-bot)
|
5
|
-
[![Build Status](https://travis-ci.org/dblock/slack-ruby-bot.
|
5
|
+
[![Build Status](https://travis-ci.org/dblock/slack-ruby-bot.svg)](https://travis-ci.org/dblock/slack-ruby-bot)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/dblock/slack-ruby-bot/badges/gpa.svg)](https://codeclimate.com/github/dblock/slack-ruby-bot)
|
6
7
|
|
7
8
|
A generic Slack bot framework written in Ruby on top of [slack-ruby-client](https://github.com/dblock/slack-ruby-client). This library does all the heavy lifting, such as message parsing, so you can focus on implementing slack bot commands. It also attempts to introduce the bare minimum number of requirements or any sorts of limitations. It's a Slack bot boilerplate.
|
8
9
|
|
9
10
|
![](slack.png)
|
10
11
|
|
12
|
+
## Stable Release
|
13
|
+
|
14
|
+
You're reading the documentation for the [stable release, v0.5.0](https://github.com/dblock/slack-ruby-bot/tree/v0.5.0).
|
15
|
+
|
11
16
|
## Usage
|
12
17
|
|
13
18
|
### A Minimal Bot
|
@@ -49,8 +54,9 @@ A typical production Slack bot is a combination of a vanilla web server and a we
|
|
49
54
|
|
50
55
|
### More Involved Examples
|
51
56
|
|
52
|
-
The following examples of
|
57
|
+
The following examples of bots based on slack-ruby-bot are listed in growing order of complexity.
|
53
58
|
|
59
|
+
* [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.
|
54
60
|
* [slack-mathbot](https://github.com/dblock/slack-mathbot): Slack integration with math.
|
55
61
|
* [slack-google-bot](https://github.com/dblock/slack-google-bot): A Slack bot that searches Google, including CSE.
|
56
62
|
* [slack-aws](https://github.com/dblock/slack-aws): Slack integration with Amazon Web Services.
|
@@ -195,6 +201,16 @@ module MyBot
|
|
195
201
|
end
|
196
202
|
```
|
197
203
|
|
204
|
+
### Disable Animated GIFs
|
205
|
+
|
206
|
+
By default bots send animated GIFs in default commands and errors. To disable animated GIFs set `send_gifs` or `ENV['SLACK_RUBY_BOT_SEND_GIFS']` to `false`.
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
SlackRubyBot.configure do |config|
|
210
|
+
config.send_gifs = false
|
211
|
+
end
|
212
|
+
```
|
213
|
+
|
198
214
|
### Message Loop Protection
|
199
215
|
|
200
216
|
By default bots do not respond to their own messages. If you wish to change that behavior, set `allow_message_loops` to `true`.
|
data/TUTORIAL.md
CHANGED
@@ -87,7 +87,7 @@ end
|
|
87
87
|
|
88
88
|
#### Config.ru
|
89
89
|
|
90
|
-
Tie all the pieces
|
90
|
+
Tie all the pieces together in `config.ru` which creates a thread for the bot and runs the web server on the main thread.
|
91
91
|
|
92
92
|
```ruby
|
93
93
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
@@ -0,0 +1 @@
|
|
1
|
+
console: bundle exec ruby weatherbot.rb
|
data/lib/initializers/giphy.rb
CHANGED
data/lib/slack-ruby-bot.rb
CHANGED
data/lib/slack-ruby-bot/app.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
module SlackRubyBot
|
2
|
-
class App
|
3
|
-
cattr_accessor :hooks
|
4
|
-
|
5
|
-
include SlackRubyBot::Hooks::Hello
|
6
|
-
include SlackRubyBot::Hooks::Message
|
7
|
-
|
2
|
+
class App < Server
|
8
3
|
def initialize
|
9
4
|
SlackRubyBot.configure do |config|
|
10
5
|
config.token = ENV['SLACK_API_TOKEN'] || fail("Missing ENV['SLACK_API_TOKEN'].")
|
@@ -23,82 +18,21 @@ module SlackRubyBot
|
|
23
18
|
@instance ||= SlackRubyBot::App.new
|
24
19
|
end
|
25
20
|
|
26
|
-
def run
|
27
|
-
auth!
|
28
|
-
start!
|
29
|
-
end
|
30
|
-
|
31
|
-
def stop!
|
32
|
-
client.stop
|
33
|
-
end
|
34
|
-
|
35
21
|
private
|
36
22
|
|
37
|
-
def logger
|
38
|
-
@logger ||= begin
|
39
|
-
$stdout.sync = true
|
40
|
-
Logger.new(STDOUT)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def start!
|
45
|
-
loop do
|
46
|
-
begin
|
47
|
-
client.start!
|
48
|
-
rescue Slack::Web::Api::Error => e
|
49
|
-
logger.error e
|
50
|
-
case e.message
|
51
|
-
when 'migration_in_progress'
|
52
|
-
sleep 1 # ignore, try again
|
53
|
-
else
|
54
|
-
raise e
|
55
|
-
end
|
56
|
-
rescue Faraday::Error::TimeoutError, Faraday::Error::ConnectionFailed, Faraday::Error::SSLError => e
|
57
|
-
logger.error e
|
58
|
-
sleep 1 # ignore, try again
|
59
|
-
rescue StandardError => e
|
60
|
-
logger.error e
|
61
|
-
raise e
|
62
|
-
ensure
|
63
|
-
@client = nil
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def client
|
69
|
-
@client ||= begin
|
70
|
-
client = Slack::RealTime::Client.new
|
71
|
-
hooks.each do |hook|
|
72
|
-
client.on hook do |data|
|
73
|
-
begin
|
74
|
-
send hook, client, data
|
75
|
-
rescue StandardError => e
|
76
|
-
logger.error e
|
77
|
-
begin
|
78
|
-
client.message(channel: data['channel'], text: e.message) if data.key?('channel')
|
79
|
-
rescue
|
80
|
-
# ignore
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
client
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
23
|
def auth!
|
90
|
-
|
24
|
+
super
|
91
25
|
SlackRubyBot.configure do |config|
|
92
|
-
config.url = auth['url']
|
93
|
-
config.team = auth['team']
|
94
|
-
config.user = auth['user']
|
95
|
-
config.team_id = auth['team_id']
|
96
|
-
config.user_id = auth['user_id']
|
26
|
+
config.url = client.auth['url']
|
27
|
+
config.team = client.auth['team']
|
28
|
+
config.user = client.auth['user']
|
29
|
+
config.team_id = client.auth['team_id']
|
30
|
+
config.user_id = client.auth['user_id']
|
97
31
|
end
|
98
|
-
logger.info "Welcome '#{SlackRubyBot.config.user}' to the '#{SlackRubyBot.config.team}' team at #{SlackRubyBot.config.url}."
|
99
32
|
end
|
100
33
|
|
101
34
|
def reset!
|
35
|
+
super
|
102
36
|
SlackRubyBot.configure do |config|
|
103
37
|
config.url = nil
|
104
38
|
config.team = nil
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SlackRubyBot
|
2
|
+
class Client < Slack::RealTime::Client
|
3
|
+
attr_accessor :auth
|
4
|
+
|
5
|
+
def names
|
6
|
+
[
|
7
|
+
SlackRubyBot::Config.user,
|
8
|
+
auth ? auth['user'] : nil,
|
9
|
+
SlackRubyBot::Config.aliases,
|
10
|
+
auth ? "<@#{auth['user_id'].downcase}>" : nil,
|
11
|
+
SlackRubyBot::Config.user_id ? "<@#{SlackRubyBot::Config.user_id.downcase}>" : nil,
|
12
|
+
auth ? "<@#{auth['user_id'].downcase}>:" : nil,
|
13
|
+
SlackRubyBot::Config.user_id ? "<@#{SlackRubyBot::Config.user_id.downcase}>:" : nil,
|
14
|
+
auth ? "#{auth['user']}:" : nil,
|
15
|
+
SlackRubyBot::Config.user ? "#{SlackRubyBot::Config.user}:" : nil
|
16
|
+
].compact.flatten
|
17
|
+
end
|
18
|
+
|
19
|
+
def name?(name)
|
20
|
+
name && names.include?(name.downcase)
|
21
|
+
end
|
22
|
+
|
23
|
+
def name
|
24
|
+
SlackRubyBot.config.user || (auth && auth['user'])
|
25
|
+
end
|
26
|
+
|
27
|
+
def url
|
28
|
+
SlackRubyBot.config.url || (auth && auth['url'])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -47,20 +47,21 @@ module SlackRubyBot
|
|
47
47
|
|
48
48
|
def self.command(*values, &block)
|
49
49
|
values.each do |value|
|
50
|
-
|
51
|
-
match Regexp.new("^(?<bot>[\\w[:punct:]@<>]*)[\\s]+(?<command>#{
|
50
|
+
escaped = Regexp.escape(value)
|
51
|
+
match Regexp.new("^(?<bot>[\\w[:punct:]@<>]*)[\\s]+(?<command>#{escaped})$", Regexp::IGNORECASE), &block
|
52
|
+
match Regexp.new("^(?<bot>[\\w[:punct:]@<>]*)[\\s]+(?<command>#{escaped})[\\s]+(?<expression>.*)$", Regexp::IGNORECASE), &block
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
55
56
|
def self.invoke(client, data)
|
56
57
|
self.finalize_routes!
|
57
|
-
expression, text = parse(data)
|
58
|
+
expression, text = parse(client, data)
|
58
59
|
called = false
|
59
60
|
routes.each_pair do |route, method|
|
60
61
|
match = route.match(expression)
|
61
62
|
match ||= route.match(text) if text
|
62
63
|
next unless match
|
63
|
-
next if match.names.include?('bot') && !
|
64
|
+
next if match.names.include?('bot') && !client.name?(match['bot'])
|
64
65
|
called = true
|
65
66
|
if method
|
66
67
|
method.call(client, data, match)
|
@@ -81,15 +82,15 @@ module SlackRubyBot
|
|
81
82
|
|
82
83
|
private
|
83
84
|
|
84
|
-
def self.parse(data)
|
85
|
+
def self.parse(client, data)
|
85
86
|
text = data.text
|
86
87
|
return text unless data.channel && data.channel[0] == 'D' && data.user && data.user != SlackRubyBot.config.user_id
|
87
|
-
|
88
|
+
client.names.each do |name|
|
88
89
|
text.downcase.tap do |td|
|
89
90
|
return text if td == name || td.starts_with?("#{name} ")
|
90
91
|
end
|
91
92
|
end
|
92
|
-
["#{
|
93
|
+
["#{client.name} #{text}", text]
|
93
94
|
end
|
94
95
|
|
95
96
|
def self.finalize_routes!
|
@@ -99,13 +100,13 @@ module SlackRubyBot
|
|
99
100
|
|
100
101
|
def self.get_gif_and_send(options = {})
|
101
102
|
options = options.dup
|
103
|
+
keywords = options.delete(:keywords)
|
102
104
|
gif = begin
|
103
|
-
keywords = options.delete(:keywords)
|
104
105
|
Giphy.random(keywords)
|
105
106
|
rescue StandardError => e
|
106
107
|
logger.warn "Giphy.random: #{e.message}"
|
107
108
|
nil
|
108
|
-
end
|
109
|
+
end if SlackRubyBot::Config.send_gifs?
|
109
110
|
client = options.delete(:client)
|
110
111
|
text = options.delete(:text)
|
111
112
|
text = [text, gif && gif.image_url.to_s].compact.join("\n")
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module SlackRubyBot
|
2
2
|
module Commands
|
3
3
|
class Unknown < Base
|
4
|
-
match(/^(?<bot>\
|
4
|
+
match(/^(?<bot>\S*)[\s]*(?<expression>.*)$/)
|
5
5
|
|
6
6
|
def self.call(client, data, _match)
|
7
7
|
send_message_with_gif client, data.channel, "Sorry <@#{data.user}>, I don't understand that command!", 'idiot'
|
@@ -10,13 +10,31 @@ module SlackRubyBot
|
|
10
10
|
attr_accessor :team
|
11
11
|
attr_accessor :team_id
|
12
12
|
attr_accessor :allow_message_loops
|
13
|
+
attr_accessor :send_gifs
|
13
14
|
|
14
|
-
def
|
15
|
-
|
15
|
+
def allow_message_loops?
|
16
|
+
allow_message_loops
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
-
|
19
|
+
def send_gifs?
|
20
|
+
v = boolean_from_env('SLACK_RUBY_BOT_SEND_GIFS')
|
21
|
+
v.nil? ? (send_gifs.nil? || send_gifs) : v
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def boolean_from_env(key)
|
27
|
+
value = ENV[key]
|
28
|
+
case value
|
29
|
+
when nil
|
30
|
+
nil
|
31
|
+
when 0, 'false', 'no'
|
32
|
+
false
|
33
|
+
when 1, 'true', 'yes'
|
34
|
+
true
|
35
|
+
else
|
36
|
+
fail ArgumentError, "Invalid value for #{key}: #{value}."
|
37
|
+
end
|
20
38
|
end
|
21
39
|
end
|
22
40
|
end
|
@@ -5,7 +5,7 @@ module SlackRubyBot
|
|
5
5
|
|
6
6
|
def message(client, data)
|
7
7
|
data = Hashie::Mash.new(data)
|
8
|
-
return if !SlackRubyBot::Config.allow_message_loops && (client.self && client.self['id'] == data.user)
|
8
|
+
return if !SlackRubyBot::Config.allow_message_loops? && (client.self && client.self['id'] == data.user)
|
9
9
|
data.text.strip! if data.text
|
10
10
|
result = child_command_classes.detect { |d| d.invoke(client, data) }
|
11
11
|
result ||= built_in_command_classes.detect { |d| d.invoke(client, data) }
|
@@ -15,9 +15,6 @@ shared_examples 'a slack ruby bot' do
|
|
15
15
|
before do
|
16
16
|
subject.send(:auth!)
|
17
17
|
end
|
18
|
-
after do
|
19
|
-
subject.send(:reset!)
|
20
|
-
end
|
21
18
|
it 'succeeds auth' do
|
22
19
|
expect(subject.config.url).to eq 'https://rubybot.slack.com/'
|
23
20
|
expect(subject.config.team).to eq 'team_name'
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module SlackRubyBot
|
2
|
+
class Server
|
3
|
+
cattr_accessor :hooks
|
4
|
+
attr_accessor :token
|
5
|
+
|
6
|
+
include SlackRubyBot::Hooks::Hello
|
7
|
+
include SlackRubyBot::Hooks::Message
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@token = options[:token]
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
auth!
|
15
|
+
loop do
|
16
|
+
begin
|
17
|
+
start!
|
18
|
+
rescue Slack::Web::Api::Error => e
|
19
|
+
logger.error e
|
20
|
+
case e.message
|
21
|
+
when 'migration_in_progress'
|
22
|
+
sleep 1 # ignore, try again
|
23
|
+
else
|
24
|
+
raise e
|
25
|
+
end
|
26
|
+
rescue Faraday::Error::TimeoutError, Faraday::Error::ConnectionFailed, Faraday::Error::SSLError => e
|
27
|
+
logger.error e
|
28
|
+
sleep 1 # ignore, try again
|
29
|
+
rescue StandardError => e
|
30
|
+
logger.error e
|
31
|
+
raise e
|
32
|
+
ensure
|
33
|
+
@client = nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def auth!
|
39
|
+
client.auth = client.web_client.auth_test
|
40
|
+
logger.info "Welcome '#{client.auth['user']}' to the '#{client.auth['team']}' team at #{client.auth['url']}."
|
41
|
+
end
|
42
|
+
|
43
|
+
def start!
|
44
|
+
@stopping = false
|
45
|
+
client.start!
|
46
|
+
end
|
47
|
+
|
48
|
+
def start_async
|
49
|
+
@stopping = false
|
50
|
+
client.start_async
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop!
|
54
|
+
@stopping = true
|
55
|
+
client.stop! if @client
|
56
|
+
end
|
57
|
+
|
58
|
+
def restart!(wait = 1)
|
59
|
+
start!
|
60
|
+
rescue StandardError => e
|
61
|
+
sleep wait
|
62
|
+
logger.error "#{e.message}, reconnecting in #{wait} second(s)."
|
63
|
+
logger.debug e
|
64
|
+
restart! [wait * 2, 60].min
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def logger
|
70
|
+
@logger ||= begin
|
71
|
+
$stdout.sync = true
|
72
|
+
Logger.new(STDOUT)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def client
|
77
|
+
@client ||= begin
|
78
|
+
client = SlackRubyBot::Client.new(token: token)
|
79
|
+
client.on :close do |_data|
|
80
|
+
@client = nil
|
81
|
+
restart! unless @stopping
|
82
|
+
end
|
83
|
+
hooks.each do |hook|
|
84
|
+
client.on hook do |data|
|
85
|
+
begin
|
86
|
+
send hook, client, data
|
87
|
+
rescue StandardError => e
|
88
|
+
logger.error e
|
89
|
+
begin
|
90
|
+
client.message(channel: data['channel'], text: e.message) if data.key?('channel')
|
91
|
+
rescue
|
92
|
+
# ignore
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
client
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/slack-ruby-bot.gemspec
CHANGED
@@ -15,10 +15,10 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.licenses = ['MIT']
|
16
16
|
s.summary = 'The easiest way to write a Slack bot in Ruby.'
|
17
17
|
s.add_dependency 'hashie'
|
18
|
-
s.add_dependency 'slack-ruby-client'
|
18
|
+
s.add_dependency 'slack-ruby-client', '>= 0.5.0'
|
19
|
+
s.add_dependency 'faye-websocket'
|
19
20
|
s.add_dependency 'activesupport'
|
20
21
|
s.add_dependency 'giphy', '~> 2.0.2'
|
21
|
-
s.add_dependency 'websocket-driver', '~> 0.5.4'
|
22
22
|
s.add_development_dependency 'rake'
|
23
23
|
s.add_development_dependency 'rspec'
|
24
24
|
s.add_development_dependency 'rack-test'
|
@@ -5,27 +5,4 @@ describe SlackRubyBot::App do
|
|
5
5
|
SlackRubyBot::App.new
|
6
6
|
end
|
7
7
|
it_behaves_like 'a slack ruby bot'
|
8
|
-
context 'retries on rtm.start errors' do
|
9
|
-
let(:client) { Slack::RealTime::Client.new }
|
10
|
-
let(:logger) { subject.send :logger }
|
11
|
-
before do
|
12
|
-
allow(subject).to receive(:sleep)
|
13
|
-
expect(Slack::RealTime::Client).to receive(:new).twice.and_return(client)
|
14
|
-
expect(logger).to receive(:error).twice
|
15
|
-
end
|
16
|
-
it 'migration_in_progress', vcr: { cassette_name: 'migration_in_progress' } do
|
17
|
-
expect do
|
18
|
-
subject.send :start!
|
19
|
-
end.to raise_error Slack::Web::Api::Error, 'unknown'
|
20
|
-
end
|
21
|
-
[Faraday::Error::ConnectionFailed, Faraday::Error::TimeoutError, Faraday::Error::SSLError].each do |err|
|
22
|
-
it "#{err}" do
|
23
|
-
expect(client).to receive(:start!) { fail err, 'Faraday' }
|
24
|
-
expect(client).to receive(:start!) { fail 'unknown' }
|
25
|
-
expect do
|
26
|
-
subject.send :start!
|
27
|
-
end.to raise_error 'unknown'
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
8
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SlackRubyBot::Commands do
|
4
|
+
let! :command do
|
5
|
+
Class.new(SlackRubyBot::Commands::Base) do
|
6
|
+
command '(' do |client, data, match|
|
7
|
+
send_message client, data.channel, "#{match[:command]}: #{match[:expression]}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
def app
|
12
|
+
SlackRubyBot::App.new
|
13
|
+
end
|
14
|
+
it 'does not return error' do
|
15
|
+
expect(message: "#{SlackRubyBot.config.user} ( /").to respond_with_slack_message('(: /')
|
16
|
+
end
|
17
|
+
end
|
@@ -29,4 +29,32 @@ describe SlackRubyBot::Commands do
|
|
29
29
|
expect(SlackRubyBot::Commands::Base).to receive(:send_client_message).with(client, channel: 'channel', text: 'message')
|
30
30
|
app.send(:message, client, text: "#{SlackRubyBot.config.user} send_message_with_gif_spec message", channel: 'channel', user: 'user')
|
31
31
|
end
|
32
|
+
context 'send_gifs' do
|
33
|
+
context 'set to false' do
|
34
|
+
before do
|
35
|
+
SlackRubyBot::Config.send_gifs = false
|
36
|
+
end
|
37
|
+
it 'does not send a gif' do
|
38
|
+
expect(Giphy).to_not receive(:random)
|
39
|
+
expect(SlackRubyBot::Commands::Base).to receive(:send_client_message).with(client, channel: 'channel', text: 'message')
|
40
|
+
app.send(:message, client, text: "#{SlackRubyBot.config.user} send_message_with_gif_spec message", channel: 'channel', user: 'user')
|
41
|
+
end
|
42
|
+
after do
|
43
|
+
SlackRubyBot::Config.send_gifs = nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
context 'set to false via SLACK_RUBY_BOT_SEND_GIFS' do
|
47
|
+
before do
|
48
|
+
ENV['SLACK_RUBY_BOT_SEND_GIFS'] = 'false'
|
49
|
+
end
|
50
|
+
it 'does not send a gif' do
|
51
|
+
expect(Giphy).to_not receive(:random)
|
52
|
+
expect(SlackRubyBot::Commands::Base).to receive(:send_client_message).with(client, channel: 'channel', text: 'message')
|
53
|
+
app.send(:message, client, text: "#{SlackRubyBot.config.user} send_message_with_gif_spec message", channel: 'channel', user: 'user')
|
54
|
+
end
|
55
|
+
after do
|
56
|
+
ENV.delete 'SLACK_RUBY_BOT_SEND_GIFS'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
32
60
|
end
|
@@ -8,6 +8,9 @@ describe SlackRubyBot::Commands::Unknown do
|
|
8
8
|
it 'invalid command' do
|
9
9
|
expect(message: "#{SlackRubyBot.config.user} foobar").to respond_with_slack_message("Sorry <@user>, I don't understand that command!")
|
10
10
|
end
|
11
|
+
it 'invalid command with @bot:' do
|
12
|
+
expect(message: "<@#{SlackRubyBot.config.user_id}>: foobar").to respond_with_slack_message("Sorry <@user>, I don't understand that command!")
|
13
|
+
end
|
11
14
|
it 'does not respond to sad face' do
|
12
15
|
expect(SlackRubyBot::Commands::Base).to_not receive(:send_message)
|
13
16
|
SlackRubyBot::App.new.send(:message, client, text: ':((')
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SlackRubyBot::Config do
|
4
|
+
context 'send_gifs?' do
|
5
|
+
context 'default' do
|
6
|
+
it 'is true' do
|
7
|
+
expect(SlackRubyBot::Config.send_gifs?).to be true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
context 'set to false' do
|
11
|
+
before do
|
12
|
+
SlackRubyBot::Config.send_gifs = false
|
13
|
+
end
|
14
|
+
it 'is false' do
|
15
|
+
expect(SlackRubyBot::Config.send_gifs?).to be false
|
16
|
+
end
|
17
|
+
after do
|
18
|
+
SlackRubyBot::Config.send_gifs = nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
context 'set to false via SLACK_RUBY_BOT_SEND_GIFS' do
|
22
|
+
before do
|
23
|
+
ENV['SLACK_RUBY_BOT_SEND_GIFS'] = 'false'
|
24
|
+
end
|
25
|
+
it 'is false' do
|
26
|
+
expect(SlackRubyBot::Config.send_gifs?).to be false
|
27
|
+
end
|
28
|
+
after do
|
29
|
+
ENV.delete 'SLACK_RUBY_BOT_SEND_GIFS'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
context 'set to true' do
|
33
|
+
before do
|
34
|
+
SlackRubyBot::Config.send_gifs = true
|
35
|
+
end
|
36
|
+
it 'is false' do
|
37
|
+
expect(SlackRubyBot::Config.send_gifs?).to be true
|
38
|
+
end
|
39
|
+
after do
|
40
|
+
SlackRubyBot::Config.send_gifs = nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
context 'set to true via SLACK_RUBY_BOT_SEND_GIFS' do
|
44
|
+
before do
|
45
|
+
ENV['SLACK_RUBY_BOT_SEND_GIFS'] = 'true'
|
46
|
+
end
|
47
|
+
it 'is false' do
|
48
|
+
expect(SlackRubyBot::Config.send_gifs?).to be true
|
49
|
+
end
|
50
|
+
after do
|
51
|
+
ENV.delete 'SLACK_RUBY_BOT_SEND_GIFS'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SlackRubyBot::Server do
|
4
|
+
context 'with a token' do
|
5
|
+
let(:client) { Slack::RealTime::Client.new }
|
6
|
+
let(:logger) { subject.send :logger }
|
7
|
+
subject do
|
8
|
+
SlackRubyBot::Server.new(token: 'token')
|
9
|
+
end
|
10
|
+
before do
|
11
|
+
allow(subject).to receive(:sleep)
|
12
|
+
allow(logger).to receive(:error)
|
13
|
+
end
|
14
|
+
it 'creates a client with a token' do
|
15
|
+
expect(client).to receive(:start!) { fail 'expected' }
|
16
|
+
expect(Slack::RealTime::Client).to receive(:new).with(token: 'token').and_return(client)
|
17
|
+
expect { subject.start! }.to raise_error RuntimeError, 'expected'
|
18
|
+
end
|
19
|
+
it 'asynchronously creates a client with a token' do
|
20
|
+
expect(client).to receive(:start_async) { fail 'expected' }
|
21
|
+
expect(Slack::RealTime::Client).to receive(:new).with(token: 'token').and_return(client)
|
22
|
+
expect { subject.start_async }.to raise_error RuntimeError, 'expected'
|
23
|
+
end
|
24
|
+
it 'stops client' do
|
25
|
+
expect(Slack::RealTime::Client).to receive(:new).with(token: 'token').and_return(client)
|
26
|
+
expect(subject.send(:client)).to_not be nil
|
27
|
+
expect(client).to receive(:started?).and_return(true)
|
28
|
+
subject.stop!
|
29
|
+
end
|
30
|
+
end
|
31
|
+
context 'retries on rtm.start errors' do
|
32
|
+
let(:client) { Slack::RealTime::Client.new }
|
33
|
+
let(:logger) { subject.send :logger }
|
34
|
+
before do
|
35
|
+
allow(subject).to receive(:auth!)
|
36
|
+
allow(subject).to receive(:sleep)
|
37
|
+
allow(SlackRubyBot::Server).to receive(:auth!)
|
38
|
+
expect(Slack::RealTime::Client).to receive(:new).twice.and_return(client)
|
39
|
+
expect(logger).to receive(:error).twice
|
40
|
+
end
|
41
|
+
it 'migration_in_progress', vcr: { cassette_name: 'migration_in_progress' } do
|
42
|
+
expect do
|
43
|
+
subject.run
|
44
|
+
end.to raise_error Slack::Web::Api::Error, 'unknown'
|
45
|
+
end
|
46
|
+
[Faraday::Error::ConnectionFailed, Faraday::Error::TimeoutError, Faraday::Error::SSLError].each do |err|
|
47
|
+
it "#{err}" do
|
48
|
+
expect(client).to receive(:start!) { fail err, 'Faraday' }
|
49
|
+
expect(client).to receive(:start!) { fail 'unknown' }
|
50
|
+
expect do
|
51
|
+
subject.run
|
52
|
+
end.to raise_error 'unknown'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slack-ruby-bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Doubrovkine
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hashie
|
@@ -30,16 +30,16 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.5.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 0.5.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: faye-websocket
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -53,33 +53,33 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: activesupport
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: '0'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: giphy
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.
|
75
|
+
version: 2.0.2
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
82
|
+
version: 2.0.2
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rake
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -186,10 +186,10 @@ files:
|
|
186
186
|
- TUTORIAL.md
|
187
187
|
- UPGRADING.md
|
188
188
|
- examples/minimal/Gemfile
|
189
|
-
- examples/minimal/Gemfile.lock
|
190
189
|
- examples/minimal/Procfile
|
191
190
|
- examples/minimal/pongbot.rb
|
192
191
|
- examples/weather/Gemfile
|
192
|
+
- examples/weather/Procfile
|
193
193
|
- examples/weather/weatherbot.rb
|
194
194
|
- lib/config/application.rb
|
195
195
|
- lib/config/boot.rb
|
@@ -198,6 +198,7 @@ files:
|
|
198
198
|
- lib/slack-ruby-bot.rb
|
199
199
|
- lib/slack-ruby-bot/about.rb
|
200
200
|
- lib/slack-ruby-bot/app.rb
|
201
|
+
- lib/slack-ruby-bot/client.rb
|
201
202
|
- lib/slack-ruby-bot/commands.rb
|
202
203
|
- lib/slack-ruby-bot/commands/about.rb
|
203
204
|
- lib/slack-ruby-bot/commands/base.rb
|
@@ -218,6 +219,7 @@ files:
|
|
218
219
|
- lib/slack-ruby-bot/rspec/support/slack_api_key.rb
|
219
220
|
- lib/slack-ruby-bot/rspec/support/slack_ruby_bot_configure.rb
|
220
221
|
- lib/slack-ruby-bot/rspec/support/vcr.rb
|
222
|
+
- lib/slack-ruby-bot/server.rb
|
221
223
|
- lib/slack-ruby-bot/version.rb
|
222
224
|
- lib/slack_ruby_bot.rb
|
223
225
|
- screenshots/aliases.gif
|
@@ -231,6 +233,7 @@ files:
|
|
231
233
|
- spec/slack-ruby-bot/commands/about_spec.rb
|
232
234
|
- spec/slack-ruby-bot/commands/aliases_spec.rb
|
233
235
|
- spec/slack-ruby-bot/commands/commands_precedence_spec.rb
|
236
|
+
- spec/slack-ruby-bot/commands/commands_regexp_escape_spec.rb
|
234
237
|
- spec/slack-ruby-bot/commands/commands_spaces_spec.rb
|
235
238
|
- spec/slack-ruby-bot/commands/commands_spec.rb
|
236
239
|
- spec/slack-ruby-bot/commands/commands_with_block_spec.rb
|
@@ -248,7 +251,9 @@ files:
|
|
248
251
|
- spec/slack-ruby-bot/commands/send_message_spec.rb
|
249
252
|
- spec/slack-ruby-bot/commands/send_message_with_gif_spec.rb
|
250
253
|
- spec/slack-ruby-bot/commands/unknown_spec.rb
|
254
|
+
- spec/slack-ruby-bot/config_spec.rb
|
251
255
|
- spec/slack-ruby-bot/rspec/respond_with_error_spec.rb
|
256
|
+
- spec/slack-ruby-bot/server_spec.rb
|
252
257
|
- spec/slack-ruby-bot/version_spec.rb
|
253
258
|
- spec/spec_helper.rb
|
254
259
|
homepage: http://github.com/dblock/slack-ruby-bot
|
@@ -280,6 +285,7 @@ test_files:
|
|
280
285
|
- spec/slack-ruby-bot/commands/about_spec.rb
|
281
286
|
- spec/slack-ruby-bot/commands/aliases_spec.rb
|
282
287
|
- spec/slack-ruby-bot/commands/commands_precedence_spec.rb
|
288
|
+
- spec/slack-ruby-bot/commands/commands_regexp_escape_spec.rb
|
283
289
|
- spec/slack-ruby-bot/commands/commands_spaces_spec.rb
|
284
290
|
- spec/slack-ruby-bot/commands/commands_spec.rb
|
285
291
|
- spec/slack-ruby-bot/commands/commands_with_block_spec.rb
|
@@ -297,6 +303,8 @@ test_files:
|
|
297
303
|
- spec/slack-ruby-bot/commands/send_message_spec.rb
|
298
304
|
- spec/slack-ruby-bot/commands/send_message_with_gif_spec.rb
|
299
305
|
- spec/slack-ruby-bot/commands/unknown_spec.rb
|
306
|
+
- spec/slack-ruby-bot/config_spec.rb
|
300
307
|
- spec/slack-ruby-bot/rspec/respond_with_error_spec.rb
|
308
|
+
- spec/slack-ruby-bot/server_spec.rb
|
301
309
|
- spec/slack-ruby-bot/version_spec.rb
|
302
310
|
- spec/spec_helper.rb
|