@openclaw/feishu 2026.7.2-beta.7 → 2026.8.1-beta.2
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/accounts-CCCdMen2.js +203 -0
- package/dist/api.js +52 -51
- package/dist/{channel-CScE82zY.js → channel-Dzb5jv3i.js} +53 -30
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-B21e1E0r.js → channel.runtime-CoPaHQQ6.js} +181 -142
- package/dist/{client-Dcbs6vml.js → client-Hp7uo_cl.js} +27 -16
- package/dist/contract-api.js +2 -2
- package/dist/{conversation-id-BeJL-wq7.js → conversation-id-VYgGQ-GX.js} +11 -15
- package/dist/doctor-contract-api.js +1 -1
- package/dist/doctor-contract-bEQXIXyP.js +158 -0
- package/dist/{drive-hSi_Utp0.js → drive-B0JkoiRj.js} +318 -41
- package/dist/{media-CEUraFOR.js → media-BjSy8fiy.js} +298 -182
- package/dist/{monitor-DngbaA6a.js → monitor-CeHCfROt.js} +3 -3
- package/dist/{monitor.account-BnTWCw55.js → monitor.account-DwdqexLU.js} +237 -158
- package/dist/{monitor.startup-CqH2tiJq.js → monitor.startup-BGErejNH.js} +1 -1
- package/dist/{probe-p3POS2RN.js → probe-DVpy58s0.js} +2 -2
- package/dist/security-audit-D6Fz2h6p.js +23 -0
- package/dist/{send-result-B9_BpUPx.js → send-result-DEAycmOk.js} +18 -9
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/{session-conversation-BksWrfzm.js → session-conversation-DFCIvQK-.js} +1 -1
- package/dist/session-key-api.js +1 -1
- package/dist/setup-api.js +1 -1
- package/dist/{subagent-hooks-Cx1cX7rW.js → subagent-hooks-B867acTt.js} +2 -2
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/{thread-bindings-N3wkgkIN.js → thread-bindings-Itvfmx6_.js} +6 -3
- package/openclaw.plugin.json +143 -1
- package/package.json +4 -4
- package/skills/feishu-doc/SKILL.md +20 -195
- package/skills/feishu-doc/references/block-types.md +9 -14
- package/skills/feishu-drive/SKILL.md +16 -102
- package/skills/feishu-perm/SKILL.md +10 -110
- package/skills/feishu-wiki/SKILL.md +13 -109
- package/dist/accounts-u9X5Wsan.js +0 -469
- package/dist/doctor-contract-BiD9tyIv.js +0 -102
- package/dist/security-audit-D7WK_BHh.js +0 -11
|
@@ -1,48 +1,50 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as resolveFeishuAccount, s as resolveFeishuRuntimeAccount } from "./accounts-CCCdMen2.js";
|
|
2
2
|
import { i as resolveReceiveIdType } from "./targets-BUjQ1TcA.js";
|
|
3
|
-
import { A as resolveFeishuChatType, C as resolveFeishuDmIngressAccess, E as resolveFeishuGroupSenderActivationIngressAccess, M as buildFeishuCardActionTextFallback, N as createFeishuCardInteractionEnvelope, O as resolveFeishuReplyPolicy, P as decodeFeishuCardAction, S as normalizeFeishuAllowEntry, T as resolveFeishuGroupConversationIngressAccess, d as chunkFeishuPostMarkdown, f as materializeFeishuPostMarkdownSoftBreaks, k as normalizeFeishuChatType$2, o as resolveFeishuCardTemplate, w as resolveFeishuGroupConfig, x as hasExplicitFeishuGroupConfig } from "./send-result-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { A as resolveFeishuChatType, C as resolveFeishuDmIngressAccess, E as resolveFeishuGroupSenderActivationIngressAccess, M as buildFeishuCardActionTextFallback, N as createFeishuCardInteractionEnvelope, O as resolveFeishuReplyPolicy, P as decodeFeishuCardAction, S as normalizeFeishuAllowEntry, T as resolveFeishuGroupConversationIngressAccess, d as chunkFeishuPostMarkdown, f as materializeFeishuPostMarkdownSoftBreaks, k as normalizeFeishuChatType$2, o as resolveFeishuCardTemplate, w as resolveFeishuGroupConfig, x as hasExplicitFeishuGroupConfig } from "./send-result-DEAycmOk.js";
|
|
4
|
+
import { i as normalizeFeishuWebhookPath } from "./doctor-contract-bEQXIXyP.js";
|
|
5
|
+
import { o as resolveConfiguredFeishuGroupSessionScope, t as buildFeishuConversationId } from "./conversation-id-VYgGQ-GX.js";
|
|
6
|
+
import { a as getFeishuUserAgent, i as createFeishuWSClient, n as createEventDispatcher, r as createFeishuClient, s as resolveConfiguredHttpTimeoutMs } from "./client-Hp7uo_cl.js";
|
|
6
7
|
import { t as getFeishuRuntime } from "./runtime-C5JxBWZp.js";
|
|
7
|
-
import { i as createCommentTypingReactionLifecycle,
|
|
8
|
-
import { t as createFeishuThreadBindingManager } from "./thread-bindings-
|
|
9
|
-
import { createReplyPrefixContext, evaluateSupplementalContextVisibility, getSessionEntry, normalizeAgentId as normalizeAgentId$
|
|
8
|
+
import { d as encodeQuery, f as extractReplyText, g as normalizeCommentFileType, h as buildFeishuCommentTarget, i as createCommentTypingReactionLifecycle, m as requestFeishuApi, p as parseCommentContentElements, s as getChatInfo, t as deliverCommentThreadText } from "./drive-B0JkoiRj.js";
|
|
9
|
+
import { t as createFeishuThreadBindingManager } from "./thread-bindings-Itvfmx6_.js";
|
|
10
|
+
import { createReplyPrefixContext, evaluateSupplementalContextVisibility, getSessionEntry, normalizeAgentId as normalizeAgentId$1, resolveChannelContextVisibilityMode } from "./runtime-api.js";
|
|
10
11
|
import { t as readFeishuJsonResponse } from "./json-response-CheljwGr.js";
|
|
11
|
-
import { _ as
|
|
12
|
-
import { a as waitForAbortableDelay, i as raceWithTimeoutAndAbort } from "./probe-
|
|
13
|
-
import { t as fetchBotIdentityForMonitor } from "./monitor.startup-
|
|
12
|
+
import { _ as normalizeFeishuExternalKey, a as getMessageFeishu, d as extractMentionTargets, f as isFeishuBroadcastMention, g as parsePostContent, h as parseInteractiveCardContent, l as sendMessageFeishu, m as isFeishuGroupChatType, n as sendMediaFeishu, o as listFeishuThreadMessages, p as isMentionForwardRequest, r as shouldSuppressFeishuTextForVoiceMedia, s as sendCardFeishu, t as saveMessageResourceFeishu, u as sendStructuredCardFeishu, v as buildFeishuMediaFallbackText, y as resolveFeishuIdentityEmoji } from "./media-BjSy8fiy.js";
|
|
13
|
+
import { a as waitForAbortableDelay, i as raceWithTimeoutAndAbort } from "./probe-DVpy58s0.js";
|
|
14
|
+
import { t as fetchBotIdentityForMonitor } from "./monitor.startup-BGErejNH.js";
|
|
14
15
|
import { DEFAULT_INGRESS_ADOPTION_STALL_MS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, formatChannelProgressDraftLineForEntry, isChannelProgressDraftWorkToolName, resolveAgentOutboundIdentity, resolveChannelPreviewStreamMode, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
15
16
|
import { createChannelPairingController, createChannelPairingController as createChannelPairingController$1 } from "openclaw/plugin-sdk/channel-pairing";
|
|
16
17
|
import { ensureConfiguredBindingRouteReady, resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRoute } from "openclaw/plugin-sdk/conversation-runtime";
|
|
18
|
+
import { collectErrorGraphCandidates, formatErrorMessage, toStringifiedError } from "openclaw/plugin-sdk/error-runtime";
|
|
17
19
|
import { asDateTimestampMs, isFutureDateTimestampMs, parseStrictNonNegativeInteger, resolveDateTimestampMs, resolveExpiresAtMsFromDurationMs, resolveExpiresAtMsFromDurationSeconds, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
|
18
|
-
import { asBoolean, asNullableRecord, isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, readStringValue, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
20
|
+
import { asBoolean, asNullableRecord, isRecord, normalizeLowercaseStringOrEmpty, normalizeNullableString, normalizeOptionalLowercaseString, normalizeOptionalString, readStringValue, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
19
21
|
import { stripReasoningTagsFromText } from "openclaw/plugin-sdk/text-chunking";
|
|
20
22
|
import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
21
23
|
import { fanInChannelIngressLifecycles } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
22
24
|
import fs from "node:fs";
|
|
23
25
|
import os from "node:os";
|
|
24
26
|
import path from "node:path";
|
|
25
|
-
import { normalizeAccountId, resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
|
|
27
|
+
import { normalizeAccountId, normalizeAgentId, resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
|
|
26
28
|
import { resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
27
29
|
import { isRecord as isRecord$1 } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
28
|
-
import {
|
|
30
|
+
import { createChannelPartialDeliveryError, formatAgentEnvelope, formatInboundMediaUnavailableText, isChannelPartialDeliveryError, recordChannelBotPairLoopAndCheckSuppression, resolveEnvelopeFormatOptions, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
|
|
29
31
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
30
|
-
import {
|
|
32
|
+
import { resolvePinnedMainDmOwnerFromAllowlist, safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
|
|
31
33
|
import { WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, applyBasicWebhookRequestGuards, createFixedWindowRateLimiter, createWebhookAnomalyTracker, resolveRequestClientIp } from "openclaw/plugin-sdk/webhook-ingress";
|
|
32
34
|
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
33
35
|
import { getReplyPayloadTtsSupplement, resolveSendableOutboundReplyParts, resolveTextChunksWithFallback, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
|
|
34
|
-
import { resolvePinnedMainDmOwnerFromAllowlist, safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
|
|
35
36
|
import * as crypto$1 from "node:crypto";
|
|
36
37
|
import crypto, { createHash } from "node:crypto";
|
|
37
|
-
import { buildChannelInboundEventContext, createChannelPartialDeliveryError, formatAgentEnvelope, formatInboundMediaUnavailableText, isChannelPartialDeliveryError, recordChannelBotPairLoopAndCheckSuppression, resolveEnvelopeFormatOptions, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
|
|
38
38
|
import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
39
39
|
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
40
40
|
import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
|
|
41
41
|
import { createChannelReplayGuard } from "openclaw/plugin-sdk/persistent-dedupe";
|
|
42
42
|
import { resolveChannelConfigWrites } from "openclaw/plugin-sdk/channel-config-writes";
|
|
43
|
+
import { formatReasoningMessage, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
|
|
43
44
|
import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
|
|
44
45
|
import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
|
|
45
46
|
import * as http from "node:http";
|
|
47
|
+
import { channelBlockedPatch, channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
46
48
|
import { installRequestBodyLimitGuard, readWebhookBodyOrReject } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
47
49
|
import { isAbortRequestText, isBtwRequestText } from "openclaw/plugin-sdk/command-primitives-runtime";
|
|
48
50
|
//#region extensions/feishu/src/bot-agent-body.ts
|
|
@@ -321,6 +323,7 @@ function parseMessageContent(content, messageType) {
|
|
|
321
323
|
return "[Forwarded message]";
|
|
322
324
|
}
|
|
323
325
|
if (messageType === "merge_forward") return "[Merged and Forwarded Message - loading...]";
|
|
326
|
+
if (messageType === "interactive") return parseInteractiveCardContent(parsed);
|
|
324
327
|
return content;
|
|
325
328
|
} catch {
|
|
326
329
|
return FEISHU_MEDIA_MESSAGE_TYPES.has(messageType) ? "" : content;
|
|
@@ -345,6 +348,7 @@ function formatSubMessageContent(content, contentType) {
|
|
|
345
348
|
switch (contentType) {
|
|
346
349
|
case "text": return parsed.text || content;
|
|
347
350
|
case "post": return parsePostContent(content).textContent;
|
|
351
|
+
case "interactive": return parseInteractiveCardContent(parsed);
|
|
348
352
|
case "image": return "[Image]";
|
|
349
353
|
case "file": return `[File: ${parsed.file_name || "unknown"}]`;
|
|
350
354
|
case "audio": return "[Audio]";
|
|
@@ -369,7 +373,8 @@ function parseMergeForwardContent(params) {
|
|
|
369
373
|
return "[Merged and Forwarded Message - parse error]";
|
|
370
374
|
}
|
|
371
375
|
if (!Array.isArray(items) || items.length === 0) return "[Merged and Forwarded Message - no sub-messages]";
|
|
372
|
-
const
|
|
376
|
+
const container = items.find((item) => item.msg_type === "merge_forward" && !item.upper_message_id);
|
|
377
|
+
const subMessages = container ? items.filter((item) => item !== container) : items.filter((item) => item.upper_message_id);
|
|
373
378
|
if (subMessages.length === 0) return "[Merged and Forwarded Message - no sub-messages found]";
|
|
374
379
|
log?.(`feishu: merge_forward contains ${subMessages.length} sub-messages`);
|
|
375
380
|
subMessages.sort((a, b) => (parseStrictNonNegativeInteger(a.create_time) ?? 0) - (parseStrictNonNegativeInteger(b.create_time) ?? 0));
|
|
@@ -1133,13 +1138,9 @@ function resolveUserPath(p) {
|
|
|
1133
1138
|
}
|
|
1134
1139
|
//#endregion
|
|
1135
1140
|
//#region extensions/feishu/src/agent-config.ts
|
|
1136
|
-
const DEFAULT_AGENT_ID = "main";
|
|
1137
|
-
function normalizeAgentId$1(value) {
|
|
1138
|
-
return (value ?? "").trim().toLowerCase() || DEFAULT_AGENT_ID;
|
|
1139
|
-
}
|
|
1140
1141
|
function resolveFeishuConfigReasoningDefault(cfg, agentId) {
|
|
1141
|
-
const id = normalizeAgentId
|
|
1142
|
-
return cfg.agents?.list?.find((entry) => normalizeAgentId
|
|
1142
|
+
const id = normalizeAgentId(agentId);
|
|
1143
|
+
return cfg.agents?.list?.find((entry) => normalizeAgentId(entry?.id) === id)?.reasoningDefault ?? cfg.agents?.defaults?.reasoningDefault ?? "off";
|
|
1143
1144
|
}
|
|
1144
1145
|
//#endregion
|
|
1145
1146
|
//#region extensions/feishu/src/reasoning-preview.ts
|
|
@@ -1242,8 +1243,14 @@ function resolveAllowedHostnames(domain) {
|
|
|
1242
1243
|
}
|
|
1243
1244
|
return ["open.feishu.cn"];
|
|
1244
1245
|
}
|
|
1246
|
+
function cancelUnreadResponseBody(response) {
|
|
1247
|
+
if (!response.bodyUsed) response.body?.cancel().catch(() => void 0);
|
|
1248
|
+
}
|
|
1245
1249
|
async function assertSuccessfulCardKitResponse(response, auditContext, action) {
|
|
1246
|
-
if (!response.ok)
|
|
1250
|
+
if (!response.ok) {
|
|
1251
|
+
cancelUnreadResponseBody(response);
|
|
1252
|
+
throw new Error(`${action} failed with HTTP ${response.status}`);
|
|
1253
|
+
}
|
|
1247
1254
|
const data = await readFeishuJsonResponse(response, auditContext);
|
|
1248
1255
|
if (data.code !== 0) throw new Error(`${action} failed: ${data.msg ?? "unknown error"} (code=${String(data.code)})`);
|
|
1249
1256
|
}
|
|
@@ -1276,7 +1283,10 @@ async function getToken(creds, deps) {
|
|
|
1276
1283
|
});
|
|
1277
1284
|
let data;
|
|
1278
1285
|
try {
|
|
1279
|
-
if (!response.ok)
|
|
1286
|
+
if (!response.ok) {
|
|
1287
|
+
cancelUnreadResponseBody(response);
|
|
1288
|
+
throw new Error(`Token request failed with HTTP ${response.status}`);
|
|
1289
|
+
}
|
|
1280
1290
|
data = await readFeishuJsonResponse(response, "feishu.streaming-card.token");
|
|
1281
1291
|
} finally {
|
|
1282
1292
|
await release();
|
|
@@ -1384,7 +1394,10 @@ var FeishuStreamingSession = class {
|
|
|
1384
1394
|
});
|
|
1385
1395
|
let createData;
|
|
1386
1396
|
try {
|
|
1387
|
-
if (!createRes.ok)
|
|
1397
|
+
if (!createRes.ok) {
|
|
1398
|
+
cancelUnreadResponseBody(createRes);
|
|
1399
|
+
throw new Error(`Create card request failed with HTTP ${createRes.status}`);
|
|
1400
|
+
}
|
|
1388
1401
|
createData = await readFeishuJsonResponse(createRes, "feishu.streaming-card.create");
|
|
1389
1402
|
} finally {
|
|
1390
1403
|
await releaseCreate();
|
|
@@ -1821,9 +1834,6 @@ function mergeStreamingFinalText(previousText, nextText, appendError) {
|
|
|
1821
1834
|
if (previousText.endsWith(`\n\n${nextText}`)) return previousText;
|
|
1822
1835
|
return `${previousText}\n\n${nextText}`;
|
|
1823
1836
|
}
|
|
1824
|
-
function toError(error) {
|
|
1825
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
1826
|
-
}
|
|
1827
1837
|
/** Maximum age (ms) for a message to receive a typing indicator reaction.
|
|
1828
1838
|
* Messages older than this are likely replays after context compaction (#30418). */
|
|
1829
1839
|
const TYPING_INDICATOR_MAX_AGE_MS = 2 * 6e4;
|
|
@@ -2191,9 +2201,10 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2191
2201
|
};
|
|
2192
2202
|
const updateStreamingStatusLine = (nextStatusLine, options) => {
|
|
2193
2203
|
statusLine = nextStatusLine;
|
|
2194
|
-
if (!Boolean(streaming?.isActive() || streamingStartPromise) && (options?.startIfNeeded === false || renderMode !== "card")) return;
|
|
2204
|
+
if (!Boolean(streaming?.isActive() || streamingStartPromise) && (options?.startIfNeeded === false || renderMode !== "card")) return false;
|
|
2195
2205
|
startStreaming();
|
|
2196
2206
|
flushStreamingCardUpdate(buildCombinedStreamText(reasoningText, streamText));
|
|
2207
|
+
return false;
|
|
2197
2208
|
};
|
|
2198
2209
|
const sendChunkedTextReply = async (paramsLocal) => {
|
|
2199
2210
|
const chunkSource = paramsLocal.useCard ? paramsLocal.text : materializeFeishuPostMarkdownSoftBreaks(core.channel.text.convertMarkdownTables(paramsLocal.text, tableMode));
|
|
@@ -2220,6 +2231,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2220
2231
|
acceptedChunks.push(chunk);
|
|
2221
2232
|
markVisibleReplySent();
|
|
2222
2233
|
} catch (error) {
|
|
2234
|
+
if (isChannelPartialDeliveryError(error)) markVisibleReplySent();
|
|
2223
2235
|
throw createFeishuPartialReplyDeliveryError(error, createFeishuReplyDeliveryResult({
|
|
2224
2236
|
results,
|
|
2225
2237
|
visibleReplySent: results.length > 0,
|
|
@@ -2280,7 +2292,11 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2280
2292
|
markVisibleReplySent();
|
|
2281
2293
|
if (result?.voiceIntentDegradedToFile && options?.fallbackText && !sentFallbackText) degradedVoiceFallbackText = options.fallbackText;
|
|
2282
2294
|
},
|
|
2283
|
-
onError: options?.fallbackText === void 0 ? void 0 : async ({ mediaUrl }) => {
|
|
2295
|
+
onError: options?.fallbackText === void 0 ? void 0 : async ({ error, mediaUrl }) => {
|
|
2296
|
+
if (isChannelPartialDeliveryError(error)) {
|
|
2297
|
+
markVisibleReplySent();
|
|
2298
|
+
throw toStringifiedError(error);
|
|
2299
|
+
}
|
|
2284
2300
|
const fallbackText = await buildFeishuMediaFallbackText({
|
|
2285
2301
|
text: sentFallbackText ? void 0 : options.fallbackText,
|
|
2286
2302
|
mediaUrl
|
|
@@ -2295,6 +2311,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2295
2311
|
}
|
|
2296
2312
|
} catch (error) {
|
|
2297
2313
|
const partial = isChannelPartialDeliveryError(error) ? error.deliveryResult : void 0;
|
|
2314
|
+
if (partial) markVisibleReplySent();
|
|
2298
2315
|
throw createFeishuPartialReplyDeliveryError(error, mergeFeishuReplyDeliveryResults([...results, ...partial ? [partial] : []]));
|
|
2299
2316
|
}
|
|
2300
2317
|
return mergeFeishuReplyDeliveryResults(results);
|
|
@@ -2396,7 +2413,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2396
2413
|
if (deliveryError !== void 0) completion.reject(createFeishuPartialReplyDeliveryError(isChannelPartialDeliveryError(deliveryError) && deliveryError instanceof Error ? deliveryError.cause ?? deliveryError : deliveryError instanceof FeishuStreamingFinalizationError ? deliveryError.cause ?? deliveryError : deliveryError, settledResult));
|
|
2397
2414
|
else completion.resolve(settledResult);
|
|
2398
2415
|
}
|
|
2399
|
-
if (closeOutcome.error !== void 0) throw
|
|
2416
|
+
if (closeOutcome.error !== void 0) throw toStringifiedError(closeOutcome.error);
|
|
2400
2417
|
} while (pendingStreamingDeliveries.length > 0);
|
|
2401
2418
|
} finally {
|
|
2402
2419
|
typingCallbacks?.onIdle?.();
|
|
@@ -2486,6 +2503,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2486
2503
|
}
|
|
2487
2504
|
};
|
|
2488
2505
|
const handleDeliveryError = async (error, info) => {
|
|
2506
|
+
if (isChannelPartialDeliveryError(error)) markVisibleReplySent();
|
|
2489
2507
|
params.runtime.error?.(`feishu[${account.accountId}] ${info.kind} reply failed: ${String(error)}`);
|
|
2490
2508
|
await queueIdleSideEffects().catch((cleanupError) => params.runtime.error?.(`feishu[${account.accountId}] reply error cleanup failed: ${String(cleanupError)}`));
|
|
2491
2509
|
};
|
|
@@ -2524,7 +2542,7 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2524
2542
|
const shouldDiscardStreamingPreview = info?.kind === "final" && (finalTextExceedsStreamingLimit || hasMedia && (hasVoiceMedia && !shouldDeliverText && !ttsTextAlreadyVisible || skipTextForDuplicateFinal));
|
|
2525
2543
|
const priorClosedStreamingSettlement = info?.kind === "final" && hasText && skipTextForDuplicateFinal ? claimClosedStreamingResult(void 0, text) : void 0;
|
|
2526
2544
|
if (!shouldDeliverText && !hasMedia) {
|
|
2527
|
-
if (priorClosedStreamingSettlement?.error !== void 0) throw
|
|
2545
|
+
if (priorClosedStreamingSettlement?.error !== void 0) throw toStringifiedError(priorClosedStreamingSettlement.error);
|
|
2528
2546
|
return priorClosedStreamingSettlement?.result ?? noVisibleFeishuReplyDelivery;
|
|
2529
2547
|
}
|
|
2530
2548
|
const deliveredResults = priorClosedStreamingSettlement ? [priorClosedStreamingSettlement.result] : [];
|
|
@@ -2654,43 +2672,40 @@ function createFeishuReplyDispatcher(params) {
|
|
|
2654
2672
|
onModelSelected: prefixContext.onModelSelected,
|
|
2655
2673
|
disableBlockStreaming: typeof blockStreamingEnabled === "boolean" ? !blockStreamingEnabled : true,
|
|
2656
2674
|
onPartialReply: previewStreamingEnabled ? (payload) => {
|
|
2657
|
-
if (!payload.text) return;
|
|
2675
|
+
if (!payload.text) return false;
|
|
2658
2676
|
const cleaned = stripReasoningTagsFromText(payload.text, {
|
|
2659
2677
|
mode: "strict",
|
|
2660
2678
|
trim: "both"
|
|
2661
2679
|
});
|
|
2662
|
-
if (!cleaned) return;
|
|
2680
|
+
if (!cleaned) return false;
|
|
2663
2681
|
startStreaming();
|
|
2664
2682
|
queueStreamingUpdate(cleaned, {
|
|
2665
2683
|
dedupeWithLastPartial: true,
|
|
2666
2684
|
mode: "snapshot"
|
|
2667
2685
|
});
|
|
2686
|
+
return false;
|
|
2668
2687
|
} : void 0,
|
|
2669
2688
|
onReasoningStream: reasoningPreviewEnabled ? (payload) => {
|
|
2670
|
-
if (!payload.text) return;
|
|
2689
|
+
if (!payload.text) return false;
|
|
2671
2690
|
startStreaming();
|
|
2672
2691
|
queueReasoningUpdate(formatReasoningMessage(payload.text));
|
|
2692
|
+
return false;
|
|
2673
2693
|
} : void 0,
|
|
2674
|
-
onReasoningEnd: reasoningPreviewEnabled ? () =>
|
|
2694
|
+
onReasoningEnd: reasoningPreviewEnabled ? () => false : void 0,
|
|
2675
2695
|
onToolStart: previewStreamingEnabled ? (payload) => {
|
|
2676
|
-
if (!isChannelProgressDraftWorkToolName(payload.name)) return;
|
|
2696
|
+
if (!isChannelProgressDraftWorkToolName(payload.name)) return false;
|
|
2677
2697
|
const statusLineLocal = formatChannelProgressDraftLineForEntry(account.config, {
|
|
2678
2698
|
event: "tool",
|
|
2679
2699
|
name: payload.name,
|
|
2680
2700
|
phase: payload.phase,
|
|
2681
2701
|
args: payload.args
|
|
2682
2702
|
}, { detailMode: payload.detailMode });
|
|
2683
|
-
if (statusLineLocal) updateStreamingStatusLine(statusLineLocal);
|
|
2703
|
+
if (statusLineLocal) return updateStreamingStatusLine(statusLineLocal);
|
|
2704
|
+
return false;
|
|
2684
2705
|
} : void 0,
|
|
2685
|
-
onAssistantMessageStart: previewStreamingEnabled ? () => {
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
onCompactionStart: previewStreamingEnabled ? () => {
|
|
2689
|
-
updateStreamingStatusLine("📦 **Compacting context...**");
|
|
2690
|
-
} : void 0,
|
|
2691
|
-
onCompactionEnd: previewStreamingEnabled ? () => {
|
|
2692
|
-
updateStreamingStatusLine("");
|
|
2693
|
-
} : void 0
|
|
2706
|
+
onAssistantMessageStart: previewStreamingEnabled ? () => updateStreamingStatusLine("", { startIfNeeded: false }) : void 0,
|
|
2707
|
+
onCompactionStart: previewStreamingEnabled ? () => updateStreamingStatusLine("📦 **Compacting context...**") : void 0,
|
|
2708
|
+
onCompactionEnd: previewStreamingEnabled ? () => updateStreamingStatusLine("") : void 0
|
|
2694
2709
|
},
|
|
2695
2710
|
ensureNoVisibleReplyFallback,
|
|
2696
2711
|
getVisibleReplyState: () => ({
|
|
@@ -2892,7 +2907,10 @@ async function handleFeishuMessage(params) {
|
|
|
2892
2907
|
if (event.message.message_type === "merge_forward") {
|
|
2893
2908
|
log(`feishu[${account.accountId}]: processing merge_forward message, fetching full content via API`);
|
|
2894
2909
|
try {
|
|
2895
|
-
const response = await createFeishuClient(account).im.message.get({
|
|
2910
|
+
const response = await createFeishuClient(account).im.message.get({
|
|
2911
|
+
params: { card_msg_content_type: "user_card_content" },
|
|
2912
|
+
path: { message_id: event.message.message_id }
|
|
2913
|
+
});
|
|
2896
2914
|
if (response.code === 0 && response.data?.items && response.data.items.length > 0) {
|
|
2897
2915
|
log(`feishu[${account.accountId}]: merge_forward API returned ${response.data.items.length} items`);
|
|
2898
2916
|
const expandedContent = parseMergeForwardContent({
|
|
@@ -2963,22 +2981,25 @@ async function handleFeishuMessage(params) {
|
|
|
2963
2981
|
feishuCfg
|
|
2964
2982
|
}) : null;
|
|
2965
2983
|
let effectiveThreadId = ctx.threadId;
|
|
2966
|
-
if (isGroup && ctx.chatType === "topic_group" && !effectiveThreadId && isFeishuTopicSessionScope(groupSessionScope ?? "group"))
|
|
2967
|
-
const
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2984
|
+
if (isGroup && ctx.chatType === "topic_group" && !effectiveThreadId && isFeishuTopicSessionScope(groupSessionScope ?? "group")) {
|
|
2985
|
+
const topicHydrationMessageId = ctx.replyTargetMessageId ?? ctx.messageId;
|
|
2986
|
+
try {
|
|
2987
|
+
const hydratedThreadId = (await getMessageFeishu({
|
|
2988
|
+
cfg,
|
|
2989
|
+
accountId: account.accountId,
|
|
2990
|
+
messageId: topicHydrationMessageId
|
|
2991
|
+
}))?.threadId?.trim();
|
|
2992
|
+
if (hydratedThreadId) {
|
|
2993
|
+
ctx = {
|
|
2994
|
+
...ctx,
|
|
2995
|
+
threadId: hydratedThreadId
|
|
2996
|
+
};
|
|
2997
|
+
effectiveThreadId = hydratedThreadId;
|
|
2998
|
+
log(`feishu[${account.accountId}]: hydrated topic thread_id=${hydratedThreadId} for message=${topicHydrationMessageId}`);
|
|
2999
|
+
}
|
|
3000
|
+
} catch (err) {
|
|
3001
|
+
log(`feishu[${account.accountId}]: failed to hydrate topic thread_id for message=${topicHydrationMessageId}: ${String(err)}`);
|
|
2979
3002
|
}
|
|
2980
|
-
} catch (err) {
|
|
2981
|
-
log(`feishu[${account.accountId}]: failed to hydrate topic thread_id for message=${ctx.messageId}: ${String(err)}`);
|
|
2982
3003
|
}
|
|
2983
3004
|
const effectiveGroupSenderAllowFrom = isGroup ? (groupConfig?.allowFrom?.length ?? 0) > 0 ? groupConfig?.allowFrom ?? [] : feishuCfg?.groupSenderAllowFrom ?? [] : [];
|
|
2984
3005
|
const groupSession = isGroup ? resolveFeishuGroupSession({
|
|
@@ -2995,7 +3016,7 @@ async function handleFeishuMessage(params) {
|
|
|
2995
3016
|
const dmPolicy = feishuCfg?.dmPolicy ?? "pairing";
|
|
2996
3017
|
const configAllowFrom = feishuCfg?.allowFrom ?? [];
|
|
2997
3018
|
const rawBroadcastAgents = isGroup ? resolveBroadcastAgents(cfg, ctx.chatId) : null;
|
|
2998
|
-
const broadcastAgents = rawBroadcastAgents ? uniqueStrings(rawBroadcastAgents.map((id) => normalizeAgentId$
|
|
3019
|
+
const broadcastAgents = rawBroadcastAgents ? uniqueStrings(rawBroadcastAgents.map((id) => normalizeAgentId$1(id))) : null;
|
|
2999
3020
|
const messageCreateTimeMs = parseStrictNonNegativeInteger(event.message.create_time) ?? Date.now();
|
|
3000
3021
|
let requireMention = false;
|
|
3001
3022
|
if (isGroup) {
|
|
@@ -3524,7 +3545,37 @@ async function handleFeishuMessage(params) {
|
|
|
3524
3545
|
const buildCtxPayloadForAgent = async (agentId, agentSessionKey, agentAccountId, wasMentioned) => {
|
|
3525
3546
|
const groupName = await resolveGroupNameForLabel();
|
|
3526
3547
|
const threadContext = await resolveThreadContextForAgent(agentId, agentSessionKey, groupName);
|
|
3527
|
-
|
|
3548
|
+
const contextBinding = {
|
|
3549
|
+
agentId,
|
|
3550
|
+
sessionKey: agentSessionKey,
|
|
3551
|
+
messageId: ctx.messageId,
|
|
3552
|
+
inboundEventKind: "user_request"
|
|
3553
|
+
};
|
|
3554
|
+
const boundChannelIngress = isGroup ? await resolveFeishuGroupSenderActivationIngressAccess({
|
|
3555
|
+
cfg: effectiveCfg,
|
|
3556
|
+
accountId: account.accountId,
|
|
3557
|
+
chatId: ctx.chatId,
|
|
3558
|
+
allowFrom: effectiveGroupSenderAllowFrom,
|
|
3559
|
+
senderOpenId: ctx.senderOpenId,
|
|
3560
|
+
senderUserId,
|
|
3561
|
+
requireMention,
|
|
3562
|
+
mentionedBot: ctx.mentionedBot,
|
|
3563
|
+
contextBinding,
|
|
3564
|
+
threadId: ctx.rootId && isTopicSessionForThread ? ctx.rootId : void 0
|
|
3565
|
+
}) : await resolveFeishuDmIngressAccess({
|
|
3566
|
+
cfg: effectiveCfg,
|
|
3567
|
+
accountId: account.accountId,
|
|
3568
|
+
dmPolicy: effectiveDmPolicy,
|
|
3569
|
+
allowFrom: effectiveConfigAllowFrom,
|
|
3570
|
+
readAllowFromStore: pairing.readAllowFromStore,
|
|
3571
|
+
senderOpenId: ctx.senderOpenId,
|
|
3572
|
+
senderUserId,
|
|
3573
|
+
conversationId: ctx.senderOpenId,
|
|
3574
|
+
mayPair: false,
|
|
3575
|
+
contextBinding
|
|
3576
|
+
});
|
|
3577
|
+
return core.channel.inbound.buildContext({
|
|
3578
|
+
channelIngress: boundChannelIngress,
|
|
3528
3579
|
channel: "feishu",
|
|
3529
3580
|
supplemental: {
|
|
3530
3581
|
quote: quotedContent ? {
|
|
@@ -3651,8 +3702,8 @@ async function handleFeishuMessage(params) {
|
|
|
3651
3702
|
}
|
|
3652
3703
|
};
|
|
3653
3704
|
const strategy = cfg.broadcast?.strategy === "sequential" ? "sequential" : "parallel";
|
|
3654
|
-
const activeAgentId = ctx.mentionedBot || !requireMention ? normalizeAgentId$
|
|
3655
|
-
const agentIds = (cfg.agents?.list ?? []).map((a) => normalizeAgentId$
|
|
3705
|
+
const activeAgentId = ctx.mentionedBot || !requireMention ? normalizeAgentId$1(route.agentId) : null;
|
|
3706
|
+
const agentIds = (cfg.agents?.list ?? []).map((a) => normalizeAgentId$1(a.id));
|
|
3656
3707
|
const hasKnownAgents = agentIds.length > 0;
|
|
3657
3708
|
log(`feishu[${account.accountId}]: broadcasting to ${broadcastAgents.length} agents (strategy=${strategy}, active=${activeAgentId ?? "none"})`);
|
|
3658
3709
|
const createBroadcastInboundAdapter = (paramsLocal) => ({
|
|
@@ -3692,7 +3743,7 @@ async function handleFeishuMessage(params) {
|
|
|
3692
3743
|
})
|
|
3693
3744
|
});
|
|
3694
3745
|
const dispatchForAgent = async (agentId) => {
|
|
3695
|
-
const normalizedAgentId = normalizeAgentId$
|
|
3746
|
+
const normalizedAgentId = normalizeAgentId$1(agentId);
|
|
3696
3747
|
if (hasKnownAgents && !agentIds.includes(normalizedAgentId)) {
|
|
3697
3748
|
log(`feishu[${account.accountId}]: broadcast agent ${agentId} not found in agents.list; skipping`);
|
|
3698
3749
|
return;
|
|
@@ -4293,9 +4344,6 @@ const FEISHU_INGRESS_PAYLOAD_VERSION = 1;
|
|
|
4293
4344
|
const FEISHU_INGRESS_POLL_INTERVAL_MS = 500;
|
|
4294
4345
|
const FEISHU_DURABLE_EVENT_TYPES = /* @__PURE__ */ new Set(["drive.notice.comment_add_v1", "im.message.receive_v1"]);
|
|
4295
4346
|
const FeishuIngressPermanentError = createChannelIngressError("FeishuIngressPermanentError", { withReason: true });
|
|
4296
|
-
function readString(value) {
|
|
4297
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
4298
|
-
}
|
|
4299
4347
|
function parseRawEnvelope(rawEnvelope) {
|
|
4300
4348
|
let parsed;
|
|
4301
4349
|
try {
|
|
@@ -4307,7 +4355,7 @@ function parseRawEnvelope(rawEnvelope) {
|
|
|
4307
4355
|
return parsed;
|
|
4308
4356
|
}
|
|
4309
4357
|
function decryptEnvelope(envelope, encryptKey) {
|
|
4310
|
-
const encrypted =
|
|
4358
|
+
const encrypted = normalizeNullableString(envelope.encrypt);
|
|
4311
4359
|
if (!encrypted) return envelope;
|
|
4312
4360
|
const key = encryptKey?.trim();
|
|
4313
4361
|
if (!key) throw new FeishuIngressPermanentError("authentication-failed", "Feishu encrypted ingress envelope has no configured encrypt key.");
|
|
@@ -4322,13 +4370,13 @@ function inspectFeishuIngressEnvelope(rawEnvelope, encryptKey, allowInvalidLane
|
|
|
4322
4370
|
const envelope = decryptEnvelope(parseRawEnvelope(rawEnvelope), encryptKey);
|
|
4323
4371
|
const nestedHeader = isRecord$1(envelope.header) ? envelope.header : null;
|
|
4324
4372
|
const nestedEvent = isRecord$1(envelope.event) ? envelope.event : null;
|
|
4325
|
-
const eventType =
|
|
4373
|
+
const eventType = normalizeNullableString(nestedHeader?.event_type) ?? normalizeNullableString(envelope.event_type);
|
|
4326
4374
|
if (!eventType || !FEISHU_DURABLE_EVENT_TYPES.has(eventType)) return null;
|
|
4327
|
-
const eventId =
|
|
4375
|
+
const eventId = normalizeNullableString(nestedHeader?.event_id) ?? normalizeNullableString(envelope.event_id);
|
|
4328
4376
|
if (!eventId) throw new FeishuIngressPermanentError("invalid-event", `Feishu ${eventType} envelope is missing header.event_id.`);
|
|
4329
4377
|
const event = nestedEvent ?? envelope;
|
|
4330
4378
|
if (eventType === "im.message.receive_v1") {
|
|
4331
|
-
const chatId =
|
|
4379
|
+
const chatId = normalizeNullableString((isRecord$1(event.message) ? event.message : null)?.chat_id);
|
|
4332
4380
|
if (!chatId) {
|
|
4333
4381
|
if (allowInvalidLane) return {
|
|
4334
4382
|
eventId,
|
|
@@ -4344,8 +4392,8 @@ function inspectFeishuIngressEnvelope(rawEnvelope, encryptKey, allowInvalidLane
|
|
|
4344
4392
|
};
|
|
4345
4393
|
}
|
|
4346
4394
|
const noticeMeta = isRecord$1(event.notice_meta) ? event.notice_meta : null;
|
|
4347
|
-
const fileType =
|
|
4348
|
-
const documentId =
|
|
4395
|
+
const fileType = normalizeNullableString(noticeMeta?.file_type);
|
|
4396
|
+
const documentId = normalizeNullableString(noticeMeta?.file_token);
|
|
4349
4397
|
if (!fileType || !documentId) {
|
|
4350
4398
|
if (allowInvalidLane) return {
|
|
4351
4399
|
eventId,
|
|
@@ -4580,7 +4628,7 @@ function createFeishuDurableIngress(options) {
|
|
|
4580
4628
|
invoke,
|
|
4581
4629
|
invokeWebhook,
|
|
4582
4630
|
resolveLifecycle: (data) => {
|
|
4583
|
-
const eventId = isRecord$1(data) ?
|
|
4631
|
+
const eventId = isRecord$1(data) ? normalizeNullableString(data.event_id) : null;
|
|
4584
4632
|
return eventId ? activeLifecycles.get(eventId) : void 0;
|
|
4585
4633
|
},
|
|
4586
4634
|
setSocketTerminator: (terminate) => {
|
|
@@ -4777,7 +4825,9 @@ function startBotIdentityRecovery(params) {
|
|
|
4777
4825
|
const log = runtime?.log ?? console.log;
|
|
4778
4826
|
log(`feishu[${accountId}]: bot open_id ${currentSource === "cache" ? "loaded from cache" : "unknown"}; starting background provider refresh (delays: ${BOT_IDENTITY_RETRY_DELAYS_MS.map((delay) => `${delay / 1e3}s`).join(", ")})`);
|
|
4779
4827
|
if (currentSource !== "cache") log(`feishu[${accountId}]: requireMention group messages stay gated until bot identity recovery succeeds`);
|
|
4780
|
-
retryBotIdentityProbe(account, accountId, runtime, abortSignal)
|
|
4828
|
+
retryBotIdentityProbe(account, accountId, runtime, abortSignal).catch((err) => {
|
|
4829
|
+
(runtime?.error ?? console.error)(`feishu[${accountId}]: bot identity background retry failed unexpectedly: ${String(err)}`);
|
|
4830
|
+
});
|
|
4781
4831
|
}
|
|
4782
4832
|
//#endregion
|
|
4783
4833
|
//#region extensions/feishu/src/card-ux-launcher.ts
|
|
@@ -5094,7 +5144,7 @@ function safeJsonStringify(value) {
|
|
|
5094
5144
|
}
|
|
5095
5145
|
}
|
|
5096
5146
|
function truncatePromptText(text, maxLength = FEISHU_PROMPT_TEXT_LIMIT) {
|
|
5097
|
-
const normalized =
|
|
5147
|
+
const normalized = normalizeOptionalString(text);
|
|
5098
5148
|
if (!normalized) return "";
|
|
5099
5149
|
return normalized.length > maxLength ? `${sliceUtf16Safe(normalized, 0, maxLength - 1)}…` : normalized;
|
|
5100
5150
|
}
|
|
@@ -5160,7 +5210,7 @@ async function resolveParsedCommentContent(params) {
|
|
|
5160
5210
|
return null;
|
|
5161
5211
|
}
|
|
5162
5212
|
const objType = normalizeCommentFileType(response.data?.node?.obj_type);
|
|
5163
|
-
const objToken =
|
|
5213
|
+
const objToken = normalizeOptionalString(response.data?.node?.obj_token);
|
|
5164
5214
|
if (!objType || !objToken) return null;
|
|
5165
5215
|
return {
|
|
5166
5216
|
resolvedObjType: objType,
|
|
@@ -5229,18 +5279,18 @@ async function fetchDriveComments(params) {
|
|
|
5229
5279
|
}
|
|
5230
5280
|
async function requestFeishuOpenApi(params) {
|
|
5231
5281
|
const formatErrorDetails = (error) => {
|
|
5232
|
-
if (!isRecord
|
|
5233
|
-
const response = isRecord
|
|
5234
|
-
const responseData = isRecord
|
|
5282
|
+
if (!isRecord(error)) return typeof error === "string" ? error : JSON.stringify(error);
|
|
5283
|
+
const response = isRecord(error.response) ? error.response : void 0;
|
|
5284
|
+
const responseData = isRecord(response?.data) ? response?.data : void 0;
|
|
5235
5285
|
return safeJsonStringify({
|
|
5236
5286
|
message: typeof error.message === "string" ? error.message : typeof error === "string" ? error : JSON.stringify(error),
|
|
5237
|
-
code:
|
|
5238
|
-
method:
|
|
5239
|
-
url:
|
|
5287
|
+
code: readStringValue(error.code),
|
|
5288
|
+
method: readStringValue(isRecord(error.config) ? error.config.method : void 0),
|
|
5289
|
+
url: readStringValue(isRecord(error.config) ? error.config.url : void 0),
|
|
5240
5290
|
http_status: typeof response?.status === "number" ? response.status : void 0,
|
|
5241
|
-
feishu_code: typeof responseData?.code === "number" ? responseData.code :
|
|
5242
|
-
feishu_msg:
|
|
5243
|
-
feishu_log_id:
|
|
5291
|
+
feishu_code: typeof responseData?.code === "number" ? responseData.code : readStringValue(responseData?.code),
|
|
5292
|
+
feishu_msg: readStringValue(responseData?.msg),
|
|
5293
|
+
feishu_log_id: readStringValue(responseData?.log_id)
|
|
5244
5294
|
});
|
|
5245
5295
|
};
|
|
5246
5296
|
const result = await raceWithTimeoutAndAbort(params.client.request({
|
|
@@ -5288,15 +5338,15 @@ async function fetchDriveCommentReplies(params) {
|
|
|
5288
5338
|
};
|
|
5289
5339
|
}
|
|
5290
5340
|
async function resolveCommentReplyContext(params) {
|
|
5291
|
-
const userId =
|
|
5292
|
-
const normalizedBotOpenIds = new Set(Array.from(params.botOpenIds ?? []).map((botId) =>
|
|
5341
|
+
const userId = normalizeOptionalString(params.reply.user_id);
|
|
5342
|
+
const normalizedBotOpenIds = new Set(Array.from(params.botOpenIds ?? []).map((botId) => normalizeOptionalString(botId)).filter((botId) => Boolean(botId)));
|
|
5293
5343
|
return {
|
|
5294
|
-
replyId:
|
|
5344
|
+
replyId: normalizeOptionalString(params.reply.reply_id),
|
|
5295
5345
|
userId,
|
|
5296
5346
|
createTime: typeof params.reply.create_time === "number" ? params.reply.create_time : void 0,
|
|
5297
5347
|
isBotAuthored: typeof userId === "string" && normalizedBotOpenIds.has(userId),
|
|
5298
5348
|
content: await resolveParsedCommentContent({
|
|
5299
|
-
elements: isRecord
|
|
5349
|
+
elements: isRecord(params.reply.content) ? params.reply.content.elements : void 0,
|
|
5300
5350
|
botOpenIds: params.botOpenIds,
|
|
5301
5351
|
currentDocument: params.currentDocument,
|
|
5302
5352
|
client: params.client,
|
|
@@ -5451,8 +5501,8 @@ async function fetchDriveCommentContext(params) {
|
|
|
5451
5501
|
createTime: right.createTime,
|
|
5452
5502
|
stableId: right.replyId
|
|
5453
5503
|
}));
|
|
5454
|
-
const rootReplyContext = resolvedReplies.find((reply) => reply.replyId ===
|
|
5455
|
-
const targetReplyContext = resolvedReplies.find((reply) => reply.replyId ===
|
|
5504
|
+
const rootReplyContext = resolvedReplies.find((reply) => reply.replyId === normalizeOptionalString(rootReply?.reply_id)) ?? resolvedReplies[0];
|
|
5505
|
+
const targetReplyContext = resolvedReplies.find((reply) => reply.replyId === normalizeOptionalString(targetReply?.reply_id)) ?? (params.replyId ? void 0 : resolvedReplies.at(-1) ?? rootReplyContext);
|
|
5456
5506
|
let wholeCommentTimeline = [];
|
|
5457
5507
|
if (commentCard?.is_whole === true) {
|
|
5458
5508
|
const wholeComments = (await fetchDriveComments({
|
|
@@ -5466,9 +5516,9 @@ async function fetchDriveCommentContext(params) {
|
|
|
5466
5516
|
})).filter((comment) => comment.is_whole === true);
|
|
5467
5517
|
wholeCommentTimeline = await Promise.all(wholeComments.map(async (comment) => {
|
|
5468
5518
|
const rootWholeReply = comment.reply_list?.replies?.[0];
|
|
5469
|
-
const normalizedBotOpenIds = new Set(Array.from(params.botOpenIds ?? []).map((botId) =>
|
|
5519
|
+
const normalizedBotOpenIds = new Set(Array.from(params.botOpenIds ?? []).map((botId) => normalizeOptionalString(botId)).filter((botId) => Boolean(botId)));
|
|
5470
5520
|
const content = await resolveParsedCommentContent({
|
|
5471
|
-
elements: isRecord
|
|
5521
|
+
elements: isRecord(rootWholeReply?.content) ? rootWholeReply.content.elements : void 0,
|
|
5472
5522
|
botOpenIds: params.botOpenIds,
|
|
5473
5523
|
currentDocument,
|
|
5474
5524
|
client: params.client,
|
|
@@ -5476,12 +5526,12 @@ async function fetchDriveCommentContext(params) {
|
|
|
5476
5526
|
logger: params.logger,
|
|
5477
5527
|
accountId: params.accountId
|
|
5478
5528
|
});
|
|
5479
|
-
const commentUserId =
|
|
5529
|
+
const commentUserId = normalizeOptionalString(rootWholeReply?.user_id) || normalizeOptionalString(comment.user_id);
|
|
5480
5530
|
return {
|
|
5481
|
-
commentId:
|
|
5531
|
+
commentId: normalizeOptionalString(comment.comment_id) ?? "",
|
|
5482
5532
|
userId: commentUserId,
|
|
5483
5533
|
createTime: typeof comment.create_time === "number" ? comment.create_time : typeof rootWholeReply?.create_time === "number" ? rootWholeReply.create_time : void 0,
|
|
5484
|
-
isCurrentComment:
|
|
5534
|
+
isCurrentComment: normalizeOptionalString(comment.comment_id) === params.commentId,
|
|
5485
5535
|
isBotAuthored: typeof commentUserId === "string" && normalizedBotOpenIds.has(commentUserId),
|
|
5486
5536
|
content
|
|
5487
5537
|
};
|
|
@@ -5496,10 +5546,10 @@ async function fetchDriveCommentContext(params) {
|
|
|
5496
5546
|
}
|
|
5497
5547
|
const currentWholeCommentIndex = wholeCommentTimeline.findIndex((entry) => entry.commentId === params.commentId);
|
|
5498
5548
|
return {
|
|
5499
|
-
documentTitle:
|
|
5500
|
-
documentUrl:
|
|
5549
|
+
documentTitle: normalizeOptionalString(meta?.title),
|
|
5550
|
+
documentUrl: normalizeOptionalString(meta?.url),
|
|
5501
5551
|
isWholeComment: commentCard?.is_whole,
|
|
5502
|
-
quoteText:
|
|
5552
|
+
quoteText: normalizeOptionalString(commentCard?.quote),
|
|
5503
5553
|
rootCommentText: rootReplyContext?.content.semanticText ?? rootReplyContext?.content.plainText,
|
|
5504
5554
|
targetReplyText: targetReplyContext?.content.semanticText ?? targetReplyContext?.content.plainText,
|
|
5505
5555
|
rootCommentContent: rootReplyContext?.content,
|
|
@@ -5566,7 +5616,7 @@ async function resolveDriveCommentEventCore(params) {
|
|
|
5566
5616
|
const fileToken = event.notice_meta?.file_token?.trim();
|
|
5567
5617
|
const fileType = normalizeCommentFileType(event.notice_meta?.file_type);
|
|
5568
5618
|
const senderId = event.notice_meta?.from_user_id?.open_id?.trim();
|
|
5569
|
-
const senderUserId =
|
|
5619
|
+
const senderUserId = normalizeOptionalString(event.notice_meta?.from_user_id?.user_id);
|
|
5570
5620
|
if (!eventId || !commentId || !noticeType || !fileToken || !fileType || !senderId) {
|
|
5571
5621
|
logger?.(`feishu[${accountId}]: drive comment notice missing required fields event=${eventId ?? "unknown"} comment=${commentId ?? "unknown"}`);
|
|
5572
5622
|
return null;
|
|
@@ -5587,7 +5637,7 @@ async function resolveDriveCommentEventCore(params) {
|
|
|
5587
5637
|
return null;
|
|
5588
5638
|
}
|
|
5589
5639
|
const context = await fetchDriveCommentContext({
|
|
5590
|
-
client: createClient ? createClient(account ?? { accountId }) : createFeishuClient((await import("./accounts-
|
|
5640
|
+
client: createClient ? createClient(account ?? { accountId }) : createFeishuClient((await import("./accounts-CCCdMen2.js").then((n) => n.t)).resolveFeishuAccount({
|
|
5591
5641
|
cfg,
|
|
5592
5642
|
accountId
|
|
5593
5643
|
})),
|
|
@@ -5617,32 +5667,32 @@ async function resolveDriveCommentEventCore(params) {
|
|
|
5617
5667
|
};
|
|
5618
5668
|
}
|
|
5619
5669
|
function parseFeishuDriveCommentNoticeEventPayload(value) {
|
|
5620
|
-
if (!isRecord
|
|
5670
|
+
if (!isRecord(value) || !isRecord(value.notice_meta)) return null;
|
|
5621
5671
|
const noticeMeta = value.notice_meta;
|
|
5622
|
-
const fromUserId = isRecord
|
|
5623
|
-
const toUserId = isRecord
|
|
5672
|
+
const fromUserId = isRecord(noticeMeta.from_user_id) ? noticeMeta.from_user_id : void 0;
|
|
5673
|
+
const toUserId = isRecord(noticeMeta.to_user_id) ? noticeMeta.to_user_id : void 0;
|
|
5624
5674
|
return {
|
|
5625
|
-
comment_id:
|
|
5626
|
-
event_id:
|
|
5675
|
+
comment_id: readStringValue(value.comment_id),
|
|
5676
|
+
event_id: readStringValue(value.event_id),
|
|
5627
5677
|
is_mentioned: asBoolean(value.is_mentioned),
|
|
5628
5678
|
notice_meta: {
|
|
5629
|
-
file_token:
|
|
5630
|
-
file_type:
|
|
5679
|
+
file_token: readStringValue(noticeMeta.file_token),
|
|
5680
|
+
file_type: readStringValue(noticeMeta.file_type),
|
|
5631
5681
|
from_user_id: fromUserId ? {
|
|
5632
|
-
open_id:
|
|
5633
|
-
user_id:
|
|
5634
|
-
union_id:
|
|
5682
|
+
open_id: readStringValue(fromUserId.open_id),
|
|
5683
|
+
user_id: readStringValue(fromUserId.user_id),
|
|
5684
|
+
union_id: readStringValue(fromUserId.union_id)
|
|
5635
5685
|
} : void 0,
|
|
5636
|
-
notice_type:
|
|
5686
|
+
notice_type: readStringValue(noticeMeta.notice_type),
|
|
5637
5687
|
to_user_id: toUserId ? {
|
|
5638
|
-
open_id:
|
|
5639
|
-
user_id:
|
|
5640
|
-
union_id:
|
|
5688
|
+
open_id: readStringValue(toUserId.open_id),
|
|
5689
|
+
user_id: readStringValue(toUserId.user_id),
|
|
5690
|
+
union_id: readStringValue(toUserId.union_id)
|
|
5641
5691
|
} : void 0
|
|
5642
5692
|
},
|
|
5643
|
-
reply_id:
|
|
5644
|
-
timestamp:
|
|
5645
|
-
type:
|
|
5693
|
+
reply_id: readStringValue(value.reply_id),
|
|
5694
|
+
timestamp: readStringValue(value.timestamp),
|
|
5695
|
+
type: readStringValue(value.type)
|
|
5646
5696
|
};
|
|
5647
5697
|
}
|
|
5648
5698
|
async function resolveDriveCommentEventTurn(params) {
|
|
@@ -5742,7 +5792,7 @@ async function handleFeishuCommentEvent(params) {
|
|
|
5742
5792
|
channel: "feishu",
|
|
5743
5793
|
accountId: account.accountId
|
|
5744
5794
|
});
|
|
5745
|
-
const resolveCommentAuthorization = async (candidateCfg, mayPair) => {
|
|
5795
|
+
const resolveCommentAuthorization = async (candidateCfg, mayPair, contextBinding) => {
|
|
5746
5796
|
const candidateAccount = resolveFeishuRuntimeAccount({
|
|
5747
5797
|
cfg: candidateCfg,
|
|
5748
5798
|
accountId: account.accountId
|
|
@@ -5761,7 +5811,8 @@ async function handleFeishuCommentEvent(params) {
|
|
|
5761
5811
|
senderOpenId: turn.senderId,
|
|
5762
5812
|
senderUserId: turn.senderUserId,
|
|
5763
5813
|
conversationId: turn.senderId,
|
|
5764
|
-
mayPair
|
|
5814
|
+
mayPair,
|
|
5815
|
+
...contextBinding ? { contextBinding } : {}
|
|
5765
5816
|
})
|
|
5766
5817
|
};
|
|
5767
5818
|
};
|
|
@@ -5853,7 +5904,14 @@ async function handleFeishuCommentEvent(params) {
|
|
|
5853
5904
|
const bodyForAgent = `[message_id: ${turn.messageId}]\n${turn.prompt}`;
|
|
5854
5905
|
const rawBody = turn.targetReplyText ?? turn.rootCommentText ?? turn.prompt;
|
|
5855
5906
|
const conversationLabel = turn.documentTitle ? `Feishu comment · ${turn.documentTitle}` : "Feishu comment";
|
|
5856
|
-
const
|
|
5907
|
+
const boundAuthorization = await resolveCommentAuthorization(effectiveCfg, false, {
|
|
5908
|
+
agentId: route.agentId,
|
|
5909
|
+
sessionKey: commentSessionKey,
|
|
5910
|
+
messageId: turn.messageId,
|
|
5911
|
+
inboundEventKind: "user_request"
|
|
5912
|
+
});
|
|
5913
|
+
const ctxPayload = core.channel.inbound.buildContext({
|
|
5914
|
+
channelIngress: boundAuthorization.ingress,
|
|
5857
5915
|
channel: "feishu",
|
|
5858
5916
|
accountId: route.accountId,
|
|
5859
5917
|
surface: "feishu-comment",
|
|
@@ -6347,7 +6405,7 @@ const FEISHU_WS_LOG_ERROR_MAX_LENGTH = 500;
|
|
|
6347
6405
|
const FEISHU_WS_RECONNECT_EXHAUSTED_RE = /^WebSocket reconnect exhausted after \d+ attempts?/;
|
|
6348
6406
|
const FEISHU_WS_AUTORECONNECT_DISABLED_ERROR = "WebSocket connect failed and autoReconnect is disabled";
|
|
6349
6407
|
function isFeishuWebhookPayload(value) {
|
|
6350
|
-
return
|
|
6408
|
+
return isRecord(value);
|
|
6351
6409
|
}
|
|
6352
6410
|
const BLOCKED_FEISHU_WEBHOOK_PAYLOAD_KEYS = /* @__PURE__ */ new Set([
|
|
6353
6411
|
"__proto__",
|
|
@@ -6452,16 +6510,15 @@ async function monitorWebSocket({ account, accountId, runtime, abortSignal, even
|
|
|
6452
6510
|
};
|
|
6453
6511
|
const publishWsConnected = () => {
|
|
6454
6512
|
const connectedAt = Date.now();
|
|
6455
|
-
statusSink?.({
|
|
6456
|
-
connected: true,
|
|
6513
|
+
statusSink?.(channelReadyPatch({
|
|
6457
6514
|
lastConnectedAt: connectedAt,
|
|
6458
|
-
lastEventAt: connectedAt
|
|
6459
|
-
|
|
6460
|
-
});
|
|
6515
|
+
lastEventAt: connectedAt
|
|
6516
|
+
}));
|
|
6461
6517
|
};
|
|
6462
6518
|
const publishWsReconnecting = () => {
|
|
6463
6519
|
statusSink?.({
|
|
6464
6520
|
connected: false,
|
|
6521
|
+
lifecycle: "recovering",
|
|
6465
6522
|
lastEventAt: Date.now()
|
|
6466
6523
|
});
|
|
6467
6524
|
};
|
|
@@ -6509,10 +6566,11 @@ async function monitorWebSocket({ account, accountId, runtime, abortSignal, even
|
|
|
6509
6566
|
});
|
|
6510
6567
|
setSocketTerminator?.(void 0);
|
|
6511
6568
|
if (abortSignal?.aborted) break;
|
|
6512
|
-
|
|
6569
|
+
const disconnectedAt = Date.now();
|
|
6570
|
+
statusSink?.(channelBlockedPatch(formatFeishuWsErrorForLog(cycleEnd), {
|
|
6513
6571
|
connected: false,
|
|
6514
|
-
lastEventAt:
|
|
6515
|
-
});
|
|
6572
|
+
lastEventAt: disconnectedAt
|
|
6573
|
+
}));
|
|
6516
6574
|
attempt += 1;
|
|
6517
6575
|
const delayMs = getFeishuWsReconnectDelayMs(attempt);
|
|
6518
6576
|
error(`feishu[${accountId}]: WebSocket connection ended, recreating client in ${delayMs}ms: ${formatFeishuWsErrorForLog(cycleEnd)}`);
|
|
@@ -6526,9 +6584,14 @@ async function monitorWebSocket({ account, accountId, runtime, abortSignal, even
|
|
|
6526
6584
|
});
|
|
6527
6585
|
setSocketTerminator?.(void 0);
|
|
6528
6586
|
if (abortSignal?.aborted) break;
|
|
6587
|
+
const failedAt = Date.now();
|
|
6588
|
+
const terminal = err instanceof Error && isFeishuWsTerminalError(err);
|
|
6529
6589
|
statusSink?.({
|
|
6530
6590
|
connected: false,
|
|
6531
|
-
|
|
6591
|
+
lifecycle: terminal ? "blocked" : "recovering",
|
|
6592
|
+
...terminal ? { terminalDisconnect: true } : {},
|
|
6593
|
+
lastEventAt: failedAt,
|
|
6594
|
+
lastError: formatFeishuWsErrorForLog(err)
|
|
6532
6595
|
});
|
|
6533
6596
|
attempt += 1;
|
|
6534
6597
|
const delayMs = getFeishuWsReconnectDelayMs(attempt);
|
|
@@ -6551,10 +6614,18 @@ async function monitorWebhook({ account, accountId, runtime, abortSignal, eventD
|
|
|
6551
6614
|
if (!encryptKey) throw new Error(`Feishu account "${accountId}" webhook mode requires encryptKey`);
|
|
6552
6615
|
const port = account.config.webhookPort ?? 3e3;
|
|
6553
6616
|
const path = account.config.webhookPath ?? "/feishu/events";
|
|
6617
|
+
if (normalizeFeishuWebhookPath(path) !== path) throw new Error(`Feishu account "${accountId}" webhookPath must be a canonical HTTP request path; run "openclaw doctor --fix" to repair it`);
|
|
6554
6618
|
const host = account.config.webhookHost ?? "127.0.0.1";
|
|
6555
6619
|
log(`feishu[${accountId}]: starting Webhook server on ${host}:${port}, path ${path}...`);
|
|
6556
6620
|
const server = http.createServer();
|
|
6557
6621
|
server.on("request", (req, res) => {
|
|
6622
|
+
const requestUrl = req.url ?? "/";
|
|
6623
|
+
const requestPath = requestUrl.split("?", 1)[0];
|
|
6624
|
+
const requestRoute = path.includes("?") ? requestUrl : requestPath;
|
|
6625
|
+
if (!requestPath?.startsWith("/") || requestUrl.includes("#") || requestRoute !== path) {
|
|
6626
|
+
respondText(res, 404, "Not Found");
|
|
6627
|
+
return;
|
|
6628
|
+
}
|
|
6558
6629
|
res.on("finish", () => {
|
|
6559
6630
|
recordWebhookStatus(runtime, accountId, path, res.statusCode);
|
|
6560
6631
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
@@ -6568,6 +6639,7 @@ async function monitorWebhook({ account, accountId, runtime, abortSignal, eventD
|
|
|
6568
6639
|
if (!applyBasicWebhookRequestGuards({
|
|
6569
6640
|
req,
|
|
6570
6641
|
res,
|
|
6642
|
+
allowMethods: ["POST"],
|
|
6571
6643
|
rateLimiter: feishuWebhookRateLimiter,
|
|
6572
6644
|
rateLimitKey: buildFeishuWebhookRateLimitKey({
|
|
6573
6645
|
accountId,
|
|
@@ -6654,15 +6726,18 @@ async function monitorWebhook({ account, accountId, runtime, abortSignal, eventD
|
|
|
6654
6726
|
server.listen(port, host, () => {
|
|
6655
6727
|
log(`feishu[${accountId}]: Webhook server listening on ${host}:${port}`);
|
|
6656
6728
|
const webhookConnectedAt = Date.now();
|
|
6657
|
-
statusSink?.({
|
|
6658
|
-
connected: true,
|
|
6729
|
+
statusSink?.(channelReadyPatch({
|
|
6659
6730
|
lastConnectedAt: webhookConnectedAt,
|
|
6660
|
-
lastEventAt: webhookConnectedAt
|
|
6661
|
-
|
|
6662
|
-
});
|
|
6731
|
+
lastEventAt: webhookConnectedAt
|
|
6732
|
+
}));
|
|
6663
6733
|
});
|
|
6664
6734
|
server.on("error", (err) => {
|
|
6665
6735
|
error(`feishu[${accountId}]: Webhook server error: ${err}`);
|
|
6736
|
+
statusSink?.({
|
|
6737
|
+
connected: false,
|
|
6738
|
+
lifecycle: "recovering",
|
|
6739
|
+
lastError: formatFeishuWsErrorForLog(err)
|
|
6740
|
+
});
|
|
6666
6741
|
abortSignal?.removeEventListener("abort", handleAbort);
|
|
6667
6742
|
reject(err);
|
|
6668
6743
|
});
|
|
@@ -6799,10 +6874,11 @@ async function resolveReactionSyntheticEvent(params) {
|
|
|
6799
6874
|
const { cfg, accountId, event, botOpenId, fetchMessage = getMessageFeishu, verificationTimeoutMs = FEISHU_REACTION_VERIFY_TIMEOUT_MS, logger, uuid = () => crypto$1.randomUUID(), action = "created" } = params;
|
|
6800
6875
|
const emoji = event.reaction_type?.emoji_type;
|
|
6801
6876
|
const messageId = event.message_id;
|
|
6802
|
-
const
|
|
6803
|
-
const senderUserId = event.user_id?.user_id;
|
|
6877
|
+
const senderOpenId = event.user_id?.open_id?.trim();
|
|
6878
|
+
const senderUserId = event.user_id?.user_id?.trim();
|
|
6879
|
+
const senderId = senderOpenId || senderUserId;
|
|
6804
6880
|
if (!emoji || !messageId || !senderId) return null;
|
|
6805
|
-
const { resolveFeishuAccount } = await import("./accounts-
|
|
6881
|
+
const { resolveFeishuAccount } = await import("./accounts-CCCdMen2.js").then((n) => n.t);
|
|
6806
6882
|
const reactionNotifications = resolveFeishuAccount({
|
|
6807
6883
|
cfg,
|
|
6808
6884
|
accountId
|
|
@@ -6836,14 +6912,17 @@ async function resolveReactionSyntheticEvent(params) {
|
|
|
6836
6912
|
return {
|
|
6837
6913
|
sender: {
|
|
6838
6914
|
sender_id: {
|
|
6839
|
-
open_id:
|
|
6915
|
+
...senderOpenId ? { open_id: senderOpenId } : {},
|
|
6840
6916
|
...senderUserId ? { user_id: senderUserId } : {}
|
|
6841
6917
|
},
|
|
6842
6918
|
sender_type: "user"
|
|
6843
6919
|
},
|
|
6844
6920
|
message: {
|
|
6845
6921
|
message_id: `${messageId}:reaction:${emoji}:${uuid()}`,
|
|
6922
|
+
reply_target_message_id: messageId,
|
|
6846
6923
|
typing_target_message_id: messageId,
|
|
6924
|
+
...reactedMsg.rootId ? { root_id: reactedMsg.rootId } : {},
|
|
6925
|
+
...reactedMsg.threadId ? { thread_id: reactedMsg.threadId } : {},
|
|
6847
6926
|
chat_id: syntheticChatId,
|
|
6848
6927
|
chat_type: syntheticChatType,
|
|
6849
6928
|
message_type: "text",
|
|
@@ -6855,41 +6934,41 @@ function normalizeFeishuChatType(value) {
|
|
|
6855
6934
|
return value === "group" || value === "topic_group" || value === "private" || value === "p2p" ? value : void 0;
|
|
6856
6935
|
}
|
|
6857
6936
|
function parseFeishuBotAddedEventPayload(value) {
|
|
6858
|
-
if (!isRecord
|
|
6937
|
+
if (!isRecord(value) || !readStringValue(value.chat_id) || !isRecord(value.operator_id)) return null;
|
|
6859
6938
|
return value;
|
|
6860
6939
|
}
|
|
6861
6940
|
function parseFeishuBotRemovedChatId(value) {
|
|
6862
|
-
if (!isRecord
|
|
6863
|
-
return
|
|
6941
|
+
if (!isRecord(value)) return null;
|
|
6942
|
+
return readStringValue(value.chat_id) ?? null;
|
|
6864
6943
|
}
|
|
6865
6944
|
function firstString(...values) {
|
|
6866
6945
|
for (const value of values) {
|
|
6867
|
-
const trimmed =
|
|
6946
|
+
const trimmed = readStringValue(value)?.trim();
|
|
6868
6947
|
if (trimmed) return trimmed;
|
|
6869
6948
|
}
|
|
6870
6949
|
}
|
|
6871
6950
|
function readFeishuIdentityField(value, field) {
|
|
6872
|
-
if (!isRecord
|
|
6951
|
+
if (!isRecord(value)) return;
|
|
6873
6952
|
return firstString(value[field]);
|
|
6874
6953
|
}
|
|
6875
6954
|
function parseFeishuCardActionEventPayload(value) {
|
|
6876
|
-
if (!isRecord
|
|
6877
|
-
const operator = isRecord
|
|
6955
|
+
if (!isRecord(value)) return null;
|
|
6956
|
+
const operator = isRecord(value.operator) ? value.operator : {};
|
|
6878
6957
|
const action = value.action;
|
|
6879
|
-
const context = isRecord
|
|
6880
|
-
if (!isRecord
|
|
6958
|
+
const context = isRecord(value.context) ? value.context : {};
|
|
6959
|
+
if (!isRecord(action)) return null;
|
|
6881
6960
|
const operatorUserId = operator.user_id;
|
|
6882
|
-
const token =
|
|
6961
|
+
const token = readStringValue(value.token);
|
|
6883
6962
|
const openId = firstString(operator.open_id, readFeishuIdentityField(operatorUserId, "open_id"), value.open_id, context.open_id);
|
|
6884
6963
|
const userId = firstString(operator.user_id, readFeishuIdentityField(operatorUserId, "user_id"), value.user_id, context.user_id);
|
|
6885
6964
|
const unionId = firstString(operator.union_id, readFeishuIdentityField(operatorUserId, "union_id"));
|
|
6886
|
-
const tag =
|
|
6965
|
+
const tag = readStringValue(action.tag);
|
|
6887
6966
|
const actionValue = action.value;
|
|
6888
6967
|
const openMessageId = firstString(context.open_message_id, value.open_message_id);
|
|
6889
6968
|
const contextOpenId = firstString(context.open_id, openId);
|
|
6890
6969
|
const contextUserId = firstString(context.user_id, userId);
|
|
6891
6970
|
const chatId = firstString(context.chat_id, context.open_chat_id);
|
|
6892
|
-
if (!token || !openId || !tag || !isRecord
|
|
6971
|
+
if (!token || !openId || !tag || !isRecord(actionValue)) return null;
|
|
6893
6972
|
return {
|
|
6894
6973
|
operator: {
|
|
6895
6974
|
open_id: openId,
|