@cuylabs/channel-slack 0.8.0 → 0.10.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.
@@ -0,0 +1,650 @@
1
+ // src/interactive/blocks.ts
2
+ var MAX_BLOCK_TEXT = 2800;
3
+ function buildApprovalRequestMessage(request, actionIds) {
4
+ const rememberScopes = request.rememberScopes ?? [];
5
+ const canRemember = rememberScopes.length > 0 || Boolean(request.defaultRememberScope);
6
+ const text = `Approval required for ${request.tool}`;
7
+ return {
8
+ text,
9
+ blocks: [
10
+ section(
11
+ `*Approval required*
12
+ ${escapeMrkdwn(request.description || request.tool)}`
13
+ ),
14
+ fields([
15
+ `*Tool*
16
+ ${escapeMrkdwn(request.tool)}`,
17
+ `*Risk*
18
+ ${escapeMrkdwn(request.risk)}`
19
+ ]),
20
+ section(`*Arguments*
21
+ \`\`\`${truncate(formatArgs(request.args))}\`\`\``),
22
+ {
23
+ type: "actions",
24
+ elements: [
25
+ button("Allow", "primary", actionIds.approvalAllow, {
26
+ requestId: request.id
27
+ }),
28
+ button("Deny", "danger", actionIds.approvalDeny, {
29
+ requestId: request.id
30
+ }),
31
+ ...canRemember ? [
32
+ button("Remember", void 0, actionIds.approvalRemember, {
33
+ requestId: request.id,
34
+ rememberScope: request.defaultRememberScope ?? rememberScopes[0]
35
+ })
36
+ ] : []
37
+ ]
38
+ }
39
+ ]
40
+ };
41
+ }
42
+ function buildHumanInputRequestMessage(request, actionIds) {
43
+ const text = request.title || "Input required";
44
+ const confirmLabel = request.confirmLabel ?? "Submit";
45
+ const denyLabel = request.denyLabel ?? "Cancel";
46
+ if (request.kind === "confirm") {
47
+ return {
48
+ text,
49
+ blocks: [
50
+ section(`*${escapeMrkdwn(text)}*
51
+ ${escapeMrkdwn(request.question)}`),
52
+ {
53
+ type: "actions",
54
+ elements: [
55
+ button(confirmLabel, "primary", actionIds.humanConfirm, {
56
+ requestId: request.id
57
+ }),
58
+ button(denyLabel, "danger", actionIds.humanDeny, {
59
+ requestId: request.id
60
+ })
61
+ ]
62
+ }
63
+ ]
64
+ };
65
+ }
66
+ return {
67
+ text,
68
+ blocks: [
69
+ section(`*${escapeMrkdwn(text)}*
70
+ ${escapeMrkdwn(request.question)}`),
71
+ {
72
+ type: "actions",
73
+ elements: [
74
+ button(confirmLabel, "primary", actionIds.humanOpen, {
75
+ requestId: request.id
76
+ }),
77
+ button(denyLabel, "danger", actionIds.humanDeny, {
78
+ requestId: request.id
79
+ })
80
+ ]
81
+ }
82
+ ]
83
+ };
84
+ }
85
+ function buildResolvedMessage(label, resolution) {
86
+ const text = resolution.kind === "approval" ? resolution.action === "deny" ? "Approval denied" : "Approval granted" : "Input submitted";
87
+ return {
88
+ text,
89
+ blocks: [section(`*${escapeMrkdwn(text)}*
90
+ ${escapeMrkdwn(label)}`)]
91
+ };
92
+ }
93
+ function buildHumanInputModal(request, actionIds) {
94
+ const title = truncatePlain(request.title || "Input required", 24);
95
+ const submit = truncatePlain(request.confirmLabel ?? "Submit", 24);
96
+ const close = truncatePlain(request.denyLabel ?? "Cancel", 24);
97
+ if (request.kind === "choice") {
98
+ const options = (request.options ?? []).slice(0, 100).map((option) => ({
99
+ text: {
100
+ type: "plain_text",
101
+ text: truncatePlain(option.label, 75)
102
+ },
103
+ value: option.value ?? option.label,
104
+ ...option.description ? {
105
+ description: {
106
+ type: "plain_text",
107
+ text: truncatePlain(option.description, 75)
108
+ }
109
+ } : {}
110
+ }));
111
+ return {
112
+ type: "modal",
113
+ callback_id: actionIds.humanSubmit,
114
+ private_metadata: JSON.stringify({ requestId: request.id }),
115
+ title: { type: "plain_text", text: title },
116
+ submit: { type: "plain_text", text: submit },
117
+ close: { type: "plain_text", text: close },
118
+ blocks: [
119
+ {
120
+ type: "input",
121
+ block_id: "input",
122
+ label: {
123
+ type: "plain_text",
124
+ text: truncatePlain(request.question, 200)
125
+ },
126
+ element: {
127
+ type: request.allowMultiple ? "checkboxes" : "radio_buttons",
128
+ action_id: "value",
129
+ options
130
+ }
131
+ }
132
+ ]
133
+ };
134
+ }
135
+ return {
136
+ type: "modal",
137
+ callback_id: actionIds.humanSubmit,
138
+ private_metadata: JSON.stringify({ requestId: request.id }),
139
+ title: { type: "plain_text", text: title },
140
+ submit: { type: "plain_text", text: submit },
141
+ close: { type: "plain_text", text: close },
142
+ blocks: [
143
+ {
144
+ type: "input",
145
+ block_id: "input",
146
+ label: {
147
+ type: "plain_text",
148
+ text: truncatePlain(request.question, 200)
149
+ },
150
+ element: {
151
+ type: "plain_text_input",
152
+ action_id: "value",
153
+ multiline: true,
154
+ ...request.placeholder ? {
155
+ placeholder: {
156
+ type: "plain_text",
157
+ text: truncatePlain(request.placeholder, 150)
158
+ }
159
+ } : {}
160
+ }
161
+ }
162
+ ]
163
+ };
164
+ }
165
+ function encodeActionValue(payload) {
166
+ return JSON.stringify(payload);
167
+ }
168
+ function decodeActionValue(value) {
169
+ if (typeof value !== "string") return {};
170
+ try {
171
+ const parsed = JSON.parse(value);
172
+ return parsed && typeof parsed === "object" ? parsed : {};
173
+ } catch {
174
+ return {};
175
+ }
176
+ }
177
+ function button(text, style, actionId, value) {
178
+ return {
179
+ type: "button",
180
+ text: { type: "plain_text", text },
181
+ action_id: actionId,
182
+ value: encodeActionValue(value),
183
+ ...style ? { style } : {}
184
+ };
185
+ }
186
+ function section(text) {
187
+ return { type: "section", text: { type: "mrkdwn", text: truncate(text) } };
188
+ }
189
+ function fields(items) {
190
+ return {
191
+ type: "section",
192
+ fields: items.map((text) => ({ type: "mrkdwn", text: truncate(text) }))
193
+ };
194
+ }
195
+ function formatArgs(args) {
196
+ if (typeof args === "string") return args;
197
+ try {
198
+ return JSON.stringify(args, null, 2);
199
+ } catch {
200
+ return String(args);
201
+ }
202
+ }
203
+ function truncate(value, max = MAX_BLOCK_TEXT) {
204
+ return value.length <= max ? value : `${value.slice(0, max - 3)}...`;
205
+ }
206
+ function truncatePlain(value, max) {
207
+ const normalized = value.trim() || "Input";
208
+ return normalized.length <= max ? normalized : `${normalized.slice(0, max - 3)}...`;
209
+ }
210
+ function escapeMrkdwn(value) {
211
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
212
+ }
213
+
214
+ // src/interactive/store.ts
215
+ function createInMemorySlackInteractiveRequestStore() {
216
+ const records = /* @__PURE__ */ new Map();
217
+ return {
218
+ async get(requestId) {
219
+ const record = records.get(requestId);
220
+ return record ? cloneRecord(record) : void 0;
221
+ },
222
+ async upsert(record) {
223
+ const existing = records.get(record.id);
224
+ if (existing?.status === "resolved") {
225
+ return cloneRecord(existing);
226
+ }
227
+ const next = existing ? {
228
+ ...existing,
229
+ ...record,
230
+ target: record.target ?? existing.target,
231
+ resolution: record.resolution ?? existing.resolution,
232
+ updatedAt: nowIso()
233
+ } : { ...record };
234
+ records.set(record.id, cloneRecord(next));
235
+ return cloneRecord(next);
236
+ },
237
+ async attachTarget(requestId, target) {
238
+ const existing = records.get(requestId);
239
+ if (!existing) return void 0;
240
+ const next = {
241
+ ...existing,
242
+ target: cloneTarget(target),
243
+ updatedAt: nowIso()
244
+ };
245
+ records.set(requestId, cloneRecord(next));
246
+ return cloneRecord(next);
247
+ },
248
+ async resolve(requestId, resolution) {
249
+ const existing = records.get(requestId);
250
+ if (!existing) return void 0;
251
+ if (existing.status === "resolved") {
252
+ return cloneRecord(existing);
253
+ }
254
+ const next = {
255
+ ...existing,
256
+ status: "resolved",
257
+ resolution: cloneResolution(resolution),
258
+ updatedAt: nowIso()
259
+ };
260
+ records.set(requestId, cloneRecord(next));
261
+ return cloneRecord(next);
262
+ },
263
+ async delete(requestId) {
264
+ records.delete(requestId);
265
+ }
266
+ };
267
+ }
268
+ function nowIso() {
269
+ return (/* @__PURE__ */ new Date()).toISOString();
270
+ }
271
+ function cloneRecord(record) {
272
+ return {
273
+ ...record,
274
+ request: structuredClone(record.request),
275
+ ...record.target ? { target: cloneTarget(record.target) } : {},
276
+ ...record.resolution ? { resolution: cloneResolution(record.resolution) } : {}
277
+ };
278
+ }
279
+ function cloneTarget(target) {
280
+ return { ...target };
281
+ }
282
+ function cloneResolution(resolution) {
283
+ return structuredClone(resolution);
284
+ }
285
+
286
+ // src/interactive/postgres-store.ts
287
+ var DEFAULT_TABLE = "channel_slack_interactive_requests";
288
+ var DEFAULT_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
289
+ var DEFAULT_PRUNE_BATCH_SIZE = 1e3;
290
+ var DEFAULT_PRUNE_INTERVAL_MS = 6 * 60 * 60 * 1e3;
291
+ function createPostgresSlackInteractiveRequestStore({
292
+ client,
293
+ connectionString,
294
+ ensureSchema = true,
295
+ onPruneError,
296
+ pruneBatchSize = DEFAULT_PRUNE_BATCH_SIZE,
297
+ pruneIntervalMs = DEFAULT_PRUNE_INTERVAL_MS,
298
+ retentionMs = DEFAULT_RETENTION_MS,
299
+ schema,
300
+ tableName = DEFAULT_TABLE
301
+ }) {
302
+ let activeClient = client;
303
+ let ownsClient = false;
304
+ let initialized;
305
+ let pruneTimer;
306
+ async function getClient() {
307
+ if (activeClient) {
308
+ return activeClient;
309
+ }
310
+ if (!connectionString) {
311
+ throw new Error(
312
+ "connectionString is required when a Postgres Slack interactive request client is not provided"
313
+ );
314
+ }
315
+ const Pool = await importPostgresPoolConstructor();
316
+ activeClient = new Pool({ connectionString });
317
+ ownsClient = true;
318
+ return activeClient;
319
+ }
320
+ async function ensureInitialized() {
321
+ const currentClient = await getClient();
322
+ initialized ??= initializePostgresSlackInteractiveRequestStore({
323
+ client: currentClient,
324
+ ensureSchema,
325
+ schema,
326
+ tableName
327
+ });
328
+ await initialized;
329
+ startPruneTimer();
330
+ return currentClient;
331
+ }
332
+ function startPruneTimer() {
333
+ if (pruneTimer || pruneIntervalMs <= 0) {
334
+ return;
335
+ }
336
+ pruneTimer = setInterval(() => {
337
+ void prune().catch((error) => {
338
+ onPruneError?.(error);
339
+ });
340
+ }, pruneIntervalMs);
341
+ pruneTimer.unref?.();
342
+ }
343
+ async function prune() {
344
+ const currentClient = await ensureInitialized();
345
+ return prunePostgresSlackInteractiveRequestStore({
346
+ client: currentClient,
347
+ pruneBatchSize,
348
+ retentionMs,
349
+ schema,
350
+ tableName
351
+ });
352
+ }
353
+ return {
354
+ async get(requestId) {
355
+ const currentClient = await ensureInitialized();
356
+ const result = await currentClient.query(
357
+ `SELECT * FROM ${qualifiedTableName({
358
+ schema,
359
+ tableName
360
+ })} WHERE id = $1::text LIMIT 1`,
361
+ [requestId]
362
+ );
363
+ return rowToRecord(result.rows[0]);
364
+ },
365
+ async upsert(record) {
366
+ const currentClient = await ensureInitialized();
367
+ const result = await currentClient.query(
368
+ `INSERT INTO ${qualifiedTableName({
369
+ schema,
370
+ tableName
371
+ })} (
372
+ id,
373
+ kind,
374
+ request,
375
+ status,
376
+ created_at,
377
+ updated_at,
378
+ target,
379
+ resolution
380
+ )
381
+ VALUES (
382
+ $1::text,
383
+ $2::text,
384
+ $3::jsonb,
385
+ $4::text,
386
+ $5::timestamptz,
387
+ $6::timestamptz,
388
+ $7::jsonb,
389
+ $8::jsonb
390
+ )
391
+ ON CONFLICT (id) DO UPDATE SET
392
+ kind = CASE
393
+ WHEN ${qualifiedTableName({ schema, tableName })}.status = 'resolved'
394
+ THEN ${qualifiedTableName({ schema, tableName })}.kind
395
+ ELSE EXCLUDED.kind
396
+ END,
397
+ request = CASE
398
+ WHEN ${qualifiedTableName({ schema, tableName })}.status = 'resolved'
399
+ THEN ${qualifiedTableName({ schema, tableName })}.request
400
+ ELSE EXCLUDED.request
401
+ END,
402
+ status = CASE
403
+ WHEN ${qualifiedTableName({ schema, tableName })}.status = 'resolved'
404
+ THEN ${qualifiedTableName({ schema, tableName })}.status
405
+ ELSE EXCLUDED.status
406
+ END,
407
+ target = CASE
408
+ WHEN ${qualifiedTableName({ schema, tableName })}.status = 'resolved'
409
+ THEN ${qualifiedTableName({ schema, tableName })}.target
410
+ ELSE COALESCE(EXCLUDED.target, ${qualifiedTableName({
411
+ schema,
412
+ tableName
413
+ })}.target)
414
+ END,
415
+ resolution = CASE
416
+ WHEN ${qualifiedTableName({ schema, tableName })}.status = 'resolved'
417
+ THEN ${qualifiedTableName({ schema, tableName })}.resolution
418
+ ELSE COALESCE(EXCLUDED.resolution, ${qualifiedTableName({
419
+ schema,
420
+ tableName
421
+ })}.resolution)
422
+ END,
423
+ updated_at = CASE
424
+ WHEN ${qualifiedTableName({ schema, tableName })}.status = 'resolved'
425
+ THEN ${qualifiedTableName({ schema, tableName })}.updated_at
426
+ ELSE now()
427
+ END
428
+ RETURNING *`,
429
+ [
430
+ record.id,
431
+ record.kind,
432
+ JSON.stringify(record.request),
433
+ record.status,
434
+ record.createdAt,
435
+ record.updatedAt,
436
+ record.target ? JSON.stringify(record.target) : null,
437
+ record.resolution ? JSON.stringify(record.resolution) : null
438
+ ]
439
+ );
440
+ return requireReturnedRecord(result.rows[0], record.id);
441
+ },
442
+ async attachTarget(requestId, target) {
443
+ const currentClient = await ensureInitialized();
444
+ const result = await currentClient.query(
445
+ `UPDATE ${qualifiedTableName({ schema, tableName })}
446
+ SET target = $2::jsonb, updated_at = now()
447
+ WHERE id = $1::text
448
+ RETURNING *`,
449
+ [requestId, JSON.stringify(target)]
450
+ );
451
+ return rowToRecord(result.rows[0]);
452
+ },
453
+ async resolve(requestId, resolution) {
454
+ const currentClient = await ensureInitialized();
455
+ const result = await currentClient.query(
456
+ `UPDATE ${qualifiedTableName({ schema, tableName })}
457
+ SET status = 'resolved',
458
+ resolution = $2::jsonb,
459
+ updated_at = now()
460
+ WHERE id = $1::text AND status <> 'resolved'
461
+ RETURNING *`,
462
+ [requestId, JSON.stringify(resolution)]
463
+ );
464
+ const resolved = rowToRecord(result.rows[0]);
465
+ if (resolved) {
466
+ return resolved;
467
+ }
468
+ const existing = await currentClient.query(
469
+ `SELECT * FROM ${qualifiedTableName({
470
+ schema,
471
+ tableName
472
+ })} WHERE id = $1::text LIMIT 1`,
473
+ [requestId]
474
+ );
475
+ return rowToRecord(existing.rows[0]);
476
+ },
477
+ async delete(requestId) {
478
+ const currentClient = await ensureInitialized();
479
+ await currentClient.query(
480
+ `DELETE FROM ${qualifiedTableName({
481
+ schema,
482
+ tableName
483
+ })} WHERE id = $1::text`,
484
+ [requestId]
485
+ );
486
+ },
487
+ prune,
488
+ async close() {
489
+ if (pruneTimer) {
490
+ clearInterval(pruneTimer);
491
+ pruneTimer = void 0;
492
+ }
493
+ if (ownsClient) {
494
+ await activeClient?.end?.();
495
+ }
496
+ }
497
+ };
498
+ }
499
+ async function initializePostgresSlackInteractiveRequestStore({
500
+ client,
501
+ ensureSchema = true,
502
+ schema,
503
+ tableName = DEFAULT_TABLE
504
+ }) {
505
+ if (schema && ensureSchema) {
506
+ await client.query(
507
+ `CREATE SCHEMA IF NOT EXISTS ${quoteIdentifier(schema)}`
508
+ );
509
+ }
510
+ const table = qualifiedTableName({ schema, tableName });
511
+ await client.query(`
512
+ CREATE TABLE IF NOT EXISTS ${table} (
513
+ id text PRIMARY KEY,
514
+ kind text NOT NULL CHECK (kind IN ('approval', 'human-input')),
515
+ request jsonb NOT NULL,
516
+ status text NOT NULL CHECK (status IN ('pending', 'resolved')),
517
+ target jsonb,
518
+ resolution jsonb,
519
+ created_at timestamptz NOT NULL DEFAULT now(),
520
+ updated_at timestamptz NOT NULL DEFAULT now()
521
+ )
522
+ `);
523
+ const indexPrefix = interactiveIndexPrefix({ schema, tableName });
524
+ await client.query(
525
+ `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(
526
+ `${indexPrefix}_status_updated_idx`
527
+ )} ON ${table} (status, updated_at DESC)`
528
+ );
529
+ await client.query(
530
+ `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(
531
+ `${indexPrefix}_updated_idx`
532
+ )} ON ${table} (updated_at DESC)`
533
+ );
534
+ }
535
+ async function prunePostgresSlackInteractiveRequestStore({
536
+ client,
537
+ pruneBatchSize = DEFAULT_PRUNE_BATCH_SIZE,
538
+ retentionMs = DEFAULT_RETENTION_MS,
539
+ schema,
540
+ tableName = DEFAULT_TABLE
541
+ }) {
542
+ if (retentionMs <= 0) {
543
+ return { deleted: 0 };
544
+ }
545
+ const batchSize = Math.max(1, Math.floor(pruneBatchSize));
546
+ const result = await client.query(
547
+ `WITH expired AS (
548
+ SELECT id
549
+ FROM ${qualifiedTableName({ schema, tableName })}
550
+ WHERE updated_at < now() - ($1::bigint * interval '1 millisecond')
551
+ ORDER BY updated_at ASC
552
+ LIMIT $2::integer
553
+ )
554
+ DELETE FROM ${qualifiedTableName({ schema, tableName })} target
555
+ USING expired
556
+ WHERE target.id = expired.id`,
557
+ [Math.max(1, Math.floor(retentionMs)), batchSize]
558
+ );
559
+ return { deleted: result.rowCount ?? 0 };
560
+ }
561
+ function rowToRecord(row) {
562
+ if (!row) return void 0;
563
+ return cloneRecord({
564
+ id: row.id,
565
+ kind: row.kind,
566
+ request: readJsonValue(
567
+ row.request
568
+ ),
569
+ status: row.status,
570
+ createdAt: toIsoString(row.created_at),
571
+ updatedAt: toIsoString(row.updated_at),
572
+ ...row.target ? {
573
+ target: readJsonValue(
574
+ row.target
575
+ )
576
+ } : {},
577
+ ...row.resolution ? {
578
+ resolution: readJsonValue(
579
+ row.resolution
580
+ )
581
+ } : {}
582
+ });
583
+ }
584
+ function requireReturnedRecord(row, requestId) {
585
+ const record = rowToRecord(row);
586
+ if (!record) {
587
+ throw new Error(
588
+ `Postgres Slack interactive request store did not return request ${requestId}.`
589
+ );
590
+ }
591
+ return record;
592
+ }
593
+ function readJsonValue(value) {
594
+ if (typeof value === "string") {
595
+ return JSON.parse(value);
596
+ }
597
+ return value;
598
+ }
599
+ function toIsoString(value) {
600
+ return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
601
+ }
602
+ function qualifiedTableName({
603
+ schema,
604
+ tableName
605
+ }) {
606
+ return schema ? `${quoteIdentifier(schema)}.${quoteIdentifier(tableName)}` : quoteIdentifier(tableName);
607
+ }
608
+ function quoteIdentifier(value) {
609
+ return `"${value.replace(/"/g, '""')}"`;
610
+ }
611
+ function interactiveIndexPrefix({
612
+ schema,
613
+ tableName
614
+ }) {
615
+ const raw = [schema, tableName].filter(Boolean).join("_");
616
+ return raw.replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 40) || "channel_slack_interactive";
617
+ }
618
+ async function importPostgresPoolConstructor() {
619
+ const dynamicImport = new Function(
620
+ "specifier",
621
+ "return import(specifier)"
622
+ );
623
+ try {
624
+ const pg = await dynamicImport("pg");
625
+ return pg.Pool;
626
+ } catch (error) {
627
+ throw new Error(
628
+ `The "pg" package is required when using connectionString with createPostgresSlackInteractiveRequestStore. Install pg or pass a client. ${formatImportError(
629
+ error
630
+ )}`
631
+ );
632
+ }
633
+ }
634
+ function formatImportError(error) {
635
+ return error instanceof Error ? error.message : String(error);
636
+ }
637
+ export {
638
+ buildApprovalRequestMessage,
639
+ buildHumanInputModal,
640
+ buildHumanInputRequestMessage,
641
+ buildResolvedMessage,
642
+ cloneRecord,
643
+ createInMemorySlackInteractiveRequestStore,
644
+ createPostgresSlackInteractiveRequestStore,
645
+ decodeActionValue,
646
+ encodeActionValue,
647
+ initializePostgresSlackInteractiveRequestStore,
648
+ nowIso,
649
+ prunePostgresSlackInteractiveRequestStore
650
+ };
@@ -0,0 +1,46 @@
1
+ import { S as SlackActivityInfo, b as SlackUserIdentity } from './activity-ByrD9Ftr.js';
2
+
3
+ /**
4
+ * Transport-neutral interactive request contracts.
5
+ *
6
+ * These types let callers delegate approval and human-input requests to a
7
+ * Slack-specific renderer without importing Bolt or Web API types into
8
+ * `shared/`.
9
+ */
10
+
11
+ type SlackInteractiveRequestKind = "approval" | "human-input";
12
+ interface SlackApprovalRequest {
13
+ id: string;
14
+ }
15
+ interface SlackHumanInputRequest {
16
+ id: string;
17
+ }
18
+ type SlackInteractiveRequest = SlackApprovalRequest | SlackHumanInputRequest;
19
+ interface SlackInteractiveMessageRef {
20
+ channel: string;
21
+ ts: string;
22
+ }
23
+ interface SlackInteractiveMessage {
24
+ text: string;
25
+ blocks?: unknown[];
26
+ }
27
+ interface SlackInteractiveResponder {
28
+ postMessage(message: SlackInteractiveMessage): Promise<SlackInteractiveMessageRef>;
29
+ updateMessage(ref: SlackInteractiveMessageRef, message: SlackInteractiveMessage): Promise<void>;
30
+ }
31
+ interface SlackInteractiveRequestBaseContext {
32
+ kind: SlackInteractiveRequestKind;
33
+ request: SlackInteractiveRequest;
34
+ fallbackText: string;
35
+ }
36
+ interface SlackInteractiveRequestContext extends SlackInteractiveRequestBaseContext {
37
+ slackActivity: SlackActivityInfo;
38
+ user: SlackUserIdentity;
39
+ sessionId: string;
40
+ message: string;
41
+ responder: SlackInteractiveResponder;
42
+ }
43
+ type SlackInteractiveRequestHandler = (context: SlackInteractiveRequestContext) => boolean | void | Promise<boolean | void>;
44
+ type SlackEventInteractiveRequestHandler = (context: SlackInteractiveRequestBaseContext) => boolean | void | Promise<boolean | void>;
45
+
46
+ export type { SlackApprovalRequest as S, SlackEventInteractiveRequestHandler as a, SlackHumanInputRequest as b, SlackInteractiveMessage as c, SlackInteractiveMessageRef as d, SlackInteractiveRequest as e, SlackInteractiveRequestBaseContext as f, SlackInteractiveRequestContext as g, SlackInteractiveRequestHandler as h, SlackInteractiveRequestKind as i, SlackInteractiveResponder as j };