slackrb 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +29 -0
  5. data/.rubocop_todo.yml +49 -0
  6. data/CHANGELOG.md +219 -0
  7. data/Dangerfile +5 -0
  8. data/Gemfile +20 -0
  9. data/LICENSE.md +22 -0
  10. data/README.md +766 -0
  11. data/Rakefile +21 -0
  12. data/lib/config/application.rb +14 -0
  13. data/lib/config/boot.rb +8 -0
  14. data/lib/config/environment.rb +5 -0
  15. data/lib/slack-ruby-bot/about.rb +9 -0
  16. data/lib/slack-ruby-bot/app.rb +56 -0
  17. data/lib/slack-ruby-bot/bot.rb +19 -0
  18. data/lib/slack-ruby-bot/client.rb +65 -0
  19. data/lib/slack-ruby-bot/commands/about.rb +14 -0
  20. data/lib/slack-ruby-bot/commands/base.rb +158 -0
  21. data/lib/slack-ruby-bot/commands/help.rb +43 -0
  22. data/lib/slack-ruby-bot/commands/hi.rb +16 -0
  23. data/lib/slack-ruby-bot/commands/support/attrs.rb +36 -0
  24. data/lib/slack-ruby-bot/commands/support/help.rb +84 -0
  25. data/lib/slack-ruby-bot/commands/support/match.rb +23 -0
  26. data/lib/slack-ruby-bot/commands/unknown.rb +13 -0
  27. data/lib/slack-ruby-bot/commands.rb +7 -0
  28. data/lib/slack-ruby-bot/config.rb +38 -0
  29. data/lib/slack-ruby-bot/hooks/hello.rb +35 -0
  30. data/lib/slack-ruby-bot/hooks/hook_support.rb +45 -0
  31. data/lib/slack-ruby-bot/hooks/message.rb +56 -0
  32. data/lib/slack-ruby-bot/hooks/set.rb +45 -0
  33. data/lib/slack-ruby-bot/hooks.rb +6 -0
  34. data/lib/slack-ruby-bot/mvc/controller/base.rb +172 -0
  35. data/lib/slack-ruby-bot/mvc/model/base.rb +27 -0
  36. data/lib/slack-ruby-bot/mvc/mvc.rb +7 -0
  37. data/lib/slack-ruby-bot/mvc/view/base.rb +30 -0
  38. data/lib/slack-ruby-bot/mvc.rb +3 -0
  39. data/lib/slack-ruby-bot/rspec/support/bots_for_tests.rb +35 -0
  40. data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/it_behaves_like_a_slack_bot.rb +16 -0
  41. data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/not_respond.rb +25 -0
  42. data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_error.rb +36 -0
  43. data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_slack_message.rb +34 -0
  44. data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_slack_messages.rb +45 -0
  45. data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/start_typing.rb +32 -0
  46. data/lib/slack-ruby-bot/rspec/support/slack_api_key.rb +10 -0
  47. data/lib/slack-ruby-bot/rspec/support/slack_ruby_bot_configure.rb +15 -0
  48. data/lib/slack-ruby-bot/rspec/support/spec_helpers.rb +14 -0
  49. data/lib/slack-ruby-bot/rspec.rb +14 -0
  50. data/lib/slack-ruby-bot/server.rb +88 -0
  51. data/lib/slack-ruby-bot/support/loggable.rb +25 -0
  52. data/lib/slack-ruby-bot/version.rb +5 -0
  53. data/lib/slack-ruby-bot.rb +29 -0
  54. data/lib/slack_ruby_bot.rb +3 -0
  55. data/screenshots/aliases.gif +0 -0
  56. data/screenshots/create-classic-app.png +0 -0
  57. data/screenshots/demo.gif +0 -0
  58. data/screenshots/dms.gif +0 -0
  59. data/screenshots/help.png +0 -0
  60. data/screenshots/market.gif +0 -0
  61. data/screenshots/weather.gif +0 -0
  62. data/slack-ruby-bot.gemspec +32 -0
  63. data/slack.png +0 -0
  64. metadata +244 -0
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ require 'bundler/gem_tasks'
6
+
7
+ Bundler.setup :default, :development
8
+
9
+ unless ENV['RACK_ENV'] == 'production'
10
+ require 'rspec/core'
11
+ require 'rspec/core/rake_task'
12
+
13
+ RSpec::Core::RakeTask.new(:spec) do |spec|
14
+ spec.pattern = FileList['spec/**/*_spec.rb']
15
+ end
16
+
17
+ require 'rubocop/rake_task'
18
+ RuboCop::RakeTask.new
19
+
20
+ task default: %i[rubocop spec]
21
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+
6
+ require 'boot'
7
+
8
+ Dir[File.expand_path('../initializers', __dir__) + '/**/*.rb'].sort.each do |file|
9
+ require file
10
+ end
11
+
12
+ require File.expand_path('application', __dir__)
13
+
14
+ require 'slack_ruby_bot'
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems'
4
+ require 'logger'
5
+ require 'active_support'
6
+ require 'active_support/core_ext'
7
+ require 'hashie'
8
+ require 'slack'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV['RACK_ENV'] ||= 'development'
4
+
5
+ require File.expand_path('application', __dir__)
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlackRubyBot
4
+ ABOUT = <<-ABOUT
5
+ #{SlackRubyBot::VERSION}
6
+ https://github.com/slack-ruby/slack-ruby-bot
7
+ https://twitter.com/dblockdotorg
8
+ ABOUT
9
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+ require 'botwayrb'
3
+
4
+ module SlackRubyBot
5
+ class App < Server
6
+ def config
7
+ SlackRubyBot.config
8
+ end
9
+
10
+ def self.instance
11
+ @instance ||= begin
12
+ configure!
13
+ new(token: SlackRubyBot.config.token)
14
+ end
15
+ end
16
+
17
+ def self.configure!
18
+ SlackRubyBot.configure do |config|
19
+ bw = Botwayrb::Core.new
20
+
21
+ config.token = bw.get_token
22
+ raise('Missing Slack API Token.') unless config.token.present?
23
+ end
24
+ Slack.configure do |config|
25
+ config.token = SlackRubyBot.config.token
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def hello(client, _data)
32
+ if client.team && client.self
33
+ SlackRubyBot.configure do |config|
34
+ config.url = "https://#{client.team.domain}.slack.com"
35
+ config.team = client.team.name
36
+ config.team_id = client.team.id
37
+ config.user = client.self.name
38
+ config.user_id = client.self.id
39
+ logger.info "Welcome #{config.user} to the #{config.team} team."
40
+ end
41
+ end
42
+ super
43
+ end
44
+
45
+ def reset!
46
+ super
47
+ SlackRubyBot.configure do |config|
48
+ config.url = nil
49
+ config.team = nil
50
+ config.user = nil
51
+ config.team_id = nil
52
+ config.user_id = nil
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlackRubyBot
4
+ class Bot < SlackRubyBot::Commands::Base
5
+ delegate :client, to: :instance
6
+
7
+ def self.run
8
+ instance.run
9
+ end
10
+
11
+ def self.instance
12
+ SlackRubyBot::App.instance
13
+ end
14
+
15
+ def self.call(client, data, _match)
16
+ client.say(channel: data.channel, text: "Sorry <@#{data.user}>, I don't understand that command!")
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlackRubyBot
4
+ class Client < Slack::RealTime::Client
5
+ include Loggable
6
+ attr_accessor :aliases
7
+ attr_accessor :allow_bot_messages
8
+ attr_accessor :allow_message_loops
9
+
10
+ def initialize(attrs = {})
11
+ super(attrs)
12
+ @aliases = attrs[:aliases]
13
+ @allow_message_loops = attrs[:allow_message_loops]
14
+ @allow_bot_messages = attrs[:allow_bot_messages]
15
+ end
16
+
17
+ def allow_message_loops?
18
+ @allow_message_loops.nil? ? SlackRubyBot::Config.allow_message_loops? : !!@allow_message_loops
19
+ end
20
+
21
+ def allow_bot_messages?
22
+ @allow_bot_messages.nil? ? SlackRubyBot::Config.allow_bot_messages? : !!@allow_bot_messages
23
+ end
24
+
25
+ def message_to_self?(data)
26
+ !!(self.self && self.self.id == data.user)
27
+ end
28
+
29
+ def bot_message?(data)
30
+ data.subtype == 'bot_message'
31
+ end
32
+
33
+ def names
34
+ [
35
+ SlackRubyBot::Config.user,
36
+ self.self ? self.self.name : nil,
37
+ aliases ? aliases.map(&:downcase) : nil,
38
+ SlackRubyBot::Config.aliases ? SlackRubyBot::Config.aliases.map(&:downcase) : nil,
39
+ self.self && self.self.id ? "<@#{self.self.id.downcase}>" : nil,
40
+ SlackRubyBot::Config.user_id ? "<@#{SlackRubyBot::Config.user_id.downcase}>" : nil,
41
+ self.self && self.self.id ? "<@#{self.self.id.downcase}>:" : nil,
42
+ SlackRubyBot::Config.user_id ? "<@#{SlackRubyBot::Config.user_id.downcase}>:" : nil,
43
+ self.self && self.self.name ? "#{self.self.name.downcase}:" : nil,
44
+ SlackRubyBot::Config.user ? "#{SlackRubyBot::Config.user}:" : nil
45
+ ].compact.flatten
46
+ end
47
+
48
+ def name?(name)
49
+ name && names.include?(name.downcase)
50
+ end
51
+
52
+ def name
53
+ SlackRubyBot.config.user || self.self&.name
54
+ end
55
+
56
+ def url
57
+ SlackRubyBot.config.url || super
58
+ end
59
+
60
+ def say(options = {})
61
+ logger.warn '[DEPRECATION] `gif:` is deprecated and has no effect.' if options.key?(:gif)
62
+ message({ text: '' }.merge(options))
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlackRubyBot
4
+ module Commands
5
+ class Default < Base
6
+ command 'about'
7
+ match(/^#{bot_matcher}$/u)
8
+
9
+ def self.call(client, data, _match)
10
+ client.say(channel: data.channel, text: SlackRubyBot::ABOUT)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'support/match'
4
+ require_relative 'support/help'
5
+
6
+ module SlackRubyBot
7
+ module Commands
8
+ class Base
9
+ include Loggable
10
+
11
+ class << self
12
+ attr_accessor :command_classes
13
+
14
+ def inherited(subclass)
15
+ SlackRubyBot::Commands::Base.command_classes ||= []
16
+ SlackRubyBot::Commands::Base.command_classes << subclass
17
+ end
18
+
19
+ def help(&block)
20
+ Support::Help.instance.capture_help(self, &block)
21
+ end
22
+
23
+ def command_name_from_class
24
+ name ? name.split(':').last.downcase : object_id.to_s
25
+ end
26
+
27
+ def operator(*values, &block)
28
+ values = values.map { |value| Regexp.escape(value) }.join('|')
29
+ match Regexp.new("^(?<operator>#{values})(?<expression>.*)$", Regexp::IGNORECASE), &block
30
+ end
31
+
32
+ def command(*values, &block)
33
+ values = values.map { |value| value.is_a?(Regexp) ? value.source : Regexp.escape(value) }.join('|')
34
+ match Regexp.new("^#{bot_matcher}[[:space:]]+(?<command>#{values})([[:space:]]+(?<expression>.*)|)$", Regexp::IGNORECASE | Regexp::MULTILINE), &block
35
+ end
36
+
37
+ def invoke(client, data)
38
+ finalize_routes!
39
+ expression, text = parse(client, data)
40
+ return false unless expression || data.attachments
41
+
42
+ routes.each_pair do |route, options|
43
+ match_method = options[:match_method]
44
+ case match_method
45
+ when :match
46
+ next unless expression
47
+
48
+ match = route.match(expression)
49
+ match ||= route.match(text) if text
50
+ next unless match
51
+ next if match.names.include?('bot') && !client.name?(match['bot'])
52
+
53
+ match = Support::Match.new(match)
54
+ when :scan
55
+ next unless expression
56
+
57
+ match = expression.scan(route)
58
+ next unless match.any?
59
+ when :attachment
60
+ next unless data.attachments && !data.attachments.empty?
61
+
62
+ match, attachment, field = match_attachments(data, route, options[:fields_to_scan])
63
+ next unless match
64
+
65
+ match = Support::Match.new(match, attachment, field)
66
+ end
67
+ call_command(client, data, match, options[:block])
68
+ return true
69
+ end
70
+ false
71
+ end
72
+
73
+ def match(match, &block)
74
+ routes[match] = { match_method: :match, block: block }
75
+ end
76
+
77
+ def scan(match, &block)
78
+ routes[match] = { match_method: :scan, block: block }
79
+ end
80
+
81
+ def attachment(match, fields_to_scan = nil, &block)
82
+ fields_to_scan = [fields_to_scan] unless fields_to_scan.nil? || fields_to_scan.is_a?(Array)
83
+ routes[match] = {
84
+ match_method: :attachment,
85
+ block: block,
86
+ fields_to_scan: fields_to_scan
87
+ }
88
+ end
89
+
90
+ def bot_matcher
91
+ '(?<bot>\S*)'
92
+ end
93
+
94
+ def routes
95
+ @routes ||= ActiveSupport::OrderedHash.new
96
+ end
97
+
98
+ private
99
+
100
+ def call_command(client, data, match, block)
101
+ if block
102
+ block.call(client, data, match) if permitted?(client, data, match)
103
+ elsif respond_to?(:call)
104
+ send(:call, client, data, match) if permitted?(client, data, match)
105
+ else
106
+ raise NotImplementedError, data.text
107
+ end
108
+ end
109
+
110
+ def parse(client, data)
111
+ text = data.text
112
+ return text unless direct_message?(data) && message_from_another_user?(data)
113
+ return text if message_begins_with_bot_mention?(text, client.names)
114
+
115
+ ["#{client.name} #{text}", text]
116
+ end
117
+
118
+ def direct_message?(data)
119
+ data.channel && data.channel[0] == 'D'
120
+ end
121
+
122
+ def message_from_another_user?(data)
123
+ data.user && data.user != SlackRubyBot.config.user_id
124
+ end
125
+
126
+ def message_begins_with_bot_mention?(text, bot_names)
127
+ bot_names = bot_names.join('|')
128
+ !!text.downcase.match(/\A(#{bot_names})\s|\A(#{bot_names})\z/)
129
+ end
130
+
131
+ def finalize_routes!
132
+ return if routes&.any?
133
+
134
+ command command_name_from_class
135
+ end
136
+
137
+ def match_attachments(data, route, fields_to_scan = nil)
138
+ fields_to_scan ||= %i[pretext text title]
139
+ data.attachments.each do |attachment|
140
+ fields_to_scan.each do |field|
141
+ next unless attachment[field]
142
+
143
+ match = route.match(attachment[field])
144
+ return match, attachment, field if match
145
+ end
146
+ end
147
+ false
148
+ end
149
+
150
+ # Intended to be overridden by subclasses to hook in an
151
+ # authorization mechanism.
152
+ def permitted?(_client, _data, _match)
153
+ true
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlackRubyBot
4
+ module Commands
5
+ class Help < Base
6
+ help do
7
+ title 'help'
8
+ desc 'Shows help information.'
9
+ end
10
+
11
+ command 'help' do |client, data, match|
12
+ command = match[:expression]
13
+
14
+ text = if command.present?
15
+ Support::Help.instance.command_full_desc(command)
16
+ else
17
+ general_text
18
+ end
19
+
20
+ client.say(channel: data.channel, text: text)
21
+ end
22
+
23
+ class << self
24
+ private
25
+
26
+ def general_text
27
+ bot_desc = Support::Help.instance.bot_desc_and_commands
28
+ other_commands_descs = Support::Help.instance.other_commands_descs
29
+ <<~TEXT
30
+ #{bot_desc.join("\n")}
31
+
32
+ *Other commands:*
33
+ #{other_commands_descs.join("\n")}
34
+
35
+ For getting description of the command use: *help <command>*
36
+
37
+ For more information see https://github.com/slack-ruby/slack-ruby-bot, please.
38
+ TEXT
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlackRubyBot
4
+ module Commands
5
+ class Hi < Base
6
+ help do
7
+ title 'hi'
8
+ desc 'Says hello.'
9
+ end
10
+
11
+ def self.call(client, data, _match)
12
+ client.say(channel: data.channel, text: "Hi <@#{data.user}>!")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlackRubyBot
4
+ module Commands
5
+ module Support
6
+ class Attrs
7
+ attr_accessor :command_name, :command_desc, :command_long_desc
8
+ attr_reader :klass, :commands
9
+
10
+ def initialize(klass)
11
+ @klass = klass
12
+ @commands = []
13
+ end
14
+
15
+ def title(title)
16
+ self.command_name = title
17
+ end
18
+
19
+ def desc(desc)
20
+ self.command_desc = desc
21
+ end
22
+
23
+ def long_desc(long_desc)
24
+ self.command_long_desc = long_desc
25
+ end
26
+
27
+ def command(title, &block)
28
+ @commands << self.class.new(klass).tap do |k|
29
+ k.title(title)
30
+ k.instance_eval(&block)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative 'attrs'
5
+
6
+ module SlackRubyBot
7
+ module Commands
8
+ module Support
9
+ class Help
10
+ include Singleton
11
+ attr_reader :commands_help_attrs
12
+
13
+ def initialize
14
+ @commands_help_attrs = []
15
+ end
16
+
17
+ def capture_help(klass, &block)
18
+ k = Commands::Support::Attrs.new(klass)
19
+ k.instance_eval(&block)
20
+ @commands_help_attrs << k
21
+ end
22
+
23
+ def bot_desc_and_commands
24
+ collect_help_attrs(bot_help_attrs) do |help_attrs|
25
+ bot_commands_descs = collect_name_and_desc(help_attrs.commands)
26
+ "#{command_name_and_desc(help_attrs)}\n\n*Commands:*\n#{bot_commands_descs.join("\n")}"
27
+ end
28
+ end
29
+
30
+ def other_commands_descs
31
+ collect_name_and_desc(other_commands_help_attrs)
32
+ end
33
+
34
+ def command_full_desc(name)
35
+ unescaped_name = Slack::Messages::Formatting.unescape(name)
36
+ help_attrs = find_command_help_attrs(unescaped_name)
37
+ return "There's no command *#{unescaped_name}*" unless help_attrs
38
+ return "There's no description for command *#{unescaped_name}*" if help_attrs.command_long_desc.blank?
39
+
40
+ "#{command_name_and_desc(help_attrs)}\n\n#{help_attrs.command_long_desc}"
41
+ end
42
+
43
+ def find_command_help_attrs(name)
44
+ help_attrs = commands_help_attrs.find { |k| k.command_name == name }
45
+ return help_attrs if help_attrs
46
+
47
+ commands_help_attrs.each { |k| k.commands.each { |c| return c if c.command_name == name } }
48
+ nil
49
+ end
50
+
51
+ private
52
+
53
+ def collect_help_attrs(help_attrs)
54
+ help_attrs_with_present_names(help_attrs).map do |ha|
55
+ yield(ha)
56
+ end
57
+ end
58
+
59
+ def collect_name_and_desc(help_attrs)
60
+ collect_help_attrs(help_attrs) do |ha|
61
+ command_name_and_desc(ha)
62
+ end
63
+ end
64
+
65
+ def command_name_and_desc(help_attrs)
66
+ desc = help_attrs.command_desc.present? ? " - #{help_attrs.command_desc}" : ''
67
+ "*#{help_attrs.command_name}*#{desc}"
68
+ end
69
+
70
+ def help_attrs_with_present_names(help_attrs)
71
+ help_attrs.select { |k| k.command_name.present? }
72
+ end
73
+
74
+ def bot_help_attrs
75
+ commands_help_attrs.select { |k| k.klass.ancestors.include?(SlackRubyBot::Bot) }
76
+ end
77
+
78
+ def other_commands_help_attrs
79
+ commands_help_attrs.select { |k| k.klass.ancestors.include?(SlackRubyBot::Commands::Base) && !k.klass.ancestors.include?(SlackRubyBot::Bot) }
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlackRubyBot
4
+ module Commands
5
+ module Support
6
+ class Match
7
+ extend Forwardable
8
+
9
+ delegate MatchData.public_instance_methods(false) => :@match_data
10
+
11
+ attr_reader :attachment, :attachment_field
12
+
13
+ def initialize(match_data, attachment = nil, attachment_field = nil)
14
+ raise ArgumentError, 'match_data should be a type of MatchData' unless match_data.is_a? MatchData
15
+
16
+ @match_data = match_data
17
+ @attachment = attachment
18
+ @attachment_field = attachment_field
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlackRubyBot
4
+ module Commands
5
+ class Unknown < Base
6
+ match(/^(?<bot>\S*)[\s]*(?<expression>.*)$/)
7
+
8
+ def self.call(client, data, _match)
9
+ client.say(channel: data.channel, text: "Sorry <@#{data.user}>, I don't understand that command!")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'slack-ruby-bot/commands/base'
4
+ require 'slack-ruby-bot/commands/about'
5
+ require 'slack-ruby-bot/commands/help'
6
+ require 'slack-ruby-bot/commands/hi'
7
+ require 'slack-ruby-bot/commands/unknown'
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlackRubyBot
4
+ module Config
5
+ extend self
6
+
7
+ ATTRS = %i[token url aliases user user_id team team_id allow_bot_messages allow_message_loops logger].freeze
8
+ attr_accessor(*ATTRS)
9
+
10
+ def allow_bot_messages?
11
+ !!allow_bot_messages
12
+ end
13
+
14
+ def allow_message_loops?
15
+ !!allow_message_loops
16
+ end
17
+
18
+ def reset!
19
+ ATTRS.each { |attr| send("#{attr}=", nil) }
20
+ end
21
+
22
+ private
23
+
24
+ def boolean_from_env(key)
25
+ value = ENV[key]
26
+ case value
27
+ when nil
28
+ nil
29
+ when 0, 'false', 'no'
30
+ false
31
+ when 1, 'true', 'yes'
32
+ true
33
+ else
34
+ raise ArgumentError, "Invalid value for #{key}: #{value}."
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlackRubyBot
4
+ module Hooks
5
+ class Hello
6
+ attr_accessor :logger, :connected_at
7
+
8
+ def initialize(logger)
9
+ self.logger = logger
10
+ end
11
+
12
+ def call(client, _data)
13
+ return unless client&.team
14
+
15
+ new_connected_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
16
+ log = [
17
+ 'Successfully',
18
+ connected_at ? 'reconnected' : 'connected',
19
+ "team #{client.team.name} (#{client.team.id}) to https://#{client.team.domain}.slack.com",
20
+ connected_at ? "after #{last_connection_till(new_connected_at)}s" : nil
21
+ ].compact.join(' ') + '.'
22
+
23
+ logger.info log
24
+
25
+ self.connected_at = new_connected_at
26
+ end
27
+
28
+ private
29
+
30
+ def last_connection_till(time)
31
+ (time - connected_at).round(2)
32
+ end
33
+ end
34
+ end
35
+ end