chat_sdk-slack 0.9.0 → 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 +4 -4
- data/lib/chat_sdk/slack/adapter.rb +124 -25
- 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: 17c05a1b3391a1d2f8a4a248fc2ee2e4048b5fbdf608cfd10becba0239f76cde
|
|
4
|
+
data.tar.gz: a5be2b1d3ac3738c28d92e53bc3209b4d565ceb3f08635e469c65e606e43cdc7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f405de4643b259ed5477d52f2f6b733b4af2fb12eeb184a019ac9ac98d0aa982a8a156752f48aae95989e3ff6a1d5bf236255056e2e75c0d2ea58f7f886a1320
|
|
7
|
+
data.tar.gz: 99a0b0a0e7b9d85f17b203c483252dadd24a82269cce6b8776fe09153bc99215372c6275975dbef80c810a46aba34752efc85946546107cb21927edd11350d4d
|
|
@@ -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,15 @@ 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)
|
|
190
268
|
end
|
|
191
269
|
|
|
192
270
|
def publish_home_view(user_id:, view:)
|
|
193
|
-
|
|
271
|
+
client.views_publish(user_id: user_id, view: view)
|
|
194
272
|
end
|
|
195
273
|
|
|
196
274
|
def set_suggested_prompts(channel_id:, thread_id:, prompts:)
|
|
197
|
-
|
|
275
|
+
client.assistant_threads_setSuggestedPrompts(
|
|
198
276
|
channel_id: channel_id,
|
|
199
277
|
thread_ts: thread_id,
|
|
200
278
|
prompts: prompts
|
|
@@ -202,7 +280,7 @@ module ChatSDK
|
|
|
202
280
|
end
|
|
203
281
|
|
|
204
282
|
def set_assistant_status(channel_id:, thread_id:, status:)
|
|
205
|
-
|
|
283
|
+
client.assistant_threads_setStatus(
|
|
206
284
|
channel_id: channel_id,
|
|
207
285
|
thread_ts: thread_id,
|
|
208
286
|
status: status
|
|
@@ -210,7 +288,7 @@ module ChatSDK
|
|
|
210
288
|
end
|
|
211
289
|
|
|
212
290
|
def set_assistant_title(channel_id:, thread_id:, title:)
|
|
213
|
-
|
|
291
|
+
client.assistant_threads_setTitle(
|
|
214
292
|
channel_id: channel_id,
|
|
215
293
|
thread_ts: thread_id,
|
|
216
294
|
title: title
|
|
@@ -234,7 +312,7 @@ module ChatSDK
|
|
|
234
312
|
app_token ||= ENV["SLACK_APP_TOKEN"]
|
|
235
313
|
raise ChatSDK::ConfigurationError, "Slack app_token required for socket mode" unless app_token
|
|
236
314
|
|
|
237
|
-
socket = SocketMode.new(app_token: app_token, bot_client:
|
|
315
|
+
socket = SocketMode.new(app_token: app_token, bot_client: client)
|
|
238
316
|
socket.start(&block)
|
|
239
317
|
end
|
|
240
318
|
|
|
@@ -252,6 +330,27 @@ module ChatSDK
|
|
|
252
330
|
|
|
253
331
|
private
|
|
254
332
|
|
|
333
|
+
def resolve_team_client(team_id)
|
|
334
|
+
return unless @client_id && @state
|
|
335
|
+
|
|
336
|
+
cached = @team_clients[team_id]
|
|
337
|
+
if cached
|
|
338
|
+
::Thread.current[:chat_sdk_slack_client] = cached
|
|
339
|
+
return
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
installation = get_installation(team_id)
|
|
343
|
+
return unless installation
|
|
344
|
+
|
|
345
|
+
new_client = ::Slack::Web::Client.new(token: installation["bot_token"])
|
|
346
|
+
@team_clients[team_id] = new_client
|
|
347
|
+
::Thread.current[:chat_sdk_slack_client] = new_client
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def installation_key(team_id)
|
|
351
|
+
"slack:installation:#{team_id}"
|
|
352
|
+
end
|
|
353
|
+
|
|
255
354
|
def apply_message_params(params, msg)
|
|
256
355
|
if msg.card?
|
|
257
356
|
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.0
|
|
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
|