@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 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 (tables not supported due to API limitations)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m1heng-clawd/feishu",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "type": "module",
5
5
  "description": "OpenClaw Feishu/Lark channel plugin",
6
6
  "license": "MIT",
@@ -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
- // Fallback for degraded startup/probe scenarios: if botOpenId is unavailable,
211
- // keep historical behavior and treat any mention as a mention trigger.
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" }),