chat_sdk-slack 0.9.0 → 1.0.1
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 +4 -4
- data/lib/chat_sdk/slack/adapter.rb +148 -26
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f3267f830fd79846f929e7e61be15c2b65d1e6183f6f5d56271f88fbb6fdf85b
|
|
4
|
+
data.tar.gz: 9fe0cd67184ef4561a2e6ccb6d5678f5a639f2b53add2be5ef6436b239a1a7c3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c686e9ac2a237c82166200783289f2310cb57cc3760a4d55b11447fa0f7a746b10ffb40b713d18334f277f3efb91663733977f082cf0152ee96ed387ddbd2ed1
|
|
7
|
+
data.tar.gz: 1e897869f36b3571e31dea33be0e78f9bd46bcbc5e3c839da11453337242378933dcfec8eec289ceb4c6cbae4c339bce0a1c4399f39fe74a7980703f5e1e4c40
|
|
@@ -8,22 +8,92 @@ module ChatSDK
|
|
|
8
8
|
:streaming_edit, :threads, :direct_messages, :message_history,
|
|
9
9
|
:scheduled_messages
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def initialize(bot_token: nil, signing_secret: nil)
|
|
11
|
+
def initialize(bot_token: nil, signing_secret: nil,
|
|
12
|
+
client_id: nil, client_secret: nil)
|
|
14
13
|
@bot_token = bot_token || ENV["SLACK_BOT_TOKEN"]
|
|
15
14
|
@signing_secret = signing_secret || ENV["SLACK_SIGNING_SECRET"]
|
|
15
|
+
@client_id = client_id || ENV["SLACK_CLIENT_ID"]
|
|
16
|
+
@client_secret = client_secret || ENV["SLACK_CLIENT_SECRET"]
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
unless @bot_token || @client_id
|
|
19
|
+
raise ChatSDK::ConfigurationError, "Slack bot_token or client_id required"
|
|
20
|
+
end
|
|
18
21
|
raise ChatSDK::ConfigurationError, "Slack signing_secret required" unless @signing_secret
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
if @bot_token
|
|
24
|
+
::Slack.configure do |config|
|
|
25
|
+
config.token = @bot_token
|
|
26
|
+
end
|
|
27
|
+
@client = ::Slack::Web::Client.new(token: @bot_token)
|
|
22
28
|
end
|
|
23
29
|
|
|
24
|
-
@client = ::Slack::Web::Client.new(token: @bot_token)
|
|
25
30
|
@renderer = BlockKitRenderer.new
|
|
26
31
|
@modal_renderer = ModalRenderer.new
|
|
32
|
+
@team_clients = {}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns the per-request client (multi-workspace) or the static client (single-workspace).
|
|
36
|
+
def client
|
|
37
|
+
::Thread.current[:chat_sdk_slack_client] || @client
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Inject state store after initialization (e.g., from Chat instance).
|
|
41
|
+
def set_state(state)
|
|
42
|
+
@state = state
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# --- Multi-workspace installation management ---
|
|
46
|
+
|
|
47
|
+
def set_installation(team_id, bot_token:, bot_user_id: nil, team_name: nil)
|
|
48
|
+
raise ChatSDK::ConfigurationError, "Multi-workspace mode requires state" unless @state
|
|
49
|
+
|
|
50
|
+
@team_clients.delete(team_id)
|
|
51
|
+
@state.set(installation_key(team_id), {
|
|
52
|
+
"bot_token" => bot_token,
|
|
53
|
+
"bot_user_id" => bot_user_id,
|
|
54
|
+
"team_name" => team_name
|
|
55
|
+
})
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def get_installation(team_id)
|
|
59
|
+
return nil unless @state
|
|
60
|
+
|
|
61
|
+
@state.get(installation_key(team_id))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def delete_installation(team_id)
|
|
65
|
+
@team_clients.delete(team_id)
|
|
66
|
+
return unless @state
|
|
67
|
+
|
|
68
|
+
@state.delete(installation_key(team_id))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def handle_oauth_callback(code:, redirect_uri: nil)
|
|
72
|
+
raise ChatSDK::ConfigurationError, "client_id required for OAuth" unless @client_id
|
|
73
|
+
|
|
74
|
+
temp_client = ::Slack::Web::Client.new
|
|
75
|
+
params = {
|
|
76
|
+
client_id: @client_id,
|
|
77
|
+
client_secret: @client_secret,
|
|
78
|
+
code: code
|
|
79
|
+
}
|
|
80
|
+
params[:redirect_uri] = redirect_uri if redirect_uri
|
|
81
|
+
|
|
82
|
+
result = temp_client.oauth_v2_access(**params)
|
|
83
|
+
|
|
84
|
+
team_id = result["team"]["id"]
|
|
85
|
+
installation = {
|
|
86
|
+
"bot_token" => result["access_token"],
|
|
87
|
+
"bot_user_id" => result["bot_user_id"],
|
|
88
|
+
"team_name" => result["team"]["name"]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
set_installation(team_id,
|
|
92
|
+
bot_token: installation["bot_token"],
|
|
93
|
+
bot_user_id: installation["bot_user_id"],
|
|
94
|
+
team_name: installation["team_name"])
|
|
95
|
+
|
|
96
|
+
{team_id: team_id, installation: installation}
|
|
27
97
|
end
|
|
28
98
|
|
|
29
99
|
def name
|
|
@@ -74,6 +144,14 @@ module ChatSDK
|
|
|
74
144
|
parsed = parse_body(body, rack_request.content_type)
|
|
75
145
|
return [] unless parsed
|
|
76
146
|
|
|
147
|
+
# Multi-workspace: resolve per-team client from payload
|
|
148
|
+
# Clear any stale client from a previous request on this thread (Puma thread pool safety)
|
|
149
|
+
if @client_id
|
|
150
|
+
::Thread.current[:chat_sdk_slack_client] = nil
|
|
151
|
+
team_id = parsed["team_id"] || parsed.dig("team", "id")
|
|
152
|
+
resolve_team_client(team_id) if team_id
|
|
153
|
+
end
|
|
154
|
+
|
|
77
155
|
EventParser.parse(parsed)
|
|
78
156
|
end
|
|
79
157
|
|
|
@@ -85,7 +163,7 @@ module ChatSDK
|
|
|
85
163
|
|
|
86
164
|
apply_message_params(params, msg)
|
|
87
165
|
|
|
88
|
-
result =
|
|
166
|
+
result = client.chat_postMessage(**params)
|
|
89
167
|
|
|
90
168
|
ChatSDK::Message.new(
|
|
91
169
|
id: result["ts"],
|
|
@@ -104,11 +182,11 @@ module ChatSDK
|
|
|
104
182
|
|
|
105
183
|
apply_message_params(params, msg)
|
|
106
184
|
|
|
107
|
-
|
|
185
|
+
client.chat_update(**params)
|
|
108
186
|
end
|
|
109
187
|
|
|
110
188
|
def delete_message(channel_id:, message_id:)
|
|
111
|
-
|
|
189
|
+
client.chat_delete(channel: channel_id, ts: message_id)
|
|
112
190
|
end
|
|
113
191
|
|
|
114
192
|
def post_ephemeral(channel_id:, user_id:, message:, thread_id: nil)
|
|
@@ -118,26 +196,26 @@ module ChatSDK
|
|
|
118
196
|
|
|
119
197
|
apply_message_params(params, msg)
|
|
120
198
|
|
|
121
|
-
|
|
199
|
+
client.chat_postEphemeral(**params)
|
|
122
200
|
end
|
|
123
201
|
|
|
124
202
|
def upload_file(channel_id:, io:, filename:, thread_id: nil, comment: nil)
|
|
125
203
|
params = {channels: channel_id, file: Faraday::Multipart::FilePart.new(io, nil, filename)}
|
|
126
204
|
params[:thread_ts] = thread_id if thread_id
|
|
127
205
|
params[:initial_comment] = comment if comment
|
|
128
|
-
|
|
206
|
+
client.files_upload(**params)
|
|
129
207
|
end
|
|
130
208
|
|
|
131
209
|
def add_reaction(channel_id:, message_id:, emoji:)
|
|
132
|
-
|
|
210
|
+
client.reactions_add(channel: channel_id, timestamp: message_id, name: emoji)
|
|
133
211
|
end
|
|
134
212
|
|
|
135
213
|
def remove_reaction(channel_id:, message_id:, emoji:)
|
|
136
|
-
|
|
214
|
+
client.reactions_remove(channel: channel_id, timestamp: message_id, name: emoji)
|
|
137
215
|
end
|
|
138
216
|
|
|
139
217
|
def get_user(user_id)
|
|
140
|
-
result =
|
|
218
|
+
result = client.users_info(user: user_id)
|
|
141
219
|
return nil unless result&.dig("user", "id")
|
|
142
220
|
|
|
143
221
|
ChatSDK::Author.new(
|
|
@@ -150,7 +228,7 @@ module ChatSDK
|
|
|
150
228
|
end
|
|
151
229
|
|
|
152
230
|
def open_dm(user_id)
|
|
153
|
-
result =
|
|
231
|
+
result = client.conversations_open(users: user_id)
|
|
154
232
|
result["channel"]["id"]
|
|
155
233
|
end
|
|
156
234
|
|
|
@@ -161,7 +239,7 @@ module ChatSDK
|
|
|
161
239
|
params = {channel: channel_id, text: text, post_at: post_at.to_i}
|
|
162
240
|
params[:thread_ts] = thread_id if thread_id
|
|
163
241
|
|
|
164
|
-
result =
|
|
242
|
+
result = client.chat_scheduleMessage(**params)
|
|
165
243
|
|
|
166
244
|
ChatSDK::Message.new(
|
|
167
245
|
id: result["scheduled_message_id"],
|
|
@@ -176,9 +254,9 @@ module ChatSDK
|
|
|
176
254
|
|
|
177
255
|
def fetch_messages(channel_id:, thread_id: nil, cursor: nil, limit: 50)
|
|
178
256
|
result = if thread_id
|
|
179
|
-
|
|
257
|
+
client.conversations_replies(channel: channel_id, ts: thread_id, cursor: cursor, limit: limit)
|
|
180
258
|
else
|
|
181
|
-
|
|
259
|
+
client.conversations_history(channel: channel_id, cursor: cursor, limit: limit)
|
|
182
260
|
end
|
|
183
261
|
messages = result["messages"].map { |m| parse_slack_message(m, channel_id) }
|
|
184
262
|
[messages, result["response_metadata"]&.dig("next_cursor")]
|
|
@@ -186,15 +264,19 @@ module ChatSDK
|
|
|
186
264
|
|
|
187
265
|
def open_modal(trigger_id:, modal:)
|
|
188
266
|
view = @modal_renderer.render(modal)
|
|
189
|
-
|
|
267
|
+
client.views_open(trigger_id: trigger_id, view: view)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def update_modal(view_id:, modal:)
|
|
271
|
+
client.views_update(view_id: view_id, view: @modal_renderer.render(modal))
|
|
190
272
|
end
|
|
191
273
|
|
|
192
274
|
def publish_home_view(user_id:, view:)
|
|
193
|
-
|
|
275
|
+
client.views_publish(user_id: user_id, view: view)
|
|
194
276
|
end
|
|
195
277
|
|
|
196
278
|
def set_suggested_prompts(channel_id:, thread_id:, prompts:)
|
|
197
|
-
|
|
279
|
+
client.assistant_threads_setSuggestedPrompts(
|
|
198
280
|
channel_id: channel_id,
|
|
199
281
|
thread_ts: thread_id,
|
|
200
282
|
prompts: prompts
|
|
@@ -202,7 +284,7 @@ module ChatSDK
|
|
|
202
284
|
end
|
|
203
285
|
|
|
204
286
|
def set_assistant_status(channel_id:, thread_id:, status:)
|
|
205
|
-
|
|
287
|
+
client.assistant_threads_setStatus(
|
|
206
288
|
channel_id: channel_id,
|
|
207
289
|
thread_ts: thread_id,
|
|
208
290
|
status: status
|
|
@@ -210,13 +292,28 @@ module ChatSDK
|
|
|
210
292
|
end
|
|
211
293
|
|
|
212
294
|
def set_assistant_title(channel_id:, thread_id:, title:)
|
|
213
|
-
|
|
295
|
+
client.assistant_threads_setTitle(
|
|
214
296
|
channel_id: channel_id,
|
|
215
297
|
thread_ts: thread_id,
|
|
216
298
|
title: title
|
|
217
299
|
)
|
|
218
300
|
end
|
|
219
301
|
|
|
302
|
+
def send_to_response_url(response_url:, message:)
|
|
303
|
+
msg = ChatSDK::PostableMessage.from(message)
|
|
304
|
+
payload = {}
|
|
305
|
+
apply_message_params(payload, msg)
|
|
306
|
+
Faraday.post(response_url) do |req|
|
|
307
|
+
req.headers["Content-Type"] = "application/json"
|
|
308
|
+
req.body = JSON.generate(payload)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def fetch_thread(channel_id:, thread_id: nil)
|
|
313
|
+
result = client.conversations_info(channel: channel_id)
|
|
314
|
+
result["channel"]
|
|
315
|
+
end
|
|
316
|
+
|
|
220
317
|
def start_typing(channel_id:, thread_id: nil)
|
|
221
318
|
# Slack doesn't have a native typing indicator API for bots
|
|
222
319
|
# This is a no-op but the capability is declared for streaming support
|
|
@@ -234,8 +331,12 @@ module ChatSDK
|
|
|
234
331
|
app_token ||= ENV["SLACK_APP_TOKEN"]
|
|
235
332
|
raise ChatSDK::ConfigurationError, "Slack app_token required for socket mode" unless app_token
|
|
236
333
|
|
|
237
|
-
|
|
238
|
-
|
|
334
|
+
@socket_mode = SocketMode.new(app_token: app_token, bot_client: client)
|
|
335
|
+
@socket_mode.start(&block)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def stop_socket_mode
|
|
339
|
+
@socket_mode&.stop
|
|
239
340
|
end
|
|
240
341
|
|
|
241
342
|
def mention(user_id)
|
|
@@ -252,6 +353,27 @@ module ChatSDK
|
|
|
252
353
|
|
|
253
354
|
private
|
|
254
355
|
|
|
356
|
+
def resolve_team_client(team_id)
|
|
357
|
+
return unless @client_id && @state
|
|
358
|
+
|
|
359
|
+
cached = @team_clients[team_id]
|
|
360
|
+
if cached
|
|
361
|
+
::Thread.current[:chat_sdk_slack_client] = cached
|
|
362
|
+
return
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
installation = get_installation(team_id)
|
|
366
|
+
return unless installation
|
|
367
|
+
|
|
368
|
+
new_client = ::Slack::Web::Client.new(token: installation["bot_token"])
|
|
369
|
+
@team_clients[team_id] = new_client
|
|
370
|
+
::Thread.current[:chat_sdk_slack_client] = new_client
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def installation_key(team_id)
|
|
374
|
+
"slack:installation:#{team_id}"
|
|
375
|
+
end
|
|
376
|
+
|
|
255
377
|
def apply_message_params(params, msg)
|
|
256
378
|
if msg.card?
|
|
257
379
|
params[:blocks] = @renderer.render(msg.card)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: chat_sdk-slack
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Quentin Rousseau
|
|
@@ -13,14 +13,14 @@ dependencies:
|
|
|
13
13
|
name: chat_sdk
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
16
|
+
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
18
|
version: '0.1'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
|
-
- - "
|
|
23
|
+
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0.1'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|