slack-ruby-bot-bhe 0.5.5.3

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.
Files changed (90) hide show
  1. checksums.yaml +17 -0
  2. data/.gitignore +3 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +6 -0
  5. data/.rubocop_todo.yml +58 -0
  6. data/.travis.yml +3 -0
  7. data/CHANGELOG.md +81 -0
  8. data/CONTRIBUTING.md +139 -0
  9. data/DEPLOYMENT.md +33 -0
  10. data/Gemfile +3 -0
  11. data/LICENSE.md +22 -0
  12. data/README.md +263 -0
  13. data/RELEASING.md +67 -0
  14. data/Rakefile +19 -0
  15. data/TUTORIAL.md +205 -0
  16. data/UPGRADING.md +64 -0
  17. data/examples/minimal/Gemfile +3 -0
  18. data/examples/minimal/Procfile +1 -0
  19. data/examples/minimal/pongbot.rb +9 -0
  20. data/examples/weather/Gemfile +3 -0
  21. data/examples/weather/Procfile +1 -0
  22. data/examples/weather/weatherbot.rb +9 -0
  23. data/lib/config/application.rb +14 -0
  24. data/lib/config/boot.rb +8 -0
  25. data/lib/config/environment.rb +3 -0
  26. data/lib/initializers/giphy.rb +5 -0
  27. data/lib/slack-ruby-bot.rb +25 -0
  28. data/lib/slack-ruby-bot/about.rb +7 -0
  29. data/lib/slack-ruby-bot/app.rb +46 -0
  30. data/lib/slack-ruby-bot/bot.rb +7 -0
  31. data/lib/slack-ruby-bot/client.rb +44 -0
  32. data/lib/slack-ruby-bot/commands.rb +5 -0
  33. data/lib/slack-ruby-bot/commands/about.rb +12 -0
  34. data/lib/slack-ruby-bot/commands/base.rb +121 -0
  35. data/lib/slack-ruby-bot/commands/help.rb +9 -0
  36. data/lib/slack-ruby-bot/commands/hi.rb +9 -0
  37. data/lib/slack-ruby-bot/commands/unknown.rb +11 -0
  38. data/lib/slack-ruby-bot/config.rb +40 -0
  39. data/lib/slack-ruby-bot/hooks.rb +3 -0
  40. data/lib/slack-ruby-bot/hooks/base.rb +10 -0
  41. data/lib/slack-ruby-bot/hooks/hello.rb +11 -0
  42. data/lib/slack-ruby-bot/hooks/message.rb +50 -0
  43. data/lib/slack-ruby-bot/rspec.rb +12 -0
  44. data/lib/slack-ruby-bot/rspec/support/fixtures/slack/auth_test.yml +16 -0
  45. data/lib/slack-ruby-bot/rspec/support/fixtures/slack/migration_in_progress.yml +30 -0
  46. data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/it_behaves_like_a_slack_bot.rb +27 -0
  47. data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_error.rb +30 -0
  48. data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_slack_message.rb +19 -0
  49. data/lib/slack-ruby-bot/rspec/support/slack_api_key.rb +5 -0
  50. data/lib/slack-ruby-bot/rspec/support/slack_ruby_bot_configure.rb +9 -0
  51. data/lib/slack-ruby-bot/rspec/support/vcr.rb +8 -0
  52. data/lib/slack-ruby-bot/server.rb +111 -0
  53. data/lib/slack-ruby-bot/version.rb +3 -0
  54. data/lib/slack_ruby_bot.rb +1 -0
  55. data/screenshots/aliases.gif +0 -0
  56. data/screenshots/demo.gif +0 -0
  57. data/screenshots/dms.gif +0 -0
  58. data/screenshots/register-bot.png +0 -0
  59. data/screenshots/weather.gif +0 -0
  60. data/slack-ruby-bot.gemspec +28 -0
  61. data/slack.png +0 -0
  62. data/spec/slack-ruby-bot/app_spec.rb +15 -0
  63. data/spec/slack-ruby-bot/commands/about_spec.rb +19 -0
  64. data/spec/slack-ruby-bot/commands/aliases_spec.rb +22 -0
  65. data/spec/slack-ruby-bot/commands/bot_spec.rb +19 -0
  66. data/spec/slack-ruby-bot/commands/commands_precedence_spec.rb +22 -0
  67. data/spec/slack-ruby-bot/commands/commands_regexp_escape_spec.rb +17 -0
  68. data/spec/slack-ruby-bot/commands/commands_spaces_spec.rb +20 -0
  69. data/spec/slack-ruby-bot/commands/commands_spec.rb +21 -0
  70. data/spec/slack-ruby-bot/commands/commands_with_block_spec.rb +23 -0
  71. data/spec/slack-ruby-bot/commands/direct_messages_spec.rb +38 -0
  72. data/spec/slack-ruby-bot/commands/empty_text_spec.rb +22 -0
  73. data/spec/slack-ruby-bot/commands/help_spec.rb +10 -0
  74. data/spec/slack-ruby-bot/commands/hi_spec.rb +19 -0
  75. data/spec/slack-ruby-bot/commands/match_spec.rb +17 -0
  76. data/spec/slack-ruby-bot/commands/message_loop_spec.rb +34 -0
  77. data/spec/slack-ruby-bot/commands/nil_message_spec.rb +22 -0
  78. data/spec/slack-ruby-bot/commands/not_implemented_spec.rb +15 -0
  79. data/spec/slack-ruby-bot/commands/operators_spec.rb +20 -0
  80. data/spec/slack-ruby-bot/commands/operators_with_block_spec.rb +23 -0
  81. data/spec/slack-ruby-bot/commands/send_gif_spec.rb +32 -0
  82. data/spec/slack-ruby-bot/commands/send_message_spec.rb +19 -0
  83. data/spec/slack-ruby-bot/commands/send_message_with_gif_spec.rb +73 -0
  84. data/spec/slack-ruby-bot/commands/unknown_spec.rb +18 -0
  85. data/spec/slack-ruby-bot/config_spec.rb +55 -0
  86. data/spec/slack-ruby-bot/rspec/respond_with_error_spec.rb +19 -0
  87. data/spec/slack-ruby-bot/server_spec.rb +73 -0
  88. data/spec/slack-ruby-bot/version_spec.rb +7 -0
  89. data/spec/spec_helper.rb +1 -0
  90. metadata +313 -0
