@m1heng-clawd/feishu 0.1.10 → 0.1.11
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 +157 -3
- package/package.json +1 -1
- package/skills/feishu-doc/SKILL.md +24 -0
- package/src/bot.ts +3 -3
- package/src/doc-schema.ts +8 -0
- package/src/doc-write-service.ts +711 -0
- package/src/docx.ts +76 -658
- package/src/drive.ts +2 -29
- package/src/onboarding.ts +14 -4
package/README.md
CHANGED
|
@@ -83,7 +83,7 @@ openclaw plugins update feishu
|
|
|
83
83
|
| Permission | Tool | Description |
|
|
84
84
|
|------------|------|-------------|
|
|
85
85
|
| `docx:document` | `feishu_doc` | Create/edit documents |
|
|
86
|
-
| `docx:document.block:convert` | `feishu_doc` | Markdown to blocks conversion (required for write/append) |
|
|
86
|
+
| `docx:document.block:convert` | `feishu_doc` | Markdown to blocks conversion (required for write/append/create_and_write; also used by `feishu_drive.import_document`) |
|
|
87
87
|
| `drive:drive` | `feishu_doc`, `feishu_drive` | Upload images to documents, create folders, move/delete files |
|
|
88
88
|
| `wiki:wiki` | `feishu_wiki` | Create/move/rename wiki nodes |
|
|
89
89
|
| `bitable:app` | `feishu_bitable` | Create/update/delete bitable records and manage fields |
|
|
@@ -168,6 +168,8 @@ channels:
|
|
|
168
168
|
connectionMode: "websocket"
|
|
169
169
|
# DM policy: "pairing" | "open" | "allowlist"
|
|
170
170
|
dmPolicy: "pairing"
|
|
171
|
+
# DM allowlist (open_id/user_id). Include "*" when dmPolicy="open"
|
|
172
|
+
allowFrom: []
|
|
171
173
|
# Group policy: "open" | "allowlist" | "disabled"
|
|
172
174
|
groupPolicy: "allowlist"
|
|
173
175
|
# Require @mention in groups
|
|
@@ -178,6 +180,81 @@ channels:
|
|
|
178
180
|
renderMode: "auto"
|
|
179
181
|
```
|
|
180
182
|
|
|
183
|
+
#### DM Policy & Access Control
|
|
184
|
+
|
|
185
|
+
`dmPolicy` controls who can interact with the bot in direct messages (DM).
|
|
186
|
+
In multi-account mode, this is resolved per account (`channels.feishu.accounts.<accountId>`).
|
|
187
|
+
|
|
188
|
+
| `dmPolicy` | Who can send DM | How to grant access to a user |
|
|
189
|
+
|------------|------------------|--------------------------------|
|
|
190
|
+
| `pairing` | Users in `allowFrom`, or users approved through pairing | User sends a DM and gets a pairing code; bot owner runs `openclaw pairing approve feishu <code>`. |
|
|
191
|
+
| `open` | Everyone | Set `allowFrom: ["*"]` so all users are treated as allowed. |
|
|
192
|
+
| `allowlist` | Only users in `allowFrom` | Add the user's `open_id`/`user_id` to `allowFrom`, then reload config. |
|
|
193
|
+
|
|
194
|
+
Notes:
|
|
195
|
+
- `allowFrom` accepts Feishu user IDs (`open_id` recommended, `user_id` also supported).
|
|
196
|
+
- If `dmPolicy: "open"`, use `allowFrom: ["*"]`. This is required by top-level schema validation and keeps access behavior explicit.
|
|
197
|
+
- `pairing` and `allowlist` can both pre-authorize users with `allowFrom`.
|
|
198
|
+
|
|
199
|
+
Pairing flow (owner approval):
|
|
200
|
+
1. User sends any DM to the bot.
|
|
201
|
+
2. Bot replies with a pairing code (for example `H9ZEHY8R`).
|
|
202
|
+
3. Bot owner approves:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
openclaw pairing approve feishu H9ZEHY8R
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
4. The user is added to the allow store and can chat immediately.
|
|
209
|
+
|
|
210
|
+
Example: open to everyone
|
|
211
|
+
|
|
212
|
+
```yaml
|
|
213
|
+
channels:
|
|
214
|
+
feishu:
|
|
215
|
+
dmPolicy: "open"
|
|
216
|
+
allowFrom: ["*"]
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Example: controlled rollout (pairing + pre-approved users)
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
channels:
|
|
223
|
+
feishu:
|
|
224
|
+
dmPolicy: "pairing"
|
|
225
|
+
allowFrom:
|
|
226
|
+
- "ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Example: strict allowlist
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
channels:
|
|
233
|
+
feishu:
|
|
234
|
+
dmPolicy: "allowlist"
|
|
235
|
+
allowFrom:
|
|
236
|
+
- "ou_alice"
|
|
237
|
+
- "ou_bob"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Example: account-level isolation
|
|
241
|
+
|
|
242
|
+
```yaml
|
|
243
|
+
channels:
|
|
244
|
+
feishu:
|
|
245
|
+
accounts:
|
|
246
|
+
lobster-1:
|
|
247
|
+
dmPolicy: "open"
|
|
248
|
+
allowFrom: ["*"]
|
|
249
|
+
lobster-5:
|
|
250
|
+
dmPolicy: "pairing"
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Top-level `channels.feishu.dmPolicy` / `channels.feishu.allowFrom` are fallback defaults for accounts that do not override them.
|
|
254
|
+
|
|
255
|
+
> `dmPolicy` only controls who can trigger the bot.
|
|
256
|
+
> To actually read/write docs or files, you still need: (1) correct Feishu app scopes, and (2) sharing the target resources (Drive/Wiki/Bitable) with the bot.
|
|
257
|
+
|
|
181
258
|
#### Connection Mode
|
|
182
259
|
|
|
183
260
|
Two connection modes are available for receiving events from Feishu:
|
|
@@ -276,7 +353,7 @@ session:
|
|
|
276
353
|
- Pairing flow for DM approval
|
|
277
354
|
- User and group directory lookup
|
|
278
355
|
- **Card render mode**: Optional markdown rendering with syntax highlighting
|
|
279
|
-
- **Document tools**: Read, create, and write Feishu documents with markdown
|
|
356
|
+
- **Document tools**: Read, create, and write Feishu documents with markdown, including atomic `create_and_write` / `import_document` flows for reliable create+content write
|
|
280
357
|
- **Wiki tools**: Navigate knowledge bases, list spaces, get node details, search, create/move/rename nodes
|
|
281
358
|
- **Drive tools**: List folders, get file info, create folders, move/delete files
|
|
282
359
|
- **Bitable tools**: Manage bitable (多维表格) fields and records (read/create/update/delete), supports both `/base/` and `/wiki/` URLs
|
|
@@ -400,7 +477,7 @@ openclaw plugins update feishu
|
|
|
400
477
|
| 权限 | 工具 | 说明 |
|
|
401
478
|
|------|------|------|
|
|
402
479
|
| `docx:document` | `feishu_doc` | 创建/编辑文档 |
|
|
403
|
-
| `docx:document.block:convert` | `feishu_doc` | Markdown 转 blocks(write/append
|
|
480
|
+
| `docx:document.block:convert` | `feishu_doc` | Markdown 转 blocks(write/append/create_and_write 必需,`feishu_drive.import_document` 也会用到) |
|
|
404
481
|
| `drive:drive` | `feishu_doc`, `feishu_drive` | 上传图片到文档、创建文件夹、移动/删除文件 |
|
|
405
482
|
| `wiki:wiki` | `feishu_wiki` | 创建/移动/重命名知识库节点 |
|
|
406
483
|
| `bitable:app` | `feishu_bitable` | 创建/更新/删除多维表格记录并管理字段 |
|
|
@@ -485,6 +562,8 @@ channels:
|
|
|
485
562
|
connectionMode: "websocket"
|
|
486
563
|
# 私聊策略: "pairing" | "open" | "allowlist"
|
|
487
564
|
dmPolicy: "pairing"
|
|
565
|
+
# 私聊白名单(open_id/user_id);当 dmPolicy="open" 时请包含 "*"
|
|
566
|
+
allowFrom: []
|
|
488
567
|
# 群聊策略: "open" | "allowlist" | "disabled"
|
|
489
568
|
groupPolicy: "allowlist"
|
|
490
569
|
# 群聊是否需要 @机器人
|
|
@@ -495,6 +574,81 @@ channels:
|
|
|
495
574
|
renderMode: "auto"
|
|
496
575
|
```
|
|
497
576
|
|
|
577
|
+
#### 私聊策略(dmPolicy)与访问授权
|
|
578
|
+
|
|
579
|
+
`dmPolicy` 控制的是“谁可以在私聊里触发机器人”。
|
|
580
|
+
在多账号模式下,它按账号生效(`channels.feishu.accounts.<accountId>`)。
|
|
581
|
+
|
|
582
|
+
| `dmPolicy` | 谁能私聊触发机器人 | 如何给用户开通 |
|
|
583
|
+
|------------|------------------|----------------|
|
|
584
|
+
| `pairing` | `allowFrom` 中的用户,或已通过配对审批的用户 | 用户先私聊机器人拿到配对码;管理员执行 `openclaw pairing approve feishu <code>`。 |
|
|
585
|
+
| `open` | 所有人 | 配置 `allowFrom: ["*"]`,表示全部放开。 |
|
|
586
|
+
| `allowlist` | 仅 `allowFrom` 中的用户 | 将用户 `open_id`/`user_id` 加入 `allowFrom`,然后重载配置。 |
|
|
587
|
+
|
|
588
|
+
说明:
|
|
589
|
+
- `allowFrom` 支持飞书用户 ID(推荐 `open_id`,也支持 `user_id`)。
|
|
590
|
+
- 当 `dmPolicy: "open"` 时,建议固定写 `allowFrom: ["*"]`,语义最清晰,也满足顶层配置校验要求。
|
|
591
|
+
- `pairing` 和 `allowlist` 都可以先通过 `allowFrom` 预授权部分用户。
|
|
592
|
+
|
|
593
|
+
配对审批流程(pairing):
|
|
594
|
+
1. 用户先给机器人发一条私聊消息。
|
|
595
|
+
2. 机器人返回配对码(例如 `H9ZEHY8R`)。
|
|
596
|
+
3. 管理员执行审批命令:
|
|
597
|
+
|
|
598
|
+
```bash
|
|
599
|
+
openclaw pairing approve feishu H9ZEHY8R
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
4. 审批后该用户立即可用。
|
|
603
|
+
|
|
604
|
+
示例:全部放开
|
|
605
|
+
|
|
606
|
+
```yaml
|
|
607
|
+
channels:
|
|
608
|
+
feishu:
|
|
609
|
+
dmPolicy: "open"
|
|
610
|
+
allowFrom: ["*"]
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
示例:灰度放开(pairing + 预授权)
|
|
614
|
+
|
|
615
|
+
```yaml
|
|
616
|
+
channels:
|
|
617
|
+
feishu:
|
|
618
|
+
dmPolicy: "pairing"
|
|
619
|
+
allowFrom:
|
|
620
|
+
- "ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
示例:严格白名单
|
|
624
|
+
|
|
625
|
+
```yaml
|
|
626
|
+
channels:
|
|
627
|
+
feishu:
|
|
628
|
+
dmPolicy: "allowlist"
|
|
629
|
+
allowFrom:
|
|
630
|
+
- "ou_alice"
|
|
631
|
+
- "ou_bob"
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
示例:按账号隔离配置
|
|
635
|
+
|
|
636
|
+
```yaml
|
|
637
|
+
channels:
|
|
638
|
+
feishu:
|
|
639
|
+
accounts:
|
|
640
|
+
lobster-1:
|
|
641
|
+
dmPolicy: "open"
|
|
642
|
+
allowFrom: ["*"]
|
|
643
|
+
lobster-5:
|
|
644
|
+
dmPolicy: "pairing"
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
`channels.feishu.dmPolicy` / `channels.feishu.allowFrom` 是“默认值”;账号下未覆盖时才会继承。
|
|
648
|
+
|
|
649
|
+
> `dmPolicy` 只控制“是否允许触发机器人”。
|
|
650
|
+
> 真正执行文档/云盘/知识库/多维表格操作,还需要两层权限:1)应用 API 权限(scopes);2)把目标资源分享给机器人。
|
|
651
|
+
|
|
498
652
|
#### 连接模式
|
|
499
653
|
|
|
500
654
|
支持两种从飞书接收事件的连接模式:
|
package/package.json
CHANGED
|
@@ -32,6 +32,28 @@ Replaces entire document with markdown content. Supports: headings, lists, code
|
|
|
32
32
|
|
|
33
33
|
**Limitation:** Markdown tables are NOT supported.
|
|
34
34
|
|
|
35
|
+
### Create + Write (Atomic, Recommended)
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"action": "create_and_write",
|
|
40
|
+
"title": "New Document",
|
|
41
|
+
"content": "# Title\n\nMarkdown content..."
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
With folder:
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"action": "create_and_write",
|
|
49
|
+
"title": "New Document",
|
|
50
|
+
"content": "# Title\n\nMarkdown content...",
|
|
51
|
+
"folder_token": "fldcnXXX"
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Creates the document and writes content in one call. Prefer this over separate `create` + `write`.
|
|
56
|
+
|
|
35
57
|
### Append Content
|
|
36
58
|
|
|
37
59
|
```json
|
|
@@ -51,6 +73,8 @@ With folder:
|
|
|
51
73
|
{ "action": "create", "title": "New Document", "folder_token": "fldcnXXX" }
|
|
52
74
|
```
|
|
53
75
|
|
|
76
|
+
Creates an empty document (title only).
|
|
77
|
+
|
|
54
78
|
### List Blocks
|
|
55
79
|
|
|
56
80
|
```json
|
package/src/bot.ts
CHANGED
|
@@ -207,9 +207,8 @@ function parseMessageContent(content: string, messageType: string): string {
|
|
|
207
207
|
function checkBotMentioned(event: FeishuMessageEvent, botOpenId?: string): boolean {
|
|
208
208
|
const mentions = event.message.mentions ?? [];
|
|
209
209
|
if (mentions.length === 0) return false;
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
if (!botOpenId) return mentions.length > 0;
|
|
210
|
+
// Keep explicit bot mention semantics: without a resolved botOpenId, do not trigger.
|
|
211
|
+
if (!botOpenId) return false;
|
|
213
212
|
return mentions.some((m) => m.id.open_id === botOpenId);
|
|
214
213
|
}
|
|
215
214
|
|
|
@@ -933,6 +932,7 @@ export async function handleFeishuMessage(params: {
|
|
|
933
932
|
CommandAuthorized: commandAuthorized,
|
|
934
933
|
OriginatingChannel: "feishu" as const,
|
|
935
934
|
OriginatingTo: feishuTo,
|
|
935
|
+
ReplyToBody: quotedContent,
|
|
936
936
|
...mediaPayload,
|
|
937
937
|
});
|
|
938
938
|
|
package/src/doc-schema.ts
CHANGED
|
@@ -22,6 +22,14 @@ export const FeishuDocSchema = Type.Union([
|
|
|
22
22
|
title: Type.String({ description: "Document title" }),
|
|
23
23
|
folder_token: Type.Optional(Type.String({ description: "Target folder token (optional)" })),
|
|
24
24
|
}),
|
|
25
|
+
Type.Object({
|
|
26
|
+
action: Type.Literal("create_and_write"),
|
|
27
|
+
title: Type.String({ description: "Document title" }),
|
|
28
|
+
content: Type.String({
|
|
29
|
+
description: "Markdown content to write immediately after document creation",
|
|
30
|
+
}),
|
|
31
|
+
folder_token: Type.Optional(Type.String({ description: "Target folder token (optional)" })),
|
|
32
|
+
}),
|
|
25
33
|
Type.Object({
|
|
26
34
|
action: Type.Literal("list_blocks"),
|
|
27
35
|
doc_token: Type.String({ description: "Document token" }),
|