grape-slack-bot 1.0.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/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
|