slack-ruby-bot 0.6.2 → 0.7.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 875e496a2ad1228dd53fb09a429cef5d26bb2b33
4
- data.tar.gz: 115852404bdba1534728728a3cd56cf818c4ac0e
3
+ metadata.gz: 59c7b21c6857323515327cf3b14c4cd79bdfe25b
4
+ data.tar.gz: ae4beccb7a8d933d2f7b6fffb170241fad4a85d4
5
5
  SHA512:
6
- metadata.gz: e7442eeded435d0f5b23b63310086946170f8c15b51b9bf4d45592a01197aa3eaf95ef27df7bbaa6bab1290c538b0ebb1847f030d5979cc7e43db0385a7c00bd
7
- data.tar.gz: 670e97f790446e1974429ac8dad199e6a3312356df6b55f28ed605e9464262468c9527f0b0f6a768ad9e7f125d8d652c704722db96d018e7a6cea92358b3b65e
6
+ metadata.gz: 213767e2c36e1287e2916e24652b4ed873a3fb9bc84ff60e16bb7e6fcdf847d11dd622174421bed755febf65bf934bfc3000a3d3f0874afab7751b7c0bfc3db0
7
+ data.tar.gz: aaff927afb06fce22a67e090bf4954cea920a383a84277bf681a00e5c29154f8ff66b5c9ed979625bd03b2a1cf1f79d28b9beb37e0d53fe370bc12aa49ef77da
@@ -1,5 +1,5 @@
1
1
  # This configuration was generated by `rubocop --auto-gen-config`
2
- # on 2016-02-02 07:10:58 -0500 using RuboCop version 0.32.1.
2
+ # on 2016-02-09 15:33:27 -0500 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
@@ -11,27 +11,32 @@ Lint/HandleExceptions:
11
11
 
12
12
  # Offense count: 6
13
13
  Metrics/AbcSize:
14
- Max: 28
14
+ Max: 42
15
+
16
+ # Offense count: 1
17
+ # Configuration parameters: CountComments.
18
+ Metrics/ClassLength:
19
+ Max: 102
15
20
 
16
21
  # Offense count: 2
17
22
  Metrics/CyclomaticComplexity:
18
- Max: 8
23
+ Max: 12
19
24
 
20
- # Offense count: 126
25
+ # Offense count: 130
21
26
  # Configuration parameters: AllowURI, URISchemes.
22
27
  Metrics/LineLength:
23
28
  Max: 158
24
29
 
25
- # Offense count: 6
30
+ # Offense count: 7
26
31
  # Configuration parameters: CountComments.
27
32
  Metrics/MethodLength:
28
- Max: 21
33
+ Max: 27
29
34
 
30
35
  # Offense count: 2
31
36
  Metrics/PerceivedComplexity:
32
- Max: 8
37
+ Max: 12
33
38
 
34
- # Offense count: 19
39
+ # Offense count: 20
35
40
  Style/Documentation:
36
41
  Enabled: false
37
42
 
