@cuylabs/channel-slack 0.1.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.
Files changed (47) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +168 -0
  3. package/dist/activity-ByrD9Ftr.d.ts +66 -0
  4. package/dist/assistant.d.ts +58 -0
  5. package/dist/assistant.js +188 -0
  6. package/dist/bolt.d.ts +344 -0
  7. package/dist/bolt.js +705 -0
  8. package/dist/chunk-BODPT4I6.js +322 -0
  9. package/dist/chunk-FPCE5V5Y.js +292 -0
  10. package/dist/chunk-FX2JOVX5.js +405 -0
  11. package/dist/chunk-JZG4IETE.js +141 -0
  12. package/dist/chunk-NE57BLLU.js +0 -0
  13. package/dist/chunk-TWJGVDA2.js +108 -0
  14. package/dist/core.d.ts +425 -0
  15. package/dist/core.js +42 -0
  16. package/dist/diagnostics.d.ts +105 -0
  17. package/dist/diagnostics.js +8 -0
  18. package/dist/feedback.d.ts +137 -0
  19. package/dist/feedback.js +128 -0
  20. package/dist/history.d.ts +266 -0
  21. package/dist/history.js +747 -0
  22. package/dist/index.d.ts +4 -0
  23. package/dist/index.js +57 -0
  24. package/dist/logging-Bl3HfcC8.d.ts +8 -0
  25. package/dist/policy.d.ts +130 -0
  26. package/dist/policy.js +16 -0
  27. package/dist/setup.d.ts +165 -0
  28. package/dist/setup.js +453 -0
  29. package/dist/shared.d.ts +2 -0
  30. package/dist/shared.js +43 -0
  31. package/dist/targets.d.ts +113 -0
  32. package/dist/targets.js +484 -0
  33. package/dist/users.d.ts +109 -0
  34. package/dist/users.js +240 -0
  35. package/docs/concepts/activity.md +33 -0
  36. package/docs/concepts/bolt-runtime.md +30 -0
  37. package/docs/concepts/message-policy.md +49 -0
  38. package/docs/concepts/setup-requirements.md +44 -0
  39. package/docs/concepts/supplemental-history.md +55 -0
  40. package/docs/recipes/app-mention-handler.md +34 -0
  41. package/docs/recipes/assistant-thread-handler.md +28 -0
  42. package/docs/recipes/generate-slack-manifest.md +28 -0
  43. package/docs/recipes/history-visibility.md +36 -0
  44. package/docs/recipes/socket-mode-app.md +29 -0
  45. package/docs/reference/channel-slack-boundary.md +50 -0
  46. package/docs/reference/exports.md +32 -0
  47. package/package.json +130 -0
