@chat-adapter/slack 4.16.1 → 4.18.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.
- package/dist/index.d.ts +17 -6
- package/dist/index.js +299 -71
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -59,6 +59,12 @@ declare class SlackFormatConverter extends BaseFormatConverter {
|
|
|
59
59
|
* Parse Slack mrkdwn into an AST.
|
|
60
60
|
*/
|
|
61
61
|
toAst(mrkdwn: string): Root;
|
|
62
|
+
/**
|
|
63
|
+
* Convert AST to Slack blocks, using a native table block for the first table.
|
|
64
|
+
* Returns null if the AST contains no tables (caller should use regular text).
|
|
65
|
+
* Slack allows at most one table block per message; additional tables use ASCII.
|
|
66
|
+
*/
|
|
67
|
+
toBlocksWithTable(ast: Root): SlackBlock[] | null;
|
|
62
68
|
private nodeToMrkdwn;
|
|
63
69
|
}
|
|
64
70
|
|
|
@@ -81,10 +87,10 @@ interface SlackAdapterConfig {
|
|
|
81
87
|
* Defaults to `slack:installation`. The full key will be `{prefix}:{teamId}`.
|
|
82
88
|
*/
|
|
83
89
|
installationKeyPrefix?: string;
|
|
84
|
-
/** Logger instance for error reporting */
|
|
85
|
-
logger
|
|
86
|
-
/** Signing secret for webhook verification */
|
|
87
|
-
signingSecret
|
|
90
|
+
/** Logger instance for error reporting. Defaults to ConsoleLogger. */
|
|
91
|
+
logger?: Logger;
|
|
92
|
+
/** Signing secret for webhook verification. Defaults to SLACK_SIGNING_SECRET env var. */
|
|
93
|
+
signingSecret?: string;
|
|
88
94
|
/** Override bot username (optional) */
|
|
89
95
|
userName?: string;
|
|
90
96
|
}
|
|
@@ -163,7 +169,7 @@ declare class SlackAdapter implements Adapter<SlackThreadId, unknown> {
|
|
|
163
169
|
private readonly requestContext;
|
|
164
170
|
/** Bot user ID (e.g., U_BOT_123) used for mention detection */
|
|
165
171
|
get botUserId(): string | undefined;
|
|
166
|
-
constructor(config
|
|
172
|
+
constructor(config?: SlackAdapterConfig);
|
|
167
173
|
/**
|
|
168
174
|
* Get the current bot token for API calls.
|
|
169
175
|
* Checks request context (multi-workspace) → default token (single-workspace) → throws.
|
|
@@ -307,6 +313,11 @@ declare class SlackAdapter implements Adapter<SlackThreadId, unknown> {
|
|
|
307
313
|
* Includes a fetchData method that uses the bot token for auth.
|
|
308
314
|
*/
|
|
309
315
|
private createAttachment;
|
|
316
|
+
/**
|
|
317
|
+
* Try to render a message using native Slack table blocks.
|
|
318
|
+
* Returns blocks + fallback text if the message contains tables, null otherwise.
|
|
319
|
+
*/
|
|
320
|
+
private renderWithTableBlocks;
|
|
310
321
|
postMessage(threadId: string, message: AdapterPostableMessage): Promise<RawMessage<unknown>>;
|
|
311
322
|
postEphemeral(threadId: string, userId: string, message: AdapterPostableMessage): Promise<EphemeralMessage>;
|
|
312
323
|
openModal(triggerId: string, modal: ModalElement, contextId?: string): Promise<{
|
|
@@ -445,6 +456,6 @@ declare class SlackAdapter implements Adapter<SlackThreadId, unknown> {
|
|
|
445
456
|
*/
|
|
446
457
|
private sendToResponseUrl;
|
|
447
458
|
}
|
|
448
|
-
declare function createSlackAdapter(config?:
|
|
459
|
+
declare function createSlackAdapter(config?: SlackAdapterConfig): SlackAdapter;
|
|
449
460
|
|
|
450
461
|
export { type EncryptedTokenData, SlackAdapter, type SlackAdapterConfig, type SlackEvent, SlackFormatConverter, type SlackInstallation, SlackFormatConverter as SlackMarkdownConverter, type SlackReactionEvent, type SlackThreadId, cardToBlockKit, cardToFallbackText, createSlackAdapter, decodeKey };
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
defaultEmojiResolver,
|
|
18
18
|
isJSX,
|
|
19
19
|
Message,
|
|
20
|
+
parseMarkdown as parseMarkdown2,
|
|
20
21
|
StreamingMarkdownRenderer,
|
|
21
22
|
toModalElement
|
|
22
23
|
} from "chat";
|
|
@@ -59,13 +60,14 @@ function cardToBlockKit(card) {
|
|
|
59
60
|
alt_text: card.title || "Card image"
|
|
60
61
|
});
|
|
61
62
|
}
|
|
63
|
+
const state = { usedNativeTable: false };
|
|
62
64
|
for (const child of card.children) {
|
|
63
|
-
const childBlocks = convertChildToBlocks(child);
|
|
65
|
+
const childBlocks = convertChildToBlocks(child, state);
|
|
64
66
|
blocks.push(...childBlocks);
|
|
65
67
|
}
|
|
66
68
|
return blocks;
|
|
67
69
|
}
|
|
68
|
-
function convertChildToBlocks(child) {
|
|
70
|
+
function convertChildToBlocks(child, state) {
|
|
69
71
|
switch (child.type) {
|
|
70
72
|
case "text":
|
|
71
73
|
return [convertTextToBlock(child)];
|
|
@@ -76,13 +78,13 @@ function convertChildToBlocks(child) {
|
|
|
76
78
|
case "actions":
|
|
77
79
|
return [convertActionsToBlock(child)];
|
|
78
80
|
case "section":
|
|
79
|
-
return convertSectionToBlocks(child);
|
|
81
|
+
return convertSectionToBlocks(child, state);
|
|
80
82
|
case "fields":
|
|
81
83
|
return [convertFieldsToBlock(child)];
|
|
82
84
|
case "link":
|
|
83
85
|
return [convertLinkToBlock(child)];
|
|
84
86
|
case "table":
|
|
85
|
-
return convertTableToBlocks(child);
|
|
87
|
+
return convertTableToBlocks(child, state);
|
|
86
88
|
default: {
|
|
87
89
|
const text = cardChildToFallbackText(child);
|
|
88
90
|
if (text) {
|
|
@@ -250,10 +252,10 @@ function convertRadioSelectToElement(radioSelect) {
|
|
|
250
252
|
}
|
|
251
253
|
return element;
|
|
252
254
|
}
|
|
253
|
-
function convertTableToBlocks(element) {
|
|
255
|
+
function convertTableToBlocks(element, state) {
|
|
254
256
|
const MAX_ROWS = 100;
|
|
255
257
|
const MAX_COLS = 20;
|
|
256
|
-
if (element.rows.length > MAX_ROWS || element.headers.length > MAX_COLS) {
|
|
258
|
+
if (state.usedNativeTable || element.rows.length > MAX_ROWS || element.headers.length > MAX_COLS) {
|
|
257
259
|
return [
|
|
258
260
|
{
|
|
259
261
|
type: "section",
|
|
@@ -266,31 +268,28 @@ ${tableElementToAscii(element.headers, element.rows)}
|
|
|
266
268
|
}
|
|
267
269
|
];
|
|
268
270
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
text: convertEmoji(header)
|
|
274
|
-
}
|
|
271
|
+
state.usedNativeTable = true;
|
|
272
|
+
const headerRow = element.headers.map((header) => ({
|
|
273
|
+
type: "raw_text",
|
|
274
|
+
text: convertEmoji(header)
|
|
275
275
|
}));
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
type: "
|
|
276
|
+
const dataRows = element.rows.map(
|
|
277
|
+
(row) => row.map((cell) => ({
|
|
278
|
+
type: "raw_text",
|
|
279
279
|
text: convertEmoji(cell)
|
|
280
280
|
}))
|
|
281
|
-
|
|
281
|
+
);
|
|
282
282
|
return [
|
|
283
283
|
{
|
|
284
284
|
type: "table",
|
|
285
|
-
|
|
286
|
-
rows
|
|
285
|
+
rows: [headerRow, ...dataRows]
|
|
287
286
|
}
|
|
288
287
|
];
|
|
289
288
|
}
|
|
290
|
-
function convertSectionToBlocks(element) {
|
|
289
|
+
function convertSectionToBlocks(element, state) {
|
|
291
290
|
const blocks = [];
|
|
292
291
|
for (const child of element.children) {
|
|
293
|
-
blocks.push(...convertChildToBlocks(child));
|
|
292
|
+
blocks.push(...convertChildToBlocks(child, state));
|
|
294
293
|
}
|
|
295
294
|
return blocks;
|
|
296
295
|
}
|
|
@@ -428,16 +427,68 @@ var SlackFormatConverter = class extends BaseFormatConverter {
|
|
|
428
427
|
*/
|
|
429
428
|
toAst(mrkdwn) {
|
|
430
429
|
let markdown = mrkdwn;
|
|
431
|
-
markdown = markdown.replace(/<@([
|
|
432
|
-
markdown = markdown.replace(/<@([
|
|
433
|
-
markdown = markdown.replace(/<#[
|
|
434
|
-
markdown = markdown.replace(/<#([
|
|
430
|
+
markdown = markdown.replace(/<@([A-Z0-9_]+)\|([^>]+)>/g, "@$2");
|
|
431
|
+
markdown = markdown.replace(/<@([A-Z0-9_]+)>/g, "@$1");
|
|
432
|
+
markdown = markdown.replace(/<#[A-Z0-9_]+\|([^>]+)>/g, "#$1");
|
|
433
|
+
markdown = markdown.replace(/<#([A-Z0-9_]+)>/g, "#$1");
|
|
435
434
|
markdown = markdown.replace(/<(https?:\/\/[^|>]+)\|([^>]+)>/g, "[$2]($1)");
|
|
436
435
|
markdown = markdown.replace(/<(https?:\/\/[^>]+)>/g, "$1");
|
|
437
436
|
markdown = markdown.replace(/(?<![_*\\])\*([^*\n]+)\*(?![_*])/g, "**$1**");
|
|
438
437
|
markdown = markdown.replace(/(?<!~)~([^~\n]+)~(?!~)/g, "~~$1~~");
|
|
439
438
|
return parseMarkdown(markdown);
|
|
440
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Convert AST to Slack blocks, using a native table block for the first table.
|
|
442
|
+
* Returns null if the AST contains no tables (caller should use regular text).
|
|
443
|
+
* Slack allows at most one table block per message; additional tables use ASCII.
|
|
444
|
+
*/
|
|
445
|
+
toBlocksWithTable(ast) {
|
|
446
|
+
const hasTable = ast.children.some((node) => isTableNode(node));
|
|
447
|
+
if (!hasTable) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
const blocks = [];
|
|
451
|
+
let usedNativeTable = false;
|
|
452
|
+
let textBuffer = [];
|
|
453
|
+
const flushText = () => {
|
|
454
|
+
if (textBuffer.length > 0) {
|
|
455
|
+
const text = textBuffer.join("\n\n");
|
|
456
|
+
if (text.trim()) {
|
|
457
|
+
blocks.push({
|
|
458
|
+
type: "section",
|
|
459
|
+
text: { type: "mrkdwn", text }
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
textBuffer = [];
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
for (const child of ast.children) {
|
|
466
|
+
const node = child;
|
|
467
|
+
if (isTableNode(node)) {
|
|
468
|
+
flushText();
|
|
469
|
+
if (usedNativeTable) {
|
|
470
|
+
blocks.push({
|
|
471
|
+
type: "section",
|
|
472
|
+
text: {
|
|
473
|
+
type: "mrkdwn",
|
|
474
|
+
text: `\`\`\`
|
|
475
|
+
${tableToAscii(node)}
|
|
476
|
+
\`\`\``
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
} else {
|
|
480
|
+
blocks.push(
|
|
481
|
+
mdastTableToSlackBlock(node, this.nodeToMrkdwn.bind(this))
|
|
482
|
+
);
|
|
483
|
+
usedNativeTable = true;
|
|
484
|
+
}
|
|
485
|
+
} else {
|
|
486
|
+
textBuffer.push(this.nodeToMrkdwn(node));
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
flushText();
|
|
490
|
+
return blocks;
|
|
491
|
+
}
|
|
441
492
|
nodeToMrkdwn(node) {
|
|
442
493
|
if (isParagraphNode(node)) {
|
|
443
494
|
return getNodeChildren(node).map((child) => this.nodeToMrkdwn(child)).join("");
|
|
@@ -489,6 +540,26 @@ ${tableToAscii(node)}
|
|
|
489
540
|
return this.defaultNodeToText(node, (child) => this.nodeToMrkdwn(child));
|
|
490
541
|
}
|
|
491
542
|
};
|
|
543
|
+
function mdastTableToSlackBlock(node, cellConverter) {
|
|
544
|
+
const rows = [];
|
|
545
|
+
for (const row of node.children) {
|
|
546
|
+
const cells = getNodeChildren(row).map((cell) => ({
|
|
547
|
+
type: "raw_text",
|
|
548
|
+
text: getNodeChildren(cell).map(cellConverter).join("")
|
|
549
|
+
}));
|
|
550
|
+
rows.push(cells);
|
|
551
|
+
}
|
|
552
|
+
const block = { type: "table", rows };
|
|
553
|
+
if (node.align) {
|
|
554
|
+
const columnSettings = node.align.map(
|
|
555
|
+
(a) => ({
|
|
556
|
+
align: a || "left"
|
|
557
|
+
})
|
|
558
|
+
);
|
|
559
|
+
block.column_settings = columnSettings;
|
|
560
|
+
}
|
|
561
|
+
return block;
|
|
562
|
+
}
|
|
492
563
|
|
|
493
564
|
// src/modals.ts
|
|
494
565
|
function encodeModalMetadata(meta) {
|
|
@@ -636,6 +707,7 @@ function radioSelectToBlock(radioSelect) {
|
|
|
636
707
|
}
|
|
637
708
|
|
|
638
709
|
// src/index.ts
|
|
710
|
+
var SLACK_USER_ID_PATTERN = /^[A-Z0-9_]+$/;
|
|
639
711
|
var SlackAdapter = class _SlackAdapter {
|
|
640
712
|
name = "slack";
|
|
641
713
|
userName;
|
|
@@ -664,18 +736,28 @@ var SlackAdapter = class _SlackAdapter {
|
|
|
664
736
|
}
|
|
665
737
|
return this._botUserId || void 0;
|
|
666
738
|
}
|
|
667
|
-
constructor(config) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
739
|
+
constructor(config = {}) {
|
|
740
|
+
const signingSecret = config.signingSecret ?? process.env.SLACK_SIGNING_SECRET;
|
|
741
|
+
if (!signingSecret) {
|
|
742
|
+
throw new ValidationError(
|
|
743
|
+
"slack",
|
|
744
|
+
"signingSecret is required. Set SLACK_SIGNING_SECRET or provide it in config."
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
const zeroConfig = !(config.signingSecret || config.botToken || config.clientId || config.clientSecret);
|
|
748
|
+
const botToken = config.botToken ?? (zeroConfig ? process.env.SLACK_BOT_TOKEN : void 0);
|
|
749
|
+
this.client = new WebClient(botToken);
|
|
750
|
+
this.signingSecret = signingSecret;
|
|
751
|
+
this.defaultBotToken = botToken;
|
|
752
|
+
this.logger = config.logger ?? new ConsoleLogger("info").child("slack");
|
|
672
753
|
this.userName = config.userName || "bot";
|
|
673
754
|
this._botUserId = config.botUserId || null;
|
|
674
|
-
this.clientId = config.clientId;
|
|
675
|
-
this.clientSecret = config.clientSecret;
|
|
755
|
+
this.clientId = config.clientId ?? (zeroConfig ? process.env.SLACK_CLIENT_ID : void 0);
|
|
756
|
+
this.clientSecret = config.clientSecret ?? (zeroConfig ? process.env.SLACK_CLIENT_SECRET : void 0);
|
|
676
757
|
this.installationKeyPrefix = config.installationKeyPrefix ?? "slack:installation";
|
|
677
|
-
|
|
678
|
-
|
|
758
|
+
const encryptionKey = config.encryptionKey ?? process.env.SLACK_ENCRYPTION_KEY;
|
|
759
|
+
if (encryptionKey) {
|
|
760
|
+
this.encryptionKey = decodeKey(encryptionKey);
|
|
679
761
|
}
|
|
680
762
|
}
|
|
681
763
|
/**
|
|
@@ -1281,7 +1363,28 @@ var SlackAdapter = class _SlackAdapter {
|
|
|
1281
1363
|
this.logger.warn("Chat instance not initialized, ignoring event");
|
|
1282
1364
|
return;
|
|
1283
1365
|
}
|
|
1284
|
-
|
|
1366
|
+
const ignoredSubtypes = /* @__PURE__ */ new Set([
|
|
1367
|
+
"message_changed",
|
|
1368
|
+
"message_deleted",
|
|
1369
|
+
"message_replied",
|
|
1370
|
+
"channel_join",
|
|
1371
|
+
"channel_leave",
|
|
1372
|
+
"channel_topic",
|
|
1373
|
+
"channel_purpose",
|
|
1374
|
+
"channel_name",
|
|
1375
|
+
"channel_archive",
|
|
1376
|
+
"channel_unarchive",
|
|
1377
|
+
"group_join",
|
|
1378
|
+
"group_leave",
|
|
1379
|
+
"group_topic",
|
|
1380
|
+
"group_purpose",
|
|
1381
|
+
"group_name",
|
|
1382
|
+
"group_archive",
|
|
1383
|
+
"group_unarchive",
|
|
1384
|
+
"ekm_access_denied",
|
|
1385
|
+
"tombstone"
|
|
1386
|
+
]);
|
|
1387
|
+
if (event.subtype && ignoredSubtypes.has(event.subtype)) {
|
|
1285
1388
|
this.logger.debug("Ignoring message subtype", {
|
|
1286
1389
|
subtype: event.subtype
|
|
1287
1390
|
});
|
|
@@ -1538,12 +1641,22 @@ var SlackAdapter = class _SlackAdapter {
|
|
|
1538
1641
|
* detection doesn't apply.
|
|
1539
1642
|
*/
|
|
1540
1643
|
async resolveInlineMentions(text, skipSelfMention) {
|
|
1541
|
-
const mentionPattern = /<@([A-Z0-9]+)(?:\|[^>]*)?>/g;
|
|
1542
1644
|
const userIds = /* @__PURE__ */ new Set();
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1645
|
+
for (const segment of text.split("<")) {
|
|
1646
|
+
const end = segment.indexOf(">");
|
|
1647
|
+
if (end === -1) {
|
|
1648
|
+
continue;
|
|
1649
|
+
}
|
|
1650
|
+
const inner = segment.slice(0, end);
|
|
1651
|
+
if (!inner.startsWith("@")) {
|
|
1652
|
+
continue;
|
|
1653
|
+
}
|
|
1654
|
+
const rest = inner.slice(1);
|
|
1655
|
+
const pipeIdx = rest.indexOf("|");
|
|
1656
|
+
const uid = pipeIdx >= 0 ? rest.slice(0, pipeIdx) : rest;
|
|
1657
|
+
if (SLACK_USER_ID_PATTERN.test(uid)) {
|
|
1658
|
+
userIds.add(uid);
|
|
1659
|
+
}
|
|
1547
1660
|
}
|
|
1548
1661
|
if (userIds.size === 0) {
|
|
1549
1662
|
return text;
|
|
@@ -1561,10 +1674,29 @@ var SlackAdapter = class _SlackAdapter {
|
|
|
1561
1674
|
})
|
|
1562
1675
|
);
|
|
1563
1676
|
const nameMap = new Map(lookups);
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1677
|
+
let result = "";
|
|
1678
|
+
let remaining = text;
|
|
1679
|
+
let startIdx = remaining.indexOf("<@");
|
|
1680
|
+
while (startIdx !== -1) {
|
|
1681
|
+
result += remaining.slice(0, startIdx);
|
|
1682
|
+
remaining = remaining.slice(startIdx);
|
|
1683
|
+
const endIdx = remaining.indexOf(">");
|
|
1684
|
+
if (endIdx === -1) {
|
|
1685
|
+
break;
|
|
1686
|
+
}
|
|
1687
|
+
const inner = remaining.slice(2, endIdx);
|
|
1688
|
+
const pipeIdx = inner.indexOf("|");
|
|
1689
|
+
const uid = pipeIdx >= 0 ? inner.slice(0, pipeIdx) : inner;
|
|
1690
|
+
if (SLACK_USER_ID_PATTERN.test(uid)) {
|
|
1691
|
+
const name = nameMap.get(uid);
|
|
1692
|
+
result += name ? `<@${uid}|${name}>` : `<@${uid}>`;
|
|
1693
|
+
} else {
|
|
1694
|
+
result += remaining.slice(0, endIdx + 1);
|
|
1695
|
+
}
|
|
1696
|
+
remaining = remaining.slice(endIdx + 1);
|
|
1697
|
+
startIdx = remaining.indexOf("<@");
|
|
1698
|
+
}
|
|
1699
|
+
return result + remaining;
|
|
1568
1700
|
}
|
|
1569
1701
|
async parseSlackMessage(event, threadId, options) {
|
|
1570
1702
|
const isMe = this.isMessageFromSelf(event);
|
|
@@ -1641,13 +1773,39 @@ var SlackAdapter = class _SlackAdapter {
|
|
|
1641
1773
|
} : void 0
|
|
1642
1774
|
};
|
|
1643
1775
|
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Try to render a message using native Slack table blocks.
|
|
1778
|
+
* Returns blocks + fallback text if the message contains tables, null otherwise.
|
|
1779
|
+
*/
|
|
1780
|
+
renderWithTableBlocks(message) {
|
|
1781
|
+
let ast = null;
|
|
1782
|
+
if (typeof message === "object" && message !== null) {
|
|
1783
|
+
if ("ast" in message) {
|
|
1784
|
+
ast = message.ast;
|
|
1785
|
+
} else if ("markdown" in message) {
|
|
1786
|
+
ast = parseMarkdown2(message.markdown);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
if (!ast) {
|
|
1790
|
+
return null;
|
|
1791
|
+
}
|
|
1792
|
+
const blocks = this.formatConverter.toBlocksWithTable(ast);
|
|
1793
|
+
if (!blocks) {
|
|
1794
|
+
return null;
|
|
1795
|
+
}
|
|
1796
|
+
const fallbackText = convertEmojiPlaceholders(
|
|
1797
|
+
this.formatConverter.renderPostable(message),
|
|
1798
|
+
"slack"
|
|
1799
|
+
);
|
|
1800
|
+
return { text: fallbackText, blocks };
|
|
1801
|
+
}
|
|
1644
1802
|
async postMessage(threadId, message) {
|
|
1645
1803
|
const { channel, threadTs } = this.decodeThreadId(threadId);
|
|
1646
1804
|
try {
|
|
1647
1805
|
const files = extractFiles(message);
|
|
1648
1806
|
if (files.length > 0) {
|
|
1649
1807
|
await this.uploadFiles(files, channel, threadTs || void 0);
|
|
1650
|
-
const hasText = typeof message === "string" || typeof message === "object" && message !== null && ("raw" in message || "markdown" in message || "ast" in message);
|
|
1808
|
+
const hasText = typeof message === "string" || typeof message === "object" && message !== null && ("raw" in message && message.raw || "markdown" in message && message.markdown || "ast" in message && message.ast);
|
|
1651
1809
|
const card2 = extractCard(message);
|
|
1652
1810
|
if (!(hasText || card2)) {
|
|
1653
1811
|
return {
|
|
@@ -1687,6 +1845,33 @@ var SlackAdapter = class _SlackAdapter {
|
|
|
1687
1845
|
raw: result2
|
|
1688
1846
|
};
|
|
1689
1847
|
}
|
|
1848
|
+
const tableResult = this.renderWithTableBlocks(message);
|
|
1849
|
+
if (tableResult) {
|
|
1850
|
+
this.logger.debug("Slack API: chat.postMessage (table blocks)", {
|
|
1851
|
+
channel,
|
|
1852
|
+
threadTs,
|
|
1853
|
+
blockCount: tableResult.blocks.length
|
|
1854
|
+
});
|
|
1855
|
+
const result2 = await this.client.chat.postMessage(
|
|
1856
|
+
this.withToken({
|
|
1857
|
+
channel,
|
|
1858
|
+
thread_ts: threadTs,
|
|
1859
|
+
text: tableResult.text,
|
|
1860
|
+
blocks: tableResult.blocks,
|
|
1861
|
+
unfurl_links: false,
|
|
1862
|
+
unfurl_media: false
|
|
1863
|
+
})
|
|
1864
|
+
);
|
|
1865
|
+
this.logger.debug("Slack API: chat.postMessage response", {
|
|
1866
|
+
messageId: result2.ts,
|
|
1867
|
+
ok: result2.ok
|
|
1868
|
+
});
|
|
1869
|
+
return {
|
|
1870
|
+
id: result2.ts,
|
|
1871
|
+
threadId,
|
|
1872
|
+
raw: result2
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1690
1875
|
const text = convertEmojiPlaceholders(
|
|
1691
1876
|
this.formatConverter.renderPostable(message),
|
|
1692
1877
|
"slack"
|
|
@@ -1751,6 +1936,34 @@ var SlackAdapter = class _SlackAdapter {
|
|
|
1751
1936
|
raw: result2
|
|
1752
1937
|
};
|
|
1753
1938
|
}
|
|
1939
|
+
const tableResult = this.renderWithTableBlocks(message);
|
|
1940
|
+
if (tableResult) {
|
|
1941
|
+
this.logger.debug("Slack API: chat.postEphemeral (table blocks)", {
|
|
1942
|
+
channel,
|
|
1943
|
+
threadTs,
|
|
1944
|
+
userId,
|
|
1945
|
+
blockCount: tableResult.blocks.length
|
|
1946
|
+
});
|
|
1947
|
+
const result2 = await this.client.chat.postEphemeral(
|
|
1948
|
+
this.withToken({
|
|
1949
|
+
channel,
|
|
1950
|
+
thread_ts: threadTs || void 0,
|
|
1951
|
+
user: userId,
|
|
1952
|
+
text: tableResult.text,
|
|
1953
|
+
blocks: tableResult.blocks
|
|
1954
|
+
})
|
|
1955
|
+
);
|
|
1956
|
+
this.logger.debug("Slack API: chat.postEphemeral response", {
|
|
1957
|
+
messageTs: result2.message_ts,
|
|
1958
|
+
ok: result2.ok
|
|
1959
|
+
});
|
|
1960
|
+
return {
|
|
1961
|
+
id: result2.message_ts || "",
|
|
1962
|
+
threadId,
|
|
1963
|
+
usedFallback: false,
|
|
1964
|
+
raw: result2
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1754
1967
|
const text = convertEmojiPlaceholders(
|
|
1755
1968
|
this.formatConverter.renderPostable(message),
|
|
1756
1969
|
"slack"
|
|
@@ -1927,6 +2140,31 @@ var SlackAdapter = class _SlackAdapter {
|
|
|
1927
2140
|
raw: result2
|
|
1928
2141
|
};
|
|
1929
2142
|
}
|
|
2143
|
+
const tableResult = this.renderWithTableBlocks(message);
|
|
2144
|
+
if (tableResult) {
|
|
2145
|
+
this.logger.debug("Slack API: chat.update (table blocks)", {
|
|
2146
|
+
channel,
|
|
2147
|
+
messageId,
|
|
2148
|
+
blockCount: tableResult.blocks.length
|
|
2149
|
+
});
|
|
2150
|
+
const result2 = await this.client.chat.update(
|
|
2151
|
+
this.withToken({
|
|
2152
|
+
channel,
|
|
2153
|
+
ts: messageId,
|
|
2154
|
+
text: tableResult.text,
|
|
2155
|
+
blocks: tableResult.blocks
|
|
2156
|
+
})
|
|
2157
|
+
);
|
|
2158
|
+
this.logger.debug("Slack API: chat.update response", {
|
|
2159
|
+
messageId: result2.ts,
|
|
2160
|
+
ok: result2.ok
|
|
2161
|
+
});
|
|
2162
|
+
return {
|
|
2163
|
+
id: result2.ts,
|
|
2164
|
+
threadId,
|
|
2165
|
+
raw: result2
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
1930
2168
|
const text = convertEmojiPlaceholders(
|
|
1931
2169
|
this.formatConverter.renderPostable(message),
|
|
1932
2170
|
"slack"
|
|
@@ -2733,13 +2971,22 @@ var SlackAdapter = class _SlackAdapter {
|
|
|
2733
2971
|
blocks: cardToBlockKit(card)
|
|
2734
2972
|
};
|
|
2735
2973
|
} else {
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2974
|
+
const tableResult = this.renderWithTableBlocks(message);
|
|
2975
|
+
if (tableResult) {
|
|
2976
|
+
payload = {
|
|
2977
|
+
replace_original: true,
|
|
2978
|
+
text: tableResult.text,
|
|
2979
|
+
blocks: tableResult.blocks
|
|
2980
|
+
};
|
|
2981
|
+
} else {
|
|
2982
|
+
payload = {
|
|
2983
|
+
replace_original: true,
|
|
2984
|
+
text: convertEmojiPlaceholders(
|
|
2985
|
+
this.formatConverter.renderPostable(message),
|
|
2986
|
+
"slack"
|
|
2987
|
+
)
|
|
2988
|
+
};
|
|
2989
|
+
}
|
|
2743
2990
|
}
|
|
2744
2991
|
if (options?.threadTs) {
|
|
2745
2992
|
payload.thread_ts = options.threadTs;
|
|
@@ -2778,26 +3025,7 @@ var SlackAdapter = class _SlackAdapter {
|
|
|
2778
3025
|
}
|
|
2779
3026
|
};
|
|
2780
3027
|
function createSlackAdapter(config) {
|
|
2781
|
-
|
|
2782
|
-
if (!signingSecret) {
|
|
2783
|
-
throw new ValidationError(
|
|
2784
|
-
"slack",
|
|
2785
|
-
"signingSecret is required. Set SLACK_SIGNING_SECRET or provide it in config."
|
|
2786
|
-
);
|
|
2787
|
-
}
|
|
2788
|
-
const zeroConfig = !config;
|
|
2789
|
-
const resolved = {
|
|
2790
|
-
signingSecret,
|
|
2791
|
-
botToken: config?.botToken ?? (zeroConfig ? process.env.SLACK_BOT_TOKEN : void 0),
|
|
2792
|
-
clientId: config?.clientId ?? (zeroConfig ? process.env.SLACK_CLIENT_ID : void 0),
|
|
2793
|
-
clientSecret: config?.clientSecret ?? (zeroConfig ? process.env.SLACK_CLIENT_SECRET : void 0),
|
|
2794
|
-
encryptionKey: config?.encryptionKey ?? process.env.SLACK_ENCRYPTION_KEY,
|
|
2795
|
-
installationKeyPrefix: config?.installationKeyPrefix,
|
|
2796
|
-
logger: config?.logger ?? new ConsoleLogger("info").child("slack"),
|
|
2797
|
-
userName: config?.userName,
|
|
2798
|
-
botUserId: config?.botUserId
|
|
2799
|
-
};
|
|
2800
|
-
return new SlackAdapter(resolved);
|
|
3028
|
+
return new SlackAdapter(config ?? {});
|
|
2801
3029
|
}
|
|
2802
3030
|
export {
|
|
2803
3031
|
SlackAdapter,
|