@openclaw/feishu 2026.5.7 → 2026.5.10-beta.1
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/api.js +3 -2
- package/dist/{channel-BegH3cJm.js → channel-DCSECj4a.js} +63 -9
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-DYsXcD36.js → channel.runtime-BlDkd6xR.js} +35 -18
- package/dist/{monitor-BVR-x39Q.js → monitor-BnbCJWIU.js} +1 -1
- package/dist/{monitor.account-CUZxYkjE.js → monitor.account-88yMtLAU.js} +178 -129
- package/dist/policy-DrLgxWF_.js +181 -0
- package/dist/runtime-api.js +1 -1
- package/dist/{send-DowxxbpH.js → send-DKHEQrzH.js} +48 -61
- package/dist/{card-interaction-BfRLgvw_.js → send-result-BVFTskB_.js} +45 -1
- package/dist/setup-api.js +1 -1
- package/package.json +6 -6
- package/dist/policy-D6c-wMPl.js +0 -118
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { t as detectIdType } from "./targets-JMFJRKSe.js";
|
|
2
|
+
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
|
|
3
|
+
import { normalizeAccountId, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
|
|
4
|
+
import { createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
5
|
+
//#region extensions/feishu/src/policy.ts
|
|
6
|
+
const FEISHU_PROVIDER_PREFIX_RE = /^(feishu|lark):/i;
|
|
7
|
+
const FEISHU_TYPED_PREFIX_RE = /^(chat|group|channel|user|dm|open_id):/i;
|
|
8
|
+
const FEISHU_ID_KIND = "plugin:feishu-id";
|
|
9
|
+
const feishuIngressIdentity = defineStableChannelIngressIdentity({
|
|
10
|
+
key: "feishu-id",
|
|
11
|
+
kind: FEISHU_ID_KIND,
|
|
12
|
+
normalize: normalizeFeishuAllowEntry,
|
|
13
|
+
sensitivity: "pii",
|
|
14
|
+
aliases: [{
|
|
15
|
+
key: "feishu-alt-id",
|
|
16
|
+
kind: FEISHU_ID_KIND,
|
|
17
|
+
normalizeEntry: () => null,
|
|
18
|
+
normalizeSubject: normalizeFeishuAllowEntry,
|
|
19
|
+
sensitivity: "pii"
|
|
20
|
+
}],
|
|
21
|
+
isWildcardEntry: (entry) => normalizeFeishuAllowEntry(entry) === "*",
|
|
22
|
+
resolveEntryId: ({ entryIndex }) => `feishu-entry-${entryIndex + 1}`
|
|
23
|
+
});
|
|
24
|
+
function normalizeFeishuAllowEntry(raw) {
|
|
25
|
+
const trimmed = raw.trim();
|
|
26
|
+
if (!trimmed) return "";
|
|
27
|
+
if (trimmed === "*") return "*";
|
|
28
|
+
let withoutProviderPrefix = trimmed;
|
|
29
|
+
while (FEISHU_PROVIDER_PREFIX_RE.test(withoutProviderPrefix)) withoutProviderPrefix = withoutProviderPrefix.replace(FEISHU_PROVIDER_PREFIX_RE, "").trim();
|
|
30
|
+
if (withoutProviderPrefix === "*") return "*";
|
|
31
|
+
const lowered = normalizeOptionalLowercaseString(withoutProviderPrefix) ?? "";
|
|
32
|
+
if (!lowered) return "";
|
|
33
|
+
const prefixed = lowered.match(FEISHU_TYPED_PREFIX_RE);
|
|
34
|
+
if (prefixed?.[1]) {
|
|
35
|
+
const kind = [
|
|
36
|
+
"chat",
|
|
37
|
+
"group",
|
|
38
|
+
"channel"
|
|
39
|
+
].includes(prefixed[1]) ? "chat" : "user";
|
|
40
|
+
const value = withoutProviderPrefix.slice(prefixed[0].length).trim();
|
|
41
|
+
return value === "*" ? "*" : value ? `${kind}:${value}` : "";
|
|
42
|
+
}
|
|
43
|
+
const detectedType = detectIdType(withoutProviderPrefix);
|
|
44
|
+
if (detectedType === "chat_id") return `chat:${withoutProviderPrefix}`;
|
|
45
|
+
if (detectedType === "open_id" || detectedType === "user_id") return `user:${withoutProviderPrefix}`;
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
function normalizeFeishuDmPolicy(policy) {
|
|
49
|
+
return policy === "open" || policy === "pairing" || policy === "allowlist" || policy === "disabled" ? policy : "pairing";
|
|
50
|
+
}
|
|
51
|
+
function normalizeFeishuGroupPolicy(policy) {
|
|
52
|
+
return policy === "allowall" ? "open" : policy;
|
|
53
|
+
}
|
|
54
|
+
function createFeishuIngressSubject(params) {
|
|
55
|
+
const ids = [params.primaryId, ...params.alternateIds ?? []].map((value) => value?.trim()).filter((value) => Boolean(value));
|
|
56
|
+
return {
|
|
57
|
+
stableId: ids[0],
|
|
58
|
+
aliases: { "feishu-alt-id": ids[1] }
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function createFeishuIngressResolver(params) {
|
|
62
|
+
return createChannelIngressResolver({
|
|
63
|
+
channelId: "feishu",
|
|
64
|
+
accountId: normalizeAccountId(params.accountId) ?? "default",
|
|
65
|
+
identity: feishuIngressIdentity,
|
|
66
|
+
cfg: params.cfg,
|
|
67
|
+
...params.readAllowFromStore ? { readStoreAllowFrom: params.readAllowFromStore } : {}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function resolveFeishuDmIngressAccess(params) {
|
|
71
|
+
return await createFeishuIngressResolver({
|
|
72
|
+
cfg: params.cfg,
|
|
73
|
+
accountId: params.accountId,
|
|
74
|
+
readAllowFromStore: params.readAllowFromStore
|
|
75
|
+
}).message({
|
|
76
|
+
subject: createFeishuIngressSubject({
|
|
77
|
+
primaryId: params.senderOpenId,
|
|
78
|
+
alternateIds: [params.senderUserId]
|
|
79
|
+
}),
|
|
80
|
+
conversation: {
|
|
81
|
+
kind: "direct",
|
|
82
|
+
id: params.conversationId
|
|
83
|
+
},
|
|
84
|
+
event: { mayPair: params.mayPair },
|
|
85
|
+
dmPolicy: normalizeFeishuDmPolicy(params.dmPolicy),
|
|
86
|
+
groupPolicy: "disabled",
|
|
87
|
+
allowFrom: params.allowFrom ?? [],
|
|
88
|
+
...params.command ? { command: params.command } : {}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async function resolveFeishuGroupConversationIngressAccess(params) {
|
|
92
|
+
const groupPolicy = normalizeFeishuGroupPolicy(params.groupPolicy);
|
|
93
|
+
const groupAllowFrom = groupPolicy === "allowlist" && params.groupExplicitlyConfigured ? [...params.groupAllowFrom ?? [], params.chatId] : params.groupAllowFrom ?? [];
|
|
94
|
+
return await createFeishuIngressResolver({
|
|
95
|
+
cfg: params.cfg,
|
|
96
|
+
accountId: params.accountId
|
|
97
|
+
}).message({
|
|
98
|
+
subject: createFeishuIngressSubject({ primaryId: params.chatId }),
|
|
99
|
+
conversation: {
|
|
100
|
+
kind: "group",
|
|
101
|
+
id: params.chatId
|
|
102
|
+
},
|
|
103
|
+
dmPolicy: "disabled",
|
|
104
|
+
groupPolicy,
|
|
105
|
+
groupAllowFrom
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async function resolveFeishuGroupSenderActivationIngressAccess(params) {
|
|
109
|
+
const groupAllowFrom = params.allowFrom ?? [];
|
|
110
|
+
return await createFeishuIngressResolver({
|
|
111
|
+
cfg: params.cfg,
|
|
112
|
+
accountId: params.accountId
|
|
113
|
+
}).message({
|
|
114
|
+
subject: createFeishuIngressSubject({
|
|
115
|
+
primaryId: params.senderOpenId,
|
|
116
|
+
alternateIds: [params.senderUserId]
|
|
117
|
+
}),
|
|
118
|
+
conversation: {
|
|
119
|
+
kind: "group",
|
|
120
|
+
id: params.chatId
|
|
121
|
+
},
|
|
122
|
+
dmPolicy: "disabled",
|
|
123
|
+
groupPolicy: groupAllowFrom.length > 0 ? "allowlist" : "open",
|
|
124
|
+
groupAllowFrom,
|
|
125
|
+
mentionFacts: {
|
|
126
|
+
canDetectMention: true,
|
|
127
|
+
wasMentioned: params.mentionedBot
|
|
128
|
+
},
|
|
129
|
+
policy: { activation: {
|
|
130
|
+
requireMention: params.requireMention,
|
|
131
|
+
allowTextCommands: false
|
|
132
|
+
} },
|
|
133
|
+
...params.command ? { command: params.command } : {}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
function resolveFeishuGroupConfig(params) {
|
|
137
|
+
const groups = params.cfg?.groups ?? {};
|
|
138
|
+
const wildcard = groups["*"];
|
|
139
|
+
const groupId = params.groupId?.trim();
|
|
140
|
+
if (!groupId) return;
|
|
141
|
+
const direct = groups[groupId];
|
|
142
|
+
if (direct) return direct;
|
|
143
|
+
const lowered = normalizeOptionalLowercaseString(groupId) ?? "";
|
|
144
|
+
const matchKey = Object.keys(groups).find((key) => normalizeOptionalLowercaseString(key) === lowered);
|
|
145
|
+
if (matchKey) return groups[matchKey];
|
|
146
|
+
return wildcard;
|
|
147
|
+
}
|
|
148
|
+
function hasExplicitFeishuGroupConfig(params) {
|
|
149
|
+
const groups = params.cfg?.groups ?? {};
|
|
150
|
+
const groupId = params.groupId?.trim();
|
|
151
|
+
if (!groupId) return false;
|
|
152
|
+
if (Object.prototype.hasOwnProperty.call(groups, groupId) && groupId !== "*") return true;
|
|
153
|
+
const lowered = normalizeOptionalLowercaseString(groupId) ?? "";
|
|
154
|
+
return Object.keys(groups).some((key) => key !== "*" && normalizeOptionalLowercaseString(key) === lowered);
|
|
155
|
+
}
|
|
156
|
+
function resolveFeishuGroupToolPolicy(params) {
|
|
157
|
+
const cfg = params.cfg.channels?.feishu;
|
|
158
|
+
if (!cfg) return;
|
|
159
|
+
return resolveFeishuGroupConfig({
|
|
160
|
+
cfg,
|
|
161
|
+
groupId: params.groupId
|
|
162
|
+
})?.tools;
|
|
163
|
+
}
|
|
164
|
+
function resolveFeishuReplyPolicy(params) {
|
|
165
|
+
if (params.isDirectMessage) return { requireMention: false };
|
|
166
|
+
const feishuCfg = params.cfg.channels?.feishu;
|
|
167
|
+
const resolvedCfg = resolveMergedAccountConfig({
|
|
168
|
+
channelConfig: feishuCfg,
|
|
169
|
+
accounts: feishuCfg?.accounts,
|
|
170
|
+
accountId: normalizeAccountId(params.accountId),
|
|
171
|
+
normalizeAccountId,
|
|
172
|
+
omitKeys: ["defaultAccount"]
|
|
173
|
+
});
|
|
174
|
+
const groupRequireMention = resolveFeishuGroupConfig({
|
|
175
|
+
cfg: resolvedCfg,
|
|
176
|
+
groupId: params.groupId
|
|
177
|
+
})?.requireMention;
|
|
178
|
+
return { requireMention: typeof groupRequireMention === "boolean" ? groupRequireMention : typeof resolvedCfg.requireMention === "boolean" ? resolvedCfg.requireMention : params.groupPolicy !== "open" };
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
181
|
+
export { resolveFeishuGroupSenderActivationIngressAccess as a, resolveFeishuGroupConversationIngressAccess as i, resolveFeishuDmIngressAccess as n, resolveFeishuGroupToolPolicy as o, resolveFeishuGroupConfig as r, resolveFeishuReplyPolicy as s, hasExplicitFeishuGroupConfig as t };
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { n as setFeishuRuntime } from "./runtime-CG0DuRCy.js";
|
|
2
2
|
import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
|
|
3
|
+
import { createReplyPrefixContext } from "openclaw/plugin-sdk/channel-message";
|
|
3
4
|
import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
|
|
4
5
|
import { PAIRING_APPROVED_MESSAGE, buildProbeChannelStatusSummary, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/channel-status";
|
|
5
6
|
import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
|
|
6
7
|
import { DEFAULT_ACCOUNT_ID, buildChannelConfigSchema, createActionGate, createDedupeCache } from "openclaw/plugin-sdk/core";
|
|
7
8
|
import { buildAgentMediaPayload } from "openclaw/plugin-sdk/agent-media-payload";
|
|
8
|
-
import { createReplyPrefixContext } from "openclaw/plugin-sdk/channel-reply-pipeline";
|
|
9
9
|
import { evaluateSupplementalContextVisibility, filterSupplementalContextItems, resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
|
|
10
10
|
import { loadSessionStore, resolveSessionStoreEntry } from "openclaw/plugin-sdk/session-store-runtime";
|
|
11
11
|
import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { i as resolveReceiveIdType, r as normalizeFeishuTarget } from "./targets-JMFJRKSe.js";
|
|
2
2
|
import { c as createFeishuApiError, f as isRecord$2, g as requestFeishuApi, s as resolveFeishuRuntimeAccount } from "./accounts-Ba3-WP1z.js";
|
|
3
|
+
import { i as toFeishuSendResult, r as resolveFeishuReceiptKind, t as assertFeishuMessageApiSuccess } from "./send-result-BVFTskB_.js";
|
|
3
4
|
import { t as getFeishuRuntime } from "./runtime-CG0DuRCy.js";
|
|
4
5
|
import { r as createFeishuClient } from "./client-DBVoQL5w.js";
|
|
5
6
|
import { convertMarkdownTables, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
|
|
6
7
|
import fs from "node:fs";
|
|
7
8
|
import path from "node:path";
|
|
9
|
+
import { mediaKindFromMime } from "openclaw/plugin-sdk/media-mime";
|
|
8
10
|
import { MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS, runFfmpeg } from "openclaw/plugin-sdk/media-runtime";
|
|
11
|
+
import { readRegularFile, writeExternalFileWithinRoot } from "openclaw/plugin-sdk/security-runtime";
|
|
9
12
|
import { Readable } from "node:stream";
|
|
10
|
-
import {
|
|
11
|
-
import { resolvePreferredOpenClawTmpDir, withTempDownloadPath } from "openclaw/plugin-sdk/temp-path";
|
|
13
|
+
import { resolvePreferredOpenClawTmpDir, withTempDownloadPath, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
|
|
12
14
|
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
13
15
|
//#region extensions/feishu/src/external-keys.ts
|
|
14
16
|
const CONTROL_CHARS_RE = /\p{Cc}/u;
|
|
@@ -22,17 +24,6 @@ function normalizeFeishuExternalKey(value) {
|
|
|
22
24
|
return normalized;
|
|
23
25
|
}
|
|
24
26
|
//#endregion
|
|
25
|
-
//#region extensions/feishu/src/send-result.ts
|
|
26
|
-
function assertFeishuMessageApiSuccess(response, errorPrefix) {
|
|
27
|
-
if (response.code !== 0) throw new Error(`${errorPrefix}: ${response.msg || `code ${response.code}`}`);
|
|
28
|
-
}
|
|
29
|
-
function toFeishuSendResult(response, chatId) {
|
|
30
|
-
return {
|
|
31
|
-
messageId: response.data?.message_id ?? "unknown",
|
|
32
|
-
chatId
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
//#endregion
|
|
36
27
|
//#region extensions/feishu/src/send-target.ts
|
|
37
28
|
function resolveFeishuSendTarget(params) {
|
|
38
29
|
const target = params.to.trim();
|
|
@@ -237,7 +228,7 @@ async function uploadImageFeishu(params) {
|
|
|
237
228
|
cfg,
|
|
238
229
|
accountId
|
|
239
230
|
});
|
|
240
|
-
const imageData = typeof image === "string" ?
|
|
231
|
+
const imageData = typeof image === "string" ? (await readRegularFile({ filePath: image })).buffer : image;
|
|
241
232
|
return { imageKey: extractFeishuUploadKey(await requestFeishuApi(() => client.im.image.create({ data: {
|
|
242
233
|
image_type: imageType,
|
|
243
234
|
image: imageData
|
|
@@ -269,7 +260,7 @@ async function uploadFileFeishu(params) {
|
|
|
269
260
|
cfg,
|
|
270
261
|
accountId
|
|
271
262
|
});
|
|
272
|
-
const fileData = typeof file === "string" ?
|
|
263
|
+
const fileData = typeof file === "string" ? (await readRegularFile({ filePath: file })).buffer : file;
|
|
273
264
|
const safeFileName = sanitizeFileNameForUpload(fileName);
|
|
274
265
|
return { fileKey: extractFeishuUploadKey(await requestFeishuApi(() => client.im.file.create({ data: {
|
|
275
266
|
file_type: fileType,
|
|
@@ -302,7 +293,7 @@ async function sendImageFeishu(params) {
|
|
|
302
293
|
}
|
|
303
294
|
}), "Feishu image reply failed", { includeNestedErrorLogId: true });
|
|
304
295
|
assertFeishuMessageApiSuccess(response, "Feishu image reply failed");
|
|
305
|
-
return toFeishuSendResult(response, receiveId);
|
|
296
|
+
return toFeishuSendResult(response, receiveId, "media");
|
|
306
297
|
}
|
|
307
298
|
const response = await requestFeishuApi(() => client.im.message.create({
|
|
308
299
|
params: { receive_id_type: receiveIdType },
|
|
@@ -313,7 +304,7 @@ async function sendImageFeishu(params) {
|
|
|
313
304
|
}
|
|
314
305
|
}), "Feishu image send failed", { includeNestedErrorLogId: true });
|
|
315
306
|
assertFeishuMessageApiSuccess(response, "Feishu image send failed");
|
|
316
|
-
return toFeishuSendResult(response, receiveId);
|
|
307
|
+
return toFeishuSendResult(response, receiveId, "media");
|
|
317
308
|
}
|
|
318
309
|
/**
|
|
319
310
|
* Send a file message using a file_key
|
|
@@ -337,7 +328,7 @@ async function sendFileFeishu(params) {
|
|
|
337
328
|
}
|
|
338
329
|
}), "Feishu file reply failed", { includeNestedErrorLogId: true });
|
|
339
330
|
assertFeishuMessageApiSuccess(response, "Feishu file reply failed");
|
|
340
|
-
return toFeishuSendResult(response, receiveId);
|
|
331
|
+
return toFeishuSendResult(response, receiveId, resolveFeishuReceiptKind(msgType));
|
|
341
332
|
}
|
|
342
333
|
const response = await requestFeishuApi(() => client.im.message.create({
|
|
343
334
|
params: { receive_id_type: receiveIdType },
|
|
@@ -348,7 +339,7 @@ async function sendFileFeishu(params) {
|
|
|
348
339
|
}
|
|
349
340
|
}), "Feishu file send failed", { includeNestedErrorLogId: true });
|
|
350
341
|
assertFeishuMessageApiSuccess(response, "Feishu file send failed");
|
|
351
|
-
return toFeishuSendResult(response, receiveId);
|
|
342
|
+
return toFeishuSendResult(response, receiveId, resolveFeishuReceiptKind(msgType));
|
|
352
343
|
}
|
|
353
344
|
/**
|
|
354
345
|
* Helper to detect file type from extension
|
|
@@ -432,51 +423,47 @@ function isLikelyTranscodableAudio(params) {
|
|
|
432
423
|
return FEISHU_TRANSCODABLE_AUDIO_EXTS.has(ext) || mediaKindFromMime(contentType) === "audio";
|
|
433
424
|
}
|
|
434
425
|
async function transcodeToFeishuVoiceOpus(params) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
});
|
|
440
|
-
const tempDir = await fs.promises.mkdtemp(path.join(tempRoot, "feishu-voice-"));
|
|
441
|
-
try {
|
|
426
|
+
return await withTempWorkspace({
|
|
427
|
+
rootDir: resolvePreferredOpenClawTmpDir(),
|
|
428
|
+
prefix: "feishu-voice-"
|
|
429
|
+
}, async (workspace) => {
|
|
442
430
|
const ext = normalizeLowercaseStringOrEmpty(path.extname(params.fileName));
|
|
443
431
|
const inputExt = ext && ext.length <= 12 ? ext : ".audio";
|
|
444
|
-
const inputPath =
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
432
|
+
const inputPath = await workspace.write(`input${inputExt}`, params.buffer);
|
|
433
|
+
await writeExternalFileWithinRoot({
|
|
434
|
+
rootDir: workspace.dir,
|
|
435
|
+
path: FEISHU_VOICE_FILE_NAME,
|
|
436
|
+
write: async (outputPath) => {
|
|
437
|
+
await runFfmpeg([
|
|
438
|
+
"-hide_banner",
|
|
439
|
+
"-loglevel",
|
|
440
|
+
"error",
|
|
441
|
+
"-y",
|
|
442
|
+
"-i",
|
|
443
|
+
inputPath,
|
|
444
|
+
"-vn",
|
|
445
|
+
"-sn",
|
|
446
|
+
"-dn",
|
|
447
|
+
"-t",
|
|
448
|
+
String(MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS),
|
|
449
|
+
"-ar",
|
|
450
|
+
String(FEISHU_VOICE_SAMPLE_RATE_HZ),
|
|
451
|
+
"-ac",
|
|
452
|
+
"1",
|
|
453
|
+
"-c:a",
|
|
454
|
+
"libopus",
|
|
455
|
+
"-b:a",
|
|
456
|
+
FEISHU_VOICE_BITRATE,
|
|
457
|
+
outputPath
|
|
458
|
+
]);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
469
461
|
return {
|
|
470
|
-
buffer: await
|
|
462
|
+
buffer: await workspace.read(FEISHU_VOICE_FILE_NAME),
|
|
471
463
|
fileName: FEISHU_VOICE_FILE_NAME,
|
|
472
464
|
contentType: "audio/ogg"
|
|
473
465
|
};
|
|
474
|
-
}
|
|
475
|
-
await fs.promises.rm(tempDir, {
|
|
476
|
-
recursive: true,
|
|
477
|
-
force: true
|
|
478
|
-
});
|
|
479
|
-
}
|
|
466
|
+
});
|
|
480
467
|
}
|
|
481
468
|
async function prepareFeishuVoiceMedia(params) {
|
|
482
469
|
if (isFeishuNativeVoiceAudio(params)) return params;
|
|
@@ -845,7 +832,7 @@ async function sendFallbackDirect(client, params, errorPrefix) {
|
|
|
845
832
|
}
|
|
846
833
|
}), errorPrefix, { includeNestedErrorLogId: true });
|
|
847
834
|
assertFeishuMessageApiSuccess(response, errorPrefix);
|
|
848
|
-
return toFeishuSendResult(response, params.receiveId);
|
|
835
|
+
return toFeishuSendResult(response, params.receiveId, resolveFeishuReceiptKind(params.msgType));
|
|
849
836
|
}
|
|
850
837
|
async function sendReplyOrFallbackDirect(client, params) {
|
|
851
838
|
if (!params.replyToMessageId) return sendFallbackDirect(client, params.directParams, params.directErrorPrefix);
|
|
@@ -870,7 +857,7 @@ async function sendReplyOrFallbackDirect(client, params) {
|
|
|
870
857
|
return sendFallbackDirect(client, params.directParams, params.directErrorPrefix);
|
|
871
858
|
}
|
|
872
859
|
assertFeishuMessageApiSuccess(response, params.replyErrorPrefix);
|
|
873
|
-
return toFeishuSendResult(response, params.directParams.receiveId);
|
|
860
|
+
return toFeishuSendResult(response, params.directParams.receiveId, resolveFeishuReceiptKind(params.msgType));
|
|
874
861
|
}
|
|
875
862
|
function normalizeCardTemplateVariable(value) {
|
|
876
863
|
if (typeof value === "string") return value;
|
|
@@ -1215,4 +1202,4 @@ async function sendMarkdownCardFeishu(params) {
|
|
|
1215
1202
|
});
|
|
1216
1203
|
}
|
|
1217
1204
|
//#endregion
|
|
1218
|
-
export {
|
|
1205
|
+
export { normalizeFeishuExternalKey as _, sendCardFeishu as a, sendStructuredCardFeishu as c, isFeishuBroadcastMention as d, isMentionForwardRequest as f, shouldSuppressFeishuTextForVoiceMedia as g, sendMediaFeishu as h, resolveFeishuCardTemplate as i, parsePostContent as l, downloadMessageResourceFeishu as m, getMessageFeishu as n, sendMarkdownCardFeishu as o, isFeishuGroupChatType as p, listFeishuThreadMessages as r, sendMessageFeishu as s, editMessageFeishu as t, extractMentionTargets as u };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { f as isRecord } from "./accounts-Ba3-WP1z.js";
|
|
2
|
+
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-message";
|
|
2
3
|
//#region extensions/feishu/src/card-interaction.ts
|
|
3
4
|
const FEISHU_CARD_INTERACTION_VERSION = "ocf1";
|
|
4
5
|
function isInteractionKind(value) {
|
|
@@ -93,4 +94,47 @@ function decodeFeishuCardAction(params) {
|
|
|
93
94
|
};
|
|
94
95
|
}
|
|
95
96
|
//#endregion
|
|
96
|
-
|
|
97
|
+
//#region extensions/feishu/src/send-result.ts
|
|
98
|
+
function resolveFeishuReceiptKind(msgType) {
|
|
99
|
+
switch (msgType) {
|
|
100
|
+
case "audio": return "voice";
|
|
101
|
+
case "image":
|
|
102
|
+
case "media":
|
|
103
|
+
case "file": return "media";
|
|
104
|
+
case "interactive": return "card";
|
|
105
|
+
case "post":
|
|
106
|
+
case "text": return "text";
|
|
107
|
+
default: return "unknown";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function createFeishuSendReceipt(params) {
|
|
111
|
+
const messageId = params.messageId?.trim();
|
|
112
|
+
const chatId = params.chatId.trim();
|
|
113
|
+
return createMessageReceiptFromOutboundResults({
|
|
114
|
+
results: messageId ? [{
|
|
115
|
+
channel: "feishu",
|
|
116
|
+
messageId,
|
|
117
|
+
chatId,
|
|
118
|
+
conversationId: chatId
|
|
119
|
+
}] : [],
|
|
120
|
+
...chatId ? { threadId: chatId } : {},
|
|
121
|
+
kind: params.kind ?? "unknown"
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function assertFeishuMessageApiSuccess(response, errorPrefix) {
|
|
125
|
+
if (response.code !== 0) throw new Error(`${errorPrefix}: ${response.msg || `code ${response.code}`}`);
|
|
126
|
+
}
|
|
127
|
+
function toFeishuSendResult(response, chatId, kind) {
|
|
128
|
+
const messageId = response.data?.message_id ?? "unknown";
|
|
129
|
+
return {
|
|
130
|
+
messageId,
|
|
131
|
+
chatId,
|
|
132
|
+
receipt: createFeishuSendReceipt({
|
|
133
|
+
messageId,
|
|
134
|
+
chatId,
|
|
135
|
+
kind
|
|
136
|
+
})
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
export { FEISHU_CARD_INTERACTION_VERSION as a, decodeFeishuCardAction as c, toFeishuSendResult as i, createFeishuSendReceipt as n, buildFeishuCardActionTextFallback as o, resolveFeishuReceiptKind as r, createFeishuCardInteractionEnvelope as s, assertFeishuMessageApiSuccess as t };
|
package/dist/setup-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-
|
|
1
|
+
import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-DCSECj4a.js";
|
|
2
2
|
export { feishuPlugin, feishuSetupAdapter, feishuSetupWizard };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/feishu",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.10-beta.1",
|
|
4
4
|
"description": "OpenClaw Feishu/Lark channel plugin (community maintained by @m1heng)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@larksuiteoapi/node-sdk": "^1.
|
|
12
|
-
"typebox": "1.1.
|
|
11
|
+
"@larksuiteoapi/node-sdk": "^1.63.1",
|
|
12
|
+
"typebox": "1.1.38"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@openclaw/plugin-sdk": "workspace:*",
|
|
16
16
|
"openclaw": "workspace:*"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"openclaw": ">=2026.5.
|
|
19
|
+
"openclaw": ">=2026.5.10-beta.1"
|
|
20
20
|
},
|
|
21
21
|
"peerDependenciesMeta": {
|
|
22
22
|
"openclaw": {
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"minHostVersion": ">=2026.4.25"
|
|
48
48
|
},
|
|
49
49
|
"compat": {
|
|
50
|
-
"pluginApi": ">=2026.5.
|
|
50
|
+
"pluginApi": ">=2026.5.10-beta.1"
|
|
51
51
|
},
|
|
52
52
|
"build": {
|
|
53
|
-
"openclawVersion": "2026.5.
|
|
53
|
+
"openclawVersion": "2026.5.10-beta.1"
|
|
54
54
|
},
|
|
55
55
|
"release": {
|
|
56
56
|
"publishToClawHub": true,
|
package/dist/policy-D6c-wMPl.js
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { t as detectIdType } from "./targets-JMFJRKSe.js";
|
|
2
|
-
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
|
|
3
|
-
import { normalizeAccountId, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
|
|
4
|
-
import { evaluateSenderGroupAccessForPolicy } from "openclaw/plugin-sdk/group-access";
|
|
5
|
-
//#region extensions/feishu/src/policy.ts
|
|
6
|
-
const FEISHU_PROVIDER_PREFIX_RE = /^(feishu|lark):/i;
|
|
7
|
-
function stripRepeatedFeishuProviderPrefixes(raw) {
|
|
8
|
-
let normalized = raw.trim();
|
|
9
|
-
while (FEISHU_PROVIDER_PREFIX_RE.test(normalized)) normalized = normalized.replace(FEISHU_PROVIDER_PREFIX_RE, "").trim();
|
|
10
|
-
return normalized;
|
|
11
|
-
}
|
|
12
|
-
function canonicalizeFeishuAllowlistKey(params) {
|
|
13
|
-
const value = params.value.trim();
|
|
14
|
-
if (!value) return "";
|
|
15
|
-
if (value === "*") return "*";
|
|
16
|
-
return `${params.kind}:${value}`;
|
|
17
|
-
}
|
|
18
|
-
function normalizeFeishuAllowEntry(raw) {
|
|
19
|
-
const trimmed = raw.trim();
|
|
20
|
-
if (!trimmed) return "";
|
|
21
|
-
if (trimmed === "*") return "*";
|
|
22
|
-
const withoutProviderPrefix = stripRepeatedFeishuProviderPrefixes(trimmed);
|
|
23
|
-
if (withoutProviderPrefix === "*") return "*";
|
|
24
|
-
const lowered = normalizeOptionalLowercaseString(withoutProviderPrefix) ?? "";
|
|
25
|
-
if (!lowered) return "";
|
|
26
|
-
if (lowered.startsWith("chat:") || lowered.startsWith("group:") || lowered.startsWith("channel:")) return canonicalizeFeishuAllowlistKey({
|
|
27
|
-
kind: "chat",
|
|
28
|
-
value: withoutProviderPrefix.slice(withoutProviderPrefix.indexOf(":") + 1)
|
|
29
|
-
});
|
|
30
|
-
if (lowered.startsWith("user:") || lowered.startsWith("dm:")) return canonicalizeFeishuAllowlistKey({
|
|
31
|
-
kind: "user",
|
|
32
|
-
value: withoutProviderPrefix.slice(withoutProviderPrefix.indexOf(":") + 1)
|
|
33
|
-
});
|
|
34
|
-
if (lowered.startsWith("open_id:")) return canonicalizeFeishuAllowlistKey({
|
|
35
|
-
kind: "user",
|
|
36
|
-
value: withoutProviderPrefix.slice(withoutProviderPrefix.indexOf(":") + 1)
|
|
37
|
-
});
|
|
38
|
-
const detectedType = detectIdType(withoutProviderPrefix);
|
|
39
|
-
if (detectedType === "chat_id") return canonicalizeFeishuAllowlistKey({
|
|
40
|
-
kind: "chat",
|
|
41
|
-
value: withoutProviderPrefix
|
|
42
|
-
});
|
|
43
|
-
if (detectedType === "open_id" || detectedType === "user_id") return canonicalizeFeishuAllowlistKey({
|
|
44
|
-
kind: "user",
|
|
45
|
-
value: withoutProviderPrefix
|
|
46
|
-
});
|
|
47
|
-
return "";
|
|
48
|
-
}
|
|
49
|
-
function resolveFeishuAllowlistMatch(params) {
|
|
50
|
-
const allowFrom = params.allowFrom.map((entry) => normalizeFeishuAllowEntry(String(entry))).filter(Boolean);
|
|
51
|
-
if (allowFrom.length === 0) return { allowed: false };
|
|
52
|
-
if (allowFrom.includes("*")) return {
|
|
53
|
-
allowed: true,
|
|
54
|
-
matchKey: "*",
|
|
55
|
-
matchSource: "wildcard"
|
|
56
|
-
};
|
|
57
|
-
const senderCandidates = [params.senderId, ...params.senderIds ?? []].map((entry) => normalizeFeishuAllowEntry(entry ?? "")).filter(Boolean);
|
|
58
|
-
for (const senderId of senderCandidates) if (allowFrom.includes(senderId)) return {
|
|
59
|
-
allowed: true,
|
|
60
|
-
matchKey: senderId,
|
|
61
|
-
matchSource: "id"
|
|
62
|
-
};
|
|
63
|
-
return { allowed: false };
|
|
64
|
-
}
|
|
65
|
-
function resolveFeishuGroupConfig(params) {
|
|
66
|
-
const groups = params.cfg?.groups ?? {};
|
|
67
|
-
const wildcard = groups["*"];
|
|
68
|
-
const groupId = params.groupId?.trim();
|
|
69
|
-
if (!groupId) return;
|
|
70
|
-
const direct = groups[groupId];
|
|
71
|
-
if (direct) return direct;
|
|
72
|
-
const lowered = normalizeOptionalLowercaseString(groupId) ?? "";
|
|
73
|
-
const matchKey = Object.keys(groups).find((key) => normalizeOptionalLowercaseString(key) === lowered);
|
|
74
|
-
if (matchKey) return groups[matchKey];
|
|
75
|
-
return wildcard;
|
|
76
|
-
}
|
|
77
|
-
function hasExplicitFeishuGroupConfig(params) {
|
|
78
|
-
const groups = params.cfg?.groups ?? {};
|
|
79
|
-
const groupId = params.groupId?.trim();
|
|
80
|
-
if (!groupId) return false;
|
|
81
|
-
if (Object.prototype.hasOwnProperty.call(groups, groupId) && groupId !== "*") return true;
|
|
82
|
-
const lowered = normalizeOptionalLowercaseString(groupId) ?? "";
|
|
83
|
-
return Object.keys(groups).some((key) => key !== "*" && normalizeOptionalLowercaseString(key) === lowered);
|
|
84
|
-
}
|
|
85
|
-
function resolveFeishuGroupToolPolicy(params) {
|
|
86
|
-
const cfg = params.cfg.channels?.feishu;
|
|
87
|
-
if (!cfg) return;
|
|
88
|
-
return resolveFeishuGroupConfig({
|
|
89
|
-
cfg,
|
|
90
|
-
groupId: params.groupId
|
|
91
|
-
})?.tools;
|
|
92
|
-
}
|
|
93
|
-
function isFeishuGroupAllowed(params) {
|
|
94
|
-
return evaluateSenderGroupAccessForPolicy({
|
|
95
|
-
groupPolicy: params.groupPolicy === "allowall" ? "open" : params.groupPolicy,
|
|
96
|
-
groupAllowFrom: params.allowFrom.map((entry) => String(entry)),
|
|
97
|
-
senderId: params.senderId,
|
|
98
|
-
isSenderAllowed: () => resolveFeishuAllowlistMatch(params).allowed
|
|
99
|
-
}).allowed;
|
|
100
|
-
}
|
|
101
|
-
function resolveFeishuReplyPolicy(params) {
|
|
102
|
-
if (params.isDirectMessage) return { requireMention: false };
|
|
103
|
-
const feishuCfg = params.cfg.channels?.feishu;
|
|
104
|
-
const resolvedCfg = resolveMergedAccountConfig({
|
|
105
|
-
channelConfig: feishuCfg,
|
|
106
|
-
accounts: feishuCfg?.accounts,
|
|
107
|
-
accountId: normalizeAccountId(params.accountId),
|
|
108
|
-
normalizeAccountId,
|
|
109
|
-
omitKeys: ["defaultAccount"]
|
|
110
|
-
});
|
|
111
|
-
const groupRequireMention = resolveFeishuGroupConfig({
|
|
112
|
-
cfg: resolvedCfg,
|
|
113
|
-
groupId: params.groupId
|
|
114
|
-
})?.requireMention;
|
|
115
|
-
return { requireMention: typeof groupRequireMention === "boolean" ? groupRequireMention : typeof resolvedCfg.requireMention === "boolean" ? resolvedCfg.requireMention : params.groupPolicy !== "open" };
|
|
116
|
-
}
|
|
117
|
-
//#endregion
|
|
118
|
-
export { resolveFeishuGroupToolPolicy as a, resolveFeishuGroupConfig as i, isFeishuGroupAllowed as n, resolveFeishuReplyPolicy as o, resolveFeishuAllowlistMatch as r, hasExplicitFeishuGroupConfig as t };
|