@@ -0,0 +1,405 @@
1
+ // src/diagnostics/inspect.ts
2
+ var DEFAULT_REQUEST_TIMEOUT_MS = 2500;
3
+ var DEFAULT_SCOPE_METHODS = [
4
+ "auth.test",
5
+ "auth.scopes",
6
+ "apps.permissions.info"
7
+ ];
8
+ async function inspectSlackConnection(options = {}) {
9
+ const startedAt = Date.now();
10
+ const resolved = await resolveDiagnosticsClient(options);
11
+ const requiredScopes = normalizeScopes(options.requiredScopes ?? []);
12
+ const optionalScopes = normalizeScopes(options.optionalScopes ?? []);
13
+ const base = {
14
+ tokenSource: resolved.tokenSource,
15
+ requiredScopes,
16
+ optionalScopes,
17
+ missingRequiredScopes: [],
18
+ missingOptionalScopes: [],
19
+ findings: []
20
+ };
21
+ if (!resolved.client) {
22
+ return {
23
+ ...base,
24
+ ok: false,
25
+ elapsedMs: elapsed(startedAt),
26
+ missingRequiredScopes: requiredScopes,
27
+ findings: [
28
+ {
29
+ severity: "error",
30
+ code: "missing-token",
31
+ message: "Slack token is missing. Pass `token`, inject `client`, or set SLACK_BOT_TOKEN."
32
+ }
33
+ ],
34
+ error: { message: "Slack token is missing.", code: "missing-token" }
35
+ };
36
+ }
37
+ const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
38
+ const tokenArgs = resolved.token ? { token: resolved.token } : void 0;
39
+ let authPayload;
40
+ try {
41
+ authPayload = await withRequestTimeout(
42
+ () => resolved.client.auth.test(tokenArgs),
43
+ requestTimeoutMs,
44
+ "auth.test"
45
+ );
46
+ } catch (error) {
47
+ const apiError = toSlackApiInspectionError(error, "auth.test");
48
+ return {
49
+ ...base,
50
+ ok: false,
51
+ elapsedMs: elapsed(startedAt),
52
+ missingRequiredScopes: requiredScopes,
53
+ findings: [
54
+ {
55
+ severity: "error",
56
+ code: "auth-test-failed",
57
+ message: apiError.message
58
+ }
59
+ ],
60
+ error: apiError
61
+ };
62
+ }
63
+ const authError = slackPayloadError(authPayload, "auth.test");
64
+ if (authError) {
65
+ return {
66
+ ...base,
67
+ ok: false,
68
+ elapsedMs: elapsed(startedAt),
69
+ missingRequiredScopes: requiredScopes,
70
+ auth: normalizeAuthInfo(authPayload),
71
+ findings: [
72
+ {
73
+ severity: "error",
74
+ code: "auth-test-failed",
75
+ message: authError.message
76
+ }
77
+ ],
78
+ error: authError
79
+ };
80
+ }
81
+ const auth = normalizeAuthInfo(authPayload);
82
+ const findings = [];
83
+ let scopes;
84
+ let missingRequiredScopes = [];
85
+ let missingOptionalScopes = [];
86
+ if (options.inspectScopes !== false) {
87
+ scopes = await inspectSlackTokenScopes({
88
+ token: resolved.token,
89
+ client: resolved.client,
90
+ requestTimeoutMs,
91
+ methods: options.scopeMethods
92
+ });
93
+ if (scopes.ok) {
94
+ missingRequiredScopes = missingScopes(requiredScopes, scopes.scopes);
95
+ missingOptionalScopes = missingScopes(optionalScopes, scopes.scopes);
96
+ } else if (requiredScopes.length > 0) {
97
+ missingRequiredScopes = requiredScopes;
98
+ }
99
+ if (!scopes.ok) {
100
+ findings.push({
101
+ severity: requiredScopes.length > 0 ? "error" : "warning",
102
+ code: "scope-inspection-unavailable",
103
+ message: requiredScopes.length > 0 ? "Slack token scopes could not be inspected, so required scopes could not be verified." : "Slack token scopes could not be inspected."
104
+ });
105
+ }
106
+ }
107
+ if (missingRequiredScopes.length > 0) {
108
+ findings.push({
109
+ severity: "error",
110
+ code: "missing-required-scopes",
111
+ message: `Slack token is missing required scope(s): ${missingRequiredScopes.join(
112
+ ", "
113
+ )}.`
114
+ });
115
+ }
116
+ if (missingOptionalScopes.length > 0) {
117
+ findings.push({
118
+ severity: "warning",
119
+ code: "missing-optional-scopes",
120
+ message: `Slack token is missing optional scope(s): ${missingOptionalScopes.join(
121
+ ", "
122
+ )}.`
123
+ });
124
+ }
125
+ return {
126
+ ...base,
127
+ ok: findings.every((finding) => finding.severity !== "error"),
128
+ elapsedMs: elapsed(startedAt),
129
+ auth,
130
+ ...scopes ? { scopes } : {},
131
+ missingRequiredScopes,
132
+ missingOptionalScopes,
133
+ findings
134
+ };
135
+ }
136
+ async function inspectSlackTokenScopes(options = {}) {
137
+ const resolved = await resolveDiagnosticsClient(options);
138
+ if (!resolved.client) {
139
+ return {
140
+ ok: false,
141
+ scopes: [],
142
+ errors: [
143
+ {
144
+ method: "auth.test",
145
+ error: {
146
+ message: "Slack token is missing. Pass `token`, inject `client`, or set SLACK_BOT_TOKEN.",
147
+ code: "missing-token",
148
+ method: "auth.test"
149
+ }
150
+ }
151
+ ]
152
+ };
153
+ }
154
+ const methods = options.methods ?? DEFAULT_SCOPE_METHODS;
155
+ const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
156
+ const args = resolved.token ? { token: resolved.token } : void 0;
157
+ const errors = [];
158
+ for (const method of methods) {
159
+ try {
160
+ const payload = await withRequestTimeout(
161
+ () => resolved.client.apiCall(method, args),
162
+ requestTimeoutMs,
163
+ method
164
+ );
165
+ const payloadError = slackPayloadError(payload, method);
166
+ if (payloadError) {
167
+ errors.push({
168
+ method,
169
+ error: payloadError
170
+ });
171
+ continue;
172
+ }
173
+ const scopes = extractScopes(payload);
174
+ if (scopes.length > 0) {
175
+ return {
176
+ ok: true,
177
+ scopes,
178
+ source: method,
179
+ errors
180
+ };
181
+ }
182
+ errors.push({
183
+ method,
184
+ error: {
185
+ message: `${method} did not return scope data.`,
186
+ code: "no-scope-data",
187
+ method
188
+ }
189
+ });
190
+ } catch (error) {
191
+ errors.push({
192
+ method,
193
+ error: toSlackApiInspectionError(error, method)
194
+ });
195
+ }
196
+ }
197
+ return {
198
+ ok: false,
199
+ scopes: [],
200
+ errors
201
+ };
202
+ }
203
+ async function resolveDiagnosticsClient(options) {
204
+ const explicitToken = normalizeToken(options.token);
205
+ if (options.client) {
206
+ return {
207
+ client: options.client,
208
+ token: explicitToken,
209
+ tokenSource: explicitToken ? "provided" : "client"
210
+ };
211
+ }
212
+ if (explicitToken) {
213
+ return {
214
+ client: await createSlackWebClient(explicitToken),
215
+ token: explicitToken,
216
+ tokenSource: "provided"
217
+ };
218
+ }
219
+ const envToken = normalizeToken(process.env.SLACK_BOT_TOKEN);
220
+ if (envToken) {
221
+ return {
222
+ client: await createSlackWebClient(envToken),
223
+ token: envToken,
224
+ tokenSource: "environment"
225
+ };
226
+ }
227
+ return { tokenSource: "none" };
228
+ }
229
+ async function createSlackWebClient(token) {
230
+ const { WebClient } = await import("@slack/web-api");
231
+ return new WebClient(token);
232
+ }
233
+ async function withRequestTimeout(operation, timeoutMs, method) {
234
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
235
+ return await operation();
236
+ }
237
+ let timeoutId;
238
+ try {
239
+ return await Promise.race([
240
+ operation(),
241
+ new Promise((_resolve, reject) => {
242
+ timeoutId = setTimeout(() => {
243
+ const error = new Error(`${method} timed out after ${timeoutMs}ms.`);
244
+ error.code = "timeout";
245
+ reject(error);
246
+ }, timeoutMs);
247
+ })
248
+ ]);
249
+ } finally {
250
+ if (timeoutId) {
251
+ clearTimeout(timeoutId);
252
+ }
253
+ }
254
+ }
255
+ function normalizeAuthInfo(payload) {
256
+ const record = readRecord(payload);
257
+ const auth = {
258
+ ok: readBoolean(record?.ok) ?? false
259
+ };
260
+ const url = readString(record?.url);
261
+ const team = readString(record?.team);
262
+ const teamId = readString(record?.team_id);
263
+ const user = readString(record?.user);
264
+ const userId = readString(record?.user_id);
265
+ const botId = readString(record?.bot_id);
266
+ const enterpriseId = readString(record?.enterprise_id);
267
+ if (url) auth.url = url;
268
+ if (team) auth.team = team;
269
+ if (teamId) auth.teamId = teamId;
270
+ if (user) auth.user = user;
271
+ if (userId) auth.userId = userId;
272
+ if (botId) auth.botId = botId;
273
+ if (enterpriseId) auth.enterpriseId = enterpriseId;
274
+ const isEnterpriseInstall = readBoolean(record?.is_enterprise_install);
275
+ if (isEnterpriseInstall !== void 0) {
276
+ auth.isEnterpriseInstall = isEnterpriseInstall;
277
+ }
278
+ return auth;
279
+ }
280
+ function slackPayloadError(payload, method) {
281
+ const record = readRecord(payload);
282
+ if (!record || record.ok !== false) {
283
+ return void 0;
284
+ }
285
+ const code = readString(record.error);
286
+ return {
287
+ message: `${method} failed${code ? `: ${code}` : ""}.`,
288
+ ...code ? { code } : {},
289
+ method,
290
+ ...readScopeErrorDetails(record)
291
+ };
292
+ }
293
+ function toSlackApiInspectionError(error, method) {
294
+ if (error instanceof Error) {
295
+ const record = error;
296
+ const data = readRecord(record.data);
297
+ const dataCode = data ? readString(data.error) : void 0;
298
+ const code = dataCode ?? readString(record.code);
299
+ return {
300
+ message: error.message || `${method} failed.`,
301
+ ...code ? { code } : {},
302
+ method,
303
+ ...typeof record.statusCode === "number" ? { statusCode: record.statusCode } : {},
304
+ ...data ? readScopeErrorDetails(data) : {}
305
+ };
306
+ }
307
+ return {
308
+ message: typeof error === "string" ? error : `${method} failed.`,
309
+ method
310
+ };
311
+ }
312
+ function readScopeErrorDetails(record) {
313
+ const responseMetadata = readRecord(record.response_metadata);
314
+ const providedScopes = extractScopes(responseMetadata?.scopes);
315
+ const acceptedScopes = extractScopes(responseMetadata?.acceptedScopes);
316
+ const needed = readString(record.needed);
317
+ return {
318
+ ...needed ? { needed } : {},
319
+ ...providedScopes.length > 0 ? { providedScopes } : {},
320
+ ...acceptedScopes.length > 0 ? { acceptedScopes } : {}
321
+ };
322
+ }
323
+ function extractScopes(payload) {
324
+ const scopes = [];
325
+ if (typeof payload === "string" || Array.isArray(payload)) {
326
+ collectScopes(payload, scopes);
327
+ }
328
+ const record = readRecord(payload);
329
+ if (record) {
330
+ collectScopes(record.scopes, scopes);
331
+ collectScopes(record.scope, scopes);
332
+ const responseMetadata = readRecord(record.response_metadata);
333
+ collectScopes(responseMetadata?.scopes, scopes);
334
+ const info = readRecord(record.info);
335
+ collectScopes(info?.scopes, scopes);
336
+ collectScopes(info?.scope, scopes);
337
+ collectScopes(info?.user_scopes, scopes);
338
+ collectScopes(info?.bot_scopes, scopes);
339
+ }
340
+ return normalizeScopes(scopes);
341
+ }
342
+ function collectScopes(value, into) {
343
+ if (!value) {
344
+ return;
345
+ }
346
+ if (typeof value === "string") {
347
+ for (const entry of value.split(/[,\s]+/)) {
348
+ const normalized = entry.trim();
349
+ if (normalized) {
350
+ into.push(normalized);
351
+ }
352
+ }
353
+ return;
354
+ }
355
+ if (Array.isArray(value)) {
356
+ for (const entry of value) {
357
+ collectScopes(entry, into);
358
+ }
359
+ return;
360
+ }
361
+ const record = readRecord(value);
362
+ if (!record) {
363
+ return;
364
+ }
365
+ for (const entry of Object.values(record)) {
366
+ if (typeof entry === "string" || Array.isArray(entry)) {
367
+ collectScopes(entry, into);
368
+ }
369
+ }
370
+ }
371
+ function normalizeScopes(scopes) {
372
+ const unique = /* @__PURE__ */ new Map();
373
+ for (const scope of scopes) {
374
+ const trimmed = scope.trim();
375
+ if (trimmed) {
376
+ unique.set(trimmed.toLowerCase(), trimmed);
377
+ }
378
+ }
379
+ return [...unique.values()].sort((a, b) => a.localeCompare(b));
380
+ }
381
+ function missingScopes(expected, actual) {
382
+ const actualSet = new Set(actual.map((scope) => scope.toLowerCase()));
383
+ return expected.filter((scope) => !actualSet.has(scope.toLowerCase()));
384
+ }
385
+ function normalizeToken(token) {
386
+ const trimmed = token?.trim();
387
+ return trimmed || void 0;
388
+ }
389
+ function readRecord(value) {
390
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
391
+ }
392
+ function readString(value) {
393
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
394
+ }
395
+ function readBoolean(value) {
396
+ return typeof value === "boolean" ? value : void 0;
397
+ }
398
+ function elapsed(startedAt) {
399
+ return Date.now() - startedAt;
400
+ }
401
+
402
+ export {
403
+ inspectSlackConnection,
404
+ inspectSlackTokenScopes
405
+ };
@@ -0,0 +1,141 @@
1
+ // src/shared/formatting.ts
2
+ function resolveSlackMessageFormatter(options) {
3
+ if (options.formatMessageText) {
4
+ return options.formatMessageText;
5
+ }
6
+ if (options.formatChatMarkdown === false) {
7
+ return identity;
8
+ }
9
+ return markdownToSlackMrkdwn;
10
+ }
11
+ function markdownToSlackMrkdwn(markdown) {
12
+ if (!markdown) {
13
+ return markdown;
14
+ }
15
+ const fences = [];
16
+ const withoutFences = markdown.replace(
17
+ /```[^\n`]*\n([\s\S]*?)```/g,
18
+ (_match, code) => {
19
+ const index = fences.push(`\`\`\`
20
+ ${code}\`\`\``) - 1;
21
+ return `@@SLACK_FENCE_${index}@@`;
22
+ }
23
+ );
24
+ const boldSegments = [];
25
+ const withBoldPlaceholders = withoutFences.split("\n").map(formatMarkdownLine).join("\n").replace(/\[([^\]\n]+)\]\((https?:\/\/[^)\s]+)\)/g, "<$2|$1>").replace(/\*\*([^*\n]+)\*\*/g, (_match, text) => {
26
+ const index = boldSegments.push(`*${text}*`) - 1;
27
+ return `@@SLACK_BOLD_${index}@@`;
28
+ }).replace(/__([^_\n]+)__/g, (_match, text) => {
29
+ const index = boldSegments.push(`*${text}*`) - 1;
30
+ return `@@SLACK_BOLD_${index}@@`;
31
+ });
32
+ const converted = withBoldPlaceholders.replace(
33
+ /(^|[\s([{"'])\*([^*\n][^*\n]*?)\*(?=[\s.,;:!?)}\]'"]|$)/g,
34
+ "$1_$2_"
35
+ );
36
+ return converted.replace(/@@SLACK_BOLD_(\d+)@@/g, (_match, rawIndex) => {
37
+ const index = Number(rawIndex);
38
+ return boldSegments[index] ?? "";
39
+ }).replace(/@@SLACK_FENCE_(\d+)@@/g, (_match, rawIndex) => {
40
+ const index = Number(rawIndex);
41
+ return fences[index] ?? "";
42
+ });
43
+ }
44
+ function formatMarkdownLine(line) {
45
+ return line.replace(/^(\s*)#{1,6}\s+(.+?)\s*#*\s*$/, "$1**$2**");
46
+ }
47
+ function identity(text) {
48
+ return text;
49
+ }
50
+
51
+ // src/shared/turn-context.ts
52
+ import { AsyncLocalStorage } from "async_hooks";
53
+ var store = new AsyncLocalStorage();
54
+ function currentSlackTurnContext() {
55
+ return store.getStore();
56
+ }
57
+ function runWithSlackTurnContext(value, fn) {
58
+ return Promise.resolve(store.run({ ...value }, fn));
59
+ }
60
+
61
+ // src/shared/follow-up.ts
62
+ function formatSlackAttributedFollowUp(message) {
63
+ const slack = currentSlackTurnContext();
64
+ if (!slack) {
65
+ return message;
66
+ }
67
+ const sender = formatSlackSender(slack);
68
+ const conversation = formatSlackConversation(slack);
69
+ if (!sender && !conversation) {
70
+ return message;
71
+ }
72
+ const lines = ["Additional Slack follow-up for the running request."];
73
+ if (sender) {
74
+ lines.push(`Sender: ${sender}`);
75
+ }
76
+ if (conversation) {
77
+ lines.push(`Conversation: ${conversation}`);
78
+ }
79
+ lines.push("Message:", message);
80
+ return lines.join("\n");
81
+ }
82
+ function formatSlackSender(slack) {
83
+ const userId = slack.slackActivity.userId || slack.user.userId;
84
+ const displayName = readPreparedSlackDisplayName(slack.context) ?? slack.user.userName;
85
+ if (displayName && userId) {
86
+ return `${displayName} (${userId})`;
87
+ }
88
+ return displayName ?? userId ?? "unknown Slack user";
89
+ }
90
+ function formatSlackConversation(slack) {
91
+ const activity = slack.slackActivity;
92
+ const surface = formatSlackSurface(activity.channelType);
93
+ const parts = [
94
+ activity.teamId ? `team ${activity.teamId}` : void 0,
95
+ activity.channelId ? `channel ${activity.channelId}` : void 0,
96
+ activity.threadTs ? `thread ${activity.threadTs}` : activity.messageTs ? `message ${activity.messageTs}` : void 0
97
+ ].filter((part) => Boolean(part));
98
+ return parts.length > 0 ? `${surface} (${parts.join(", ")})` : surface;
99
+ }
100
+ function formatSlackSurface(channelType) {
101
+ switch (channelType) {
102
+ case "dm":
103
+ return "private Slack DM";
104
+ case "thread":
105
+ return "shared Slack thread";
106
+ case "channel":
107
+ return "shared Slack channel";
108
+ case "group":
109
+ return "shared Slack group conversation";
110
+ default:
111
+ return "Slack conversation";
112
+ }
113
+ }
114
+ function readPreparedSlackDisplayName(context) {
115
+ const slack = context?.slack;
116
+ if (!slack || typeof slack !== "object") {
117
+ return void 0;
118
+ }
119
+ const value = slack.userDisplayName;
120
+ return typeof value === "string" && value.length > 0 ? value : void 0;
121
+ }
122
+
123
+ // src/shared/session.ts
124
+ function resolveThreadAwareSlackSessionId(info) {
125
+ if (info.channelType === "dm") {
126
+ return info.channelId;
127
+ }
128
+ if (info.threadTs) {
129
+ return `${info.channelId}:${info.threadTs}`;
130
+ }
131
+ return `${info.channelId}:${info.messageTs ?? info.channelId}`;
132
+ }
133
+
134
+ export {
135
+ resolveSlackMessageFormatter,
136
+ markdownToSlackMrkdwn,
137
+ currentSlackTurnContext,
138
+ runWithSlackTurnContext,
139
+ formatSlackAttributedFollowUp,
140
+ resolveThreadAwareSlackSessionId
141
+ };
File without changes
@@ -0,0 +1,108 @@
1
+ import {
2
+ extractSlackMessageText,
3
+ resolveSlackChannelType
4
+ } from "./chunk-FPCE5V5Y.js";
5
+
6
+ // src/shared/activity/parse.ts
7
+ function extractSlackActionToken(payload) {
8
+ const direct = asNonEmptyString(payload?.action_token);
9
+ if (direct) return direct;
10
+ const assistantThread = payload?.assistant_thread;
11
+ if (!assistantThread || typeof assistantThread !== "object") {
12
+ return void 0;
13
+ }
14
+ return asNonEmptyString(
15
+ assistantThread.action_token
16
+ );
17
+ }
18
+ function parseSlackMessageActivity(payload) {
19
+ const channelId = payload.channel ?? "unknown";
20
+ const userId = payload.user ?? "unknown";
21
+ const messageTs = payload.ts;
22
+ const threadTs = payload.thread_ts;
23
+ const isThread = !!threadTs && threadTs !== messageTs;
24
+ const channelType = resolveSlackChannelType(channelId, threadTs, isThread);
25
+ const text = extractSlackMessageText(payload, {
26
+ stripLeadingMentions: channelType !== "dm"
27
+ });
28
+ return {
29
+ channelId,
30
+ channelType,
31
+ userId,
32
+ teamId: payload.team,
33
+ threadTs: isThread ? threadTs : void 0,
34
+ messageTs,
35
+ parentUserId: payload.parent_user_id,
36
+ actionToken: extractSlackActionToken(payload),
37
+ text,
38
+ isMention: false
39
+ };
40
+ }
41
+ function parseSlackMentionActivity(payload) {
42
+ const channelId = payload.channel ?? "unknown";
43
+ const userId = payload.user ?? "unknown";
44
+ const messageTs = payload.ts;
45
+ const threadTs = payload.thread_ts;
46
+ const isThread = !!threadTs && threadTs !== messageTs;
47
+ const channelType = resolveSlackChannelType(channelId, threadTs, isThread);
48
+ const text = extractSlackMessageText(payload, {
49
+ stripLeadingMentions: true
50
+ });
51
+ return {
52
+ channelId,
53
+ channelType,
54
+ userId,
55
+ teamId: payload.team,
56
+ threadTs: isThread ? threadTs : void 0,
57
+ messageTs,
58
+ parentUserId: payload.parent_user_id,
59
+ actionToken: extractSlackActionToken(payload),
60
+ text,
61
+ isMention: true
62
+ };
63
+ }
64
+ function asNonEmptyString(value) {
65
+ return typeof value === "string" && value.length > 0 ? value : void 0;
66
+ }
67
+
68
+ // src/shared/activity/identity.ts
69
+ function extractSlackUserIdentity(info) {
70
+ return {
71
+ userId: info.userId,
72
+ channelId: info.channelId,
73
+ teamId: info.teamId,
74
+ threadTs: info.threadTs,
75
+ messageTs: info.messageTs
76
+ };
77
+ }
78
+ function extractSlackAuthContext(context, eventUserId) {
79
+ const ctx = context && typeof context === "object" ? context : {};
80
+ const pickString = (key) => {
81
+ const value = ctx[key];
82
+ return typeof value === "string" && value.length > 0 ? value : void 0;
83
+ };
84
+ const auth = {};
85
+ const botToken = pickString("botToken");
86
+ if (botToken) auth.botToken = botToken;
87
+ const userToken = pickString("userToken");
88
+ if (userToken) auth.userToken = userToken;
89
+ const teamId = pickString("teamId");
90
+ if (teamId) auth.teamId = teamId;
91
+ const enterpriseId = pickString("enterpriseId");
92
+ if (enterpriseId) auth.enterpriseId = enterpriseId;
93
+ const botUserId = pickString("botUserId");
94
+ if (botUserId) auth.botUserId = botUserId;
95
+ const botId = pickString("botId");
96
+ if (botId) auth.botId = botId;
97
+ const userId = pickString("userId") ?? eventUserId;
98
+ if (userId) auth.userId = userId;
99
+ return auth;
100
+ }
101
+
102
+ export {
103
+ extractSlackActionToken,
104
+ parseSlackMessageActivity,
105
+ parseSlackMentionActivity,
106
+ extractSlackUserIdentity,
107
+ extractSlackAuthContext
108
+ };