@@ -0,0 +1,7 @@
1
+ module SlackRubyBot
2
+ ABOUT = <<-ABOUT
3
+ #{SlackRubyBot::VERSION}
4
+ https://github.com/dblock/slack-ruby-bot
5
+ https://twitter.com/dblockdotorg
6
+ ABOUT
7
+ end
@@ -0,0 +1,46 @@
1
+ module SlackRubyBot
2
+ class App < Server
3
+ def initialize(options = {})
4
+ SlackRubyBot.configure do |config|
5
+ config.token = ENV['SLACK_API_TOKEN'] || fail("Missing ENV['SLACK_API_TOKEN'].")
6
+ config.aliases = ENV['SLACK_RUBY_BOT_ALIASES'].split(' ') if ENV['SLACK_RUBY_BOT_ALIASES']
7
+ end
8
+ Slack.configure do |config|
9
+ config.token = SlackRubyBot.config.token
10
+ end
11
+ super
12
+ end
13
+
14
+ def config
15
+ SlackRubyBot.config
16
+ end
17
+
18
+ def self.instance
19
+ @instance ||= new
20
+ end
21
+
22
+ private
23
+
24
+ def auth!
25
+ super
26
+ 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
33
+ end
34
+
35
+ def reset!
36
+ super
37
+ SlackRubyBot.configure do |config|
38
+ config.url = nil
39
+ config.team = nil
40
+ config.user = nil
41
+ config.team_id = nil
42
+ config.user_id = nil
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,7 @@
1
+ module SlackRubyBot
2
+ class Bot < SlackRubyBot::Commands::Base
3
+ def self.run
4
+ SlackRubyBot::App.instance.run
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,44 @@
1
+ module SlackRubyBot
2
+ class Client < Slack::RealTime::Client
3
+ attr_accessor :auth
4
+ attr_accessor :aliases
5
+ attr_accessor :send_gifs
6
+
7
+ def initialize(attrs = {})
8
+ super(attrs)
9
+ @aliases = attrs[:aliases]
10
+ @send_gifs = attrs.key?(:send_gifs) ? !!attrs[:send_gifs] : true
11
+ end
12
+
13
+ def names
14
+ [
15
+ SlackRubyBot::Config.user,
16
+ auth ? auth['user'] : nil,
17
+ aliases,
18
+ SlackRubyBot::Config.aliases,
19
+ auth ? "<@#{auth['user_id'].downcase}>" : nil,
20
+ SlackRubyBot::Config.user_id ? "<@#{SlackRubyBot::Config.user_id.downcase}>" : nil,
21
+ auth ? "<@#{auth['user_id'].downcase}>:" : nil,
22
+ SlackRubyBot::Config.user_id ? "<@#{SlackRubyBot::Config.user_id.downcase}>:" : nil,
23
+ auth ? "#{auth['user']}:" : nil,
24
+ SlackRubyBot::Config.user ? "#{SlackRubyBot::Config.user}:" : nil
25
+ ].compact.flatten
26
+ end
27
+
28
+ def name?(name)
29
+ name && names.include?(name.downcase)
30
+ end
31
+
32
+ def send_gifs?
33
+ send_gifs
34
+ end
35
+
36
+ def name
37
+ SlackRubyBot.config.user || (auth && auth['user'])
38
+ end
39
+
40
+ def url
41
+ SlackRubyBot.config.url || (auth && auth['url'])
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ require 'slack-ruby-bot/commands/base'
2
+ require 'slack-ruby-bot/commands/about'
3
+ require 'slack-ruby-bot/commands/help'
4
+ require 'slack-ruby-bot/commands/hi'
5
+ require 'slack-ruby-bot/commands/unknown'
@@ -0,0 +1,12 @@
1
+ module SlackRubyBot
2
+ module Commands
3
+ class Default < Base
4
+ command 'about'
5
+ match(/^(?<bot>[[:alnum:][:punct:]@<>]*)$/u)
6
+
7
+ def self.call(client, data, _match)
8
+ send_message_with_gif client, data.channel, SlackRubyBot::ABOUT, 'selfie'
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,121 @@
1
+ module SlackRubyBot
2
+ module Commands
3
+ class Base
4
+ class_attribute :routes
5
+
6
+ def self.send_message(client, channel, text, options = {})
7
+ if text && text.length > 0
8
+ send_client_message(client, { channel: channel, text: text }.merge(options))
9
+ else
10
+ send_message_with_gif client, channel, 'Nothing to see here.', 'nothing', options
11
+ end
12
+ end
13
+
14
+ def self.send_message_with_gif(client, channel, text, keywords, options = {})
15
+ get_gif_and_send({
16
+ client: client,
17
+ channel: channel,
18
+ text: text,
19
+ keywords: keywords
20
+ }.merge(options))
21
+ end
22
+
23
+ def self.send_gif(client, channel, keywords, options = {})
24
+ get_gif_and_send({
25
+ client: client,
26
+ channel: channel,
27
+ keywords: keywords
28
+ }.merge(options))
29
+ end
30
+
31
+ def self.logger
32
+ @logger ||= begin
33
+ $stdout.sync = true
34
+ Logger.new(STDOUT)
35
+ end
36
+ end
37
+
38
+ def self.default_command_name
39
+ name && name.split(':').last.downcase
40
+ end
41
+
42
+ def self.operator(*values, &block)
43
+ values.each do |value|
44
+ match Regexp.new("^(?<operator>\\#{value})(?<expression>.*)$", Regexp::IGNORECASE), &block
45
+ end
46
+ end
47
+
48
+ def self.command(*values, &block)
49
+ values.each do |value|
50
+ escaped = Regexp.escape(value)
51
+ match Regexp.new("^(?<bot>[[:alnum:][:punct:]@<>]*)[\\s]+(?<command>#{escaped})$", Regexp::IGNORECASE), &block
52
+ match Regexp.new("^(?<bot>[[:alnum:][:punct:]@<>]*)[\\s]+(?<command>#{escaped})[\\s]+(?<expression>.*)$", Regexp::IGNORECASE), &block
53
+ end
54
+ end
55
+
56
+ def self.invoke(client, data)
57
+ self.finalize_routes!
58
+ expression, text = parse(client, data)
59
+ called = false
60
+ routes.each_pair do |route, method|
61
+ match = route.match(expression)
62
+ match ||= route.match(text) if text
63
+ next unless match
64
+ next if match.names.include?('bot') && !client.name?(match['bot'])
65
+ called = true
66
+ if method
67
+ method.call(client, data, match)
68
+ elsif self.respond_to?(:call)
69
+ send(:call, client, data, match)
70
+ else
71
+ fail NotImplementedError, data.text
72
+ end
73
+ break
74
+ end
75
+ called
76
+ end
77
+
78
+ def self.match(match, &block)
79
+ self.routes ||= {}
80
+ self.routes[match] = block
81
+ end
82
+
83
+ private
84
+
85
+ def self.parse(client, data)
86
+ text = data.text
87
+ return text unless data.channel && data.channel[0] == 'D' && data.user && data.user != SlackRubyBot.config.user_id
88
+ client.names.each do |name|
89
+ text.downcase.tap do |td|
90
+ return text if td == name || td.starts_with?("#{name} ")
91
+ end
92
+ end
93
+ ["#{client.name} #{text}", text]
94
+ end
95
+
96
+ def self.finalize_routes!
97
+ return if self.routes && self.routes.any?
98
+ command default_command_name
99
+ end
100
+
101
+ def self.get_gif_and_send(options = {})
102
+ options = options.dup
103
+ keywords = options.delete(:keywords)
104
+ client = options.delete(:client)
105
+ gif = begin
106
+ Giphy.random(keywords)
107
+ rescue StandardError => e
108
+ logger.warn "Giphy.random: #{e.message}"
109
+ nil
110
+ end if SlackRubyBot::Config.send_gifs? && client.send_gifs?
111
+ text = options.delete(:text)
112
+ text = [text, gif && gif.image_url.to_s].compact.join("\n")
113
+ send_client_message(client, { text: text }.merge(options))
114
+ end
115
+
116
+ def self.send_client_message(client, data)
117
+ client.message(data)
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,9 @@
1
+ module SlackRubyBot
2
+ module Commands
3
+ class Help < Base
4
+ def self.call(client, data, _match)
5
+ send_message_with_gif client, data.channel, 'See https://github.com/dblock/slack-ruby-bot, please.', 'help'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module SlackRubyBot
2
+ module Commands
3
+ class Hi < Base
4
+ def self.call(client, data, _match)
5
+ send_message_with_gif client, data.channel, "Hi <@#{data.user}>!", 'hi'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module SlackRubyBot
2
+ module Commands
3
+ class Unknown < Base
4
+ match(/^(?<bot>\S*)[\s]*(?<expression>.*)$/)
5
+
6
+ def self.call(client, data, _match)
7
+ send_message_with_gif client, data.channel, "Sorry <@#{data.user}>, I don't understand that command!", 'idiot'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ module SlackRubyBot
2
+ module Config
3
+ extend self
4
+
5
+ attr_accessor :token
6
+ attr_accessor :url
7
+ attr_accessor :aliases
8
+ attr_accessor :user
9
+ attr_accessor :user_id
10
+ attr_accessor :team
11
+ attr_accessor :team_id
12
+ attr_accessor :allow_message_loops
13
+ attr_accessor :send_gifs
14
+
15
+ def allow_message_loops?
16
+ allow_message_loops
17
+ end
18
+
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
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ require 'slack-ruby-bot/hooks/base'
2
+ require 'slack-ruby-bot/hooks/hello'
3
+ require 'slack-ruby-bot/hooks/message'
@@ -0,0 +1,10 @@
1
+ module SlackRubyBot
2
+ module Hooks
3
+ module Base
4
+ def included(caller)
5
+ caller.hooks ||= []
6
+ caller.hooks << name.demodulize.underscore.to_sym
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ module SlackRubyBot
2
+ module Hooks
3
+ module Hello
4
+ extend Base
5
+
6
+ def hello(client, _data)
7
+ logger.info "Successfully connected to #{client.url || 'slack'}."
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,50 @@
1
+ module SlackRubyBot
2
+ module Hooks
3
+ module Message
4
+ extend Base
5
+
6
+ def message(client, data)
7
+ data = Hashie::Mash.new(data)
8
+ return if !SlackRubyBot::Config.allow_message_loops? && (client.self && client.self['id'] == data.user)
9
+ data.text.strip! if data.text
10
+ result = child_command_classes.detect { |d| d.invoke(client, data) }
11
+ result ||= built_in_command_classes.detect { |d| d.invoke(client, data) }
12
+ result ||= SlackRubyBot::Commands::Unknown.tap { |d| d.invoke(client, data) }
13
+ result
14
+ end
15
+
16
+ private
17
+
18
+ #
19
+ # All commands.
20
+ #
21
+ # @return [Array] Descendants of SlackRubyBot::Commands::Base.
22
+ #
23
+ def command_classes
24
+ SlackRubyBot::Commands::Base.descendants
25
+ end
26
+
27
+ #
28
+ # All non-built-in, ie. custom commands.
29
+ #
30
+ # @return [Array] Non-built-in descendants of SlackRubyBot::Commands::Base.
31
+ #
32
+ def child_command_classes
33
+ command_classes.reject do |k|
34
+ k.name && k.name.starts_with?('SlackRubyBot::Commands::')
35
+ end
36
+ end
37
+
38
+ #
39
+ # All built-in commands.
40
+ #
41
+ # @return [Array] Built-in descendants of SlackRubyBot::Commands::Base.
42
+ #
43
+ def built_in_command_classes
44
+ command_classes.select do |k|
45
+ k.name && k.name.starts_with?('SlackRubyBot::Commands::') && k != SlackRubyBot::Commands::Unknown
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
2
+
3
+ require 'rubygems'
4
+ require 'rspec'
5
+ require 'rack/test'
6
+
7
+ require 'config/environment'
8
+ require 'slack-ruby-bot'
9
+
10
+ Dir[File.join(File.dirname(__FILE__), 'rspec/support', '**/*.rb')].each do |file|
11
+ require file
12
+ end
@@ -0,0 +1,16 @@
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
@@ -0,0 +1,30 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://slack.com/api/rtm.start
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":false,"error":"migration_in_progress"}'
15
+ http_version:
16
+ recorded_at: Tue, 28 Apr 2015 12:55:22 GMT
17
+ - request:
18
+ method: post
19
+ uri: https://slack.com/api/rtm.start
20
+ body:
21
+ string: token=token
22
+ response:
23
+ status:
24
+ code: 200
25
+ message: OK
26
+ body:
27
+ encoding: UTF-8
28
+ string: '{"ok":false,"error":"unknown"}'
29
+ http_version:
30
+ recorded_at: Tue, 28 Apr 2015 12:55:22 GMT