@openclaw/feishu 2026.5.2 → 2026.5.3-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-Ba3-WP1z.js +423 -0
- package/dist/api.js +2280 -0
- package/dist/app-registration-B8qc1MCM.js +184 -0
- package/dist/audio-preflight.runtime-BPlzkO3l.js +7 -0
- package/dist/card-interaction-BfRLgvw_.js +96 -0
- package/dist/channel-CSD_Jt8I.js +1668 -0
- package/dist/channel-entry.js +22 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-DYsXcD36.js +700 -0
- package/dist/client-DBVoQL5w.js +157 -0
- package/dist/contract-api.js +9 -0
- package/dist/conversation-id-DWS3Ep2A.js +139 -0
- package/dist/directory.static-f3EeoRJd.js +44 -0
- package/dist/drive-C5eJLJr7.js +883 -0
- package/dist/index.js +68 -0
- package/dist/monitor-CT189QfR.js +60 -0
- package/dist/monitor.account-dJV2jO8C.js +4990 -0
- package/dist/monitor.state-DYM02ipp.js +100 -0
- package/dist/policy-D6c-wMPl.js +118 -0
- package/dist/probe-BNzzU_uR.js +149 -0
- package/dist/rolldown-runtime-DUslC3ob.js +14 -0
- package/dist/runtime-CG0DuRCy.js +8 -0
- package/dist/runtime-api.js +14 -0
- package/dist/secret-contract-Dm4Z_zQN.js +119 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/security-audit-DqJdocrN.js +11 -0
- package/dist/security-audit-shared-ByuMx9cJ.js +38 -0
- package/dist/security-contract-api.js +2 -0
- package/dist/send-DowxxbpH.js +1218 -0
- package/dist/session-conversation-B4nrW-vo.js +27 -0
- package/dist/session-key-api.js +2 -0
- package/dist/setup-api.js +2 -0
- package/dist/setup-entry.js +15 -0
- package/dist/subagent-hooks-C3UhPVLV.js +227 -0
- package/dist/subagent-hooks-api.js +23 -0
- package/dist/targets-JMFJRKSe.js +48 -0
- package/dist/thread-bindings-BmS6TLes.js +222 -0
- package/package.json +15 -6
- package/api.ts +0 -31
- package/channel-entry.ts +0 -20
- package/channel-plugin-api.ts +0 -1
- package/contract-api.ts +0 -16
- package/index.ts +0 -82
- package/runtime-api.ts +0 -55
- package/secret-contract-api.ts +0 -5
- package/security-contract-api.ts +0 -1
- package/session-key-api.ts +0 -1
- package/setup-api.ts +0 -3
- package/setup-entry.test.ts +0 -14
- package/setup-entry.ts +0 -13
- package/src/accounts.test.ts +0 -459
- package/src/accounts.ts +0 -326
- package/src/app-registration.ts +0 -331
- package/src/approval-auth.test.ts +0 -24
- package/src/approval-auth.ts +0 -25
- package/src/async.test.ts +0 -35
- package/src/async.ts +0 -104
- package/src/audio-preflight.runtime.ts +0 -9
- package/src/bitable.test.ts +0 -131
- package/src/bitable.ts +0 -762
- package/src/bot-content.ts +0 -474
- package/src/bot-group-name.test.ts +0 -108
- package/src/bot-runtime-api.ts +0 -12
- package/src/bot-sender-name.ts +0 -125
- package/src/bot.broadcast.test.ts +0 -463
- package/src/bot.card-action.test.ts +0 -577
- package/src/bot.checkBotMentioned.test.ts +0 -265
- package/src/bot.helpers.test.ts +0 -118
- package/src/bot.stripBotMention.test.ts +0 -126
- package/src/bot.test.ts +0 -3040
- package/src/bot.ts +0 -1559
- package/src/card-action.ts +0 -447
- package/src/card-interaction.test.ts +0 -129
- package/src/card-interaction.ts +0 -159
- package/src/card-test-helpers.ts +0 -47
- package/src/card-ux-approval.ts +0 -65
- package/src/card-ux-launcher.test.ts +0 -99
- package/src/card-ux-launcher.ts +0 -121
- package/src/card-ux-shared.ts +0 -33
- package/src/channel-runtime-api.ts +0 -16
- package/src/channel.runtime.ts +0 -47
- package/src/channel.test.ts +0 -959
- package/src/channel.ts +0 -1313
- package/src/chat-schema.ts +0 -25
- package/src/chat.test.ts +0 -196
- package/src/chat.ts +0 -188
- package/src/client.test.ts +0 -433
- package/src/client.ts +0 -290
- package/src/comment-dispatcher-runtime-api.ts +0 -6
- package/src/comment-dispatcher.test.ts +0 -169
- package/src/comment-dispatcher.ts +0 -107
- package/src/comment-handler-runtime-api.ts +0 -3
- package/src/comment-handler.test.ts +0 -486
- package/src/comment-handler.ts +0 -309
- package/src/comment-reaction.test.ts +0 -166
- package/src/comment-reaction.ts +0 -259
- package/src/comment-shared.test.ts +0 -182
- package/src/comment-shared.ts +0 -406
- package/src/comment-target.ts +0 -44
- package/src/config-schema.test.ts +0 -309
- package/src/config-schema.ts +0 -333
- package/src/conversation-id.test.ts +0 -18
- package/src/conversation-id.ts +0 -199
- package/src/dedup-runtime-api.ts +0 -1
- package/src/dedup.ts +0 -141
- package/src/directory.static.ts +0 -61
- package/src/directory.test.ts +0 -136
- package/src/directory.ts +0 -124
- package/src/doc-schema.ts +0 -182
- package/src/docx-batch-insert.test.ts +0 -91
- package/src/docx-batch-insert.ts +0 -223
- package/src/docx-color-text.ts +0 -154
- package/src/docx-table-ops.test.ts +0 -53
- package/src/docx-table-ops.ts +0 -316
- package/src/docx-types.ts +0 -38
- package/src/docx.account-selection.test.ts +0 -79
- package/src/docx.test.ts +0 -685
- package/src/docx.ts +0 -1616
- package/src/drive-schema.ts +0 -92
- package/src/drive.test.ts +0 -1219
- package/src/drive.ts +0 -829
- package/src/dynamic-agent.ts +0 -137
- package/src/event-types.ts +0 -45
- package/src/external-keys.test.ts +0 -20
- package/src/external-keys.ts +0 -19
- package/src/lifecycle.test-support.ts +0 -220
- package/src/media.test.ts +0 -900
- package/src/media.ts +0 -861
- package/src/mention-target.types.ts +0 -5
- package/src/mention.ts +0 -114
- package/src/message-action-contract.ts +0 -13
- package/src/monitor-state-runtime-api.ts +0 -7
- package/src/monitor-transport-runtime-api.ts +0 -7
- package/src/monitor.account.ts +0 -468
- package/src/monitor.acp-init-failure.lifecycle.test-support.ts +0 -219
- package/src/monitor.bot-identity.ts +0 -86
- package/src/monitor.bot-menu-handler.ts +0 -165
- package/src/monitor.bot-menu.lifecycle.test-support.ts +0 -224
- package/src/monitor.bot-menu.test.ts +0 -178
- package/src/monitor.broadcast.reply-once.lifecycle.test-support.ts +0 -264
- package/src/monitor.card-action.lifecycle.test-support.ts +0 -373
- package/src/monitor.cleanup.test.ts +0 -376
- package/src/monitor.comment-notice-handler.ts +0 -105
- package/src/monitor.comment.test.ts +0 -937
- package/src/monitor.comment.ts +0 -1386
- package/src/monitor.lifecycle.test.ts +0 -4
- package/src/monitor.message-handler.ts +0 -339
- package/src/monitor.reaction.lifecycle.test-support.ts +0 -68
- package/src/monitor.reaction.test.ts +0 -713
- package/src/monitor.startup.test.ts +0 -192
- package/src/monitor.startup.ts +0 -74
- package/src/monitor.state.defaults.test.ts +0 -46
- package/src/monitor.state.ts +0 -170
- package/src/monitor.synthetic-error.ts +0 -18
- package/src/monitor.test-mocks.ts +0 -45
- package/src/monitor.transport.ts +0 -424
- package/src/monitor.ts +0 -100
- package/src/monitor.webhook-e2e.test.ts +0 -272
- package/src/monitor.webhook-security.test.ts +0 -264
- package/src/monitor.webhook.test-helpers.ts +0 -116
- package/src/outbound-runtime-api.ts +0 -1
- package/src/outbound.test.ts +0 -935
- package/src/outbound.ts +0 -718
- package/src/perm-schema.ts +0 -52
- package/src/perm.ts +0 -170
- package/src/pins.ts +0 -108
- package/src/policy.test.ts +0 -334
- package/src/policy.ts +0 -236
- package/src/post.test.ts +0 -105
- package/src/post.ts +0 -275
- package/src/probe.test.ts +0 -275
- package/src/probe.ts +0 -166
- package/src/processing-claims.ts +0 -59
- package/src/qr-terminal.ts +0 -1
- package/src/reactions.ts +0 -123
- package/src/reasoning-preview.test.ts +0 -59
- package/src/reasoning-preview.ts +0 -20
- package/src/reply-dispatcher-runtime-api.ts +0 -7
- package/src/reply-dispatcher.test.ts +0 -1144
- package/src/reply-dispatcher.ts +0 -650
- package/src/runtime.ts +0 -9
- package/src/secret-contract.ts +0 -145
- package/src/secret-input.ts +0 -1
- package/src/security-audit-shared.ts +0 -69
- package/src/security-audit.test.ts +0 -61
- package/src/security-audit.ts +0 -1
- package/src/send-result.ts +0 -29
- package/src/send-target.test.ts +0 -80
- package/src/send-target.ts +0 -35
- package/src/send.reply-fallback.test.ts +0 -292
- package/src/send.test.ts +0 -550
- package/src/send.ts +0 -800
- package/src/sequential-key.test.ts +0 -72
- package/src/sequential-key.ts +0 -28
- package/src/sequential-queue.test.ts +0 -92
- package/src/sequential-queue.ts +0 -16
- package/src/session-conversation.ts +0 -42
- package/src/session-route.ts +0 -48
- package/src/setup-core.ts +0 -51
- package/src/setup-surface.test.ts +0 -174
- package/src/setup-surface.ts +0 -581
- package/src/streaming-card.test.ts +0 -190
- package/src/streaming-card.ts +0 -490
- package/src/subagent-hooks.test.ts +0 -603
- package/src/subagent-hooks.ts +0 -397
- package/src/targets.ts +0 -97
- package/src/test-support/lifecycle-test-support.ts +0 -453
- package/src/thread-bindings.test.ts +0 -143
- package/src/thread-bindings.ts +0 -330
- package/src/tool-account-routing.test.ts +0 -187
- package/src/tool-account.test.ts +0 -44
- package/src/tool-account.ts +0 -93
- package/src/tool-factory-test-harness.ts +0 -79
- package/src/tool-result.test.ts +0 -32
- package/src/tool-result.ts +0 -16
- package/src/tools-config.test.ts +0 -21
- package/src/tools-config.ts +0 -22
- package/src/types.ts +0 -104
- package/src/typing.test.ts +0 -144
- package/src/typing.ts +0 -214
- package/src/wiki-schema.ts +0 -55
- package/src/wiki.ts +0 -227
- package/subagent-hooks-api.ts +0 -31
- package/tsconfig.json +0 -16
package/src/channel.ts
DELETED
|
@@ -1,1313 +0,0 @@
|
|
|
1
|
-
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
2
|
-
import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
|
|
3
|
-
import {
|
|
4
|
-
adaptScopedAccountAccessor,
|
|
5
|
-
createHybridChannelConfigAdapter,
|
|
6
|
-
} from "openclaw/plugin-sdk/channel-config-helpers";
|
|
7
|
-
import type {
|
|
8
|
-
ChannelMessageActionAdapter,
|
|
9
|
-
ChannelMessageToolDiscovery,
|
|
10
|
-
} from "openclaw/plugin-sdk/channel-contract";
|
|
11
|
-
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
|
12
|
-
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
|
|
13
|
-
import {
|
|
14
|
-
createAllowlistProviderGroupPolicyWarningCollector,
|
|
15
|
-
projectConfigAccountIdWarningCollector,
|
|
16
|
-
} from "openclaw/plugin-sdk/channel-policy";
|
|
17
|
-
import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-runtime";
|
|
18
|
-
import {
|
|
19
|
-
createChannelDirectoryAdapter,
|
|
20
|
-
createRuntimeDirectoryLiveAdapter,
|
|
21
|
-
} from "openclaw/plugin-sdk/directory-runtime";
|
|
22
|
-
import {
|
|
23
|
-
normalizeMessagePresentation,
|
|
24
|
-
renderMessagePresentationFallbackText,
|
|
25
|
-
} from "openclaw/plugin-sdk/interactive-runtime";
|
|
26
|
-
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
|
|
27
|
-
import { createRuntimeOutboundDelegates } from "openclaw/plugin-sdk/outbound-runtime";
|
|
28
|
-
import { createComputedAccountStatusAdapter } from "openclaw/plugin-sdk/status-helpers";
|
|
29
|
-
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
30
|
-
import {
|
|
31
|
-
inspectFeishuCredentials,
|
|
32
|
-
listEnabledFeishuAccounts,
|
|
33
|
-
listFeishuAccountIds,
|
|
34
|
-
resolveDefaultFeishuAccountId,
|
|
35
|
-
resolveFeishuAccount,
|
|
36
|
-
resolveFeishuRuntimeAccount,
|
|
37
|
-
} from "./accounts.js";
|
|
38
|
-
import { feishuApprovalAuth } from "./approval-auth.js";
|
|
39
|
-
import { FEISHU_CARD_INTERACTION_VERSION } from "./card-interaction.js";
|
|
40
|
-
import type {
|
|
41
|
-
ChannelMessageActionName,
|
|
42
|
-
ChannelMeta,
|
|
43
|
-
ChannelPlugin,
|
|
44
|
-
ClawdbotConfig,
|
|
45
|
-
} from "./channel-runtime-api.js";
|
|
46
|
-
import {
|
|
47
|
-
buildChannelConfigSchema,
|
|
48
|
-
buildProbeChannelStatusSummary,
|
|
49
|
-
chunkTextForOutbound,
|
|
50
|
-
createActionGate,
|
|
51
|
-
createDefaultChannelRuntimeState,
|
|
52
|
-
DEFAULT_ACCOUNT_ID,
|
|
53
|
-
PAIRING_APPROVED_MESSAGE,
|
|
54
|
-
} from "./channel-runtime-api.js";
|
|
55
|
-
import { isRecord } from "./comment-shared.js";
|
|
56
|
-
import { FeishuConfigSchema } from "./config-schema.js";
|
|
57
|
-
import {
|
|
58
|
-
buildFeishuConversationId,
|
|
59
|
-
buildFeishuModelOverrideParentCandidates,
|
|
60
|
-
parseFeishuConversationId,
|
|
61
|
-
parseFeishuDirectConversationId,
|
|
62
|
-
parseFeishuTargetId,
|
|
63
|
-
} from "./conversation-id.js";
|
|
64
|
-
import { listFeishuDirectoryGroups, listFeishuDirectoryPeers } from "./directory.static.js";
|
|
65
|
-
import { messageActionTargetAliases } from "./message-action-contract.js";
|
|
66
|
-
import { resolveFeishuGroupToolPolicy } from "./policy.js";
|
|
67
|
-
import { collectRuntimeConfigAssignments, secretTargetRegistryEntries } from "./secret-contract.js";
|
|
68
|
-
import { collectFeishuSecurityAuditFindings } from "./security-audit.js";
|
|
69
|
-
import { resolveFeishuSessionConversation } from "./session-conversation.js";
|
|
70
|
-
import { resolveFeishuOutboundSessionRoute } from "./session-route.js";
|
|
71
|
-
import { feishuSetupAdapter } from "./setup-core.js";
|
|
72
|
-
import { feishuSetupWizard, runFeishuLogin } from "./setup-surface.js";
|
|
73
|
-
import { looksLikeFeishuId, normalizeFeishuTarget } from "./targets.js";
|
|
74
|
-
import type { FeishuConfig, FeishuProbeResult, ResolvedFeishuAccount } from "./types.js";
|
|
75
|
-
|
|
76
|
-
function readFeishuMediaParam(params: Record<string, unknown>): string | undefined {
|
|
77
|
-
const media = params.media;
|
|
78
|
-
if (typeof media !== "string") {
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
return media.trim() ? media : undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function readBooleanParam(params: Record<string, unknown>, keys: string[]): boolean | undefined {
|
|
85
|
-
for (const key of keys) {
|
|
86
|
-
const value = params[key];
|
|
87
|
-
if (typeof value === "boolean") {
|
|
88
|
-
return value;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function hasLegacyFeishuCardCommandValue(actionValue: unknown): boolean {
|
|
95
|
-
return (
|
|
96
|
-
isRecord(actionValue) &&
|
|
97
|
-
actionValue.oc !== FEISHU_CARD_INTERACTION_VERSION &&
|
|
98
|
-
(Boolean(typeof actionValue.command === "string" && actionValue.command.trim()) ||
|
|
99
|
-
Boolean(typeof actionValue.text === "string" && actionValue.text.trim()))
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function containsLegacyFeishuCardCommandValue(node: unknown): boolean {
|
|
104
|
-
if (Array.isArray(node)) {
|
|
105
|
-
return node.some((item) => containsLegacyFeishuCardCommandValue(item));
|
|
106
|
-
}
|
|
107
|
-
if (!isRecord(node)) {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (node.tag === "button" && hasLegacyFeishuCardCommandValue(node.value)) {
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return Object.values(node).some((value) => containsLegacyFeishuCardCommandValue(value));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const meta: ChannelMeta = {
|
|
119
|
-
id: "feishu",
|
|
120
|
-
label: "Feishu",
|
|
121
|
-
selectionLabel: "Feishu/Lark (飞书)",
|
|
122
|
-
docsPath: "/channels/feishu",
|
|
123
|
-
docsLabel: "feishu",
|
|
124
|
-
blurb: "飞书/Lark enterprise messaging.",
|
|
125
|
-
aliases: ["lark"],
|
|
126
|
-
order: 70,
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const loadFeishuChannelRuntime = createLazyRuntimeNamedExport(
|
|
130
|
-
() => import("./channel.runtime.js"),
|
|
131
|
-
"feishuChannelRuntime",
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
function buildFeishuPresentationCard(params: {
|
|
135
|
-
presentation: NonNullable<ReturnType<typeof normalizeMessagePresentation>>;
|
|
136
|
-
fallbackText?: string;
|
|
137
|
-
}): Record<string, unknown> {
|
|
138
|
-
const fallbackPresentation: NonNullable<ReturnType<typeof normalizeMessagePresentation>> = {
|
|
139
|
-
...(params.presentation.tone ? { tone: params.presentation.tone } : {}),
|
|
140
|
-
blocks: params.presentation.blocks,
|
|
141
|
-
};
|
|
142
|
-
return {
|
|
143
|
-
schema: "2.0",
|
|
144
|
-
config: {
|
|
145
|
-
width_mode: "fill",
|
|
146
|
-
},
|
|
147
|
-
...(params.presentation.title
|
|
148
|
-
? {
|
|
149
|
-
header: {
|
|
150
|
-
title: { tag: "plain_text", content: params.presentation.title },
|
|
151
|
-
template: "blue",
|
|
152
|
-
},
|
|
153
|
-
}
|
|
154
|
-
: {}),
|
|
155
|
-
body: {
|
|
156
|
-
elements: [
|
|
157
|
-
{
|
|
158
|
-
tag: "markdown",
|
|
159
|
-
content: renderMessagePresentationFallbackText({
|
|
160
|
-
text: params.fallbackText,
|
|
161
|
-
presentation: fallbackPresentation,
|
|
162
|
-
}),
|
|
163
|
-
},
|
|
164
|
-
],
|
|
165
|
-
},
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async function createFeishuActionClient(account: ResolvedFeishuAccount) {
|
|
170
|
-
const { createFeishuClient } = await import("./client.js");
|
|
171
|
-
return createFeishuClient(account);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const collectFeishuSecurityWarnings = createAllowlistProviderGroupPolicyWarningCollector<{
|
|
175
|
-
cfg: ClawdbotConfig;
|
|
176
|
-
accountId?: string | null;
|
|
177
|
-
}>({
|
|
178
|
-
providerConfigPresent: (cfg) => cfg.channels?.feishu !== undefined,
|
|
179
|
-
resolveGroupPolicy: ({ cfg, accountId }) =>
|
|
180
|
-
resolveFeishuAccount({ cfg, accountId }).config?.groupPolicy,
|
|
181
|
-
collect: ({ cfg, accountId, groupPolicy }) => {
|
|
182
|
-
if (groupPolicy !== "open") {
|
|
183
|
-
return [];
|
|
184
|
-
}
|
|
185
|
-
const account = resolveFeishuAccount({ cfg, accountId });
|
|
186
|
-
return [
|
|
187
|
-
`- Feishu[${account.accountId}] groups: groupPolicy="open" allows any member to trigger (mention-gated). Set channels.feishu.groupPolicy="allowlist" + channels.feishu.groupAllowFrom to restrict senders.`,
|
|
188
|
-
];
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
function describeFeishuMessageTool({
|
|
193
|
-
cfg,
|
|
194
|
-
accountId,
|
|
195
|
-
}: Parameters<
|
|
196
|
-
NonNullable<ChannelMessageActionAdapter["describeMessageTool"]>
|
|
197
|
-
>[0]): ChannelMessageToolDiscovery {
|
|
198
|
-
const enabledAccounts = accountId
|
|
199
|
-
? [resolveFeishuAccount({ cfg, accountId })].filter(
|
|
200
|
-
(account) => account.enabled && account.configured,
|
|
201
|
-
)
|
|
202
|
-
: listEnabledFeishuAccounts(cfg);
|
|
203
|
-
const enabled =
|
|
204
|
-
enabledAccounts.length > 0 ||
|
|
205
|
-
(!accountId &&
|
|
206
|
-
cfg.channels?.feishu?.enabled !== false &&
|
|
207
|
-
Boolean(inspectFeishuCredentials(cfg.channels?.feishu as FeishuConfig | undefined)));
|
|
208
|
-
if (enabledAccounts.length === 0) {
|
|
209
|
-
return {
|
|
210
|
-
actions: [],
|
|
211
|
-
capabilities: enabled ? ["presentation"] : [],
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
const actions = new Set<ChannelMessageActionName>([
|
|
215
|
-
"send",
|
|
216
|
-
"read",
|
|
217
|
-
"edit",
|
|
218
|
-
"thread-reply",
|
|
219
|
-
"pin",
|
|
220
|
-
"list-pins",
|
|
221
|
-
"unpin",
|
|
222
|
-
"member-info",
|
|
223
|
-
"channel-info",
|
|
224
|
-
"channel-list",
|
|
225
|
-
]);
|
|
226
|
-
if (
|
|
227
|
-
accountId
|
|
228
|
-
? enabledAccounts.some((account) => isFeishuReactionsActionEnabled({ cfg, account }))
|
|
229
|
-
: areAnyFeishuReactionActionsEnabled(cfg)
|
|
230
|
-
) {
|
|
231
|
-
actions.add("react");
|
|
232
|
-
actions.add("reactions");
|
|
233
|
-
}
|
|
234
|
-
return {
|
|
235
|
-
actions: Array.from(actions),
|
|
236
|
-
capabilities: enabled ? ["presentation"] : [],
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function setFeishuNamedAccountEnabled(
|
|
241
|
-
cfg: ClawdbotConfig,
|
|
242
|
-
accountId: string,
|
|
243
|
-
enabled: boolean,
|
|
244
|
-
): ClawdbotConfig {
|
|
245
|
-
const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
|
|
246
|
-
return {
|
|
247
|
-
...cfg,
|
|
248
|
-
channels: {
|
|
249
|
-
...cfg.channels,
|
|
250
|
-
feishu: {
|
|
251
|
-
...feishuCfg,
|
|
252
|
-
accounts: {
|
|
253
|
-
...feishuCfg?.accounts,
|
|
254
|
-
[accountId]: {
|
|
255
|
-
...feishuCfg?.accounts?.[accountId],
|
|
256
|
-
enabled,
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
},
|
|
260
|
-
},
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const feishuConfigAdapter = createHybridChannelConfigAdapter<
|
|
265
|
-
ResolvedFeishuAccount,
|
|
266
|
-
ResolvedFeishuAccount
|
|
267
|
-
>({
|
|
268
|
-
sectionKey: "feishu",
|
|
269
|
-
listAccountIds: listFeishuAccountIds,
|
|
270
|
-
resolveAccount: adaptScopedAccountAccessor(resolveFeishuAccount),
|
|
271
|
-
defaultAccountId: resolveDefaultFeishuAccountId,
|
|
272
|
-
clearBaseFields: [],
|
|
273
|
-
resolveAllowFrom: (account) => account.config.allowFrom,
|
|
274
|
-
formatAllowFrom: (allowFrom) => formatAllowFromLowercase({ allowFrom }),
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
function isFeishuReactionsActionEnabled(params: {
|
|
278
|
-
cfg: ClawdbotConfig;
|
|
279
|
-
account: ResolvedFeishuAccount;
|
|
280
|
-
}): boolean {
|
|
281
|
-
if (!params.account.enabled || !params.account.configured) {
|
|
282
|
-
return false;
|
|
283
|
-
}
|
|
284
|
-
const gate = createActionGate(
|
|
285
|
-
(params.account.config.actions ??
|
|
286
|
-
(params.cfg.channels?.feishu as { actions?: unknown } | undefined)?.actions) as Record<
|
|
287
|
-
string,
|
|
288
|
-
boolean | undefined
|
|
289
|
-
>,
|
|
290
|
-
);
|
|
291
|
-
return gate("reactions");
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function areAnyFeishuReactionActionsEnabled(cfg: ClawdbotConfig): boolean {
|
|
295
|
-
for (const account of listEnabledFeishuAccounts(cfg)) {
|
|
296
|
-
if (isFeishuReactionsActionEnabled({ cfg, account })) {
|
|
297
|
-
return true;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return false;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function isSupportedFeishuDirectConversationId(conversationId: string): boolean {
|
|
304
|
-
const trimmed = conversationId.trim();
|
|
305
|
-
if (!trimmed || trimmed.includes(":")) {
|
|
306
|
-
return false;
|
|
307
|
-
}
|
|
308
|
-
if (trimmed.startsWith("oc_") || trimmed.startsWith("on_")) {
|
|
309
|
-
return false;
|
|
310
|
-
}
|
|
311
|
-
return true;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function normalizeFeishuAcpConversationId(conversationId: string) {
|
|
315
|
-
const parsed = parseFeishuConversationId({ conversationId });
|
|
316
|
-
if (
|
|
317
|
-
!parsed ||
|
|
318
|
-
(parsed.scope !== "group_topic" &&
|
|
319
|
-
parsed.scope !== "group_topic_sender" &&
|
|
320
|
-
!isSupportedFeishuDirectConversationId(parsed.canonicalConversationId))
|
|
321
|
-
) {
|
|
322
|
-
return null;
|
|
323
|
-
}
|
|
324
|
-
return {
|
|
325
|
-
conversationId: parsed.canonicalConversationId,
|
|
326
|
-
parentConversationId:
|
|
327
|
-
parsed.scope === "group_topic" || parsed.scope === "group_topic_sender"
|
|
328
|
-
? parsed.chatId
|
|
329
|
-
: undefined,
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function matchFeishuAcpConversation(params: {
|
|
334
|
-
bindingConversationId: string;
|
|
335
|
-
conversationId: string;
|
|
336
|
-
parentConversationId?: string;
|
|
337
|
-
}) {
|
|
338
|
-
const binding = normalizeFeishuAcpConversationId(params.bindingConversationId);
|
|
339
|
-
if (!binding) {
|
|
340
|
-
return null;
|
|
341
|
-
}
|
|
342
|
-
const incoming = parseFeishuConversationId({
|
|
343
|
-
conversationId: params.conversationId,
|
|
344
|
-
parentConversationId: params.parentConversationId,
|
|
345
|
-
});
|
|
346
|
-
if (
|
|
347
|
-
!incoming ||
|
|
348
|
-
(incoming.scope !== "group_topic" &&
|
|
349
|
-
incoming.scope !== "group_topic_sender" &&
|
|
350
|
-
!isSupportedFeishuDirectConversationId(incoming.canonicalConversationId))
|
|
351
|
-
) {
|
|
352
|
-
return null;
|
|
353
|
-
}
|
|
354
|
-
const matchesCanonicalConversation = binding.conversationId === incoming.canonicalConversationId;
|
|
355
|
-
const matchesParentTopicForSenderScopedConversation =
|
|
356
|
-
incoming.scope === "group_topic_sender" &&
|
|
357
|
-
binding.parentConversationId === incoming.chatId &&
|
|
358
|
-
binding.conversationId === `${incoming.chatId}:topic:${incoming.topicId}`;
|
|
359
|
-
if (!matchesCanonicalConversation && !matchesParentTopicForSenderScopedConversation) {
|
|
360
|
-
return null;
|
|
361
|
-
}
|
|
362
|
-
return {
|
|
363
|
-
conversationId: matchesParentTopicForSenderScopedConversation
|
|
364
|
-
? binding.conversationId
|
|
365
|
-
: incoming.canonicalConversationId,
|
|
366
|
-
parentConversationId:
|
|
367
|
-
incoming.scope === "group_topic" || incoming.scope === "group_topic_sender"
|
|
368
|
-
? incoming.chatId
|
|
369
|
-
: undefined,
|
|
370
|
-
matchPriority: matchesCanonicalConversation ? 2 : 1,
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
function resolveFeishuSenderScopedCommandConversation(params: {
|
|
375
|
-
accountId: string;
|
|
376
|
-
parentConversationId?: string;
|
|
377
|
-
threadId?: string;
|
|
378
|
-
senderId?: string;
|
|
379
|
-
sessionKey?: string;
|
|
380
|
-
parentSessionKey?: string;
|
|
381
|
-
}): string | undefined {
|
|
382
|
-
const parentConversationId = params.parentConversationId?.trim();
|
|
383
|
-
const threadId = params.threadId?.trim();
|
|
384
|
-
const senderId = params.senderId?.trim();
|
|
385
|
-
if (!parentConversationId || !threadId || !senderId) {
|
|
386
|
-
return undefined;
|
|
387
|
-
}
|
|
388
|
-
const expectedScopePrefix = `feishu:group:${normalizeLowercaseStringOrEmpty(parentConversationId)}:topic:${normalizeLowercaseStringOrEmpty(threadId)}:sender:`;
|
|
389
|
-
const isSenderScopedSession = [params.sessionKey, params.parentSessionKey].some((candidate) => {
|
|
390
|
-
const normalized = normalizeLowercaseStringOrEmpty(candidate ?? "");
|
|
391
|
-
if (!normalized) {
|
|
392
|
-
return false;
|
|
393
|
-
}
|
|
394
|
-
const scopedRest = normalized.replace(/^agent:[^:]+:/, "");
|
|
395
|
-
return scopedRest.startsWith(expectedScopePrefix);
|
|
396
|
-
});
|
|
397
|
-
const senderScopedConversationId = buildFeishuConversationId({
|
|
398
|
-
chatId: parentConversationId,
|
|
399
|
-
scope: "group_topic_sender",
|
|
400
|
-
topicId: threadId,
|
|
401
|
-
senderOpenId: senderId,
|
|
402
|
-
});
|
|
403
|
-
if (isSenderScopedSession) {
|
|
404
|
-
return senderScopedConversationId;
|
|
405
|
-
}
|
|
406
|
-
if (!params.sessionKey?.trim()) {
|
|
407
|
-
return undefined;
|
|
408
|
-
}
|
|
409
|
-
const boundConversation = getSessionBindingService()
|
|
410
|
-
.listBySession(params.sessionKey)
|
|
411
|
-
.find((binding) => {
|
|
412
|
-
if (
|
|
413
|
-
binding.conversation.channel !== "feishu" ||
|
|
414
|
-
binding.conversation.accountId !== params.accountId
|
|
415
|
-
) {
|
|
416
|
-
return false;
|
|
417
|
-
}
|
|
418
|
-
return binding.conversation.conversationId === senderScopedConversationId;
|
|
419
|
-
});
|
|
420
|
-
return boundConversation?.conversation.conversationId;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function resolveFeishuCommandConversation(params: {
|
|
424
|
-
accountId: string;
|
|
425
|
-
threadId?: string;
|
|
426
|
-
senderId?: string;
|
|
427
|
-
sessionKey?: string;
|
|
428
|
-
parentSessionKey?: string;
|
|
429
|
-
originatingTo?: string;
|
|
430
|
-
commandTo?: string;
|
|
431
|
-
fallbackTo?: string;
|
|
432
|
-
}) {
|
|
433
|
-
if (params.threadId) {
|
|
434
|
-
const parentConversationId =
|
|
435
|
-
parseFeishuTargetId(params.originatingTo) ??
|
|
436
|
-
parseFeishuTargetId(params.commandTo) ??
|
|
437
|
-
parseFeishuTargetId(params.fallbackTo);
|
|
438
|
-
if (!parentConversationId) {
|
|
439
|
-
return null;
|
|
440
|
-
}
|
|
441
|
-
const senderScopedConversationId = resolveFeishuSenderScopedCommandConversation({
|
|
442
|
-
accountId: params.accountId,
|
|
443
|
-
parentConversationId,
|
|
444
|
-
threadId: params.threadId,
|
|
445
|
-
senderId: params.senderId,
|
|
446
|
-
sessionKey: params.sessionKey,
|
|
447
|
-
parentSessionKey: params.parentSessionKey,
|
|
448
|
-
});
|
|
449
|
-
return {
|
|
450
|
-
conversationId:
|
|
451
|
-
senderScopedConversationId ??
|
|
452
|
-
buildFeishuConversationId({
|
|
453
|
-
chatId: parentConversationId,
|
|
454
|
-
scope: "group_topic",
|
|
455
|
-
topicId: params.threadId,
|
|
456
|
-
}),
|
|
457
|
-
parentConversationId,
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
const conversationId =
|
|
461
|
-
parseFeishuDirectConversationId(params.originatingTo) ??
|
|
462
|
-
parseFeishuDirectConversationId(params.commandTo) ??
|
|
463
|
-
parseFeishuDirectConversationId(params.fallbackTo);
|
|
464
|
-
return conversationId ? { conversationId } : null;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
function jsonActionResult(details: Record<string, unknown>) {
|
|
468
|
-
return {
|
|
469
|
-
content: [{ type: "text" as const, text: JSON.stringify(details) }],
|
|
470
|
-
details,
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
function readFirstString(
|
|
475
|
-
params: Record<string, unknown>,
|
|
476
|
-
keys: string[],
|
|
477
|
-
fallback?: string | null,
|
|
478
|
-
): string | undefined {
|
|
479
|
-
for (const key of keys) {
|
|
480
|
-
const value = params[key];
|
|
481
|
-
if (typeof value === "string" && value.trim()) {
|
|
482
|
-
return value.trim();
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
if (typeof fallback === "string" && fallback.trim()) {
|
|
486
|
-
return fallback.trim();
|
|
487
|
-
}
|
|
488
|
-
return undefined;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
function readOptionalNumber(params: Record<string, unknown>, keys: string[]): number | undefined {
|
|
492
|
-
for (const key of keys) {
|
|
493
|
-
const value = params[key];
|
|
494
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
495
|
-
return value;
|
|
496
|
-
}
|
|
497
|
-
if (typeof value === "string" && value.trim()) {
|
|
498
|
-
const parsed = Number(value);
|
|
499
|
-
if (Number.isFinite(parsed)) {
|
|
500
|
-
return parsed;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
return undefined;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function resolveFeishuActionTarget(ctx: {
|
|
508
|
-
params: Record<string, unknown>;
|
|
509
|
-
toolContext?: { currentChannelId?: string } | null;
|
|
510
|
-
}): string | undefined {
|
|
511
|
-
return readFirstString(ctx.params, ["to", "target"], ctx.toolContext?.currentChannelId);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
function resolveFeishuChatId(ctx: {
|
|
515
|
-
params: Record<string, unknown>;
|
|
516
|
-
toolContext?: { currentChannelId?: string } | null;
|
|
517
|
-
}): string | undefined {
|
|
518
|
-
const raw = readFirstString(
|
|
519
|
-
ctx.params,
|
|
520
|
-
["chatId", "chat_id", "channelId", "channel_id", "to", "target"],
|
|
521
|
-
ctx.toolContext?.currentChannelId,
|
|
522
|
-
);
|
|
523
|
-
if (!raw) {
|
|
524
|
-
return undefined;
|
|
525
|
-
}
|
|
526
|
-
if (/^(user|dm|open_id):/i.test(raw)) {
|
|
527
|
-
return undefined;
|
|
528
|
-
}
|
|
529
|
-
if (/^(chat|group|channel):/i.test(raw)) {
|
|
530
|
-
return normalizeFeishuTarget(raw) ?? undefined;
|
|
531
|
-
}
|
|
532
|
-
return raw;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
function resolveFeishuMessageId(params: Record<string, unknown>): string | undefined {
|
|
536
|
-
return readFirstString(params, ["messageId", "message_id", "replyTo", "reply_to"]);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
function resolveFeishuMemberId(params: Record<string, unknown>): string | undefined {
|
|
540
|
-
return readFirstString(params, [
|
|
541
|
-
"memberId",
|
|
542
|
-
"member_id",
|
|
543
|
-
"userId",
|
|
544
|
-
"user_id",
|
|
545
|
-
"openId",
|
|
546
|
-
"open_id",
|
|
547
|
-
"unionId",
|
|
548
|
-
"union_id",
|
|
549
|
-
]);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
function resolveFeishuMemberIdType(
|
|
553
|
-
params: Record<string, unknown>,
|
|
554
|
-
): "open_id" | "user_id" | "union_id" {
|
|
555
|
-
const raw = readFirstString(params, [
|
|
556
|
-
"memberIdType",
|
|
557
|
-
"member_id_type",
|
|
558
|
-
"userIdType",
|
|
559
|
-
"user_id_type",
|
|
560
|
-
]);
|
|
561
|
-
if (raw === "open_id" || raw === "user_id" || raw === "union_id") {
|
|
562
|
-
return raw;
|
|
563
|
-
}
|
|
564
|
-
if (
|
|
565
|
-
readFirstString(params, ["userId", "user_id"]) &&
|
|
566
|
-
!readFirstString(params, ["openId", "open_id", "unionId", "union_id"])
|
|
567
|
-
) {
|
|
568
|
-
return "user_id";
|
|
569
|
-
}
|
|
570
|
-
if (
|
|
571
|
-
readFirstString(params, ["unionId", "union_id"]) &&
|
|
572
|
-
!readFirstString(params, ["openId", "open_id"])
|
|
573
|
-
) {
|
|
574
|
-
return "union_id";
|
|
575
|
-
}
|
|
576
|
-
return "open_id";
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount, FeishuProbeResult> =
|
|
580
|
-
createChatChannelPlugin({
|
|
581
|
-
base: {
|
|
582
|
-
id: "feishu",
|
|
583
|
-
meta: {
|
|
584
|
-
...meta,
|
|
585
|
-
},
|
|
586
|
-
capabilities: {
|
|
587
|
-
chatTypes: ["direct", "channel"],
|
|
588
|
-
polls: false,
|
|
589
|
-
threads: true,
|
|
590
|
-
media: true,
|
|
591
|
-
tts: {
|
|
592
|
-
voice: {
|
|
593
|
-
synthesisTarget: "voice-note",
|
|
594
|
-
transcodesAudio: true,
|
|
595
|
-
},
|
|
596
|
-
},
|
|
597
|
-
reactions: true,
|
|
598
|
-
edit: true,
|
|
599
|
-
reply: true,
|
|
600
|
-
},
|
|
601
|
-
agentPrompt: {
|
|
602
|
-
messageToolHints: () => [
|
|
603
|
-
"- Feishu targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `user:open_id` or `chat:chat_id`.",
|
|
604
|
-
"- Feishu supports interactive cards plus native image, file, audio, and video/media delivery.",
|
|
605
|
-
"- Feishu supports `send`, `read`, `edit`, `thread-reply`, pins, and channel/member lookup, plus reactions when enabled.",
|
|
606
|
-
],
|
|
607
|
-
},
|
|
608
|
-
groups: {
|
|
609
|
-
resolveToolPolicy: resolveFeishuGroupToolPolicy,
|
|
610
|
-
},
|
|
611
|
-
conversationBindings: {
|
|
612
|
-
defaultTopLevelPlacement: "current",
|
|
613
|
-
buildModelOverrideParentCandidates: ({ parentConversationId }) =>
|
|
614
|
-
buildFeishuModelOverrideParentCandidates(parentConversationId),
|
|
615
|
-
},
|
|
616
|
-
mentions: {
|
|
617
|
-
stripPatterns: () => ['<at user_id="[^"]*">[^<]*</at>'],
|
|
618
|
-
},
|
|
619
|
-
reload: { configPrefixes: ["channels.feishu"] },
|
|
620
|
-
configSchema: buildChannelConfigSchema(FeishuConfigSchema),
|
|
621
|
-
config: {
|
|
622
|
-
...feishuConfigAdapter,
|
|
623
|
-
setAccountEnabled: ({ cfg, accountId, enabled }) => {
|
|
624
|
-
const isDefault = accountId === DEFAULT_ACCOUNT_ID;
|
|
625
|
-
if (isDefault) {
|
|
626
|
-
return {
|
|
627
|
-
...cfg,
|
|
628
|
-
channels: {
|
|
629
|
-
...cfg.channels,
|
|
630
|
-
feishu: {
|
|
631
|
-
...cfg.channels?.feishu,
|
|
632
|
-
enabled,
|
|
633
|
-
},
|
|
634
|
-
},
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
return setFeishuNamedAccountEnabled(cfg, accountId, enabled);
|
|
638
|
-
},
|
|
639
|
-
deleteAccount: ({ cfg, accountId }) => {
|
|
640
|
-
const isDefault = accountId === DEFAULT_ACCOUNT_ID;
|
|
641
|
-
|
|
642
|
-
if (isDefault) {
|
|
643
|
-
// Delete entire feishu config
|
|
644
|
-
const next = { ...cfg } as ClawdbotConfig;
|
|
645
|
-
const nextChannels = { ...cfg.channels };
|
|
646
|
-
delete (nextChannels as Record<string, unknown>).feishu;
|
|
647
|
-
if (Object.keys(nextChannels).length > 0) {
|
|
648
|
-
next.channels = nextChannels;
|
|
649
|
-
} else {
|
|
650
|
-
delete next.channels;
|
|
651
|
-
}
|
|
652
|
-
return next;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
// Delete specific account from accounts
|
|
656
|
-
const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
|
|
657
|
-
const accounts = { ...feishuCfg?.accounts };
|
|
658
|
-
delete accounts[accountId];
|
|
659
|
-
|
|
660
|
-
return {
|
|
661
|
-
...cfg,
|
|
662
|
-
channels: {
|
|
663
|
-
...cfg.channels,
|
|
664
|
-
feishu: {
|
|
665
|
-
...feishuCfg,
|
|
666
|
-
accounts: Object.keys(accounts).length > 0 ? accounts : undefined,
|
|
667
|
-
},
|
|
668
|
-
},
|
|
669
|
-
};
|
|
670
|
-
},
|
|
671
|
-
isConfigured: (account) => account.configured,
|
|
672
|
-
describeAccount: (account) =>
|
|
673
|
-
describeAccountSnapshot({
|
|
674
|
-
account,
|
|
675
|
-
configured: account.configured,
|
|
676
|
-
extra: {
|
|
677
|
-
appId: account.appId,
|
|
678
|
-
domain: account.domain,
|
|
679
|
-
},
|
|
680
|
-
}),
|
|
681
|
-
},
|
|
682
|
-
approvalCapability: feishuApprovalAuth,
|
|
683
|
-
secrets: {
|
|
684
|
-
secretTargetRegistryEntries,
|
|
685
|
-
collectRuntimeConfigAssignments,
|
|
686
|
-
},
|
|
687
|
-
actions: {
|
|
688
|
-
messageActionTargetAliases,
|
|
689
|
-
describeMessageTool: describeFeishuMessageTool,
|
|
690
|
-
handleAction: async (ctx) => {
|
|
691
|
-
const account = resolveFeishuAccount({
|
|
692
|
-
cfg: ctx.cfg,
|
|
693
|
-
accountId: ctx.accountId ?? undefined,
|
|
694
|
-
});
|
|
695
|
-
if (
|
|
696
|
-
(ctx.action === "react" || ctx.action === "reactions") &&
|
|
697
|
-
!isFeishuReactionsActionEnabled({ cfg: ctx.cfg, account })
|
|
698
|
-
) {
|
|
699
|
-
throw new Error("Feishu reactions are disabled via actions.reactions.");
|
|
700
|
-
}
|
|
701
|
-
if (ctx.action === "send" || ctx.action === "thread-reply") {
|
|
702
|
-
const to = resolveFeishuActionTarget(ctx);
|
|
703
|
-
if (!to) {
|
|
704
|
-
throw new Error(`Feishu ${ctx.action} requires a target (to).`);
|
|
705
|
-
}
|
|
706
|
-
const replyToMessageId =
|
|
707
|
-
ctx.action === "thread-reply" ? resolveFeishuMessageId(ctx.params) : undefined;
|
|
708
|
-
if (ctx.action === "thread-reply" && !replyToMessageId) {
|
|
709
|
-
throw new Error("Feishu thread-reply requires messageId.");
|
|
710
|
-
}
|
|
711
|
-
const presentation = normalizeMessagePresentation(ctx.params.presentation);
|
|
712
|
-
const text = readFirstString(ctx.params, ["text", "message"]);
|
|
713
|
-
const mediaUrl = readFeishuMediaParam(ctx.params);
|
|
714
|
-
const audioAsVoice = readBooleanParam(ctx.params, ["asVoice", "audioAsVoice"]);
|
|
715
|
-
const card = presentation
|
|
716
|
-
? buildFeishuPresentationCard({ presentation, fallbackText: text })
|
|
717
|
-
: undefined;
|
|
718
|
-
if (card && mediaUrl) {
|
|
719
|
-
throw new Error(`Feishu ${ctx.action} does not support card with media.`);
|
|
720
|
-
}
|
|
721
|
-
if (!card && !text && !mediaUrl) {
|
|
722
|
-
throw new Error(`Feishu ${ctx.action} requires text/message, media, or card.`);
|
|
723
|
-
}
|
|
724
|
-
const runtime = await loadFeishuChannelRuntime();
|
|
725
|
-
const maybeSendMedia = runtime.feishuOutbound.sendMedia;
|
|
726
|
-
if (mediaUrl && !maybeSendMedia) {
|
|
727
|
-
throw new Error("Feishu media sending is not available.");
|
|
728
|
-
}
|
|
729
|
-
const sendMedia = maybeSendMedia;
|
|
730
|
-
let result;
|
|
731
|
-
if (card) {
|
|
732
|
-
if (containsLegacyFeishuCardCommandValue(card)) {
|
|
733
|
-
throw new Error(
|
|
734
|
-
"Feishu card buttons that trigger text or commands must use structured interaction envelopes.",
|
|
735
|
-
);
|
|
736
|
-
}
|
|
737
|
-
result = await runtime.sendCardFeishu({
|
|
738
|
-
cfg: ctx.cfg,
|
|
739
|
-
to,
|
|
740
|
-
card,
|
|
741
|
-
accountId: ctx.accountId ?? undefined,
|
|
742
|
-
replyToMessageId,
|
|
743
|
-
replyInThread: ctx.action === "thread-reply",
|
|
744
|
-
});
|
|
745
|
-
} else if (mediaUrl) {
|
|
746
|
-
result = await sendMedia!({
|
|
747
|
-
cfg: ctx.cfg,
|
|
748
|
-
to,
|
|
749
|
-
text: text ?? "",
|
|
750
|
-
mediaUrl,
|
|
751
|
-
accountId: ctx.accountId ?? undefined,
|
|
752
|
-
mediaLocalRoots: ctx.mediaLocalRoots,
|
|
753
|
-
replyToId: replyToMessageId,
|
|
754
|
-
...(audioAsVoice === true ? { audioAsVoice: true } : {}),
|
|
755
|
-
});
|
|
756
|
-
} else {
|
|
757
|
-
result = await runtime.sendMessageFeishu({
|
|
758
|
-
cfg: ctx.cfg,
|
|
759
|
-
to,
|
|
760
|
-
text: text!,
|
|
761
|
-
accountId: ctx.accountId ?? undefined,
|
|
762
|
-
replyToMessageId,
|
|
763
|
-
replyInThread: ctx.action === "thread-reply",
|
|
764
|
-
});
|
|
765
|
-
}
|
|
766
|
-
return jsonActionResult({
|
|
767
|
-
ok: true,
|
|
768
|
-
channel: "feishu",
|
|
769
|
-
action: ctx.action,
|
|
770
|
-
...result,
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
if (ctx.action === "read") {
|
|
775
|
-
const messageId = resolveFeishuMessageId(ctx.params);
|
|
776
|
-
if (!messageId) {
|
|
777
|
-
throw new Error("Feishu read requires messageId.");
|
|
778
|
-
}
|
|
779
|
-
const { getMessageFeishu } = await loadFeishuChannelRuntime();
|
|
780
|
-
const message = await getMessageFeishu({
|
|
781
|
-
cfg: ctx.cfg,
|
|
782
|
-
messageId,
|
|
783
|
-
accountId: ctx.accountId ?? undefined,
|
|
784
|
-
});
|
|
785
|
-
if (!message) {
|
|
786
|
-
return {
|
|
787
|
-
isError: true,
|
|
788
|
-
content: [
|
|
789
|
-
{
|
|
790
|
-
type: "text" as const,
|
|
791
|
-
text: JSON.stringify({
|
|
792
|
-
error: `Feishu read failed or message not found: ${messageId}`,
|
|
793
|
-
}),
|
|
794
|
-
},
|
|
795
|
-
],
|
|
796
|
-
details: { error: `Feishu read failed or message not found: ${messageId}` },
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
return jsonActionResult({ ok: true, channel: "feishu", action: "read", message });
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
if (ctx.action === "edit") {
|
|
803
|
-
const messageId = resolveFeishuMessageId(ctx.params);
|
|
804
|
-
if (!messageId) {
|
|
805
|
-
throw new Error("Feishu edit requires messageId.");
|
|
806
|
-
}
|
|
807
|
-
const text = readFirstString(ctx.params, ["text", "message"]);
|
|
808
|
-
const card =
|
|
809
|
-
ctx.params.card && typeof ctx.params.card === "object"
|
|
810
|
-
? (ctx.params.card as Record<string, unknown>)
|
|
811
|
-
: undefined;
|
|
812
|
-
const { editMessageFeishu } = await loadFeishuChannelRuntime();
|
|
813
|
-
const result = await editMessageFeishu({
|
|
814
|
-
cfg: ctx.cfg,
|
|
815
|
-
messageId,
|
|
816
|
-
text,
|
|
817
|
-
card,
|
|
818
|
-
accountId: ctx.accountId ?? undefined,
|
|
819
|
-
});
|
|
820
|
-
return jsonActionResult({
|
|
821
|
-
ok: true,
|
|
822
|
-
channel: "feishu",
|
|
823
|
-
action: "edit",
|
|
824
|
-
...result,
|
|
825
|
-
});
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
if (ctx.action === "pin") {
|
|
829
|
-
const messageId = resolveFeishuMessageId(ctx.params);
|
|
830
|
-
if (!messageId) {
|
|
831
|
-
throw new Error("Feishu pin requires messageId.");
|
|
832
|
-
}
|
|
833
|
-
const { createPinFeishu } = await loadFeishuChannelRuntime();
|
|
834
|
-
const pin = await createPinFeishu({
|
|
835
|
-
cfg: ctx.cfg,
|
|
836
|
-
messageId,
|
|
837
|
-
accountId: ctx.accountId ?? undefined,
|
|
838
|
-
});
|
|
839
|
-
return jsonActionResult({ ok: true, channel: "feishu", action: "pin", pin });
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
if (ctx.action === "unpin") {
|
|
843
|
-
const messageId = resolveFeishuMessageId(ctx.params);
|
|
844
|
-
if (!messageId) {
|
|
845
|
-
throw new Error("Feishu unpin requires messageId.");
|
|
846
|
-
}
|
|
847
|
-
const { removePinFeishu } = await loadFeishuChannelRuntime();
|
|
848
|
-
await removePinFeishu({
|
|
849
|
-
cfg: ctx.cfg,
|
|
850
|
-
messageId,
|
|
851
|
-
accountId: ctx.accountId ?? undefined,
|
|
852
|
-
});
|
|
853
|
-
return jsonActionResult({
|
|
854
|
-
ok: true,
|
|
855
|
-
channel: "feishu",
|
|
856
|
-
action: "unpin",
|
|
857
|
-
messageId,
|
|
858
|
-
});
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
if (ctx.action === "list-pins") {
|
|
862
|
-
const chatId = resolveFeishuChatId(ctx);
|
|
863
|
-
if (!chatId) {
|
|
864
|
-
throw new Error("Feishu list-pins requires chatId or channelId.");
|
|
865
|
-
}
|
|
866
|
-
const { listPinsFeishu } = await loadFeishuChannelRuntime();
|
|
867
|
-
const result = await listPinsFeishu({
|
|
868
|
-
cfg: ctx.cfg,
|
|
869
|
-
chatId,
|
|
870
|
-
startTime: readFirstString(ctx.params, ["startTime", "start_time"]),
|
|
871
|
-
endTime: readFirstString(ctx.params, ["endTime", "end_time"]),
|
|
872
|
-
pageSize: readOptionalNumber(ctx.params, ["pageSize", "page_size"]),
|
|
873
|
-
pageToken: readFirstString(ctx.params, ["pageToken", "page_token"]),
|
|
874
|
-
accountId: ctx.accountId ?? undefined,
|
|
875
|
-
});
|
|
876
|
-
return jsonActionResult({
|
|
877
|
-
ok: true,
|
|
878
|
-
channel: "feishu",
|
|
879
|
-
action: "list-pins",
|
|
880
|
-
...result,
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
if (ctx.action === "channel-info") {
|
|
885
|
-
const chatId = resolveFeishuChatId(ctx);
|
|
886
|
-
if (!chatId) {
|
|
887
|
-
throw new Error("Feishu channel-info requires chatId or channelId.");
|
|
888
|
-
}
|
|
889
|
-
const runtime = await loadFeishuChannelRuntime();
|
|
890
|
-
const client = await createFeishuActionClient(account);
|
|
891
|
-
const channel = await runtime.getChatInfo(client, chatId);
|
|
892
|
-
const includeMembers =
|
|
893
|
-
ctx.params.includeMembers === true || ctx.params.members === true;
|
|
894
|
-
if (!includeMembers) {
|
|
895
|
-
return jsonActionResult({
|
|
896
|
-
ok: true,
|
|
897
|
-
provider: "feishu",
|
|
898
|
-
action: "channel-info",
|
|
899
|
-
channel,
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
const members = await runtime.getChatMembers(
|
|
903
|
-
client,
|
|
904
|
-
chatId,
|
|
905
|
-
readOptionalNumber(ctx.params, ["pageSize", "page_size"]),
|
|
906
|
-
readFirstString(ctx.params, ["pageToken", "page_token"]),
|
|
907
|
-
resolveFeishuMemberIdType(ctx.params),
|
|
908
|
-
);
|
|
909
|
-
return jsonActionResult({
|
|
910
|
-
ok: true,
|
|
911
|
-
provider: "feishu",
|
|
912
|
-
action: "channel-info",
|
|
913
|
-
channel,
|
|
914
|
-
members,
|
|
915
|
-
});
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
if (ctx.action === "member-info") {
|
|
919
|
-
const runtime = await loadFeishuChannelRuntime();
|
|
920
|
-
const client = await createFeishuActionClient(account);
|
|
921
|
-
const memberId = resolveFeishuMemberId(ctx.params);
|
|
922
|
-
if (memberId) {
|
|
923
|
-
const member = await runtime.getFeishuMemberInfo(
|
|
924
|
-
client,
|
|
925
|
-
memberId,
|
|
926
|
-
resolveFeishuMemberIdType(ctx.params),
|
|
927
|
-
);
|
|
928
|
-
return jsonActionResult({
|
|
929
|
-
ok: true,
|
|
930
|
-
channel: "feishu",
|
|
931
|
-
action: "member-info",
|
|
932
|
-
member,
|
|
933
|
-
});
|
|
934
|
-
}
|
|
935
|
-
const chatId = resolveFeishuChatId(ctx);
|
|
936
|
-
if (!chatId) {
|
|
937
|
-
throw new Error("Feishu member-info requires memberId or chatId/channelId.");
|
|
938
|
-
}
|
|
939
|
-
const members = await runtime.getChatMembers(
|
|
940
|
-
client,
|
|
941
|
-
chatId,
|
|
942
|
-
readOptionalNumber(ctx.params, ["pageSize", "page_size"]),
|
|
943
|
-
readFirstString(ctx.params, ["pageToken", "page_token"]),
|
|
944
|
-
resolveFeishuMemberIdType(ctx.params),
|
|
945
|
-
);
|
|
946
|
-
return jsonActionResult({
|
|
947
|
-
ok: true,
|
|
948
|
-
channel: "feishu",
|
|
949
|
-
action: "member-info",
|
|
950
|
-
...members,
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
if (ctx.action === "channel-list") {
|
|
955
|
-
const runtime = await loadFeishuChannelRuntime();
|
|
956
|
-
const query = readFirstString(ctx.params, ["query"]);
|
|
957
|
-
const limit = readOptionalNumber(ctx.params, ["limit"]);
|
|
958
|
-
const scope = readFirstString(ctx.params, ["scope", "kind"]) ?? "all";
|
|
959
|
-
if (
|
|
960
|
-
scope === "groups" ||
|
|
961
|
-
scope === "group" ||
|
|
962
|
-
scope === "channels" ||
|
|
963
|
-
scope === "channel"
|
|
964
|
-
) {
|
|
965
|
-
const groups = await runtime.listFeishuDirectoryGroupsLive({
|
|
966
|
-
cfg: ctx.cfg,
|
|
967
|
-
query,
|
|
968
|
-
limit,
|
|
969
|
-
fallbackToStatic: false,
|
|
970
|
-
accountId: ctx.accountId ?? undefined,
|
|
971
|
-
});
|
|
972
|
-
return jsonActionResult({
|
|
973
|
-
ok: true,
|
|
974
|
-
channel: "feishu",
|
|
975
|
-
action: "channel-list",
|
|
976
|
-
groups,
|
|
977
|
-
});
|
|
978
|
-
}
|
|
979
|
-
if (
|
|
980
|
-
scope === "peers" ||
|
|
981
|
-
scope === "peer" ||
|
|
982
|
-
scope === "members" ||
|
|
983
|
-
scope === "member" ||
|
|
984
|
-
scope === "users" ||
|
|
985
|
-
scope === "user"
|
|
986
|
-
) {
|
|
987
|
-
const peers = await runtime.listFeishuDirectoryPeersLive({
|
|
988
|
-
cfg: ctx.cfg,
|
|
989
|
-
query,
|
|
990
|
-
limit,
|
|
991
|
-
fallbackToStatic: false,
|
|
992
|
-
accountId: ctx.accountId ?? undefined,
|
|
993
|
-
});
|
|
994
|
-
return jsonActionResult({
|
|
995
|
-
ok: true,
|
|
996
|
-
channel: "feishu",
|
|
997
|
-
action: "channel-list",
|
|
998
|
-
peers,
|
|
999
|
-
});
|
|
1000
|
-
}
|
|
1001
|
-
const [groups, peers] = await Promise.all([
|
|
1002
|
-
runtime.listFeishuDirectoryGroupsLive({
|
|
1003
|
-
cfg: ctx.cfg,
|
|
1004
|
-
query,
|
|
1005
|
-
limit,
|
|
1006
|
-
fallbackToStatic: false,
|
|
1007
|
-
accountId: ctx.accountId ?? undefined,
|
|
1008
|
-
}),
|
|
1009
|
-
runtime.listFeishuDirectoryPeersLive({
|
|
1010
|
-
cfg: ctx.cfg,
|
|
1011
|
-
query,
|
|
1012
|
-
limit,
|
|
1013
|
-
fallbackToStatic: false,
|
|
1014
|
-
accountId: ctx.accountId ?? undefined,
|
|
1015
|
-
}),
|
|
1016
|
-
]);
|
|
1017
|
-
return jsonActionResult({
|
|
1018
|
-
ok: true,
|
|
1019
|
-
channel: "feishu",
|
|
1020
|
-
action: "channel-list",
|
|
1021
|
-
groups,
|
|
1022
|
-
peers,
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
if (ctx.action === "react") {
|
|
1027
|
-
const messageId = resolveFeishuMessageId(ctx.params);
|
|
1028
|
-
if (!messageId) {
|
|
1029
|
-
throw new Error("Feishu reaction requires messageId.");
|
|
1030
|
-
}
|
|
1031
|
-
const emoji = typeof ctx.params.emoji === "string" ? ctx.params.emoji.trim() : "";
|
|
1032
|
-
const remove = ctx.params.remove === true;
|
|
1033
|
-
const clearAll = ctx.params.clearAll === true;
|
|
1034
|
-
if (remove) {
|
|
1035
|
-
if (!emoji) {
|
|
1036
|
-
throw new Error("Emoji is required to remove a Feishu reaction.");
|
|
1037
|
-
}
|
|
1038
|
-
const { listReactionsFeishu, removeReactionFeishu } =
|
|
1039
|
-
await loadFeishuChannelRuntime();
|
|
1040
|
-
const matches = await listReactionsFeishu({
|
|
1041
|
-
cfg: ctx.cfg,
|
|
1042
|
-
messageId,
|
|
1043
|
-
emojiType: emoji,
|
|
1044
|
-
accountId: ctx.accountId ?? undefined,
|
|
1045
|
-
});
|
|
1046
|
-
const ownReaction = matches.find((entry) => entry.operatorType === "app");
|
|
1047
|
-
if (!ownReaction) {
|
|
1048
|
-
return jsonActionResult({ ok: true, removed: null });
|
|
1049
|
-
}
|
|
1050
|
-
await removeReactionFeishu({
|
|
1051
|
-
cfg: ctx.cfg,
|
|
1052
|
-
messageId,
|
|
1053
|
-
reactionId: ownReaction.reactionId,
|
|
1054
|
-
accountId: ctx.accountId ?? undefined,
|
|
1055
|
-
});
|
|
1056
|
-
return jsonActionResult({ ok: true, removed: emoji });
|
|
1057
|
-
}
|
|
1058
|
-
if (!emoji) {
|
|
1059
|
-
if (!clearAll) {
|
|
1060
|
-
throw new Error(
|
|
1061
|
-
"Emoji is required to add a Feishu reaction. Set clearAll=true to remove all bot reactions.",
|
|
1062
|
-
);
|
|
1063
|
-
}
|
|
1064
|
-
const { listReactionsFeishu, removeReactionFeishu } =
|
|
1065
|
-
await loadFeishuChannelRuntime();
|
|
1066
|
-
const reactions = await listReactionsFeishu({
|
|
1067
|
-
cfg: ctx.cfg,
|
|
1068
|
-
messageId,
|
|
1069
|
-
accountId: ctx.accountId ?? undefined,
|
|
1070
|
-
});
|
|
1071
|
-
let removed = 0;
|
|
1072
|
-
for (const reaction of reactions.filter((entry) => entry.operatorType === "app")) {
|
|
1073
|
-
await removeReactionFeishu({
|
|
1074
|
-
cfg: ctx.cfg,
|
|
1075
|
-
messageId,
|
|
1076
|
-
reactionId: reaction.reactionId,
|
|
1077
|
-
accountId: ctx.accountId ?? undefined,
|
|
1078
|
-
});
|
|
1079
|
-
removed += 1;
|
|
1080
|
-
}
|
|
1081
|
-
return jsonActionResult({ ok: true, removed });
|
|
1082
|
-
}
|
|
1083
|
-
const { addReactionFeishu } = await loadFeishuChannelRuntime();
|
|
1084
|
-
await addReactionFeishu({
|
|
1085
|
-
cfg: ctx.cfg,
|
|
1086
|
-
messageId,
|
|
1087
|
-
emojiType: emoji,
|
|
1088
|
-
accountId: ctx.accountId ?? undefined,
|
|
1089
|
-
});
|
|
1090
|
-
return jsonActionResult({ ok: true, added: emoji });
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
if (ctx.action === "reactions") {
|
|
1094
|
-
const messageId = resolveFeishuMessageId(ctx.params);
|
|
1095
|
-
if (!messageId) {
|
|
1096
|
-
throw new Error("Feishu reactions lookup requires messageId.");
|
|
1097
|
-
}
|
|
1098
|
-
const { listReactionsFeishu } = await loadFeishuChannelRuntime();
|
|
1099
|
-
const reactions = await listReactionsFeishu({
|
|
1100
|
-
cfg: ctx.cfg,
|
|
1101
|
-
messageId,
|
|
1102
|
-
accountId: ctx.accountId ?? undefined,
|
|
1103
|
-
});
|
|
1104
|
-
return jsonActionResult({ ok: true, reactions });
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
throw new Error(`Unsupported Feishu action: "${ctx.action}"`);
|
|
1108
|
-
},
|
|
1109
|
-
},
|
|
1110
|
-
bindings: {
|
|
1111
|
-
compileConfiguredBinding: ({ conversationId }) =>
|
|
1112
|
-
normalizeFeishuAcpConversationId(conversationId),
|
|
1113
|
-
matchInboundConversation: ({ compiledBinding, conversationId, parentConversationId }) =>
|
|
1114
|
-
matchFeishuAcpConversation({
|
|
1115
|
-
bindingConversationId: compiledBinding.conversationId,
|
|
1116
|
-
conversationId,
|
|
1117
|
-
parentConversationId,
|
|
1118
|
-
}),
|
|
1119
|
-
resolveCommandConversation: ({
|
|
1120
|
-
accountId,
|
|
1121
|
-
threadId,
|
|
1122
|
-
senderId,
|
|
1123
|
-
sessionKey,
|
|
1124
|
-
parentSessionKey,
|
|
1125
|
-
originatingTo,
|
|
1126
|
-
commandTo,
|
|
1127
|
-
fallbackTo,
|
|
1128
|
-
}) =>
|
|
1129
|
-
resolveFeishuCommandConversation({
|
|
1130
|
-
accountId,
|
|
1131
|
-
threadId,
|
|
1132
|
-
senderId,
|
|
1133
|
-
sessionKey,
|
|
1134
|
-
parentSessionKey,
|
|
1135
|
-
originatingTo,
|
|
1136
|
-
commandTo,
|
|
1137
|
-
fallbackTo,
|
|
1138
|
-
}),
|
|
1139
|
-
},
|
|
1140
|
-
auth: {
|
|
1141
|
-
login: async ({ cfg }) => {
|
|
1142
|
-
const { createClackPrompter } = await import("openclaw/plugin-sdk/setup-runtime");
|
|
1143
|
-
const { replaceConfigFile } = await import("openclaw/plugin-sdk/config-mutation");
|
|
1144
|
-
const prompter = createClackPrompter();
|
|
1145
|
-
const nextCfg = await runFeishuLogin({ cfg, prompter });
|
|
1146
|
-
if (nextCfg !== cfg) {
|
|
1147
|
-
await replaceConfigFile({
|
|
1148
|
-
nextConfig: nextCfg,
|
|
1149
|
-
afterWrite: { mode: "auto" },
|
|
1150
|
-
});
|
|
1151
|
-
}
|
|
1152
|
-
},
|
|
1153
|
-
},
|
|
1154
|
-
setup: feishuSetupAdapter,
|
|
1155
|
-
setupWizard: feishuSetupWizard,
|
|
1156
|
-
messaging: {
|
|
1157
|
-
targetPrefixes: ["feishu", "lark"],
|
|
1158
|
-
normalizeTarget: (raw) => normalizeFeishuTarget(raw) ?? undefined,
|
|
1159
|
-
resolveDeliveryTarget: ({ conversationId, parentConversationId }) => {
|
|
1160
|
-
const directId = parseFeishuDirectConversationId(conversationId);
|
|
1161
|
-
if (directId) {
|
|
1162
|
-
return { to: `user:${directId}` };
|
|
1163
|
-
}
|
|
1164
|
-
const parsed = parseFeishuConversationId({ conversationId, parentConversationId });
|
|
1165
|
-
if (parsed?.topicId) {
|
|
1166
|
-
return {
|
|
1167
|
-
to: `chat:${parentConversationId?.trim() || parsed.chatId}`,
|
|
1168
|
-
threadId: parsed.topicId,
|
|
1169
|
-
};
|
|
1170
|
-
}
|
|
1171
|
-
return { to: `chat:${parsed?.chatId ?? conversationId.trim()}` };
|
|
1172
|
-
},
|
|
1173
|
-
resolveSessionConversation: ({ kind, rawId }) =>
|
|
1174
|
-
resolveFeishuSessionConversation({ kind, rawId }),
|
|
1175
|
-
resolveOutboundSessionRoute: (params) => resolveFeishuOutboundSessionRoute(params),
|
|
1176
|
-
targetResolver: {
|
|
1177
|
-
looksLikeId: looksLikeFeishuId,
|
|
1178
|
-
hint: "<chatId|user:openId|chat:chatId>",
|
|
1179
|
-
},
|
|
1180
|
-
},
|
|
1181
|
-
directory: createChannelDirectoryAdapter({
|
|
1182
|
-
listPeers: async ({ cfg, query, limit, accountId }) =>
|
|
1183
|
-
listFeishuDirectoryPeers({
|
|
1184
|
-
cfg,
|
|
1185
|
-
query: query ?? undefined,
|
|
1186
|
-
limit: limit ?? undefined,
|
|
1187
|
-
accountId: accountId ?? undefined,
|
|
1188
|
-
}),
|
|
1189
|
-
listGroups: async ({ cfg, query, limit, accountId }) =>
|
|
1190
|
-
listFeishuDirectoryGroups({
|
|
1191
|
-
cfg,
|
|
1192
|
-
query: query ?? undefined,
|
|
1193
|
-
limit: limit ?? undefined,
|
|
1194
|
-
accountId: accountId ?? undefined,
|
|
1195
|
-
}),
|
|
1196
|
-
...createRuntimeDirectoryLiveAdapter({
|
|
1197
|
-
getRuntime: loadFeishuChannelRuntime,
|
|
1198
|
-
listPeersLive:
|
|
1199
|
-
(runtime) =>
|
|
1200
|
-
async ({ cfg, query, limit, accountId }) =>
|
|
1201
|
-
await runtime.listFeishuDirectoryPeersLive({
|
|
1202
|
-
cfg,
|
|
1203
|
-
query: query ?? undefined,
|
|
1204
|
-
limit: limit ?? undefined,
|
|
1205
|
-
accountId: accountId ?? undefined,
|
|
1206
|
-
}),
|
|
1207
|
-
listGroupsLive:
|
|
1208
|
-
(runtime) =>
|
|
1209
|
-
async ({ cfg, query, limit, accountId }) =>
|
|
1210
|
-
await runtime.listFeishuDirectoryGroupsLive({
|
|
1211
|
-
cfg,
|
|
1212
|
-
query: query ?? undefined,
|
|
1213
|
-
limit: limit ?? undefined,
|
|
1214
|
-
accountId: accountId ?? undefined,
|
|
1215
|
-
}),
|
|
1216
|
-
}),
|
|
1217
|
-
}),
|
|
1218
|
-
status: createComputedAccountStatusAdapter<ResolvedFeishuAccount, FeishuProbeResult>({
|
|
1219
|
-
defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID, { port: null }),
|
|
1220
|
-
buildChannelSummary: ({ snapshot }) =>
|
|
1221
|
-
buildProbeChannelStatusSummary(snapshot, {
|
|
1222
|
-
port: snapshot.port ?? null,
|
|
1223
|
-
}),
|
|
1224
|
-
probeAccount: async ({ account }) =>
|
|
1225
|
-
await (await loadFeishuChannelRuntime()).probeFeishu(account),
|
|
1226
|
-
resolveAccountSnapshot: ({ account, runtime }) => ({
|
|
1227
|
-
accountId: account.accountId,
|
|
1228
|
-
enabled: account.enabled,
|
|
1229
|
-
configured: account.configured,
|
|
1230
|
-
name: account.name,
|
|
1231
|
-
extra: {
|
|
1232
|
-
appId: account.appId,
|
|
1233
|
-
domain: account.domain,
|
|
1234
|
-
port: runtime?.port ?? null,
|
|
1235
|
-
},
|
|
1236
|
-
}),
|
|
1237
|
-
}),
|
|
1238
|
-
gateway: {
|
|
1239
|
-
startAccount: async (ctx) => {
|
|
1240
|
-
const { monitorFeishuProvider } = await import("./monitor.js");
|
|
1241
|
-
const account = resolveFeishuRuntimeAccount(
|
|
1242
|
-
{ cfg: ctx.cfg, accountId: ctx.accountId },
|
|
1243
|
-
{ requireEventSecrets: true },
|
|
1244
|
-
);
|
|
1245
|
-
const port = account.config?.webhookPort ?? null;
|
|
1246
|
-
ctx.setStatus({ accountId: ctx.accountId, port });
|
|
1247
|
-
ctx.log?.info(
|
|
1248
|
-
`starting feishu[${ctx.accountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
|
|
1249
|
-
);
|
|
1250
|
-
return monitorFeishuProvider({
|
|
1251
|
-
config: ctx.cfg,
|
|
1252
|
-
runtime: ctx.runtime,
|
|
1253
|
-
abortSignal: ctx.abortSignal,
|
|
1254
|
-
accountId: ctx.accountId,
|
|
1255
|
-
});
|
|
1256
|
-
},
|
|
1257
|
-
},
|
|
1258
|
-
},
|
|
1259
|
-
security: {
|
|
1260
|
-
collectWarnings: projectConfigAccountIdWarningCollector<{
|
|
1261
|
-
cfg: ClawdbotConfig;
|
|
1262
|
-
accountId?: string | null;
|
|
1263
|
-
}>(collectFeishuSecurityWarnings),
|
|
1264
|
-
collectAuditFindings: ({ cfg }) => collectFeishuSecurityAuditFindings({ cfg }),
|
|
1265
|
-
},
|
|
1266
|
-
pairing: {
|
|
1267
|
-
text: {
|
|
1268
|
-
idLabel: "feishuUserId",
|
|
1269
|
-
message: PAIRING_APPROVED_MESSAGE,
|
|
1270
|
-
normalizeAllowEntry: createPairingPrefixStripper(/^(feishu|user|open_id):/i),
|
|
1271
|
-
notify: async ({ cfg, id, message, accountId }) => {
|
|
1272
|
-
const { sendMessageFeishu } = await loadFeishuChannelRuntime();
|
|
1273
|
-
await sendMessageFeishu({
|
|
1274
|
-
cfg,
|
|
1275
|
-
to: id,
|
|
1276
|
-
text: message,
|
|
1277
|
-
accountId,
|
|
1278
|
-
});
|
|
1279
|
-
},
|
|
1280
|
-
},
|
|
1281
|
-
},
|
|
1282
|
-
outbound: {
|
|
1283
|
-
deliveryMode: "direct",
|
|
1284
|
-
chunker: chunkTextForOutbound,
|
|
1285
|
-
chunkerMode: "markdown",
|
|
1286
|
-
textChunkLimit: 4000,
|
|
1287
|
-
presentationCapabilities: {
|
|
1288
|
-
supported: true,
|
|
1289
|
-
buttons: true,
|
|
1290
|
-
selects: false,
|
|
1291
|
-
context: true,
|
|
1292
|
-
divider: true,
|
|
1293
|
-
},
|
|
1294
|
-
renderPresentation: async (ctx) => {
|
|
1295
|
-
const runtime = await loadFeishuChannelRuntime();
|
|
1296
|
-
const renderPresentation = runtime.feishuOutbound.renderPresentation;
|
|
1297
|
-
return renderPresentation ? await renderPresentation(ctx) : null;
|
|
1298
|
-
},
|
|
1299
|
-
sendPayload: async (ctx) => {
|
|
1300
|
-
const runtime = await loadFeishuChannelRuntime();
|
|
1301
|
-
const sendPayload = runtime.feishuOutbound.sendPayload;
|
|
1302
|
-
if (!sendPayload) {
|
|
1303
|
-
throw new Error("Feishu payload sending is not available.");
|
|
1304
|
-
}
|
|
1305
|
-
return await sendPayload(ctx);
|
|
1306
|
-
},
|
|
1307
|
-
...createRuntimeOutboundDelegates({
|
|
1308
|
-
getRuntime: loadFeishuChannelRuntime,
|
|
1309
|
-
sendText: { resolve: (runtime) => runtime.feishuOutbound.sendText },
|
|
1310
|
-
sendMedia: { resolve: (runtime) => runtime.feishuOutbound.sendMedia },
|
|
1311
|
-
}),
|
|
1312
|
-
},
|
|
1313
|
-
});
|