@@ -1,3 +1,9 @@
1
+ ### 0.7.0 (3/6/2016)
2
+
3
+ * Improved regular expression matching performance with less matching per command - [@dblock](https://github.com/dblock).
4
+ * Don't attempt to pre-authenticate via `auth!`, use RealTime client local store - [@dblock](https://github.com/dblock).
5
+ * Extended `match` with `scan` that can make multiple captures - [@dblock](https://github.com/dblock).
6
+
1
7
  ### 0.6.2 (2/4/2016)
2
8
 
3
9
  * [#44](https://github.com/dblock/slack-ruby-bot/pull/44): Bot graceful shutdown - [@accessd](https://github.com/accessd).
data/README.md CHANGED
@@ -17,7 +17,7 @@ A generic Slack bot framework written in Ruby on top of [slack-ruby-client](http
17
17
 
18
18
  ## Stable Release
19
19
 
20
- You're reading the documentation for the **stable** release of slack-ruby-bot. See [CHANGELOG](CHANGELOG.md) for a history of changes and [UPGRADING](UPGRADING.md) for how to upgrade to more recent versions.
20
+ You're reading the documentation for the **stable** release of slack-ruby-bot, 0.7.0. See [CHANGELOG](CHANGELOG.md) for a history of changes and [UPGRADING](UPGRADING.md) for how to upgrade to more recent versions.
21
21
 
22
22
  ## Usage
23
23
 
@@ -125,6 +125,20 @@ end
125
125
 
126
126
  ![](screenshots/weather.gif)
127
127
 
128
+ You can also capture multiple matchers with `scan`.
129
+
130
+ ```ruby
131
+ class Market < SlackRubyBot::Bot
132
+ scan(/(\[A-Z]{2,5})/) do |client, data, stocks|
133
+ # lookup stock market price
134
+ end
135
+ end
136
+ ```
137
+
138
+ ![](screenshots/market.gif)
139
+
140
+ See [examples/market](examples/market/marketbot.rb) for a working example.
141
+
128
142
  ### SlackRubyBot::Commands::Base
129
143
 
130
144
  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.
@@ -223,11 +237,9 @@ You may want to integrate a bot or multiple bots into other systems, in which ca
223
237
  ```ruby
224
238
  EM.run do
225
239
  bot1 = SlackRubyBot::Server.new(token: token1, aliases: ['bot1'])
226
- bot1.auth!
227
240
  bot1.start_async
228
241
 
229
242
  bot2 = SlackRubyBot::Server.new(token: token2, send_gifs: false, aliases: ['bot2'])
230
- bot2.auth!
231
243
  bot2.start_async
232
244
  end
233
245
  ```
@@ -1,6 +1,16 @@
1
1
  Upgrading SlackRubyBot
2
2
  ======================
3
3
 
4
+ ### Upgrading to >= 0.7.0
5
+
6
+ #### Simplify Match Expression Checking
7
+
8
+ The regular expression parser for commands will now include a `nil` value for `expression` when an expression is not present. You can therefore no longer rely on checking `match.names.include?('expression')`, instead check `match['expression']`.
9
+
10
+ #### Remove any bot.auth! calls
11
+
12
+ SlackRubyBot 0.6.x versions invoked a method called `auth!`, which caused a pre-flight authentication via Slack Web API `auth_test` method and collected a number of properties, such as client and team ID or name. This method has been removed in favor of using data available in the `Slack::RealTime::Client` local store introduced in [slack-ruby-client#54](https://github.com/dblock/slack-ruby-client/issues/54). Remove any explicit calls to this method.
13
+
4
14
  ### Upgrading to >= 0.6.0
5
15
 
6
16
  While entirely compatible with the 0.5.x series, a number of methods have been deprecated and will be removed in the next release.
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'slack-ruby-bot', path: '../..'
4
+ gem 'yahoo-finance'
@@ -0,0 +1 @@
1
+ console: bundle exec ruby marketbot.rb
@@ -0,0 +1,24 @@
1
+ require 'slack-ruby-bot'
2
+ require 'yahoo-finance'
3
+
4
+ class MarketBot < SlackRubyBot::Bot
5
+ scan(/([A-Z]{2,5}+)/) do |client, data, stocks|
6
+ YahooFinance::Client.new.quotes(stocks, [:name, :symbol, :last_trade_price, :change, :change_in_percent]).each do |quote|
7
+ next if quote.symbol == 'N/A'
8
+ client.web_client.chat_postMessage(
9
+ channel: data.channel,
10
+ as_user: true,
11
+ attachments: [
12
+ {
13
+ fallback: "#{quote.name} (#{quote.symbol}): $#{quote.last_trade_price}",
14
+ title: "#{quote.name} (#{quote.symbol})",
15
+ text: "$#{quote.last_trade_price} (#{quote.change_in_percent})",
16
+ color: quote.change.to_f > 0 ? '#00FF00' : '#FF0000'
17
+ }
18
+ ]
19
+ )
20
+ end
21
+ end
22
+ end
23
+
24
+ MarketBot.run
@@ -21,15 +21,16 @@ module SlackRubyBot
21
21
 
22
22
  private
23
23
 
24
- def auth!
25
- super
24
+ def hello(client, _data)
26
25
  SlackRubyBot.configure do |config|
27
- config.url = client.auth['url']
28
- config.team = client.auth['team']
29
- config.user = client.auth['user']
30
- config.team_id = client.auth['team_id']
31
- config.user_id = client.auth['user_id']
32
- end
26
+ config.url = "https://#{client.team.domain}.slack.com"
27
+ config.team = client.team.name
28
+ config.team_id = client.team.id
29
+ config.user = client.self.name
30
+ config.user_id = client.self.id
31
+ logger.info "Welcome #{config.user} to the #{config.team} team."
32
+ end if client.team && client.self
33
+ super
33
34
  end
34
35
 
35
36
  def reset!
@@ -1,7 +1,6 @@
1
1
  module SlackRubyBot
2
2
  class Client < Slack::RealTime::Client
3
3
  include Loggable
4
- attr_accessor :auth
5
4
  attr_accessor :aliases
6
5
  attr_accessor :send_gifs
7
6
 
@@ -14,14 +13,14 @@ module SlackRubyBot
14
13
  def names
15
14
  [
16
15
  SlackRubyBot::Config.user,
17
- auth ? auth['user'] : nil,
16
+ self.self ? self.self.name : nil,
18
17
  aliases,
19
18
  SlackRubyBot::Config.aliases,
20
- auth ? "<@#{auth['user_id'].downcase}>" : nil,
19
+ self.self && self.self.id ? "<@#{self.self.id.downcase}>" : nil,
21
20
  SlackRubyBot::Config.user_id ? "<@#{SlackRubyBot::Config.user_id.downcase}>" : nil,
22
- auth ? "<@#{auth['user_id'].downcase}>:" : nil,
21
+ self.self && self.self.id ? "<@#{self.self.id.downcase}>:" : nil,
23
22
  SlackRubyBot::Config.user_id ? "<@#{SlackRubyBot::Config.user_id.downcase}>:" : nil,
24
- auth ? "#{auth['user']}:" : nil,
23
+ self.self && self.self.name ? "#{self.self.name.downcase}:" : nil,
25
24
  SlackRubyBot::Config.user ? "#{SlackRubyBot::Config.user}:" : nil
26
25
  ].compact.flatten
27
26
  end
@@ -35,11 +34,11 @@ module SlackRubyBot
35
34
  end
36
35
 
37
36
  def name
38
- SlackRubyBot.config.user || (auth && auth['user'])
37
+ SlackRubyBot.config.user || (self.self && self.self.name)
39
38
  end
40
39
 
41
40
  def url
42
- SlackRubyBot.config.url || (auth && auth['url'])
41
+ SlackRubyBot.config.url || super
43
42
  end
44
43
 
45
44
  def say(options = {})
@@ -29,44 +29,53 @@ module SlackRubyBot
29
29
  end
30
30
 
31
31
  def operator(*values, &block)
32
- values.each do |value|
33
- match Regexp.new("^(?<operator>\\#{value})(?<expression>.*)$", Regexp::IGNORECASE), &block
34
- end
32
+ values = values.map { |value| Regexp.escape(value) }.join('|')
33
+ match Regexp.new("^(?<operator>#{values})(?<expression>.*)$", Regexp::IGNORECASE), &block
35
34
  end
36
35
 
37
36
  def command(*values, &block)
38
- values.each do |value|
39
- escaped = Regexp.escape(value)
40
- match Regexp.new("^(?<bot>[[:alnum:][:punct:]@<>]*)[\\s]+(?<command>#{escaped})$", Regexp::IGNORECASE), &block
41
- match Regexp.new("^(?<bot>[[:alnum:][:punct:]@<>]*)[\\s]+(?<command>#{escaped})[\\s]+(?<expression>.*)$", Regexp::IGNORECASE), &block
42
- end
37
+ values = values.map { |value| Regexp.escape(value) }.join('|')
38
+ match Regexp.new("^(?<bot>[[:alnum:][:punct:]@<>]*)[\\s]+(?<command>#{values})([\\s]+(?<expression>.*)|)$", Regexp::IGNORECASE), &block
43
39
  end
44
40
 
45
41
  def invoke(client, data)
46
42
  finalize_routes!
47
43
  expression, text = parse(client, data)
48
44
  called = false
49
- routes.each_pair do |route, method|
50
- match = route.match(expression)
51
- match ||= route.match(text) if text
52
- next unless match
53
- next if match.names.include?('bot') && !client.name?(match['bot'])
45
+ routes.each_pair do |route, options|
46
+ match_method = options[:match_method]
47
+ case match_method
48
+ when :match
49
+ match = route.match(expression)
50
+ match ||= route.match(text) if text
51
+ next unless match
52
+ next if match.names.include?('bot') && !client.name?(match['bot'])
53
+ when :scan
54
+ match = expression.scan(route)
55
+ next unless match.any?
56
+ end
54
57
  called = true
55
- if method
56
- method.call(client, data, match)
58
+ call = options[:call]
59
+ if call
60
+ call.call(client, data, match)
57
61
  elsif respond_to?(:call)
58
62
  send(:call, client, data, match)
59
63
  else
60
64
  fail NotImplementedError, data.text
61
65
  end
62
66
  break
63
- end
67
+ end if expression
64
68
  called
65
69
  end
66
70
 
67
71
  def match(match, &block)
68
72
  self.routes ||= {}
69
- self.routes[match] = block
73
+ self.routes[match] = { match_method: :match, call: block }
74
+ end
75
+
76
+ def scan(match, &block)
77
+ self.routes ||= {}
78
+ self.routes[match] = { match_method: :scan, call: block }
70
79
  end
71
80
 
72
81
  private
@@ -4,7 +4,8 @@ module SlackRubyBot
4
4
  extend Base
5
5
 
6
6
  def hello(client, _data)
7
- logger.info "Successfully connected to #{client.url || 'slack'}."
7
+ return unless client && client.team
8
+ logger.info "Successfully connected to https://#{client.team.domain}.slack.com."
8
9
  end
9
10
  end
10
11
  end
@@ -10,18 +10,4 @@ shared_examples 'a slack ruby bot' do
10
10
  expect { subject }.to raise_error RuntimeError, "Missing ENV['SLACK_API_TOKEN']."
11
11
  end
12
12
  end
13
- context 'configured', vcr: { cassette_name: 'auth_test' } do
14
- context 'run' do
15
- before do
16
- subject.send(:auth!)
17
- end
18
- it 'succeeds auth' do
19
- expect(subject.config.url).to eq 'https://rubybot.slack.com/'
20
- expect(subject.config.team).to eq 'team_name'
21
- expect(subject.config.user).to eq 'user_name'
22
- expect(subject.config.team_id).to eq 'TDEADBEEF'
23
- expect(subject.config.user_id).to eq 'UBAADFOOD'
24
- end
25
- end
26
- end
27
13
  end
@@ -16,7 +16,6 @@ module SlackRubyBot
16
16
  end
17
17
 
18
18
  def run
19
- auth!
20
19
  loop do
21
20
  handle_execeptions do
22
21
  handle_signals
@@ -25,11 +24,6 @@ module SlackRubyBot
25
24
  end
26
25
  end
27
26
 
28
- def auth!
29
- client.auth = client.web_client.auth_test
30
- logger.info "Welcome '#{client.auth['user']}' to the '#{client.auth['team']}' team at #{client.auth['url']}."
31
- end
32
-
33
27
  def start!
34
28
  @stopping = false
35
29
  @async = false
@@ -47,6 +41,11 @@ module SlackRubyBot
47
41
  client.stop! if @client
48
42
  end
49
43
 
44
+ def hello!
45
+ return unless client.self && client.team
46
+ logger.info "Welcome '#{client.self.name}' to the '#{client.team.name}' team at https://#{client.team.domain}.slack.com."
47
+ end
48
+
50
49
  def restart!(wait = 1)
51
50
  @async ? start_async : start!
52
51
  rescue StandardError => e
@@ -1,3 +1,3 @@
1
1
  module SlackRubyBot
2
- VERSION = '0.6.2'
2
+ VERSION = '0.7.0'
3
3
  end
Binary file
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.files = `git ls-files`.split("\n")
12
12
  s.test_files = `git ls-files -- spec/*`.split("\n")
13
13
  s.require_paths = ['lib']
14
- s.homepage = 'http://github.com/dblock/slack-ruby-bot'
14
+ s.homepage = 'https://github.com/dblock/slack-ruby-bot'
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'
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe SlackRubyBot::Commands do
4
+ let! :command do
5
+ Class.new(SlackRubyBot::Commands::Base) do
6
+ command 'set' do |client, data, match|
7
+ if match['expression']
8
+ k, v = match['expression'].split(/\W+/, 2)
9
+ client.say(channel: data.channel, text: "#{match[:command]}: #{k}=#{v}")
10
+ else
11
+ client.say(channel: data.channel, text: "#{match[:command]}")
12
+ end
13
+ end
14
+ end
15
+ end
16
+ def app
17
+ SlackRubyBot::App.new
18
+ end
19
+ it 'parses expression' do
20
+ expect(message: "#{SlackRubyBot.config.user} set").to respond_with_slack_message('set')
21
+ expect(message: "#{SlackRubyBot.config.user} set x").to respond_with_slack_message('set: x=')
22
+ expect(message: "#{SlackRubyBot.config.user} set x y").to respond_with_slack_message('set: x=y')
23
+ end
24
+ end
@@ -6,7 +6,7 @@ describe SlackRubyBot::Commands do
6
6
  command 'nil_text'
7
7
 
8
8
  def self.call(_client, data, _match)
9
- send_message cilent, data.channel, nil
9
+ send_message client, data.channel, nil
10
10
  end
11
11
  end
12
12
  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
+ scan(/(\$[A-Z]+)/) do |client, data, captures|
7
+ client.say(channel: data.channel, text: "There were #{captures.count} captures: #{captures.join(', ')}.")
8
+ end
9
+ end
10
+ end
11
+ def app
12
+ SlackRubyBot::App.new
13
+ end
14
+ it 'captures' do
15
+ expect(message: 'quote $YHOO and $MSFT').to respond_with_slack_message('There were 2 captures: $YHOO, $MSFT.')
16
+ end
17
+ 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.6.2
4
+ version: 0.7.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: 2016-02-04 00:00:00.000000000 Z
11
+ date: 2016-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashie
@@ -185,6 +185,9 @@ files:
185
185
  - Rakefile
186
186
  - TUTORIAL.md
187
187
  - UPGRADING.md
188
+ - examples/market/Gemfile
189
+ - examples/market/Procfile
190
+ - examples/market/marketbot.rb
188
191
  - examples/minimal/Gemfile
189
192
  - examples/minimal/Procfile
190
193
  - examples/minimal/pongbot.rb
@@ -212,7 +215,6 @@ files:
212
215
  - lib/slack-ruby-bot/hooks/hello.rb
213
216
  - lib/slack-ruby-bot/hooks/message.rb
214
217
  - lib/slack-ruby-bot/rspec.rb
215
- - lib/slack-ruby-bot/rspec/support/fixtures/slack/auth_test.yml
216
218
  - lib/slack-ruby-bot/rspec/support/fixtures/slack/migration_in_progress.yml
217
219
  - lib/slack-ruby-bot/rspec/support/slack-ruby-bot/it_behaves_like_a_slack_bot.rb
218
220
  - lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_error.rb
@@ -227,6 +229,7 @@ files:
227
229
  - screenshots/aliases.gif
228
230
  - screenshots/demo.gif
229
231
  - screenshots/dms.gif
232
+ - screenshots/market.gif
230
233
  - screenshots/register-bot.png
231
234
  - screenshots/weather.gif
232
235
  - slack-ruby-bot.gemspec
@@ -241,6 +244,7 @@ files:
241
244
  - spec/slack-ruby-bot/commands/commands_spaces_spec.rb
242
245
  - spec/slack-ruby-bot/commands/commands_spec.rb
243
246
  - spec/slack-ruby-bot/commands/commands_with_block_spec.rb
247
+ - spec/slack-ruby-bot/commands/commands_with_expression_spec.rb
244
248
  - spec/slack-ruby-bot/commands/direct_messages_spec.rb
245
249
  - spec/slack-ruby-bot/commands/empty_text_spec.rb
246
250
  - spec/slack-ruby-bot/commands/help_spec.rb
@@ -251,6 +255,7 @@ files:
251
255
  - spec/slack-ruby-bot/commands/not_implemented_spec.rb
252
256
  - spec/slack-ruby-bot/commands/operators_spec.rb
253
257
  - spec/slack-ruby-bot/commands/operators_with_block_spec.rb
258
+ - spec/slack-ruby-bot/commands/scan_spec.rb
254
259
  - spec/slack-ruby-bot/commands/send_gif_spec.rb
255
260
  - spec/slack-ruby-bot/commands/send_message_spec.rb
256
261
  - spec/slack-ruby-bot/commands/send_message_with_gif_spec.rb
@@ -262,7 +267,7 @@ files:
262
267
  - spec/slack-ruby-bot/support/loggable_spec.rb
263
268
  - spec/slack-ruby-bot/version_spec.rb
264
269
  - spec/spec_helper.rb
265
- homepage: http://github.com/dblock/slack-ruby-bot
270
+ homepage: https://github.com/dblock/slack-ruby-bot
266
271
  licenses:
267
272
  - MIT
268
273
  metadata: {}
@@ -297,6 +302,7 @@ test_files:
297
302
  - spec/slack-ruby-bot/commands/commands_spaces_spec.rb
298
303
  - spec/slack-ruby-bot/commands/commands_spec.rb
299
304
  - spec/slack-ruby-bot/commands/commands_with_block_spec.rb
305
+ - spec/slack-ruby-bot/commands/commands_with_expression_spec.rb
300
306
  - spec/slack-ruby-bot/commands/direct_messages_spec.rb
301
307
  - spec/slack-ruby-bot/commands/empty_text_spec.rb
302
308
  - spec/slack-ruby-bot/commands/help_spec.rb
@@ -307,6 +313,7 @@ test_files:
307
313
  - spec/slack-ruby-bot/commands/not_implemented_spec.rb
308
314
  - spec/slack-ruby-bot/commands/operators_spec.rb
309
315
  - spec/slack-ruby-bot/commands/operators_with_block_spec.rb
316
+ - spec/slack-ruby-bot/commands/scan_spec.rb
310
317
  - spec/slack-ruby-bot/commands/send_gif_spec.rb
311
318
  - spec/slack-ruby-bot/commands/send_message_spec.rb
312
319
  - spec/slack-ruby-bot/commands/send_message_with_gif_spec.rb
@@ -1,16 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: post
5
- uri: https://slack.com/api/auth.test
6
- body:
7
- string: token=token
8
- response:
9
- status:
10
- code: 200
11
- message: OK
12
- body:
13
- encoding: UTF-8
14
- string: '{"ok":true,"url":"https:\/\/rubybot.slack.com\/","team":"team_name","user":"user_name","team_id":"TDEADBEEF","user_id":"UBAADFOOD"}'
15
- http_version:
16
- recorded_at: Tue, 28 Apr 2015 12:55:22 GMT