slackrb 0.17.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.
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