@openclaw/slack 2026.7.2-beta.7 → 2026.8.1-beta.3
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/{account-inspect-DEKpg9Ip.js → account-inspect-YJvFBbCR.js} +1 -1
- package/dist/account-inspect-api.js +1 -1
- package/dist/{accounts-CHAeNZOX.js → accounts-BlzxuYle.js} +7 -3
- package/dist/{accounts.runtime-BHD4Ma2R.js → accounts.runtime-CbabBL8x.js} +1 -1
- package/dist/{action-runtime-DfuYoMjG.js → action-runtime-D6jg2CQd.js} +193 -125
- package/dist/action-runtime.runtime-JtoOK9l5.js +2 -0
- package/dist/{actions-D3NuJvva.js → actions-DFk0ANVB.js} +116 -235
- package/dist/actions.runtime-CYLsYRai.js +2 -0
- package/dist/api.js +17 -17
- package/dist/{approval-handler.runtime-BA05P0QT.js → approval-handler.runtime-BNvAoKdK.js} +53 -14
- package/dist/{channel-B9fVdW3s.js → channel-CA5w7qDF.js} +209 -110
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-type-DXyos1d2.js → channel-type-CZWv55Wu.js} +9 -12
- package/dist/{config-adapter-CxBQH8fq.js → channel.setup-CpyYsEXH.js} +83 -25
- package/dist/{config-schema-SgZT6nXV.js → config-schema-CN3XZRX5.js} +27 -17
- package/dist/conversation-binding-route-DFVLUdYF.js +115 -0
- package/dist/{directory-config-CnCIAktf.js → directory-config-BGYyuzWh.js} +3 -3
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-DKunWh3A.js → directory-live-DjUJQLbq.js} +13 -22
- package/dist/{doctor-contract-CsGH1Dfc.js → doctor-contract-BAy7-EUQ.js} +30 -3
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{group-policy-NQZti9t-.js → group-policy-DyfB_ZIE.js} +282 -183
- package/dist/index.js +4 -1
- package/dist/{message-tool-api-DkhFpMoy.js → message-tool-api-CqMr5Rmc.js} +14 -3
- package/dist/message-tool-api.js +1 -1
- package/dist/{monitor-CmKz0wtI.js → monitor-Chrecrb9.js} +3 -3
- package/dist/{outbound-adapter-BkuMp2Vs.js → outbound-adapter-DuRqAQXK.js} +35 -23
- package/dist/{pipeline.runtime-DgtmuYCP.js → pipeline.runtime-Bbfd5VFW.js} +1386 -929
- package/dist/{policy-PRysPn23.js → policy-BKXSxrIS.js} +18 -14
- package/dist/{probe-CgDsSops.js → probe-BVA65leh.js} +38 -18
- package/dist/{provider-DmRizMZt.js → provider-CUUiwsg2.js} +3454 -2601
- package/dist/{relay-source-Blxj8xb4.js → relay-source-mhpMMRG-.js} +11 -21
- package/dist/{replies-4s_Vq_z9.js → replies-SnWyXcsL.js} +84 -27
- package/dist/{resolve-channels-yEqmM-3I.js → resolve-channels-BSpqC8jU.js} +39 -12
- package/dist/{resolve-users-BOrt5K-5.js → resolve-users-Dvz9pDe0.js} +28 -5
- package/dist/{runtime-api-QMb-Q5Ko.js → runtime-api-DBi7xc_g.js} +3 -3
- package/dist/runtime-api.js +11 -11
- package/dist/{scopes-BJSrkAp6.js → scopes-BSk4GZvZ.js} +1 -1
- package/dist/security-contract-api.js +1 -1
- package/dist/{send-DsLeZpST.js → send-CtnOxKCc.js} +249 -163
- package/dist/{send.runtime-gUdK6V3h.js → send.runtime-kYctMHSd.js} +1 -1
- package/dist/setup-entry.js +1 -4
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-BeNzrMrJ.js → setup-surface-BPFfp6So.js} +6 -6
- package/dist/{shared-DgIN3Dcr.js → shared-DJBaslN7.js} +45 -45
- package/dist/{block-text-DkHaoGem.js → slack-client-kind-BXyOOICI.js} +17 -2
- package/dist/{slash-dispatch.runtime-zPkyl7dB.js → slash-dispatch.runtime-Cf2FTrnf.js} +2 -2
- package/dist/{streaming-compat-3_X-21wW.js → streaming-compat-B-e0mqM0.js} +3 -8
- package/dist/{target-parsing-N6lHogYZ.js → target-parsing-BnMD2ZqZ.js} +40 -2
- package/dist/{targets-KmjP2E8s.js → targets-Cx5W_n3W.js} +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/crypto.d.ts +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/socket-mode/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/socket-mode/node_modules/@types/node/crypto.d.ts +1 -1
- package/node_modules/@slack/socket-mode/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/web-api/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/crypto.d.ts +1 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/package.json +2 -2
- package/node_modules/undici/lib/cache/memory-cache-store.js +54 -9
- package/node_modules/undici/lib/cache/sqlite-cache-store.js +7 -1
- package/node_modules/undici/lib/core/request.js +12 -1
- package/node_modules/undici/lib/dispatcher/client-h1.js +11 -2
- package/node_modules/undici/lib/handler/cache-handler.js +246 -64
- package/node_modules/undici/lib/handler/cache-revalidation-handler.js +3 -3
- package/node_modules/undici/lib/handler/retry-handler.js +24 -0
- package/node_modules/undici/lib/interceptor/cache.js +159 -52
- package/node_modules/undici/lib/util/cache.js +324 -62
- package/node_modules/undici/lib/util/date.js +23 -6
- package/node_modules/undici/lib/web/cookies/util.js +79 -9
- package/node_modules/undici/package.json +1 -1
- package/openclaw.plugin.json +280 -33
- package/package.json +9 -11
- package/skills/slack/SKILL.md +10 -68
- package/dist/account-reply-mode-DrUXogvO.js +0 -14
- package/dist/action-runtime.runtime-DQErZYpz.js +0 -2
- package/dist/actions.runtime-BIRU_G4M.js +0 -2
- package/dist/channel.setup-CLP5oTvh.js +0 -56
- package/dist/preflight-audio.runtime-D5Bgags0.js +0 -3
- package/dist/slash-plugin-commands.runtime-Dosfcx3X.js +0 -2
- package/node_modules/p-map/index.d.ts +0 -155
- package/node_modules/p-map/index.js +0 -285
- package/node_modules/p-map/license +0 -9
- package/node_modules/p-map/package.json +0 -57
- package/node_modules/p-map/readme.md +0 -190
- package/dist/{security-audit-DR097212.js → security-audit-B_yKr7MW.js} +1 -1
|
@@ -1,43 +1,44 @@
|
|
|
1
|
-
import { a as resolveSlackAccount, l as resolveSlackOperationToken } from "./accounts-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { a as resolveSlackAccount, l as resolveSlackOperationToken } from "./accounts-BlzxuYle.js";
|
|
2
|
+
import { a as parseSlackTarget, t as canonicalizeSlackApiTargetId } from "./target-parsing-BnMD2ZqZ.js";
|
|
3
|
+
import { B as SLACK_EDIT_TEXT_MAX_BYTES, D as createSlackNativeDataBaseTextConsumer, E as buildSlackNativeDataAccessibilityText, H as SLACK_MESSAGE_TEXT_RECOMMENDED_LIMIT, J as truncateSlackText, L as buildSlackCompleteBlocksFallbackText, M as stripSlackNativeDataBlocks, N as chunkSlackMrkdwnText, O as hasSlackNativeDataBlock, P as markdownToSlackMrkdwnChunks, R as renderSlackBlockFallbackText, T as appendSlackNativeDataPlainTextFallback, U as SLACK_TEXT_LIMIT, V as SLACK_MESSAGE_TEXT_HARD_LIMIT, X as SLACK_QUESTION_FINALIZATION_BLOCKS, Y as truncateSlackTextByUtf8Bytes, g as assertSlackDetachedTargetAllowed, it as resolveSlackQuestionActionIds, k as isSlackInvalidBlocksError, mt as validateSlackBlocksArray } from "./group-policy-DyfB_ZIE.js";
|
|
4
4
|
import { n as resolveSlackThreadTsValue, t as normalizeSlackThreadTsCandidate } from "./thread-ts-DUGhaYKq.js";
|
|
5
|
-
import {
|
|
5
|
+
import { a as createSlackReadClient, d as getSlackWriteClient, r as formatSlackError, s as createSlackTokenCacheKey } from "./probe-BVA65leh.js";
|
|
6
6
|
import { t as getOptionalSlackRuntime } from "./runtime-JSVZSWAj.js";
|
|
7
|
-
import { i as loadOutboundMediaFromUrl } from "./runtime-api-
|
|
7
|
+
import { i as loadOutboundMediaFromUrl } from "./runtime-api-DBi7xc_g.js";
|
|
8
8
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
9
9
|
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
|
|
10
|
-
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
11
|
-
import {
|
|
10
|
+
import { logVerbose, sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
|
|
11
|
+
import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
|
|
12
|
+
import { resolveGlobalSingleton } from "openclaw/plugin-sdk/global-singleton";
|
|
13
|
+
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
12
14
|
import { createHash, createHmac } from "node:crypto";
|
|
13
15
|
import { withTrustedEnvProxyGuardedFetchMode } from "openclaw/plugin-sdk/fetch-runtime";
|
|
14
16
|
import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
15
17
|
import { PlatformMessageNotDispatchedError, extractErrorCode, readErrorName } from "openclaw/plugin-sdk/error-runtime";
|
|
16
18
|
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
17
19
|
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
18
|
-
import { chunkMarkdownTextWithMode,
|
|
20
|
+
import { chunkMarkdownTextWithMode, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
|
|
19
21
|
import { resolveTextChunksWithFallback } from "openclaw/plugin-sdk/reply-payload";
|
|
20
22
|
import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
|
|
21
23
|
import { buildTimeoutAbortSignal } from "openclaw/plugin-sdk/extension-shared";
|
|
24
|
+
import { extensionForMime } from "openclaw/plugin-sdk/media-mime";
|
|
25
|
+
import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
|
|
22
26
|
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
23
27
|
import { createPersistentDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
|
|
28
|
+
import { createPluginStateErrorReporter } from "openclaw/plugin-sdk/plugin-state-runtime";
|
|
24
29
|
//#region extensions/slack/src/native-data-fallback.ts
|
|
25
30
|
const SLACK_SECTION_PLAIN_TEXT_MAX = 3e3;
|
|
26
31
|
const SLACK_EMPTY_BLOCK_FALLBACK = "Shared a Block Kit message";
|
|
27
32
|
function chunkSlackTextAtHardLimit(text, limit = SLACK_MESSAGE_TEXT_HARD_LIMIT) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
chunks.push(chunk);
|
|
35
|
-
offset += chunk.length;
|
|
36
|
-
}
|
|
37
|
-
return chunks;
|
|
33
|
+
if (!text) return [];
|
|
34
|
+
return chunkTextForOutbound(text, Math.max(1, Math.floor(limit)), { preserveWhitespace: true });
|
|
35
|
+
}
|
|
36
|
+
function fitsSlackTextLimit(text, limit) {
|
|
37
|
+
if (text.length <= limit) return true;
|
|
38
|
+
return truncateUtf16Safe(text, limit).length === text.length;
|
|
38
39
|
}
|
|
39
|
-
function buildPlainTextBlocks(text) {
|
|
40
|
-
return chunkSlackTextAtHardLimit(text, SLACK_SECTION_PLAIN_TEXT_MAX).map((chunk, index) => {
|
|
40
|
+
function buildPlainTextBlocks(text, textLimit) {
|
|
41
|
+
return chunkSlackTextAtHardLimit(text, Math.min(textLimit, SLACK_SECTION_PLAIN_TEXT_MAX)).map((chunk, index) => {
|
|
41
42
|
const fallbackBlock = {
|
|
42
43
|
block: {
|
|
43
44
|
type: "section",
|
|
@@ -58,11 +59,11 @@ function renderNativeDataPlainText(block) {
|
|
|
58
59
|
function buildOrderedFallbackBlocks(params) {
|
|
59
60
|
const entries = [];
|
|
60
61
|
const consumeFromBase = createSlackNativeDataBaseTextConsumer(params.baseText);
|
|
61
|
-
if (params.baseText) entries.push(...buildPlainTextBlocks(params.baseText));
|
|
62
|
+
if (params.baseText) entries.push(...buildPlainTextBlocks(params.baseText, params.textLimit));
|
|
62
63
|
for (const block of params.blocks) {
|
|
63
64
|
if (hasSlackNativeDataBlock([block])) {
|
|
64
65
|
const nativeText = renderNativeDataPlainText(block);
|
|
65
|
-
if (!consumeFromBase(nativeText)) entries.push(...buildPlainTextBlocks(nativeText));
|
|
66
|
+
if (!consumeFromBase(nativeText)) entries.push(...buildPlainTextBlocks(nativeText, params.textLimit));
|
|
66
67
|
continue;
|
|
67
68
|
}
|
|
68
69
|
const text = renderSlackBlockFallbackText(block, { nativeDataFormat: "plain" });
|
|
@@ -73,7 +74,7 @@ function buildOrderedFallbackBlocks(params) {
|
|
|
73
74
|
}
|
|
74
75
|
return entries;
|
|
75
76
|
}
|
|
76
|
-
function buildOrderedBlockMessages(entries) {
|
|
77
|
+
function buildOrderedBlockMessages(entries, textLimit) {
|
|
77
78
|
const messages = [];
|
|
78
79
|
let blocks = [];
|
|
79
80
|
let text = "";
|
|
@@ -90,10 +91,10 @@ function buildOrderedBlockMessages(entries) {
|
|
|
90
91
|
for (const entry of entries) {
|
|
91
92
|
const separator = text && entry.text && !entry.continuesText ? "\n\n" : "";
|
|
92
93
|
const nextText = entry.text ? `${text}${separator}${entry.text}` : text;
|
|
93
|
-
if (blocks.length >= 50 || nextText
|
|
94
|
+
if (blocks.length >= 50 || !fitsSlackTextLimit(nextText, textLimit)) flush();
|
|
94
95
|
const freshSeparator = text && entry.text && !entry.continuesText ? "\n\n" : "";
|
|
95
96
|
const freshText = entry.text ? `${text}${freshSeparator}${entry.text}` : text;
|
|
96
|
-
if (freshText
|
|
97
|
+
if (!fitsSlackTextLimit(freshText, textLimit)) throw new Error("One Slack fallback block exceeds the resolved message text limit.");
|
|
97
98
|
blocks.push(entry.block);
|
|
98
99
|
text = freshText;
|
|
99
100
|
}
|
|
@@ -103,17 +104,19 @@ function buildOrderedBlockMessages(entries) {
|
|
|
103
104
|
/** Build one complete, ordered retry plan after Slack rejects native data blocks. */
|
|
104
105
|
function buildSlackNativeDataDeliveryPlan(params) {
|
|
105
106
|
const baseText = params.baseText?.trim() ?? "";
|
|
107
|
+
const textLimit = Math.min(SLACK_MESSAGE_TEXT_RECOMMENDED_LIMIT, SLACK_MESSAGE_TEXT_HARD_LIMIT, Math.max(1, Math.floor(params.textLimit ?? 4e4)));
|
|
106
108
|
const hasNativeData = hasSlackNativeDataBlock(params.blocks);
|
|
107
109
|
const accessibilityText = buildSlackNativeDataAccessibilityText(baseText, params.blocks) || (hasNativeData ? "Slack could not render this chart or table data." : SLACK_EMPTY_BLOCK_FALLBACK);
|
|
108
110
|
return {
|
|
109
111
|
accessibilityText,
|
|
110
|
-
fallbackMessages: stripSlackNativeDataBlocks(params.blocks).length === 0 ? chunkSlackTextAtHardLimit(accessibilityText).map((text) => ({
|
|
112
|
+
fallbackMessages: stripSlackNativeDataBlocks(params.blocks).length === 0 ? chunkSlackTextAtHardLimit(accessibilityText, textLimit).map((text) => ({
|
|
111
113
|
text,
|
|
112
114
|
mrkdwn: false
|
|
113
115
|
})) : buildOrderedBlockMessages(buildOrderedFallbackBlocks({
|
|
114
116
|
baseText,
|
|
115
|
-
blocks: params.blocks
|
|
116
|
-
|
|
117
|
+
blocks: params.blocks,
|
|
118
|
+
textLimit
|
|
119
|
+
}), textLimit),
|
|
117
120
|
skipOriginalBlocks: accessibilityText.length > SLACK_MESSAGE_TEXT_HARD_LIMIT
|
|
118
121
|
};
|
|
119
122
|
}
|
|
@@ -142,6 +145,7 @@ function hasCustomIdentity$1(identity) {
|
|
|
142
145
|
async function postSlackMessageWithIdentityFallback(params) {
|
|
143
146
|
const { basePayload, identity, post } = params;
|
|
144
147
|
try {
|
|
148
|
+
if (!identity) return await post(basePayload);
|
|
145
149
|
if (identity?.iconUrl) return await post({
|
|
146
150
|
...basePayload,
|
|
147
151
|
...identity.username ? { username: identity.username } : {},
|
|
@@ -250,11 +254,6 @@ function hasSlackDnsRequestSignal(err) {
|
|
|
250
254
|
}
|
|
251
255
|
return false;
|
|
252
256
|
}
|
|
253
|
-
function delaySlackDnsRetry(attempt) {
|
|
254
|
-
return new Promise((resolve) => {
|
|
255
|
-
setTimeout(resolve, SLACK_DNS_RETRY_BASE_DELAY_MS * Math.max(1, attempt));
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
257
|
function resolveSlackUploadTimeoutLogUrl(url) {
|
|
259
258
|
try {
|
|
260
259
|
return new URL(url).origin;
|
|
@@ -328,14 +327,22 @@ function resolveSlackUploadTransportPolicy(params) {
|
|
|
328
327
|
};
|
|
329
328
|
}
|
|
330
329
|
async function withSlackDnsRequestRetry(operation, fn) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
330
|
+
return await retryAsync(fn, {
|
|
331
|
+
attempts: 3,
|
|
332
|
+
minDelayMs: 0,
|
|
333
|
+
shouldRetry: hasSlackDnsRequestSignal,
|
|
334
|
+
delayMs: ({ attempt }) => SLACK_DNS_RETRY_BASE_DELAY_MS * Math.max(1, attempt),
|
|
335
|
+
onRetry: ({ attempt }) => {
|
|
336
|
+
logVerbose(`slack send: retrying ${operation} after transient DNS request error (${attempt}/${SLACK_DNS_RETRY_ATTEMPTS})`);
|
|
337
|
+
},
|
|
338
|
+
sleep: (delayMs) => sleepWithAbort(delayMs)
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
function requireSlackPostMessageTimestamp(response) {
|
|
342
|
+
if (response.ok === false) throw new Error(`Slack chat.postMessage failed: ${formatSlackError(response.error, "unknown error")}`);
|
|
343
|
+
const timestamp = response.ts?.trim();
|
|
344
|
+
if (!timestamp) throw new Error("Slack chat.postMessage returned no message timestamp");
|
|
345
|
+
return timestamp;
|
|
339
346
|
}
|
|
340
347
|
async function postSlackMessageBestEffort(params) {
|
|
341
348
|
const basePayload = buildSlackPostMessagePayload(params);
|
|
@@ -344,20 +351,29 @@ async function postSlackMessageBestEffort(params) {
|
|
|
344
351
|
response: await withSlackDnsRequestRetry("chat.postMessage", () => postChatMessage(payload)),
|
|
345
352
|
identity
|
|
346
353
|
});
|
|
347
|
-
|
|
354
|
+
const posted = await postSlackMessageWithIdentityFallback({
|
|
348
355
|
basePayload,
|
|
349
356
|
identity: params.identity,
|
|
350
357
|
post
|
|
351
358
|
});
|
|
359
|
+
const timestamp = requireSlackPostMessageTimestamp(posted.response);
|
|
360
|
+
return {
|
|
361
|
+
...posted,
|
|
362
|
+
response: {
|
|
363
|
+
...posted.response,
|
|
364
|
+
ts: timestamp
|
|
365
|
+
}
|
|
366
|
+
};
|
|
352
367
|
}
|
|
353
368
|
async function uploadSlackFile(params) {
|
|
354
369
|
const { buffer, contentType, fileName } = await loadOutboundMediaFromUrl(params.mediaUrl, {
|
|
355
370
|
maxBytes: params.maxBytes,
|
|
356
371
|
mediaAccess: params.mediaAccess,
|
|
357
372
|
mediaLocalRoots: params.mediaLocalRoots,
|
|
358
|
-
mediaReadFile: params.mediaReadFile
|
|
373
|
+
mediaReadFile: params.mediaReadFile,
|
|
374
|
+
...params.optimizeImages !== void 0 ? { optimizeImages: params.optimizeImages } : {}
|
|
359
375
|
});
|
|
360
|
-
const uploadFileName = params.uploadFileName ?? fileName ??
|
|
376
|
+
const uploadFileName = params.uploadFileName ?? fileName ?? `upload${extensionForMime(contentType) ?? ""}`;
|
|
361
377
|
const uploadTitle = params.uploadTitle ?? uploadFileName;
|
|
362
378
|
const uploadUrlResp = await withSlackDnsRequestRetry("files.getUploadURLExternal", () => params.client.files.getUploadURLExternal({
|
|
363
379
|
filename: uploadFileName,
|
|
@@ -416,24 +432,32 @@ async function uploadSlackFile(params) {
|
|
|
416
432
|
}
|
|
417
433
|
//#endregion
|
|
418
434
|
//#region extensions/slack/src/sent-thread-cache.ts
|
|
435
|
+
/**
|
|
436
|
+
* Cache of Slack threads the bot has participated in.
|
|
437
|
+
* Used to auto-respond in threads without requiring @mention after the first reply.
|
|
438
|
+
*/
|
|
439
|
+
const MAX_ENTRIES = 5e3;
|
|
440
|
+
const PERSISTENT_MAX_ENTRIES = 1e3;
|
|
441
|
+
const MAX_FAILURE_NOTICES = 1e3;
|
|
442
|
+
const PERSISTENT_NAMESPACE = "slack.thread-participation";
|
|
443
|
+
/**
|
|
444
|
+
* Keep Slack thread participation shared across bundled chunks so thread
|
|
445
|
+
* auto-reply gating does not diverge between prepare/dispatch call paths.
|
|
446
|
+
*/
|
|
447
|
+
const SLACK_THREAD_PARTICIPATION_KEY = Symbol.for("openclaw.slackThreadParticipation");
|
|
448
|
+
const SLACK_THREAD_FAILURE_NOTICES_KEY = Symbol.for("openclaw.slackThreadFailureNotices");
|
|
419
449
|
const threadParticipation = createPersistentDedupeCache({
|
|
420
|
-
globalKey:
|
|
421
|
-
ttlMs:
|
|
422
|
-
maxSize:
|
|
450
|
+
globalKey: SLACK_THREAD_PARTICIPATION_KEY,
|
|
451
|
+
ttlMs: 0,
|
|
452
|
+
maxSize: MAX_ENTRIES,
|
|
423
453
|
persistent: {
|
|
424
|
-
namespace:
|
|
425
|
-
maxEntries:
|
|
454
|
+
namespace: PERSISTENT_NAMESPACE,
|
|
455
|
+
maxEntries: PERSISTENT_MAX_ENTRIES,
|
|
426
456
|
openStore: (options) => getOptionalSlackRuntime()?.state.openKeyedStore(options),
|
|
427
|
-
logError: (
|
|
428
|
-
try {
|
|
429
|
-
getOptionalSlackRuntime()?.logging.getChildLogger({
|
|
430
|
-
plugin: "slack",
|
|
431
|
-
feature: "thread-participation-state"
|
|
432
|
-
}).warn("Slack persistent thread participation state failed", { error: String(error) });
|
|
433
|
-
} catch {}
|
|
434
|
-
}
|
|
457
|
+
logError: createPluginStateErrorReporter(getOptionalSlackRuntime, "slack", "thread-participation-state", "Slack persistent thread participation state failed")
|
|
435
458
|
}
|
|
436
459
|
});
|
|
460
|
+
const threadFailureNotices = resolveGlobalSingleton(SLACK_THREAD_FAILURE_NOTICES_KEY, () => /* @__PURE__ */ new Map(), (notices) => notices.clear());
|
|
437
461
|
function makeKey(accountId, channelId, threadTs, teamId) {
|
|
438
462
|
return `${accountId}:${teamId ? `${teamId}:` : ""}${channelId}:${threadTs}`;
|
|
439
463
|
}
|
|
@@ -452,8 +476,41 @@ async function hasSlackThreadParticipationWithPersistence(params) {
|
|
|
452
476
|
if (!params.accountId || !params.channelId || !params.threadTs) return false;
|
|
453
477
|
return await threadParticipation.lookup(makeKey(params.accountId, params.channelId, params.threadTs, params.teamId));
|
|
454
478
|
}
|
|
479
|
+
function makeFailureNoticeKey(params) {
|
|
480
|
+
const scope = params.threadTs ? `thread:${params.threadTs}` : "channel";
|
|
481
|
+
return makeKey(params.accountId, params.channelId, scope, params.teamId);
|
|
482
|
+
}
|
|
483
|
+
/** Returns whether this failure was already delivered in the thread or channel. */
|
|
484
|
+
function hasSlackThreadFailureNotice(params) {
|
|
485
|
+
const { accountId, channelId, failureText } = params;
|
|
486
|
+
const fingerprint = failureText.trim().replace(/\s+/gu, " ");
|
|
487
|
+
if (!accountId || !channelId || !fingerprint) return false;
|
|
488
|
+
return threadFailureNotices.get(makeFailureNoticeKey(params)) === fingerprint;
|
|
489
|
+
}
|
|
490
|
+
/** Records a failure after it was delivered in the thread or channel. */
|
|
491
|
+
function recordSlackThreadFailureNotice(params) {
|
|
492
|
+
const { accountId, channelId, failureText } = params;
|
|
493
|
+
const fingerprint = failureText.trim().replace(/\s+/gu, " ");
|
|
494
|
+
if (!accountId || !channelId || !fingerprint) return false;
|
|
495
|
+
const key = makeFailureNoticeKey(params);
|
|
496
|
+
if (threadFailureNotices.get(key) === fingerprint) return false;
|
|
497
|
+
threadFailureNotices.delete(key);
|
|
498
|
+
threadFailureNotices.set(key, fingerprint);
|
|
499
|
+
if (threadFailureNotices.size > MAX_FAILURE_NOTICES) {
|
|
500
|
+
const oldestKey = threadFailureNotices.keys().next().value;
|
|
501
|
+
if (oldestKey !== void 0) threadFailureNotices.delete(oldestKey);
|
|
502
|
+
}
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
505
|
+
/** Clears a thread or channel outage notice after a healthy model turn completes. */
|
|
506
|
+
function clearSlackThreadFailureNotice(params) {
|
|
507
|
+
const { accountId, channelId } = params;
|
|
508
|
+
if (!accountId || !channelId) return;
|
|
509
|
+
threadFailureNotices.delete(makeFailureNoticeKey(params));
|
|
510
|
+
}
|
|
455
511
|
function clearSlackThreadParticipationCache() {
|
|
456
512
|
threadParticipation.clearForTest();
|
|
513
|
+
threadFailureNotices.clear();
|
|
457
514
|
}
|
|
458
515
|
//#endregion
|
|
459
516
|
//#region extensions/slack/src/send.ts
|
|
@@ -537,11 +594,12 @@ async function updateMessageSlack(params) {
|
|
|
537
594
|
cfg: requireRuntimeConfig(params.cfg, "Slack update"),
|
|
538
595
|
accountId: params.accountId
|
|
539
596
|
});
|
|
597
|
+
assertSlackDetachedTargetAllowed(account.accountId, params.teamId);
|
|
540
598
|
await getSlackWriteClient(resolveToken({
|
|
541
599
|
accountId: account.accountId,
|
|
542
600
|
fallbackToken: account.botToken,
|
|
543
601
|
fallbackSource: account.botTokenSource
|
|
544
|
-
})).chat.update({
|
|
602
|
+
}), { teamId: params.teamId }).chat.update({
|
|
545
603
|
channel: params.channelId,
|
|
546
604
|
ts: params.messageTs,
|
|
547
605
|
text: truncateSlackTextByUtf8Bytes(params.text, SLACK_EDIT_TEXT_MAX_BYTES),
|
|
@@ -550,7 +608,7 @@ async function updateMessageSlack(params) {
|
|
|
550
608
|
}
|
|
551
609
|
function createSlackSendReceipt(params) {
|
|
552
610
|
return createMessageReceiptFromOutboundResults({
|
|
553
|
-
results: params.platformMessageIds.map((messageId) => messageId.trim()).filter((messageId) => messageId && messageId !== "unknown"
|
|
611
|
+
results: params.platformMessageIds.map((messageId) => messageId.trim()).filter((messageId) => messageId && messageId !== "unknown").map((messageId) => {
|
|
554
612
|
const result = {
|
|
555
613
|
channel: "slack",
|
|
556
614
|
messageId
|
|
@@ -577,7 +635,8 @@ function parseRecipient(raw) {
|
|
|
577
635
|
if (!target) throw new Error("Recipient is required for Slack sends");
|
|
578
636
|
return {
|
|
579
637
|
kind: target.kind,
|
|
580
|
-
id: canonicalizeSlackApiTargetId(target.kind, target.id, raw)
|
|
638
|
+
id: canonicalizeSlackApiTargetId(target.kind, target.id, raw),
|
|
639
|
+
teamId: target.teamId
|
|
581
640
|
};
|
|
582
641
|
}
|
|
583
642
|
function parseEnterpriseEventRecipient(raw) {
|
|
@@ -588,20 +647,56 @@ function parseEnterpriseEventRecipient(raw) {
|
|
|
588
647
|
id: canonicalizeSlackApiTargetId("channel", match[1])
|
|
589
648
|
};
|
|
590
649
|
}
|
|
591
|
-
function
|
|
592
|
-
const scope = params.opts.
|
|
593
|
-
if (!scope)
|
|
594
|
-
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
if (params.account.config.enterpriseOrgInstall !== true) throw new Error("unexpected_enterprise_slack_listener_scope");
|
|
598
|
-
if (!scope.isEnterpriseInstall || !normalizeOptionalString(scope.apiAppId) || !normalizeOptionalString(scope.enterpriseId) || !/^T[A-Z0-9]+$/i.test(scope.teamId) || !scope.client || params.opts.client !== scope.client) throw new Error("invalid_enterprise_slack_listener_scope");
|
|
650
|
+
function resolveSlackSendEventScope(params) {
|
|
651
|
+
const scope = params.opts.eventScope;
|
|
652
|
+
if (!scope) return;
|
|
653
|
+
if (!/^T[A-Z0-9]+$/i.test(scope.teamId) || !scope.client) throw new Error("invalid_enterprise_slack_listener_scope");
|
|
599
654
|
return scope;
|
|
600
655
|
}
|
|
656
|
+
function resolveSlackDelivery(params) {
|
|
657
|
+
if (params.eventScope) {
|
|
658
|
+
if (params.opts.mediaUrl && !params.eventScope.uploadCompletionClient) throw new Error("missing_enterprise_slack_upload_completion_client");
|
|
659
|
+
return Object.freeze({
|
|
660
|
+
client: params.eventScope.client,
|
|
661
|
+
credential: SLACK_ENTERPRISE_LISTENER_QUEUE_CREDENTIAL,
|
|
662
|
+
identity: normalizeSlackSendIdentity(params.opts.identity),
|
|
663
|
+
recipient: params.recipient,
|
|
664
|
+
teamId: params.eventScope.teamId,
|
|
665
|
+
unfurl: { unfurlMedia: params.account.config.unfurlMedia },
|
|
666
|
+
...params.eventScope.uploadCompletionClient ? { upload: Object.freeze({
|
|
667
|
+
completionClient: params.eventScope.uploadCompletionClient,
|
|
668
|
+
auditContext: "slack-enterprise-immediate-upload"
|
|
669
|
+
}) } : {}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
const credential = resolveToken({
|
|
673
|
+
explicit: params.opts.token,
|
|
674
|
+
accountId: params.account.accountId,
|
|
675
|
+
fallbackToken: resolveSlackOperationToken(params.account, "write"),
|
|
676
|
+
fallbackSource: params.account.identity === "user" ? params.account.userTokenSource : params.account.botTokenSource
|
|
677
|
+
});
|
|
678
|
+
return Object.freeze({
|
|
679
|
+
client: params.recipient.teamId ? getSlackWriteClient(credential, { teamId: params.recipient.teamId }) : params.opts.client ?? getSlackWriteClient(credential),
|
|
680
|
+
credential,
|
|
681
|
+
identity: resolveSlackSendIdentity({
|
|
682
|
+
accountId: params.account.accountId,
|
|
683
|
+
explicit: params.opts.identity
|
|
684
|
+
}),
|
|
685
|
+
recipient: params.recipient,
|
|
686
|
+
teamId: params.recipient.teamId,
|
|
687
|
+
unfurl: params.recipient.teamId ? { unfurlMedia: params.account.config.unfurlMedia } : {
|
|
688
|
+
unfurlLinks: params.account.config.unfurlLinks,
|
|
689
|
+
unfurlMedia: params.account.config.unfurlMedia
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
function resolveSlackTextChunkLimit(params) {
|
|
694
|
+
const configuredLimit = params.textLimit ?? resolveTextChunkLimit(params.cfg, "slack", params.accountId, { fallbackLimit: 8e3 });
|
|
695
|
+
return Math.min(configuredLimit, SLACK_TEXT_LIMIT);
|
|
696
|
+
}
|
|
601
697
|
function resolveSlackTextChunks(params) {
|
|
602
698
|
const text = params.preservePlainText ? params.text : params.text.trim();
|
|
603
|
-
const
|
|
604
|
-
const chunkLimit = Math.min(configuredLimit, SLACK_TEXT_LIMIT);
|
|
699
|
+
const chunkLimit = resolveSlackTextChunkLimit(params);
|
|
605
700
|
if (params.preservePlainText) {
|
|
606
701
|
const chunks = [];
|
|
607
702
|
let remaining = text;
|
|
@@ -867,7 +962,7 @@ async function scanSlackConversationForDelivery(params) {
|
|
|
867
962
|
evidence: deliveryParts.size > 0 ? "partial" : "none"
|
|
868
963
|
};
|
|
869
964
|
}
|
|
870
|
-
async function reconcileSlackUnknownSend(ctx
|
|
965
|
+
async function reconcileSlackUnknownSend(ctx) {
|
|
871
966
|
const account = resolveSlackAccount({
|
|
872
967
|
cfg: requireRuntimeConfig(ctx.cfg, "Slack delivery reconciliation"),
|
|
873
968
|
accountId: ctx.accountId ?? void 0
|
|
@@ -878,13 +973,22 @@ async function reconcileSlackUnknownSend(ctx, opts) {
|
|
|
878
973
|
error: "Slack unknown-send reconciliation requires a durable delivery id",
|
|
879
974
|
retryable: false
|
|
880
975
|
};
|
|
976
|
+
const recipient = parseRecipient(ctx.to);
|
|
977
|
+
try {
|
|
978
|
+
assertSlackDetachedTargetAllowed(account.accountId, recipient.teamId);
|
|
979
|
+
} catch (error) {
|
|
980
|
+
return {
|
|
981
|
+
status: "unresolved",
|
|
982
|
+
error: error instanceof Error ? error.message : String(error),
|
|
983
|
+
retryable: false
|
|
984
|
+
};
|
|
985
|
+
}
|
|
881
986
|
const readToken = resolveSlackOperationToken(account, "read");
|
|
882
987
|
if (!readToken) return {
|
|
883
988
|
status: "unresolved",
|
|
884
989
|
error: `Slack read token missing for account "${account.accountId}"`,
|
|
885
990
|
retryable: false
|
|
886
991
|
};
|
|
887
|
-
const recipient = parseRecipient(ctx.to);
|
|
888
992
|
const userRecipient = isSlackUserRecipient(recipient);
|
|
889
993
|
const writeToken = resolveSlackOperationToken(account, "write");
|
|
890
994
|
if (userRecipient && !writeToken) return {
|
|
@@ -892,8 +996,8 @@ async function reconcileSlackUnknownSend(ctx, opts) {
|
|
|
892
996
|
error: `Slack write token missing for direct-message reconciliation on account "${account.accountId}"`,
|
|
893
997
|
retryable: false
|
|
894
998
|
};
|
|
895
|
-
const readClient =
|
|
896
|
-
const writeClient =
|
|
999
|
+
const readClient = createSlackReadClient(readToken, { teamId: recipient.teamId });
|
|
1000
|
+
const writeClient = writeToken ? getSlackWriteClient(writeToken, { teamId: recipient.teamId }) : void 0;
|
|
897
1001
|
const payloadReplyToId = ctx.payloads[0]?.replyToId;
|
|
898
1002
|
const threadTs = resolveSlackThreadTsValue({
|
|
899
1003
|
replyToId: Object.hasOwn(ctx, "effectiveReplyToId") ? normalizeOptionalString(ctx.effectiveReplyToId) : payloadReplyToId != null ? normalizeOptionalString(payloadReplyToId) : ctx.replyToMode === "off" ? void 0 : normalizeOptionalString(ctx.replyToId),
|
|
@@ -910,7 +1014,7 @@ async function reconcileSlackUnknownSend(ctx, opts) {
|
|
|
910
1014
|
accountId: account.accountId,
|
|
911
1015
|
token: channelToken
|
|
912
1016
|
});
|
|
913
|
-
const lookupClients =
|
|
1017
|
+
const lookupClients = [readClient, ...writeClient && writeClient !== readClient && writeToken !== readToken ? [writeClient] : []];
|
|
914
1018
|
let lookupError;
|
|
915
1019
|
let bestUnresolvedScan;
|
|
916
1020
|
for (const lookupClient of lookupClients) try {
|
|
@@ -947,59 +1051,34 @@ async function sendMessageSlack(to, message, opts) {
|
|
|
947
1051
|
cfg,
|
|
948
1052
|
accountId: opts.accountId
|
|
949
1053
|
});
|
|
950
|
-
const
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
});
|
|
954
|
-
const enterpriseDelivery = enterpriseEventScope ? Object.freeze({
|
|
955
|
-
client: enterpriseEventScope.client,
|
|
956
|
-
teamId: enterpriseEventScope.teamId,
|
|
957
|
-
...enterpriseEventScope.uploadCompletionClient ? { uploadCompletionClient: enterpriseEventScope.uploadCompletionClient } : {}
|
|
958
|
-
}) : void 0;
|
|
959
|
-
if (isSilentReplyText(normalizedMessage) && !opts.mediaUrl && !opts.blocks) {
|
|
960
|
-
logVerbose("slack send: suppressed NO_REPLY token before API call");
|
|
961
|
-
return {
|
|
962
|
-
messageId: "suppressed",
|
|
963
|
-
channelId: "",
|
|
964
|
-
receipt: createSlackSendReceipt({
|
|
965
|
-
platformMessageIds: [],
|
|
966
|
-
kind: "unknown"
|
|
967
|
-
})
|
|
968
|
-
};
|
|
969
|
-
}
|
|
1054
|
+
const eventScope = resolveSlackSendEventScope({ opts });
|
|
1055
|
+
const recipient = eventScope ? parseEnterpriseEventRecipient(to) : parseRecipient(to);
|
|
1056
|
+
if (!eventScope) assertSlackDetachedTargetAllowed(account.accountId, recipient.teamId);
|
|
970
1057
|
const blocks = opts.blocks == null ? void 0 : validateSlackBlocksArray(opts.blocks);
|
|
971
1058
|
if (!normalizedMessage && !opts.mediaUrl && !blocks) throw new Error("Slack send requires text, blocks, or media");
|
|
972
|
-
const
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1059
|
+
const queuedOpts = Object.freeze({ ...opts });
|
|
1060
|
+
const delivery = resolveSlackDelivery({
|
|
1061
|
+
account,
|
|
1062
|
+
eventScope,
|
|
1063
|
+
opts: queuedOpts,
|
|
1064
|
+
recipient
|
|
977
1065
|
});
|
|
978
|
-
const
|
|
979
|
-
const queueKey = createSlackSendQueueKey({
|
|
1066
|
+
const result = await runQueuedSlackSend(createSlackSendQueueKey({
|
|
980
1067
|
accountId: account.accountId,
|
|
981
|
-
token,
|
|
982
|
-
recipient,
|
|
1068
|
+
token: delivery.credential,
|
|
1069
|
+
recipient: delivery.recipient,
|
|
983
1070
|
threadTs: opts.threadTs,
|
|
984
|
-
|
|
985
|
-
})
|
|
986
|
-
const queuedOpts = enterpriseDelivery ? Object.freeze({
|
|
987
|
-
...opts,
|
|
988
|
-
client: enterpriseDelivery.client
|
|
989
|
-
}) : opts;
|
|
990
|
-
const result = await runQueuedSlackSend(queueKey, () => sendMessageSlackQueued({
|
|
1071
|
+
teamId: delivery.teamId
|
|
1072
|
+
}), () => sendMessageSlackQueued({
|
|
991
1073
|
trimmedMessage,
|
|
992
1074
|
opts: queuedOpts,
|
|
993
1075
|
cfg,
|
|
994
1076
|
account,
|
|
995
|
-
token,
|
|
996
|
-
recipient,
|
|
997
1077
|
blocks,
|
|
998
|
-
|
|
1078
|
+
delivery
|
|
999
1079
|
}));
|
|
1000
1080
|
const threadTs = result.threadTs ?? normalizeSlackThreadTsCandidate(queuedOpts.threadTs);
|
|
1001
|
-
if (threadTs && result.channelId && account.accountId)
|
|
1002
|
-
else recordSlackThreadParticipation(account.accountId, result.channelId, threadTs);
|
|
1081
|
+
if (threadTs && result.channelId && account.accountId) recordSlackThreadParticipation(account.accountId, result.channelId, threadTs, { teamId: delivery.teamId });
|
|
1003
1082
|
return result;
|
|
1004
1083
|
}
|
|
1005
1084
|
async function sendMessageSlackQueued(params) {
|
|
@@ -1010,17 +1089,9 @@ async function sendMessageSlackQueued(params) {
|
|
|
1010
1089
|
}
|
|
1011
1090
|
}
|
|
1012
1091
|
async function sendMessageSlackQueuedInner(params) {
|
|
1013
|
-
const { opts, cfg, account,
|
|
1014
|
-
const client
|
|
1015
|
-
const identity = enterpriseDelivery ? normalizeSlackSendIdentity(opts.identity) : resolveSlackSendIdentity({
|
|
1016
|
-
accountId: account.accountId,
|
|
1017
|
-
explicit: opts.identity
|
|
1018
|
-
});
|
|
1092
|
+
const { opts, cfg, account, blocks, trimmedMessage, delivery } = params;
|
|
1093
|
+
const { client, identity, recipient, unfurl } = delivery;
|
|
1019
1094
|
if (opts.replyBroadcast && opts.mediaUrl) throw new Error("Slack replyBroadcast is only supported for text or block thread replies.");
|
|
1020
|
-
const unfurl = enterpriseDelivery ? { unfurlMedia: account.config.unfurlMedia } : {
|
|
1021
|
-
unfurlLinks: account.config.unfurlLinks,
|
|
1022
|
-
unfurlMedia: account.config.unfurlMedia
|
|
1023
|
-
};
|
|
1024
1095
|
const directUserPostChannelId = opts.deliveryQueueId ? void 0 : resolveDirectUserPostChannelId({
|
|
1025
1096
|
recipient,
|
|
1026
1097
|
hasMedia: Boolean(opts.mediaUrl),
|
|
@@ -1028,11 +1099,20 @@ async function sendMessageSlackQueuedInner(params) {
|
|
|
1028
1099
|
});
|
|
1029
1100
|
const { channelId } = directUserPostChannelId ? { channelId: directUserPostChannelId } : await resolveChannelId(client, recipient, {
|
|
1030
1101
|
accountId: account.accountId,
|
|
1031
|
-
token
|
|
1102
|
+
token: delivery.credential
|
|
1032
1103
|
});
|
|
1033
|
-
const reportDelivery = async (result) => {
|
|
1034
|
-
|
|
1035
|
-
|
|
1104
|
+
const reportDelivery = async (result, deliveredBlocks) => {
|
|
1105
|
+
const slackQuestionActionIds = resolveSlackQuestionActionIds(deliveredBlocks);
|
|
1106
|
+
const deliveryResult = slackQuestionActionIds.length ? {
|
|
1107
|
+
...result,
|
|
1108
|
+
meta: {
|
|
1109
|
+
...result.meta,
|
|
1110
|
+
slackQuestionActionIds,
|
|
1111
|
+
[SLACK_QUESTION_FINALIZATION_BLOCKS]: deliveredBlocks.filter((block) => block.type !== "actions")
|
|
1112
|
+
}
|
|
1113
|
+
} : result;
|
|
1114
|
+
await opts.onDeliveryResult?.(deliveryResult);
|
|
1115
|
+
return deliveryResult;
|
|
1036
1116
|
};
|
|
1037
1117
|
let didDispatch = false;
|
|
1038
1118
|
const dispatchOnce = async () => {
|
|
@@ -1044,10 +1124,16 @@ async function sendMessageSlackQueuedInner(params) {
|
|
|
1044
1124
|
const authoredTextPlacement = opts.authoredTextPlacement ?? "outside-blocks";
|
|
1045
1125
|
const nativeDataFallbackBase = blocks && authoredTextPlacement === "outside-blocks" ? explicitNativeDataFallbackBase ?? trimmedMessage : "";
|
|
1046
1126
|
const hasNativeData = Boolean(blocks && hasSlackNativeDataBlock(blocks));
|
|
1127
|
+
const textChunkLimit = resolveSlackTextChunkLimit({
|
|
1128
|
+
cfg,
|
|
1129
|
+
accountId: account.accountId,
|
|
1130
|
+
...opts.textLimit !== void 0 ? { textLimit: opts.textLimit } : {}
|
|
1131
|
+
});
|
|
1047
1132
|
const usesOrderedBlockAccessibility = Boolean(blocks && (hasNativeData || opts.authoredTextPlacement !== void 0));
|
|
1048
1133
|
const orderedBlockDeliveryPlan = blocks && usesOrderedBlockAccessibility ? buildSlackNativeDataDeliveryPlan({
|
|
1049
1134
|
baseText: nativeDataFallbackBase,
|
|
1050
|
-
blocks
|
|
1135
|
+
blocks,
|
|
1136
|
+
textLimit: textChunkLimit
|
|
1051
1137
|
}) : void 0;
|
|
1052
1138
|
const orderedBlockAccessibilityText = blocks && usesOrderedBlockAccessibility ? orderedBlockDeliveryPlan?.accessibilityText ?? (buildSlackNativeDataAccessibilityText(nativeDataFallbackBase, blocks) || "Slack could not render this Block Kit message.") : void 0;
|
|
1053
1139
|
const completeBlockFallbackText = blocks ? buildSlackCompleteBlocksFallbackText(blocks) : "";
|
|
@@ -1085,8 +1171,7 @@ async function sendMessageSlackQueuedInner(params) {
|
|
|
1085
1171
|
...usesOrderedBlockAccessibility ? { mrkdwn: false } : {},
|
|
1086
1172
|
unfurl
|
|
1087
1173
|
});
|
|
1088
|
-
|
|
1089
|
-
const messageId = response.ts ?? "unknown";
|
|
1174
|
+
const messageId = response.ts;
|
|
1090
1175
|
deliveredChannelId = resolvePostedMessageChannelId(response, channelId);
|
|
1091
1176
|
const deliveredThreadTs = resolvePostedMessageThreadTs(response) ?? normalizeSlackThreadTsCandidate(opts.threadTs);
|
|
1092
1177
|
return await reportDelivery({
|
|
@@ -1099,7 +1184,7 @@ async function sendMessageSlackQueuedInner(params) {
|
|
|
1099
1184
|
kind: "card",
|
|
1100
1185
|
threadTs: deliveredThreadTs
|
|
1101
1186
|
})
|
|
1102
|
-
});
|
|
1187
|
+
}, blocks);
|
|
1103
1188
|
} catch (error) {
|
|
1104
1189
|
if (!hasNativeData || !isSlackInvalidBlocksError(error)) throw error;
|
|
1105
1190
|
logVerbose("slack send: native data rejected, delivering complete text fallback");
|
|
@@ -1108,6 +1193,7 @@ async function sendMessageSlackQueuedInner(params) {
|
|
|
1108
1193
|
}
|
|
1109
1194
|
if (pendingBlockFallback) {
|
|
1110
1195
|
const fallbackMessages = pendingBlockFallback.fallbackMessages;
|
|
1196
|
+
let questionDelivery;
|
|
1111
1197
|
for (const [partIndex, fallback] of fallbackMessages.entries()) {
|
|
1112
1198
|
const metadata = withSlackDeliveryMetadata(partIndex === 0 ? opts.metadata : void 0, {
|
|
1113
1199
|
queueId: opts.deliveryQueueId,
|
|
@@ -1130,15 +1216,13 @@ async function sendMessageSlackQueuedInner(params) {
|
|
|
1130
1216
|
unfurl
|
|
1131
1217
|
});
|
|
1132
1218
|
const response = posted.response;
|
|
1133
|
-
if (enterpriseDelivery && (!response.ok || !response.ts)) throw new Error(response.ok ? "Slack chat.postMessage returned no message timestamp" : `Slack chat.postMessage failed: ${response.error ?? "unknown error"}`);
|
|
1134
1219
|
sendIdentity = posted.identity;
|
|
1135
|
-
lastMessageId = response.ts
|
|
1220
|
+
lastMessageId = response.ts;
|
|
1136
1221
|
deliveredChannelId = resolvePostedMessageChannelId(response, deliveredChannelId);
|
|
1137
1222
|
canonicalDeliveredThreadTs ??= resolvePostedMessageThreadTs(response);
|
|
1138
|
-
if (!response.ts) continue;
|
|
1139
1223
|
sentMessageIds.push(response.ts);
|
|
1140
1224
|
const deliveredThreadTs = resolvePostedMessageThreadTs(response) ?? normalizeSlackThreadTsCandidate(opts.threadTs);
|
|
1141
|
-
await reportDelivery({
|
|
1225
|
+
const fallbackDelivery = await reportDelivery({
|
|
1142
1226
|
messageId: response.ts,
|
|
1143
1227
|
channelId: deliveredChannelId,
|
|
1144
1228
|
threadTs: deliveredThreadTs,
|
|
@@ -1148,16 +1232,21 @@ async function sendMessageSlackQueuedInner(params) {
|
|
|
1148
1232
|
kind: fallback.blocks ? "card" : "text",
|
|
1149
1233
|
threadTs: deliveredThreadTs
|
|
1150
1234
|
})
|
|
1151
|
-
});
|
|
1235
|
+
}, fallback.blocks);
|
|
1236
|
+
if (fallbackDelivery.meta?.slackQuestionActionIds.length) questionDelivery = fallbackDelivery;
|
|
1152
1237
|
}
|
|
1153
|
-
const messageId = lastMessageId
|
|
1238
|
+
const messageId = lastMessageId;
|
|
1154
1239
|
const deliveredThreadTs = canonicalDeliveredThreadTs ?? normalizeSlackThreadTsCandidate(opts.threadTs);
|
|
1155
1240
|
return {
|
|
1156
1241
|
messageId,
|
|
1157
1242
|
channelId: deliveredChannelId,
|
|
1158
1243
|
threadTs: deliveredThreadTs,
|
|
1244
|
+
...questionDelivery?.meta ? { meta: {
|
|
1245
|
+
...questionDelivery.meta,
|
|
1246
|
+
slackQuestionMessageId: questionDelivery.messageId
|
|
1247
|
+
} } : {},
|
|
1159
1248
|
receipt: createSlackSendReceipt({
|
|
1160
|
-
platformMessageIds: sentMessageIds
|
|
1249
|
+
platformMessageIds: sentMessageIds,
|
|
1161
1250
|
channelId: deliveredChannelId,
|
|
1162
1251
|
kind: fallbackMessages.some((message) => message.blocks) ? "card" : "text",
|
|
1163
1252
|
threadTs: deliveredThreadTs
|
|
@@ -1169,18 +1258,17 @@ async function sendMessageSlackQueuedInner(params) {
|
|
|
1169
1258
|
cfg,
|
|
1170
1259
|
accountId: account.accountId,
|
|
1171
1260
|
text: trimmedMessage,
|
|
1172
|
-
|
|
1261
|
+
textLimit: textChunkLimit,
|
|
1173
1262
|
...opts.textIsSlackMrkdwn ? { textIsSlackMrkdwn: true } : {},
|
|
1174
1263
|
...opts.textIsSlackPlainText ? { preservePlainText: true } : {}
|
|
1175
1264
|
});
|
|
1176
1265
|
const mediaMaxBytes = opts.mediaMaxBytes ?? (typeof account.config.mediaMaxMb === "number" ? account.config.mediaMaxMb * 1024 * 1024 : void 0);
|
|
1177
1266
|
let chunksToPost;
|
|
1178
1267
|
if (opts.mediaUrl) {
|
|
1179
|
-
if (enterpriseDelivery && !enterpriseDelivery.uploadCompletionClient) throw new Error("missing_enterprise_slack_upload_completion_client");
|
|
1180
1268
|
const [firstChunk, ...rest] = resolvedChunks;
|
|
1181
1269
|
lastMessageId = await uploadSlackFile({
|
|
1182
1270
|
client,
|
|
1183
|
-
...
|
|
1271
|
+
...delivery.upload ? { completionClient: delivery.upload.completionClient } : {},
|
|
1184
1272
|
channelId,
|
|
1185
1273
|
mediaUrl: opts.mediaUrl,
|
|
1186
1274
|
mediaAccess: opts.mediaAccess,
|
|
@@ -1191,8 +1279,9 @@ async function sendMessageSlackQueuedInner(params) {
|
|
|
1191
1279
|
caption: firstChunk,
|
|
1192
1280
|
threadTs: opts.threadTs,
|
|
1193
1281
|
maxBytes: mediaMaxBytes,
|
|
1282
|
+
...opts.forceDocument ? { optimizeImages: false } : {},
|
|
1194
1283
|
onPlatformSendDispatch: dispatchOnce,
|
|
1195
|
-
...
|
|
1284
|
+
...delivery.upload ? { auditContext: delivery.upload.auditContext } : {}
|
|
1196
1285
|
});
|
|
1197
1286
|
sentMessageIds.push(lastMessageId);
|
|
1198
1287
|
await reportDelivery({
|
|
@@ -1231,34 +1320,31 @@ async function sendMessageSlackQueuedInner(params) {
|
|
|
1231
1320
|
unfurl
|
|
1232
1321
|
});
|
|
1233
1322
|
const response = posted.response;
|
|
1234
|
-
if (enterpriseDelivery && (!response.ok || !response.ts)) throw new Error(response.ok ? "Slack chat.postMessage returned no message timestamp" : `Slack chat.postMessage failed: ${response.error ?? "unknown error"}`);
|
|
1235
1323
|
sendIdentity = posted.identity;
|
|
1236
|
-
lastMessageId = response.ts
|
|
1324
|
+
lastMessageId = response.ts;
|
|
1237
1325
|
deliveredChannelId = resolvePostedMessageChannelId(response, deliveredChannelId);
|
|
1238
1326
|
canonicalDeliveredThreadTs ??= resolvePostedMessageThreadTs(response);
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1327
|
+
sentMessageIds.push(response.ts);
|
|
1328
|
+
await reportDelivery({
|
|
1329
|
+
messageId: response.ts,
|
|
1330
|
+
channelId: deliveredChannelId,
|
|
1331
|
+
threadTs: resolvePostedMessageThreadTs(response) ?? normalizeSlackThreadTsCandidate(opts.threadTs),
|
|
1332
|
+
receipt: createSlackSendReceipt({
|
|
1333
|
+
platformMessageIds: [response.ts],
|
|
1243
1334
|
channelId: deliveredChannelId,
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
kind: "text",
|
|
1249
|
-
threadTs: resolvePostedMessageThreadTs(response) ?? normalizeSlackThreadTsCandidate(opts.threadTs)
|
|
1250
|
-
})
|
|
1251
|
-
});
|
|
1252
|
-
}
|
|
1335
|
+
kind: "text",
|
|
1336
|
+
threadTs: resolvePostedMessageThreadTs(response) ?? normalizeSlackThreadTsCandidate(opts.threadTs)
|
|
1337
|
+
})
|
|
1338
|
+
});
|
|
1253
1339
|
}
|
|
1254
|
-
const messageId = lastMessageId
|
|
1340
|
+
const messageId = lastMessageId;
|
|
1255
1341
|
const deliveredThreadTs = canonicalDeliveredThreadTs ?? normalizeSlackThreadTsCandidate(opts.threadTs);
|
|
1256
1342
|
return {
|
|
1257
1343
|
messageId,
|
|
1258
1344
|
channelId: deliveredChannelId,
|
|
1259
1345
|
threadTs: deliveredThreadTs,
|
|
1260
1346
|
receipt: createSlackSendReceipt({
|
|
1261
|
-
platformMessageIds: sentMessageIds
|
|
1347
|
+
platformMessageIds: sentMessageIds,
|
|
1262
1348
|
channelId: deliveredChannelId,
|
|
1263
1349
|
kind: opts.mediaUrl ? "media" : "text",
|
|
1264
1350
|
threadTs: deliveredThreadTs
|
|
@@ -1266,4 +1352,4 @@ async function sendMessageSlackQueuedInner(params) {
|
|
|
1266
1352
|
};
|
|
1267
1353
|
}
|
|
1268
1354
|
//#endregion
|
|
1269
|
-
export { updateMessageSlack as a,
|
|
1355
|
+
export { updateMessageSlack as a, hasSlackThreadFailureNotice as c, recordSlackThreadFailureNotice as d, recordSlackThreadParticipation as f, chunkSlackTextAtHardLimit as h, setSlackDefaultSendIdentity as i, hasSlackThreadParticipation as l, buildSlackNativeDataDeliveryPlan as m, resolveSlackDmChannelId as n, clearSlackThreadFailureNotice as o, requireSlackPostMessageTimestamp as p, sendMessageSlack as r, clearSlackThreadParticipationCache as s, reconcileSlackUnknownSend as t, hasSlackThreadParticipationWithPersistence as u };
|