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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +3 -0
- data/.rubocop.yml +29 -0
- data/.rubocop_todo.yml +49 -0
- data/CHANGELOG.md +219 -0
- data/Dangerfile +5 -0
- data/Gemfile +20 -0
- data/LICENSE.md +22 -0
- data/README.md +766 -0
- data/Rakefile +21 -0
- data/lib/config/application.rb +14 -0
- data/lib/config/boot.rb +8 -0
- data/lib/config/environment.rb +5 -0
- data/lib/slack-ruby-bot/about.rb +9 -0
- data/lib/slack-ruby-bot/app.rb +56 -0
- data/lib/slack-ruby-bot/bot.rb +19 -0
- data/lib/slack-ruby-bot/client.rb +65 -0
- data/lib/slack-ruby-bot/commands/about.rb +14 -0
- data/lib/slack-ruby-bot/commands/base.rb +158 -0
- data/lib/slack-ruby-bot/commands/help.rb +43 -0
- data/lib/slack-ruby-bot/commands/hi.rb +16 -0
- data/lib/slack-ruby-bot/commands/support/attrs.rb +36 -0
- data/lib/slack-ruby-bot/commands/support/help.rb +84 -0
- data/lib/slack-ruby-bot/commands/support/match.rb +23 -0
- data/lib/slack-ruby-bot/commands/unknown.rb +13 -0
- data/lib/slack-ruby-bot/commands.rb +7 -0
- data/lib/slack-ruby-bot/config.rb +38 -0
- data/lib/slack-ruby-bot/hooks/hello.rb +35 -0
- data/lib/slack-ruby-bot/hooks/hook_support.rb +45 -0
- data/lib/slack-ruby-bot/hooks/message.rb +56 -0
- data/lib/slack-ruby-bot/hooks/set.rb +45 -0
- data/lib/slack-ruby-bot/hooks.rb +6 -0
- data/lib/slack-ruby-bot/mvc/controller/base.rb +172 -0
- data/lib/slack-ruby-bot/mvc/model/base.rb +27 -0
- data/lib/slack-ruby-bot/mvc/mvc.rb +7 -0
- data/lib/slack-ruby-bot/mvc/view/base.rb +30 -0
- data/lib/slack-ruby-bot/mvc.rb +3 -0
- data/lib/slack-ruby-bot/rspec/support/bots_for_tests.rb +35 -0
- data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/it_behaves_like_a_slack_bot.rb +16 -0
- data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/not_respond.rb +25 -0
- data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_error.rb +36 -0
- data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_slack_message.rb +34 -0
- data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/respond_with_slack_messages.rb +45 -0
- data/lib/slack-ruby-bot/rspec/support/slack-ruby-bot/start_typing.rb +32 -0
- data/lib/slack-ruby-bot/rspec/support/slack_api_key.rb +10 -0
- data/lib/slack-ruby-bot/rspec/support/slack_ruby_bot_configure.rb +15 -0
- data/lib/slack-ruby-bot/rspec/support/spec_helpers.rb +14 -0
- data/lib/slack-ruby-bot/rspec.rb +14 -0
- data/lib/slack-ruby-bot/server.rb +88 -0
- data/lib/slack-ruby-bot/support/loggable.rb +25 -0
- data/lib/slack-ruby-bot/version.rb +5 -0
- data/lib/slack-ruby-bot.rb +29 -0
- data/lib/slack_ruby_bot.rb +3 -0
- data/screenshots/aliases.gif +0 -0
- data/screenshots/create-classic-app.png +0 -0
- data/screenshots/demo.gif +0 -0
- data/screenshots/dms.gif +0 -0
- data/screenshots/help.png +0 -0
- data/screenshots/market.gif +0 -0
- data/screenshots/weather.gif +0 -0
- data/slack-ruby-bot.gemspec +32 -0
- data/slack.png +0 -0
- 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'
|
data/lib/config/boot.rb
ADDED
@@ -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,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
|