grape-slack-bot 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.md +21 -0
- data/README.md +498 -0
- data/grape-slack-bot.gemspec +45 -0
- data/lib/slack_bot/api_client.rb +63 -0
- data/lib/slack_bot/args.rb +58 -0
- data/lib/slack_bot/callback.rb +109 -0
- data/lib/slack_bot/callback_storage.rb +15 -0
- data/lib/slack_bot/command.rb +72 -0
- data/lib/slack_bot/config.rb +168 -0
- data/lib/slack_bot/dev_console.rb +33 -0
- data/lib/slack_bot/errors.rb +39 -0
- data/lib/slack_bot/event.rb +41 -0
- data/lib/slack_bot/grape_extension.rb +198 -0
- data/lib/slack_bot/interaction.rb +139 -0
- data/lib/slack_bot/menu_options.rb +10 -0
- data/lib/slack_bot/pager.rb +30 -0
- data/lib/slack_bot/view.rb +53 -0
- data/lib/slack_bot.rb +24 -0
- data/spec/slack_bot/api_client_spec.rb +479 -0
- data/spec/slack_bot/args_spec.rb +34 -0
- data/spec/slack_bot/callback_spec.rb +104 -0
- data/spec/slack_bot/command_spec.rb +5 -0
- data/spec/slack_bot/config_spec.rb +57 -0
- data/spec/slack_bot/event_spec.rb +5 -0
- data/spec/slack_bot/grape_extension_spec.rb +5 -0
- data/spec/slack_bot/interaction_spec.rb +5 -0
- data/spec/slack_bot/menu_options_spec.rb +5 -0
- data/spec/slack_bot/pager_spec.rb +5 -0
- data/spec/slack_bot/view_spec.rb +5 -0
- data/spec/slack_bot_spec.rb +7 -0
- metadata +217 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'active_support/core_ext/object'
|
2
|
+
require 'active_support/core_ext/numeric/time'
|
3
|
+
|
4
|
+
module SlackBot
|
5
|
+
class Callback
|
6
|
+
CALLBACK_CACHE_KEY = "slack-bot-callback".freeze
|
7
|
+
|
8
|
+
def self.find(id, config: nil)
|
9
|
+
callback = new(id: id, config: config)
|
10
|
+
callback.reload
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.create(class_name:, method_name:, user:, channel_id: nil, config: nil)
|
14
|
+
callback =
|
15
|
+
new(class_name: class_name, method_name: method_name, user: user, channel_id: channel_id, config: config)
|
16
|
+
callback.save
|
17
|
+
callback
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :id, :data, :args, :config
|
21
|
+
def initialize(id: nil, class_name: nil, method_name: nil, user: nil, channel_id: nil, extra: nil, config: nil)
|
22
|
+
@id = id
|
23
|
+
@data = {
|
24
|
+
class_name: class_name,
|
25
|
+
method_name: method_name,
|
26
|
+
user_id: user&.id,
|
27
|
+
channel_id: channel_id,
|
28
|
+
extra: extra
|
29
|
+
}
|
30
|
+
@args = SlackBot::Args.new
|
31
|
+
@config = config || SlackBot::Config.current_instance
|
32
|
+
end
|
33
|
+
|
34
|
+
def reload
|
35
|
+
@data = read_data
|
36
|
+
SlackBot::DevConsole.log_check("SlackBot::Callback#read_data: #{id} | #{data}")
|
37
|
+
|
38
|
+
parse_args
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def save
|
43
|
+
@id = generate_id if id.blank?
|
44
|
+
serialize_args
|
45
|
+
|
46
|
+
SlackBot::DevConsole.log_check("SlackBot::Callback#write_data: #{id} | #{data}")
|
47
|
+
write_data(data)
|
48
|
+
end
|
49
|
+
|
50
|
+
def update(payload)
|
51
|
+
return if id.blank?
|
52
|
+
return if data.blank?
|
53
|
+
|
54
|
+
@data = data.merge(payload)
|
55
|
+
save
|
56
|
+
end
|
57
|
+
|
58
|
+
def destroy
|
59
|
+
return if id.blank?
|
60
|
+
|
61
|
+
delete_data
|
62
|
+
end
|
63
|
+
|
64
|
+
def user
|
65
|
+
@user ||= begin
|
66
|
+
user_id = data&.dig(:user_id)
|
67
|
+
config.callback_user_finder_method.call(user_id) if user_id.present?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def handler_class
|
72
|
+
return if class_name.blank?
|
73
|
+
|
74
|
+
config.find_handler_class(class_name)
|
75
|
+
end
|
76
|
+
|
77
|
+
def method_missing(method_name, *args, &block)
|
78
|
+
return data[method_name.to_sym] if data.key?(method_name.to_sym)
|
79
|
+
|
80
|
+
super
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def parse_args
|
86
|
+
args.raw_args = data&.dig(:args)
|
87
|
+
end
|
88
|
+
|
89
|
+
def serialize_args
|
90
|
+
data[:args] = args.to_s
|
91
|
+
end
|
92
|
+
|
93
|
+
def generate_id
|
94
|
+
SecureRandom.uuid
|
95
|
+
end
|
96
|
+
|
97
|
+
def read_data
|
98
|
+
config.callback_storage_instance.read("#{CALLBACK_CACHE_KEY}:#{id}")
|
99
|
+
end
|
100
|
+
|
101
|
+
def write_data(data)
|
102
|
+
config.callback_storage_instance.write("#{CALLBACK_CACHE_KEY}:#{id}", data, expires_in: 1.hour)
|
103
|
+
end
|
104
|
+
|
105
|
+
def delete_data
|
106
|
+
config.callback_storage_instance.delete("#{CALLBACK_CACHE_KEY}:#{id}")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module SlackBot
|
2
|
+
class Command
|
3
|
+
def self.interaction(klass)
|
4
|
+
define_singleton_method(:interaction_klass) { klass }
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.view(klass)
|
8
|
+
define_singleton_method(:view_klass) { klass }
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :current_user, :params, :args, :config
|
12
|
+
def initialize(current_user:, params:, args:, config: nil)
|
13
|
+
@current_user = current_user
|
14
|
+
@params = params
|
15
|
+
@config = config || SlackBot::Config.current_instance
|
16
|
+
|
17
|
+
@args = SlackBot::Args.new
|
18
|
+
@args.raw_args = args
|
19
|
+
end
|
20
|
+
|
21
|
+
def command
|
22
|
+
params[:command]
|
23
|
+
end
|
24
|
+
|
25
|
+
def text
|
26
|
+
params[:text]
|
27
|
+
end
|
28
|
+
|
29
|
+
def only_user?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def only_direct_message?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def only_slack_team?
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
def render_response(response_type = nil, **kwargs)
|
42
|
+
return if !response_type
|
43
|
+
|
44
|
+
{
|
45
|
+
response_type: response_type
|
46
|
+
}.merge(kwargs)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def open_modal(view_name, method_name: nil, context: nil)
|
52
|
+
view = self.class.view_klass.new(
|
53
|
+
args: args,
|
54
|
+
current_user: @current_user,
|
55
|
+
params: params,
|
56
|
+
context: context,
|
57
|
+
config: config
|
58
|
+
)
|
59
|
+
payload = view.send(view_name)
|
60
|
+
self.class.interaction_klass.open_modal(
|
61
|
+
trigger_id: params[:trigger_id],
|
62
|
+
channel_id: params[:channel_id],
|
63
|
+
class_name: self.class.name,
|
64
|
+
method_name: method_name,
|
65
|
+
user: @current_user,
|
66
|
+
payload: payload,
|
67
|
+
config: config
|
68
|
+
)
|
69
|
+
render_response
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'active_support/core_ext/object'
|
2
|
+
|
3
|
+
module SlackBot
|
4
|
+
class Config
|
5
|
+
def self.current_instance
|
6
|
+
@@current_instances ||= {}
|
7
|
+
@@current_instances[self.name] ||= self.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configure(&block)
|
11
|
+
current_instance.instance_eval(&block)
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :callback_storage_instance
|
15
|
+
def callback_storage(klass)
|
16
|
+
@callback_storage_instance = klass
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :callback_user_finder_method
|
20
|
+
def callback_user_finder(method_lambda)
|
21
|
+
@callback_user_finder_method = method_lambda
|
22
|
+
end
|
23
|
+
|
24
|
+
def event_handlers
|
25
|
+
@event_handlers ||= {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def event(event_type, event_klass)
|
29
|
+
event_handlers[event_type.to_sym] = event_klass
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_event_handler(event_type)
|
33
|
+
event_handlers[event_type.to_sym]
|
34
|
+
end
|
35
|
+
|
36
|
+
def slash_command_endpoint(url_token, command_klass = nil, &block)
|
37
|
+
@slash_command_endpoints ||= {}
|
38
|
+
@slash_command_endpoints[url_token.to_sym] ||=
|
39
|
+
begin
|
40
|
+
endpoint =
|
41
|
+
SlashCommandEndpointConfig.new(url_token, command_klass: command_klass, config: self)
|
42
|
+
endpoint.instance_eval(&block) if block_given?
|
43
|
+
endpoint
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def slash_command_endpoints
|
48
|
+
@slash_command_endpoints ||= {}
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_slash_command_config(url_token, command, text)
|
52
|
+
endpoint_config = slash_command_endpoints[url_token.to_sym]
|
53
|
+
return if endpoint_config.blank?
|
54
|
+
|
55
|
+
endpoint_config.find_command_config(text) || endpoint_config
|
56
|
+
end
|
57
|
+
|
58
|
+
def menu_options(action_id, klass)
|
59
|
+
@menu_options ||= {}
|
60
|
+
@menu_options[action_id.to_sym] = klass
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_menu_options(action_id)
|
64
|
+
@menu_options ||= {}
|
65
|
+
@menu_options[action_id.to_sym]
|
66
|
+
end
|
67
|
+
|
68
|
+
def handler_class(class_name, klass)
|
69
|
+
@handler_classes ||= {}
|
70
|
+
@handler_classes[class_name.to_sym] = klass
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_handler_class(class_name)
|
74
|
+
@handler_classes ||= {}
|
75
|
+
@handler_classes[class_name.to_sym]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class SlashCommandEndpointConfig
|
80
|
+
attr_reader :url_token, :command_klass, :routes, :config
|
81
|
+
def initialize(url_token, config:, command_klass: nil, routes: {})
|
82
|
+
@url_token = url_token
|
83
|
+
@command_klass = command_klass
|
84
|
+
@routes = routes
|
85
|
+
@config = config
|
86
|
+
|
87
|
+
config.handler_class(command_klass.name, command_klass) if command_klass.present?
|
88
|
+
end
|
89
|
+
|
90
|
+
def command(command_token, command_klass, &block)
|
91
|
+
@command_configs ||= {}
|
92
|
+
@command_configs[command_token.to_sym] ||=
|
93
|
+
begin
|
94
|
+
command =
|
95
|
+
SlashCommandConfig.new(
|
96
|
+
command_klass: command_klass,
|
97
|
+
token: command_token,
|
98
|
+
endpoint: self
|
99
|
+
)
|
100
|
+
command.instance_eval(&block) if block_given?
|
101
|
+
command
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def command_configs
|
106
|
+
@command_configs ||= {}
|
107
|
+
end
|
108
|
+
|
109
|
+
def find_command_config(text)
|
110
|
+
route_key = text.scan(/^(#{routes.keys.join("|")})(?:\s|$)/).flatten.first
|
111
|
+
return if route_key.blank?
|
112
|
+
|
113
|
+
routes[route_key]
|
114
|
+
end
|
115
|
+
|
116
|
+
def full_token
|
117
|
+
""
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class SlashCommandConfig
|
122
|
+
def self.delimiter
|
123
|
+
" "
|
124
|
+
end
|
125
|
+
|
126
|
+
attr_accessor :command_klass, :token, :parent_configs, :endpoint
|
127
|
+
def initialize(command_klass:, token:, endpoint:, parent_configs: [])
|
128
|
+
@command_klass = command_klass
|
129
|
+
@token = token
|
130
|
+
@parent_configs = parent_configs || []
|
131
|
+
@endpoint = endpoint
|
132
|
+
|
133
|
+
endpoint.routes[full_token] = self
|
134
|
+
endpoint.config.handler_class(command_klass.name, command_klass)
|
135
|
+
end
|
136
|
+
|
137
|
+
def argument_command(argument_token, klass = nil, &block)
|
138
|
+
@argument_command_configs ||= {}
|
139
|
+
@argument_command_configs[argument_token.to_sym] ||=
|
140
|
+
SlashCommandConfig.new(
|
141
|
+
command_klass: command_klass,
|
142
|
+
token: argument_token,
|
143
|
+
parent_configs: [self] + (parent_configs || []),
|
144
|
+
endpoint: endpoint
|
145
|
+
)
|
146
|
+
|
147
|
+
command_config = @argument_command_configs[argument_token.to_sym]
|
148
|
+
command_config.instance_eval(&block) if block_given?
|
149
|
+
|
150
|
+
command_config
|
151
|
+
end
|
152
|
+
|
153
|
+
def find_argument_command_config(argument_token)
|
154
|
+
@argument_command_configs ||= {}
|
155
|
+
@argument_command_configs[argument_token.to_sym]
|
156
|
+
end
|
157
|
+
|
158
|
+
def full_token
|
159
|
+
[parent_configs.map(&:token), token].flatten.compact.join(
|
160
|
+
self.class.delimiter
|
161
|
+
)
|
162
|
+
end
|
163
|
+
|
164
|
+
def url_token
|
165
|
+
endpoint.url_token
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module SlackBot
|
2
|
+
class DevConsole
|
3
|
+
def self.enabled=(value)
|
4
|
+
@enabled = value
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.enabled?
|
8
|
+
@enabled
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.log(message = nil, &)
|
12
|
+
return unless enabled?
|
13
|
+
|
14
|
+
message = yield if block_given?
|
15
|
+
Rails.logger.info(message)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.log_input(message = nil, &)
|
19
|
+
message = yield if block_given?
|
20
|
+
log(">>> #{message}")
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.log_output(message = nil, &)
|
24
|
+
message = yield if block_given?
|
25
|
+
log("<<< #{message}")
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.log_check(message = nil, &)
|
29
|
+
message = yield if block_given?
|
30
|
+
log("!!! #{message}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SlackBot
|
2
|
+
module Errors
|
3
|
+
class SignatureAuthenticationError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class TeamAuthenticationError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
class ChannelAuthenticationError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class UserAuthenticationError < StandardError
|
13
|
+
end
|
14
|
+
|
15
|
+
class SlashCommandNotImplemented < StandardError
|
16
|
+
end
|
17
|
+
|
18
|
+
class MenuOptionsNotImplemented < StandardError
|
19
|
+
end
|
20
|
+
|
21
|
+
class SlackResponseError < StandardError
|
22
|
+
attr_reader :error, :data, :payload
|
23
|
+
def initialize(error, data: nil, payload: nil)
|
24
|
+
@error = error
|
25
|
+
@data = data
|
26
|
+
@payload = payload
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class OpenModalError < SlackResponseError
|
31
|
+
end
|
32
|
+
|
33
|
+
class UpdateModalError < SlackResponseError
|
34
|
+
end
|
35
|
+
|
36
|
+
class PublishViewError < SlackResponseError
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module SlackBot
|
2
|
+
class Event
|
3
|
+
def self.view(klass)
|
4
|
+
define_singleton_method(:view_klass) { klass }
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :current_user, :params, :callback, :config
|
8
|
+
def initialize(current_user: nil, params: nil, callback: nil, config: nil)
|
9
|
+
@current_user = current_user
|
10
|
+
@params = params
|
11
|
+
@callback = callback
|
12
|
+
@config = config || SlackBot::Config.current_instance
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def event_type
|
22
|
+
params["event"]["type"]
|
23
|
+
end
|
24
|
+
|
25
|
+
def publish_view(view_method_name)
|
26
|
+
user_id = params["event"]["user"]
|
27
|
+
view =
|
28
|
+
self.class.view_klass
|
29
|
+
.new(current_user: current_user, params: params)
|
30
|
+
.send(view_method_name)
|
31
|
+
response =
|
32
|
+
SlackBot::ApiClient.new.views_publish(user_id: user_id, view: view)
|
33
|
+
|
34
|
+
if !response.ok?
|
35
|
+
raise SlackBot::Errors::PublishViewError.new(response.error, data: response.data, payload: view)
|
36
|
+
end
|
37
|
+
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'active_support/core_ext/object'
|
2
|
+
|
3
|
+
module SlackBot
|
4
|
+
module GrapeExtension
|
5
|
+
def self.included(base)
|
6
|
+
base.format :json
|
7
|
+
base.content_type :json, "application/json"
|
8
|
+
base.use ActionDispatch::RemoteIp
|
9
|
+
base.helpers do
|
10
|
+
def fetch_team_id
|
11
|
+
params.dig("team_id") || params.dig("team", "id")
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch_user_id
|
15
|
+
params.dig("user_id") || params.dig("user", "id") ||
|
16
|
+
params.dig("event", "user")
|
17
|
+
end
|
18
|
+
|
19
|
+
def verify_slack_signature!
|
20
|
+
slack_signing_secret = ENV.fetch("SLACK_SIGNING_SECRET")
|
21
|
+
timestamp = request.headers["X-Slack-Request-Timestamp"]
|
22
|
+
request_body = request.body.read
|
23
|
+
sig_basestring = "v0:#{timestamp}:#{request_body}"
|
24
|
+
my_signature =
|
25
|
+
"v0=" +
|
26
|
+
OpenSSL::HMAC.hexdigest(
|
27
|
+
OpenSSL::Digest.new("sha256"),
|
28
|
+
slack_signing_secret,
|
29
|
+
sig_basestring
|
30
|
+
)
|
31
|
+
slack_signature = request.headers["X-Slack-Signature"]
|
32
|
+
if ActiveSupport::SecurityUtils.secure_compare(
|
33
|
+
my_signature,
|
34
|
+
slack_signature
|
35
|
+
)
|
36
|
+
true
|
37
|
+
else
|
38
|
+
raise SlackBot::SignatureAuthenticationError.new("Signature mismatch")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def verify_slack_team!
|
43
|
+
slack_team_id = ENV.fetch("SLACK_TEAM_ID")
|
44
|
+
if slack_team_id == fetch_team_id
|
45
|
+
true
|
46
|
+
else
|
47
|
+
raise SlackBot::TeamAuthenticationError.new("Team is not authorized")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def verify_direct_message_channel!
|
52
|
+
if params[:channel_name] == "directmessage"
|
53
|
+
true
|
54
|
+
else
|
55
|
+
raise SlackBot::ChannelAuthenticationError.new(
|
56
|
+
"This command is only available in direct messages"
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def verify_current_user!
|
62
|
+
if current_user
|
63
|
+
true
|
64
|
+
else
|
65
|
+
raise SlackBot::UserAuthenticationError.new("User is not authorized")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def events_callback(params)
|
70
|
+
verify_slack_team!
|
71
|
+
|
72
|
+
SlackBot::DevConsole.log_input "SlackApi::Events#events_callback: #{params.inspect}"
|
73
|
+
handler = config.find_event_handler(params[:event][:type].to_sym)
|
74
|
+
return if handler.blank?
|
75
|
+
|
76
|
+
event = handler.new(params: params, current_user: current_user)
|
77
|
+
event.call
|
78
|
+
end
|
79
|
+
|
80
|
+
def url_verification(params)
|
81
|
+
SlackBot::DevConsole.log_input "SlackApi::Events#url_verification: #{params.inspect}"
|
82
|
+
{ challenge: params[:challenge] }
|
83
|
+
end
|
84
|
+
|
85
|
+
def handle_block_actions_view(view:, user:, params:)
|
86
|
+
callback_id = view&.dig("callback_id")
|
87
|
+
callback = SlackBot::Callback.find(callback_id, config: config)
|
88
|
+
|
89
|
+
if callback.blank?
|
90
|
+
raise "Callback not found"
|
91
|
+
end
|
92
|
+
|
93
|
+
SlackBot::DevConsole.log_check "SlackApi::Interactions##{__method__}: #{callback.id} #{callback.extra} #{callback.user_id} #{user&.id}"
|
94
|
+
|
95
|
+
if callback.user_id != user.id
|
96
|
+
raise "Callback user is not equal to action user"
|
97
|
+
end
|
98
|
+
|
99
|
+
interaction_klass = callback.handler_class&.interaction_klass
|
100
|
+
return if interaction_klass.blank?
|
101
|
+
|
102
|
+
interaction_klass.new(current_user: user, params: params, callback: callback, config: config).call
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
base.before do
|
107
|
+
verify_slack_signature!
|
108
|
+
end
|
109
|
+
|
110
|
+
base.resource :commands do
|
111
|
+
post ":url_token" do
|
112
|
+
command_config = config.find_slash_command_config(params[:url_token], params[:command], params[:text])
|
113
|
+
command_klass = command_config&.command_klass
|
114
|
+
raise SlackBot::Errors::SlashCommandNotImplemented.new if command_klass.blank?
|
115
|
+
|
116
|
+
args = params[:text].gsub(/^#{command_config.full_token}\s?/, "")
|
117
|
+
SlackBot::DevConsole.log_input "SlackApi::SlashCommands#post: #{command_config.url_token} | #{command_config.full_token} | #{args}"
|
118
|
+
|
119
|
+
action =
|
120
|
+
command_klass.new(
|
121
|
+
current_user: current_user,
|
122
|
+
params: params,
|
123
|
+
args: args,
|
124
|
+
config: config
|
125
|
+
)
|
126
|
+
verify_slack_team! if action.only_slack_team?
|
127
|
+
verify_direct_message_channel! if action.only_direct_message?
|
128
|
+
verify_current_user! if action.only_user?
|
129
|
+
|
130
|
+
result = action.call
|
131
|
+
return body false if !result
|
132
|
+
|
133
|
+
result
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
base.resource :interactions do
|
138
|
+
post do
|
139
|
+
payload = JSON.parse(params[:payload])
|
140
|
+
|
141
|
+
action_user_session =
|
142
|
+
resolve_user_session(
|
143
|
+
payload.dig("user", "team_id"),
|
144
|
+
payload.dig("user", "id")
|
145
|
+
)
|
146
|
+
action_user = action_user_session&.user
|
147
|
+
|
148
|
+
action_type = payload["type"]
|
149
|
+
result = case action_type
|
150
|
+
when "block_actions", "view_submission"
|
151
|
+
handle_block_actions_view(
|
152
|
+
view: payload["view"],
|
153
|
+
user: action_user,
|
154
|
+
params: params
|
155
|
+
)
|
156
|
+
else
|
157
|
+
raise "Unknown action type: #{action_type}"
|
158
|
+
end
|
159
|
+
|
160
|
+
return body false if result.blank?
|
161
|
+
|
162
|
+
result
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
base.resource :events do
|
167
|
+
post do
|
168
|
+
result =
|
169
|
+
case params[:type]
|
170
|
+
when "url_verification"
|
171
|
+
url_verification(params)
|
172
|
+
when "event_callback"
|
173
|
+
events_callback(params)
|
174
|
+
end
|
175
|
+
|
176
|
+
return body false if result.blank?
|
177
|
+
|
178
|
+
result
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
base.resource :menu_options do
|
183
|
+
get do
|
184
|
+
SlackBot::DevConsole.log_input "SlackApi::MenuOptions#get: #{params.inspect}"
|
185
|
+
|
186
|
+
action_id = params[:action_id]
|
187
|
+
menu_options_klass = config.find_menu_options(action_id)
|
188
|
+
raise SlackBot::Errors::MenuOptionsNotImplemented.new if menu_options_klass.blank?
|
189
|
+
|
190
|
+
menu_options = menu_options_klass.new(current_user: current_user, params: params, config: config).call
|
191
|
+
return body false if menu_options.blank?
|
192
|
+
|
193
|
+
menu_options
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|