slackrb 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|