@m1heng-clawd/feishu 0.1.2 → 0.1.4
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/README.md +61 -15
- package/index.ts +17 -3
- package/{clawdbot.plugin.json → openclaw.plugin.json} +1 -0
- package/package.json +8 -7
- package/src/accounts.ts +2 -2
- package/src/bot.ts +34 -3
- package/src/channel.ts +2 -2
- package/src/directory.ts +1 -1
- package/src/docx.ts +711 -0
- package/src/media.ts +1 -1
- package/src/mention.ts +121 -0
- package/src/monitor.ts +1 -1
- package/src/onboarding.ts +2 -2
- package/src/outbound.ts +1 -1
- package/src/policy.ts +1 -1
- package/src/reactions.ts +1 -1
- package/src/reply-dispatcher.ts +12 -2
- package/src/runtime.ts +1 -1
- package/src/send.ts +22 -5
- package/src/types.ts +5 -0
- package/src/typing.ts +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# clawd-feishu
|
|
2
2
|
|
|
3
|
-
Feishu/Lark (飞书) channel plugin for [
|
|
3
|
+
Feishu/Lark (飞书) channel plugin for [OpenClaw](https://github.com/openclaw/openclaw).
|
|
4
4
|
|
|
5
5
|
[English](#english) | [中文](#中文)
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@ Feishu/Lark (飞书) channel plugin for [Clawdbot](https://github.com/clawdbot/c
|
|
|
11
11
|
### Installation
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
|
|
14
|
+
openclaw plugins install @m1heng-clawd/feishu
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Or install via npm:
|
|
@@ -49,6 +49,18 @@ npm install @m1heng-clawd/feishu
|
|
|
49
49
|
| `im:message:recall` | Recall | Recall sent messages |
|
|
50
50
|
| `im:message.reactions:read` | Reactions | View message reactions |
|
|
51
51
|
|
|
52
|
+
#### Document Tools Permissions
|
|
53
|
+
|
|
54
|
+
Required if using Feishu document tools (`feishu_doc_*`):
|
|
55
|
+
|
|
56
|
+
| Permission | Description |
|
|
57
|
+
|------------|-------------|
|
|
58
|
+
| `docx:document` | Create/edit documents |
|
|
59
|
+
| `docx:document:readonly` | Read documents |
|
|
60
|
+
| `docx:document.block:convert` | Markdown to blocks conversion (required for write/append) |
|
|
61
|
+
| `drive:drive` | Upload images to documents |
|
|
62
|
+
| `drive:drive:readonly` | List folders |
|
|
63
|
+
|
|
52
64
|
#### Event Subscriptions ⚠️
|
|
53
65
|
|
|
54
66
|
> **This is the most commonly missed configuration!** If the bot can send messages but cannot receive them, check this section.
|
|
@@ -68,9 +80,9 @@ In the Feishu Open Platform console, go to **Events & Callbacks**:
|
|
|
68
80
|
3. Ensure the event permissions are approved
|
|
69
81
|
|
|
70
82
|
```bash
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
openclaw config set channels.feishu.appId "cli_xxxxx"
|
|
84
|
+
openclaw config set channels.feishu.appSecret "your_app_secret"
|
|
85
|
+
openclaw config set channels.feishu.enabled true
|
|
74
86
|
```
|
|
75
87
|
|
|
76
88
|
### Configuration Options
|
|
@@ -116,6 +128,17 @@ channels:
|
|
|
116
128
|
- Pairing flow for DM approval
|
|
117
129
|
- User and group directory lookup
|
|
118
130
|
- **Card render mode**: Optional markdown rendering with syntax highlighting
|
|
131
|
+
- **Document tools**: Read, create, and write Feishu documents with markdown (tables not supported due to API limitations)
|
|
132
|
+
- **@mention forwarding**: When you @mention someone in your message, the bot's reply will automatically @mention them too
|
|
133
|
+
|
|
134
|
+
#### @Mention Forwarding
|
|
135
|
+
|
|
136
|
+
When you want the bot to @mention someone in its reply, simply @mention them in your message:
|
|
137
|
+
|
|
138
|
+
- **In DM**: `@张三 say hello` → Bot replies with `@张三 Hello!`
|
|
139
|
+
- **In Group**: `@bot @张三 say hello` → Bot replies with `@张三 Hello!`
|
|
140
|
+
|
|
141
|
+
The bot automatically detects @mentions in your message and includes them in its reply. No extra permissions required beyond the standard messaging permissions.
|
|
119
142
|
|
|
120
143
|
### FAQ
|
|
121
144
|
|
|
@@ -141,14 +164,14 @@ Feishu API has rate limits. Streaming updates can easily trigger throttling. We
|
|
|
141
164
|
|
|
142
165
|
#### Windows install error `spawn npm ENOENT`
|
|
143
166
|
|
|
144
|
-
If `
|
|
167
|
+
If `openclaw plugins install` fails, install manually:
|
|
145
168
|
|
|
146
169
|
```bash
|
|
147
170
|
# 1. Download the package
|
|
148
|
-
curl -O https://registry.npmjs.org/@m1heng-clawd/feishu/-/feishu-0.1.
|
|
171
|
+
curl -O https://registry.npmjs.org/@m1heng-clawd/feishu/-/feishu-0.1.3.tgz
|
|
149
172
|
|
|
150
173
|
# 2. Install from local file
|
|
151
|
-
|
|
174
|
+
openclaw plugins install ./feishu-0.1.3.tgz
|
|
152
175
|
```
|
|
153
176
|
|
|
154
177
|
#### Cannot find the bot in Feishu
|
|
@@ -164,7 +187,7 @@ clawdbot plugins install ./feishu-0.1.1.tgz
|
|
|
164
187
|
### 安装
|
|
165
188
|
|
|
166
189
|
```bash
|
|
167
|
-
|
|
190
|
+
openclaw plugins install @m1heng-clawd/feishu
|
|
168
191
|
```
|
|
169
192
|
|
|
170
193
|
或通过 npm 安装:
|
|
@@ -202,6 +225,18 @@ npm install @m1heng-clawd/feishu
|
|
|
202
225
|
| `im:message:recall` | 撤回 | 撤回已发送消息 |
|
|
203
226
|
| `im:message.reactions:read` | 表情 | 查看消息表情回复 |
|
|
204
227
|
|
|
228
|
+
#### 文档工具权限
|
|
229
|
+
|
|
230
|
+
使用飞书文档工具(`feishu_doc_*`)需要以下权限:
|
|
231
|
+
|
|
232
|
+
| 权限 | 说明 |
|
|
233
|
+
|------|------|
|
|
234
|
+
| `docx:document` | 创建/编辑文档 |
|
|
235
|
+
| `docx:document:readonly` | 读取文档 |
|
|
236
|
+
| `docx:document.block:convert` | Markdown 转 blocks(write/append 必需) |
|
|
237
|
+
| `drive:drive` | 上传图片到文档 |
|
|
238
|
+
| `drive:drive:readonly` | 列出文件夹 |
|
|
239
|
+
|
|
205
240
|
#### 事件订阅 ⚠️
|
|
206
241
|
|
|
207
242
|
> **这是最容易遗漏的配置!** 如果机器人能发消息但收不到消息,请检查此项。
|
|
@@ -221,9 +256,9 @@ npm install @m1heng-clawd/feishu
|
|
|
221
256
|
3. 确保事件订阅的权限已申请并通过审核
|
|
222
257
|
|
|
223
258
|
```bash
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
259
|
+
openclaw config set channels.feishu.appId "cli_xxxxx"
|
|
260
|
+
openclaw config set channels.feishu.appSecret "your_app_secret"
|
|
261
|
+
openclaw config set channels.feishu.enabled true
|
|
227
262
|
```
|
|
228
263
|
|
|
229
264
|
### 配置选项
|
|
@@ -269,6 +304,17 @@ channels:
|
|
|
269
304
|
- 私聊配对审批流程
|
|
270
305
|
- 用户和群组目录查询
|
|
271
306
|
- **卡片渲染模式**:支持语法高亮的 Markdown 渲染
|
|
307
|
+
- **文档工具**:读取、创建、用 Markdown 写入飞书文档(表格因 API 限制不支持)
|
|
308
|
+
- **@ 转发功能**:在消息中 @ 某人,机器人的回复会自动 @ 该用户
|
|
309
|
+
|
|
310
|
+
#### @ 转发功能
|
|
311
|
+
|
|
312
|
+
如果你希望机器人的回复中 @ 某人,只需在你的消息中 @ 他们:
|
|
313
|
+
|
|
314
|
+
- **私聊**:`@张三 跟他问好` → 机器人回复 `@张三 你好!`
|
|
315
|
+
- **群聊**:`@机器人 @张三 跟他问好` → 机器人回复 `@张三 你好!`
|
|
316
|
+
|
|
317
|
+
机器人会自动检测消息中的 @ 并在回复时带上。无需额外权限。
|
|
272
318
|
|
|
273
319
|
### 常见问题
|
|
274
320
|
|
|
@@ -294,14 +340,14 @@ channels:
|
|
|
294
340
|
|
|
295
341
|
#### Windows 安装报错 `spawn npm ENOENT`
|
|
296
342
|
|
|
297
|
-
如果 `
|
|
343
|
+
如果 `openclaw plugins install` 失败,可以手动安装:
|
|
298
344
|
|
|
299
345
|
```bash
|
|
300
346
|
# 1. 下载插件包
|
|
301
|
-
curl -O https://registry.npmjs.org/@m1heng-clawd/feishu/-/feishu-0.1.
|
|
347
|
+
curl -O https://registry.npmjs.org/@m1heng-clawd/feishu/-/feishu-0.1.3.tgz
|
|
302
348
|
|
|
303
349
|
# 2. 从本地安装
|
|
304
|
-
|
|
350
|
+
openclaw plugins install ./feishu-0.1.3.tgz
|
|
305
351
|
```
|
|
306
352
|
|
|
307
353
|
#### 在飞书里找不到机器人
|
package/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { emptyPluginConfigSchema } from "
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
3
|
import { feishuPlugin } from "./src/channel.js";
|
|
4
4
|
import { setFeishuRuntime } from "./src/runtime.js";
|
|
5
|
+
import { registerFeishuDocTools } from "./src/docx.js";
|
|
5
6
|
|
|
6
7
|
export { monitorFeishuProvider } from "./src/monitor.js";
|
|
7
8
|
export {
|
|
@@ -25,6 +26,18 @@ export {
|
|
|
25
26
|
listReactionsFeishu,
|
|
26
27
|
FeishuEmoji,
|
|
27
28
|
} from "./src/reactions.js";
|
|
29
|
+
export {
|
|
30
|
+
extractMentionTargets,
|
|
31
|
+
extractMessageBody,
|
|
32
|
+
isMentionForwardRequest,
|
|
33
|
+
formatMentionForText,
|
|
34
|
+
formatMentionForCard,
|
|
35
|
+
formatMentionAllForText,
|
|
36
|
+
formatMentionAllForCard,
|
|
37
|
+
buildMentionedMessage,
|
|
38
|
+
buildMentionedCardContent,
|
|
39
|
+
type MentionTarget,
|
|
40
|
+
} from "./src/mention.js";
|
|
28
41
|
export { feishuPlugin } from "./src/channel.js";
|
|
29
42
|
|
|
30
43
|
const plugin = {
|
|
@@ -32,9 +45,10 @@ const plugin = {
|
|
|
32
45
|
name: "Feishu",
|
|
33
46
|
description: "Feishu/Lark channel plugin",
|
|
34
47
|
configSchema: emptyPluginConfigSchema(),
|
|
35
|
-
register(api:
|
|
48
|
+
register(api: OpenClawPluginApi) {
|
|
36
49
|
setFeishuRuntime(api.runtime);
|
|
37
50
|
api.registerChannel({ plugin: feishuPlugin });
|
|
51
|
+
registerFeishuDocTools(api);
|
|
38
52
|
},
|
|
39
53
|
};
|
|
40
54
|
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m1heng-clawd/feishu",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "OpenClaw Feishu/Lark channel plugin",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"files": [
|
|
8
8
|
"index.ts",
|
|
9
9
|
"src",
|
|
10
|
-
"
|
|
10
|
+
"openclaw.plugin.json"
|
|
11
11
|
],
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
14
|
"url": "git+https://github.com/m1heng/clawdbot-feishu.git"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
17
|
-
"
|
|
17
|
+
"openclaw",
|
|
18
18
|
"feishu",
|
|
19
19
|
"lark",
|
|
20
20
|
"飞书",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"ai",
|
|
23
23
|
"claude"
|
|
24
24
|
],
|
|
25
|
-
"
|
|
25
|
+
"openclaw": {
|
|
26
26
|
"extensions": [
|
|
27
27
|
"./index.ts"
|
|
28
28
|
],
|
|
@@ -46,15 +46,16 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@larksuiteoapi/node-sdk": "^1.30.0",
|
|
49
|
+
"@sinclair/typebox": "^0.34.48",
|
|
49
50
|
"zod": "^4.3.6"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
53
|
"@types/node": "^25.0.10",
|
|
53
|
-
"
|
|
54
|
+
"openclaw": "2026.1.29",
|
|
54
55
|
"tsx": "^4.21.0",
|
|
55
56
|
"typescript": "^5.7.0"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
|
-
"
|
|
59
|
+
"openclaw": ">=2026.1.29"
|
|
59
60
|
}
|
|
60
61
|
}
|
package/src/accounts.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ClawdbotConfig } from "
|
|
2
|
-
import { DEFAULT_ACCOUNT_ID } from "
|
|
1
|
+
import type { ClawdbotConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk";
|
|
3
3
|
import type { FeishuConfig, FeishuDomain, ResolvedFeishuAccount } from "./types.js";
|
|
4
4
|
|
|
5
5
|
export function resolveFeishuCredentials(cfg?: FeishuConfig): {
|
package/src/bot.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { ClawdbotConfig, RuntimeEnv } from "
|
|
1
|
+
import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
2
2
|
import {
|
|
3
3
|
buildPendingHistoryContextFromMap,
|
|
4
4
|
recordPendingHistoryEntryIfEnabled,
|
|
5
5
|
clearHistoryEntriesIfEnabled,
|
|
6
6
|
DEFAULT_GROUP_HISTORY_LIMIT,
|
|
7
7
|
type HistoryEntry,
|
|
8
|
-
} from "
|
|
8
|
+
} from "openclaw/plugin-sdk";
|
|
9
9
|
import type { FeishuConfig, FeishuMessageContext, FeishuMediaInfo } from "./types.js";
|
|
10
10
|
import { getFeishuRuntime } from "./runtime.js";
|
|
11
11
|
import { createFeishuClient } from "./client.js";
|
|
@@ -18,6 +18,11 @@ import {
|
|
|
18
18
|
import { createFeishuReplyDispatcher } from "./reply-dispatcher.js";
|
|
19
19
|
import { getMessageFeishu } from "./send.js";
|
|
20
20
|
import { downloadImageFeishu, downloadMessageResourceFeishu } from "./media.js";
|
|
21
|
+
import {
|
|
22
|
+
extractMentionTargets,
|
|
23
|
+
extractMessageBody,
|
|
24
|
+
isMentionForwardRequest,
|
|
25
|
+
} from "./mention.js";
|
|
21
26
|
|
|
22
27
|
// --- Sender name resolution (so the agent can distinguish who is speaking in group chats) ---
|
|
23
28
|
// Cache display names by open_id to avoid an API call on every message.
|
|
@@ -399,7 +404,7 @@ export function parseFeishuMessageEvent(
|
|
|
399
404
|
const mentionedBot = checkBotMentioned(event, botOpenId);
|
|
400
405
|
const content = stripBotMention(rawContent, event.message.mentions);
|
|
401
406
|
|
|
402
|
-
|
|
407
|
+
const ctx: FeishuMessageContext = {
|
|
403
408
|
chatId: event.message.chat_id,
|
|
404
409
|
messageId: event.message.message_id,
|
|
405
410
|
senderId: event.sender.sender_id.user_id || event.sender.sender_id.open_id || "",
|
|
@@ -411,6 +416,19 @@ export function parseFeishuMessageEvent(
|
|
|
411
416
|
content,
|
|
412
417
|
contentType: event.message.message_type,
|
|
413
418
|
};
|
|
419
|
+
|
|
420
|
+
// Detect mention forward request: message mentions bot + at least one other user
|
|
421
|
+
if (isMentionForwardRequest(event, botOpenId)) {
|
|
422
|
+
const mentionTargets = extractMentionTargets(event, botOpenId);
|
|
423
|
+
if (mentionTargets.length > 0) {
|
|
424
|
+
ctx.mentionTargets = mentionTargets;
|
|
425
|
+
// Extract message body (remove all @ placeholders)
|
|
426
|
+
const allMentionKeys = (event.message.mentions ?? []).map((m) => m.key);
|
|
427
|
+
ctx.mentionMessageBody = extractMessageBody(content, allMentionKeys);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return ctx;
|
|
414
432
|
}
|
|
415
433
|
|
|
416
434
|
export async function handleFeishuMessage(params: {
|
|
@@ -438,6 +456,12 @@ export async function handleFeishuMessage(params: {
|
|
|
438
456
|
|
|
439
457
|
log(`feishu: received message from ${ctx.senderOpenId} in ${ctx.chatId} (${ctx.chatType})`);
|
|
440
458
|
|
|
459
|
+
// Log mention targets if detected
|
|
460
|
+
if (ctx.mentionTargets && ctx.mentionTargets.length > 0) {
|
|
461
|
+
const names = ctx.mentionTargets.map((t) => t.name).join(", ");
|
|
462
|
+
log(`feishu: detected @ forward request, targets: [${names}]`);
|
|
463
|
+
}
|
|
464
|
+
|
|
441
465
|
const historyLimit = Math.max(
|
|
442
466
|
0,
|
|
443
467
|
feishuCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT,
|
|
@@ -566,6 +590,12 @@ export async function handleFeishuMessage(params: {
|
|
|
566
590
|
const speaker = ctx.senderName ?? ctx.senderOpenId;
|
|
567
591
|
messageBody = `${speaker}: ${messageBody}`;
|
|
568
592
|
|
|
593
|
+
// If there are mention targets, inform the agent that replies will auto-mention them
|
|
594
|
+
if (ctx.mentionTargets && ctx.mentionTargets.length > 0) {
|
|
595
|
+
const targetNames = ctx.mentionTargets.map((t) => t.name).join(", ");
|
|
596
|
+
messageBody += `\n\n[System: Your reply will automatically @mention: ${targetNames}. Do not write @xxx yourself.]`;
|
|
597
|
+
}
|
|
598
|
+
|
|
569
599
|
const envelopeFrom = isGroup ? `${ctx.chatId}:${ctx.senderOpenId}` : ctx.senderOpenId;
|
|
570
600
|
|
|
571
601
|
const body = core.channel.reply.formatAgentEnvelope({
|
|
@@ -626,6 +656,7 @@ export async function handleFeishuMessage(params: {
|
|
|
626
656
|
runtime: runtime as RuntimeEnv,
|
|
627
657
|
chatId: ctx.chatId,
|
|
628
658
|
replyToMessageId: ctx.messageId,
|
|
659
|
+
mentionTargets: ctx.mentionTargets,
|
|
629
660
|
});
|
|
630
661
|
|
|
631
662
|
log(`feishu: dispatching to agent (session=${route.sessionKey})`);
|
package/src/channel.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ChannelPlugin, ClawdbotConfig } from "
|
|
2
|
-
import { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE } from "
|
|
1
|
+
import type { ChannelPlugin, ClawdbotConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk";
|
|
3
3
|
import type { ResolvedFeishuAccount, FeishuConfig } from "./types.js";
|
|
4
4
|
import { resolveFeishuAccount, resolveFeishuCredentials } from "./accounts.js";
|
|
5
5
|
import { feishuOutbound } from "./outbound.js";
|
package/src/directory.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClawdbotConfig } from "
|
|
1
|
+
import type { ClawdbotConfig } from "openclaw/plugin-sdk";
|
|
2
2
|
import type { FeishuConfig } from "./types.js";
|
|
3
3
|
import { createFeishuClient } from "./client.js";
|
|
4
4
|
import { normalizeFeishuTarget } from "./targets.js";
|