@m1heng-clawd/feishu 0.1.15 → 0.1.17

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.
Files changed (39) hide show
  1. package/README.md +28 -0
  2. package/index.ts +4 -0
  3. package/package.json +2 -1
  4. package/skills/feishu-message/SKILL.md +111 -0
  5. package/skills/feishu-reaction/SKILL.md +120 -0
  6. package/src/bot.ts +4 -0
  7. package/src/channel.ts +2 -0
  8. package/src/chat-tools/actions.ts +102 -33
  9. package/src/chat-tools/schemas.ts +57 -62
  10. package/src/config-schema.ts +2 -0
  11. package/src/doc-tools/actions.ts +63 -14
  12. package/src/doc-tools/schemas.ts +41 -77
  13. package/src/doc-write-service.ts +19 -0
  14. package/src/drive-tools/actions.ts +18 -6
  15. package/src/drive-tools/register.ts +1 -1
  16. package/src/drive-tools/schemas.ts +45 -58
  17. package/src/media-duration.ts +43 -8
  18. package/src/media.ts +31 -53
  19. package/src/message-tools/actions.ts +154 -0
  20. package/src/message-tools/index.ts +2 -0
  21. package/src/message-tools/register.ts +58 -0
  22. package/src/message-tools/schemas.ts +56 -0
  23. package/src/outbound.ts +2 -2
  24. package/src/perm-tools/actions.ts +27 -3
  25. package/src/perm-tools/schemas.ts +39 -45
  26. package/src/reaction-tools/actions.ts +127 -0
  27. package/src/reaction-tools/index.ts +2 -0
  28. package/src/reaction-tools/register.ts +51 -0
  29. package/src/reaction-tools/schemas.ts +34 -0
  30. package/src/reply-dispatcher.ts +23 -33
  31. package/src/streaming-card.ts +34 -11
  32. package/src/task-tools/actions.ts +145 -10
  33. package/src/task-tools/register.ts +56 -1
  34. package/src/task-tools/schemas.ts +23 -18
  35. package/src/tools-common/tool-context.ts +2 -0
  36. package/src/tools-config.ts +2 -0
  37. package/src/types.ts +4 -0
  38. package/src/wiki-tools/actions.ts +24 -6
  39. package/src/wiki-tools/schemas.ts +32 -51
package/README.md CHANGED
@@ -16,6 +16,22 @@ Feishu/Lark (飞书) channel plugin for [OpenClaw](https://github.com/openclaw/o
16
16
 
17
17
  ---
18
18
 
19
+ ## Sponsor
20
+
21
+ ![YouYun Zhisuan](./assets/sponsor-youyunzhisuan.png)
22
+
23
+ Thanks to our sponsors for supporting the project.
24
+
25
+ If you need an API key platform whose models can be used as an OpenClaw provider and work with Kimi, MiniMax, Claude Code, Codex, or direct API usage, YouYun Zhisuan is worth considering:
26
+
27
+ > UCloud's YouYun Zhisuan is an AI cloud platform that provides stable and comprehensive domestic and international model APIs through a single API key. Its high-value Coding Plan offers both monthly and pay-as-you-go options, typically at 20% to 50% of official pricing. It supports Claude Code, Codex, and direct API integrations, as well as enterprise-grade high concurrency, 24/7 technical support, and self-service invoicing. Register through [this link](https://passport.compshare.cn/register?referral_code=5oWQU772rtSCeExpcLFxwy&ytag=GPU_YY_YX_git_clawdbot-feishu) to receive a free RMB 5 platform trial credit.
28
+
29
+ 感谢赞助方对本项目的支持。
30
+
31
+ 如果你需要一个可作为 OpenClaw 模型 provider,并适用于 Kimi、MiniMax、Claude Code、Codex 或直接 API 调用的稳定 API Key 平台,可以了解一下优云智算:
32
+
33
+ > 优云智算是UCloud旗下AI云平台,提供稳定、全面的国内外模型API,仅一个key即可调用。主打包月、按量的高性价比 Coding Plan 套餐,基于官方2~5折优惠。支持接入 Claude Code、Codex 及 API 调用。支持企业高并发、7*24技术支持、自助开票。通过[此链接](https://passport.compshare.cn/register?referral_code=5oWQU772rtSCeExpcLFxwy&ytag=GPU_YY_YX_git_clawdbot-feishu)注册的用户,可得免费5元平台体验金!
34
+
19
35
  ## English
20
36
 
21
37
  ### Installation
@@ -113,6 +129,9 @@ openclaw plugins list | rg -i feishu
113
129
  | `task:attachment:read` | `feishu_task_attachment_list`, `feishu_task_attachment_get` | List/get task attachments |
114
130
  | `im:chat.announcement:read` | `feishu_chat` | Read group announcement |
115
131
  | `im:chat:readonly` | `feishu_chat` | Get chat info, check bot membership |
132
+ | `im:message` or `im:message:readonly` | `feishu_message` | Read single/DM messages |
133
+ | `im:message.group_at_msg:readonly` | `feishu_message` | Read all messages in group chats |
134
+ | `im:message.reactions:read` | `feishu_reaction` | List reactions on messages |
116
135
 
117
136
  **Read-write** (optional, for create/edit/delete operations):
118
137
 
@@ -131,6 +150,7 @@ openclaw plugins list | rg -i feishu
131
150
  | `im:chat.announcement` | `feishu_chat` | Write/update group announcement |
132
151
  | `im:chat` | `feishu_chat` | Create and delete group chats |
133
152
  | `im:chat.members` | `feishu_chat` | Add members to group chats |
153
+ | `im:message.reactions:write_only` | `feishu_reaction` | Add and remove reactions |
134
154
 
135
155
  > Task scope names may vary slightly in Feishu console UI. If needed, search for Task / Tasklist / Comment / Attachment-related permissions and grant read/write accordingly.
136
156
 
@@ -479,6 +499,8 @@ session:
479
499
  - **Task tools**: Create, get details, update, and delete tasks via Feishu Task v2 API
480
500
  - **Chat tools**: Read and write group announcements, create group chats, add members, check bot membership, delete chats (`feishu_chat`)
481
501
  - **Urgent notification tools**: Send buzz/urgent notifications (app, SMS, voice call) via `feishu_urgent`
502
+ - **Message tools**: Read single messages or list recent chat history with time range and pagination (`feishu_message`)
503
+ - **Reaction tools**: Add, remove, and list emoji reactions on messages (`feishu_reaction`)
482
504
  - **@mention forwarding**: When you @mention someone in your message, the bot's reply will automatically @mention them too
483
505
  - **Permission error notification**: When the bot encounters a Feishu API permission error, it automatically notifies the user with the permission grant URL
484
506
  - **Dynamic agent creation**: Each DM user can have their own isolated agent instance with dedicated workspace (optional)
@@ -629,6 +651,9 @@ openclaw plugins list | rg -i feishu
629
651
  | `task:attachment:read` | `feishu_task_attachment_list`, `feishu_task_attachment_get` | 列出/获取任务附件 |
630
652
  | `im:chat.announcement:read` | `feishu_chat` | 读取群公告 |
631
653
  | `im:chat:readonly` | `feishu_chat` | 获取群信息、检查机器人是否在群内 |
654
+ | `im:message` 或 `im:message:readonly` | `feishu_message` | 读取单条/私聊消息 |
655
+ | `im:message.group_at_msg:readonly` | `feishu_message` | 读取群聊中所有消息 |
656
+ | `im:message.reactions:read` | `feishu_reaction` | 查看消息表情回复 |
632
657
 
633
658
  **读写权限**(可选,用于创建/编辑/删除操作):
634
659
 
@@ -647,6 +672,7 @@ openclaw plugins list | rg -i feishu
647
672
  | `im:chat.announcement` | `feishu_chat` | 写入/更新群公告 |
648
673
  | `im:chat` | `feishu_chat` | 创建和删除群聊 |
649
674
  | `im:chat.members` | `feishu_chat` | 向群聊添加成员 |
675
+ | `im:message.reactions:write_only` | `feishu_reaction` | 发送、删除消息表情回复 |
650
676
 
651
677
  > 飞书控制台中任务权限的显示名称可能略有差异,必要时可按关键字 `task` / `tasklist` / `comment` / `attachment` 搜索并授予对应读写权限。
652
678
 
@@ -994,6 +1020,8 @@ session:
994
1020
  - **任务工具**:基于 Task v2 API 支持任务创建、获取详情、更新和删除
995
1021
  - **群聊工具**:读写群公告、创建群聊、添加成员、检查机器人是否在群内、删除群聊(`feishu_chat`)
996
1022
  - **加急通知工具**:发送应用内加急(buzz)、短信、语音电话加急通知(`feishu_urgent`)
1023
+ - **消息工具**:读取单条消息或列出聊天历史记录,支持时间范围和分页(`feishu_message`)
1024
+ - **表情工具**:添加、删除、列出消息上的表情回复(`feishu_reaction`)
997
1025
  - **@ 转发功能**:在消息中 @ 某人,机器人的回复会自动 @ 该用户
998
1026
  - **权限错误提示**:当机器人遇到飞书 API 权限错误时,会自动通知用户并提供权限授权链接
999
1027
  - **动态 Agent 创建**:每个私聊用户可拥有独立的 agent 实例和专属 workspace(可选)
package/index.ts CHANGED
@@ -9,6 +9,8 @@ import { registerFeishuPermTools } from "./src/perm-tools/index.js";
9
9
  import { registerFeishuTaskTools } from "./src/task-tools/index.js";
10
10
  import { registerFeishuChatTools } from "./src/chat-tools/index.js";
11
11
  import { registerFeishuUrgentTools } from "./src/urgent-tools/index.js";
12
+ import { registerFeishuMessageTools } from "./src/message-tools/index.js";
13
+ import { registerFeishuReactionTools } from "./src/reaction-tools/index.js";
12
14
  import { registerFeishuWikiTools } from "./src/wiki-tools/index.js";
13
15
 
14
16
  export { monitorFeishuProvider } from "./src/monitor.js";
@@ -64,6 +66,8 @@ const plugin = {
64
66
  registerFeishuTaskTools(api);
65
67
  registerFeishuChatTools(api);
66
68
  registerFeishuUrgentTools(api);
69
+ registerFeishuMessageTools(api);
70
+ registerFeishuReactionTools(api);
67
71
  },
68
72
  };
69
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m1heng-clawd/feishu",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "type": "module",
5
5
  "description": "OpenClaw Feishu/Lark channel plugin",
6
6
  "scripts": {
@@ -57,6 +57,7 @@
57
57
  "@larksuiteoapi/node-sdk": "^1.59.0",
58
58
  "@sinclair/typebox": "0.34.48",
59
59
  "https-proxy-agent": "^7.0.6",
60
+ "music-metadata": "^11.12.1",
60
61
  "zod": "^4.3.6"
61
62
  },
62
63
  "devDependencies": {
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: feishu-message
3
+ description: |
4
+ Feishu message reading. Activate when user mentions reading messages, chat history, message lookup, or finding previous messages.
5
+ ---
6
+
7
+ # Feishu Message Tool
8
+
9
+ Single tool `feishu_message` for reading Feishu messages — get a single message by ID or list recent messages in a chat.
10
+
11
+ ## Actions
12
+
13
+ ### Get Single Message
14
+
15
+ ```json
16
+ {
17
+ "action": "get",
18
+ "message_id": "om_xxx"
19
+ }
20
+ ```
21
+
22
+ Returns:
23
+
24
+ ```json
25
+ {
26
+ "ok": true,
27
+ "action": "get",
28
+ "found": true,
29
+ "message_id": "om_xxx",
30
+ "msg_type": "text",
31
+ "content": "Hello world",
32
+ "sender_id": "ou_xxx",
33
+ "sender_type": "user",
34
+ "chat_id": "oc_xxx",
35
+ "create_time": "1710000000000",
36
+ "update_time": "1710000000000",
37
+ "mentions": []
38
+ }
39
+ ```
40
+
41
+ ### List Recent Messages
42
+
43
+ List recent messages in a chat (DM or group), newest first by default.
44
+ Omit `chat_id` to use the current conversation's chat:
45
+
46
+ ```json
47
+ {
48
+ "action": "list"
49
+ }
50
+ ```
51
+
52
+ With custom page size, sort order, and time range:
53
+
54
+ ```json
55
+ {
56
+ "action": "list",
57
+ "chat_id": "oc_xxx",
58
+ "page_size": 20,
59
+ "sort_type": "ByCreateTimeAsc",
60
+ "start_time": "1710000000",
61
+ "end_time": "1710086400"
62
+ }
63
+ ```
64
+
65
+ Returns:
66
+
67
+ ```json
68
+ {
69
+ "ok": true,
70
+ "action": "list",
71
+ "chat_id": "oc_xxx",
72
+ "total": 10,
73
+ "messages": [
74
+ {
75
+ "message_id": "om_xxx",
76
+ "msg_type": "text",
77
+ "content_preview": "Hello world",
78
+ "sender_id": "ou_xxx",
79
+ "sender_type": "user",
80
+ "create_time": "1710000000000",
81
+ "chat_id": "oc_xxx"
82
+ }
83
+ ]
84
+ }
85
+ ```
86
+
87
+ ## Parameters
88
+
89
+ | Parameter | Required | Description |
90
+ |-----------|----------|-------------|
91
+ | `action` | Yes | `get` or `list` |
92
+ | `message_id` | get: Yes | Feishu message ID (e.g., `om_xxx`) |
93
+ | `chat_id` | list: Optional | Chat ID (e.g., `oc_xxx`). Omit to use current chat. |
94
+ | `page_size` | list: Optional | Number of messages (default: 10, max: 50) |
95
+ | `sort_type` | list: Optional | `ByCreateTimeDesc` (default) or `ByCreateTimeAsc` |
96
+ | `start_time` | list: Optional | Unix timestamp in seconds (e.g., `"1710000000"`). No lower bound if omitted. |
97
+ | `end_time` | list: Optional | Unix timestamp in seconds (e.g., `"1710086400"`). No upper bound if omitted. |
98
+
99
+ ## Configuration
100
+
101
+ ```yaml
102
+ channels:
103
+ feishu:
104
+ tools:
105
+ message: true # default: true
106
+ ```
107
+
108
+ ## Permissions
109
+
110
+ - `im:message` or `im:message:readonly` — read single/DM messages
111
+ - `im:message.group_at_msg:readonly` — read all messages in group chats (not just @bot messages)
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: feishu-reaction
3
+ description: |
4
+ Feishu message emoji reactions. Activate when user mentions emoji, reaction, thumbsup, like, or responding to messages with emoji.
5
+ ---
6
+
7
+ # Feishu Reaction Tool
8
+
9
+ Single tool `feishu_reaction` for adding, removing, and listing emoji reactions on Feishu messages.
10
+
11
+ ## Workflow: Reacting to Previous Messages
12
+
13
+ To react to a message other than the current one, first use `feishu_message` with `action: "list"` to fetch recent messages and find the target `message_id`, then use `feishu_reaction` with that `message_id`.
14
+
15
+ ## Actions
16
+
17
+ ### Add Reaction
18
+
19
+ ```json
20
+ {
21
+ "action": "add",
22
+ "message_id": "om_xxx",
23
+ "emoji_type": "THUMBSUP"
24
+ }
25
+ ```
26
+
27
+ Returns:
28
+
29
+ ```json
30
+ {
31
+ "ok": true,
32
+ "action": "add",
33
+ "message_id": "om_xxx",
34
+ "emoji_type": "THUMBSUP",
35
+ "reaction_id": "ZCaCIjUBVVWSrm5L-3ZTw"
36
+ }
37
+ ```
38
+
39
+ ### Remove Reaction
40
+
41
+ ```json
42
+ {
43
+ "action": "remove",
44
+ "message_id": "om_xxx",
45
+ "reaction_id": "ZCaCIjUBVVWSrm5L-3ZTw"
46
+ }
47
+ ```
48
+
49
+ ### List Reactions
50
+
51
+ List all reactions on a message:
52
+
53
+ ```json
54
+ {
55
+ "action": "list",
56
+ "message_id": "om_xxx"
57
+ }
58
+ ```
59
+
60
+ Filter by emoji type:
61
+
62
+ ```json
63
+ {
64
+ "action": "list",
65
+ "message_id": "om_xxx",
66
+ "emoji_type": "THUMBSUP"
67
+ }
68
+ ```
69
+
70
+ Returns:
71
+
72
+ ```json
73
+ {
74
+ "ok": true,
75
+ "action": "list",
76
+ "message_id": "om_xxx",
77
+ "emoji_type_filter": "THUMBSUP",
78
+ "total": 2,
79
+ "reactions": [
80
+ {
81
+ "reaction_id": "ZCaCIjUBVVWSrm5L-3ZTw",
82
+ "emoji_type": "THUMBSUP",
83
+ "operator_type": "user",
84
+ "operator_id": "ou_xxx"
85
+ }
86
+ ]
87
+ }
88
+ ```
89
+
90
+ ## Parameters
91
+
92
+ | Parameter | Required | Description |
93
+ |-----------|----------|-------------|
94
+ | `action` | Yes | `add`, `remove`, or `list` |
95
+ | `message_id` | Yes | Feishu message ID (e.g., `om_xxx`) |
96
+ | `emoji_type` | add: Yes, list: Optional | Emoji type (e.g., `THUMBSUP`, `HEART`, `SMILE`) |
97
+ | `reaction_id` | remove: Yes | Reaction ID from add or list results |
98
+
99
+ ## Common Emoji Types
100
+
101
+ | Emoji | Type |
102
+ |-------|------|
103
+ | THUMBSUP | THUMBSDOWN | HEART | SMILE | GRINNING |
104
+ | FIRE | CLAP | OK | CHECK | CROSS |
105
+ | PARTY | PRAY | CRY | ANGRY | THINKING |
106
+ | SURPRISED | LAUGHING | FIST | QUESTION | EXCLAMATION |
107
+
108
+ ## Configuration
109
+
110
+ ```yaml
111
+ channels:
112
+ feishu:
113
+ tools:
114
+ reaction: true # default: true
115
+ ```
116
+
117
+ ## Permissions
118
+
119
+ - `im:message.reactions:write_only` — add and remove reactions
120
+ - `im:message.reactions:read` — list reactions on messages
package/src/bot.ts CHANGED
@@ -1488,6 +1488,8 @@ export async function handleFeishuMessage(params: {
1488
1488
  channel: "feishu",
1489
1489
  accountId: account.accountId,
1490
1490
  sessionKey: route.sessionKey,
1491
+ senderOpenId: ctx.senderOpenId,
1492
+ chatId: ctx.chatId,
1491
1493
  },
1492
1494
  // Keep account context available while the agent executes plugin tools.
1493
1495
  () =>
@@ -1574,6 +1576,8 @@ export async function handleFeishuMessage(params: {
1574
1576
  channel: "feishu",
1575
1577
  accountId: account.accountId,
1576
1578
  sessionKey: route.sessionKey,
1579
+ senderOpenId: ctx.senderOpenId,
1580
+ chatId: ctx.chatId,
1577
1581
  },
1578
1582
  // Tool calls produced by this turn should resolve to the same inbound account.
1579
1583
  () =>
package/src/channel.ts CHANGED
@@ -60,6 +60,8 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
60
60
  messageToolHints: () => [
61
61
  "- Feishu targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `user:open_id` or `chat:chat_id`.",
62
62
  "- Feishu supports interactive cards for rich messages.",
63
+ "- To send a file from the OpenClaw agent's local filesystem workspace (or another allowed local path) as a Feishu attachment, reply with a standalone `MEDIA:<absolute-path-or-url>` line.",
64
+ "- `feishu_drive` is for Feishu cloud Drive content, not for the OpenClaw agent's local filesystem workspace.",
63
65
  ],
64
66
  },
65
67
  groups: {
@@ -2,6 +2,23 @@ import type { ChatClient } from "./common.js";
2
2
  import type { FeishuChatParams } from "./schemas.js";
3
3
  import { runChatApiCall } from "./common.js";
4
4
 
5
+ type UserIdType = "open_id" | "user_id" | "union_id";
6
+ type MemberIdType = UserIdType | "app_id";
7
+
8
+ function requireString(value: unknown, field: string): string {
9
+ if (typeof value !== "string" || value.trim().length === 0) {
10
+ throw new Error(`${field} is required`);
11
+ }
12
+ return value;
13
+ }
14
+
15
+ function requireStringArray(value: unknown, field: string): string[] {
16
+ if (!Array.isArray(value) || value.length === 0 || value.some((item) => typeof item !== "string")) {
17
+ throw new Error(`${field} must be a non-empty string array`);
18
+ }
19
+ return value;
20
+ }
21
+
5
22
  const BLOCK_TYPE_NAMES: Record<number, string> = {
6
23
  1: "Page",
7
24
  2: "Text",
@@ -117,15 +134,35 @@ async function writeDocAnnouncement(client: ChatClient, chatId: string, content:
117
134
  }),
118
135
  );
119
136
 
120
- const res = await runChatApiCall("im.chatAnnouncement.patch", () =>
121
- (client as any).im.chatAnnouncement.patch({
122
- path: { chat_id: chatId },
123
- data: {
124
- content,
125
- revision: (current as any).data?.revision,
126
- },
127
- }),
128
- );
137
+ const revision = (current as any).data?.revision;
138
+ if (!revision) {
139
+ throw new Error("Failed to load current announcement revision");
140
+ }
141
+
142
+ let res: unknown;
143
+ try {
144
+ // Prefer SDK current payload shape (revision + requests[]).
145
+ res = await runChatApiCall("im.chatAnnouncement.patch", () =>
146
+ (client as any).im.chatAnnouncement.patch({
147
+ path: { chat_id: chatId },
148
+ data: {
149
+ revision,
150
+ requests: [content],
151
+ },
152
+ }),
153
+ );
154
+ } catch {
155
+ // Backward compatibility for tenants still accepting legacy content payload.
156
+ res = await runChatApiCall("im.chatAnnouncement.patch", () =>
157
+ (client as any).im.chatAnnouncement.patch({
158
+ path: { chat_id: chatId },
159
+ data: {
160
+ content,
161
+ revision,
162
+ },
163
+ }),
164
+ );
165
+ }
129
166
 
130
167
  return {
131
168
  success: true,
@@ -210,7 +247,13 @@ async function batchUpdateAnnouncementBlocks(
210
247
 
211
248
  // ============== New Chat Management Functions ==============
212
249
 
213
- async function createChat(client: ChatClient, name: string, userIds?: string[], description?: string) {
250
+ async function createChat(
251
+ client: ChatClient,
252
+ name: string,
253
+ userIds?: string[],
254
+ description?: string,
255
+ userIdType: UserIdType = "open_id",
256
+ ) {
214
257
  const data: any = { name };
215
258
  if (userIds && userIds.length > 0) {
216
259
  data.user_id_list = userIds;
@@ -222,7 +265,7 @@ async function createChat(client: ChatClient, name: string, userIds?: string[],
222
265
  const res = await runChatApiCall("im.chat.create", () =>
223
266
  (client as any).im.chat.create({
224
267
  data,
225
- params: { user_id_type: "open_id" },
268
+ params: { user_id_type: userIdType },
226
269
  }),
227
270
  );
228
271
 
@@ -233,11 +276,16 @@ async function createChat(client: ChatClient, name: string, userIds?: string[],
233
276
  };
234
277
  }
235
278
 
236
- async function addMembers(client: ChatClient, chatId: string, userIds: string[]) {
279
+ async function addMembers(
280
+ client: ChatClient,
281
+ chatId: string,
282
+ userIds: string[],
283
+ memberIdType: MemberIdType = "open_id",
284
+ ) {
237
285
  const res = await runChatApiCall("im.chatMembers.create", () =>
238
286
  (client as any).im.chatMembers.create({
239
287
  path: { chat_id: chatId },
240
- params: { member_id_type: "open_id" },
288
+ params: { member_id_type: memberIdType },
241
289
  data: { id_list: userIds },
242
290
  }),
243
291
  );
@@ -300,9 +348,10 @@ async function createSessionChat(
300
348
  userIds: string[],
301
349
  greeting?: string,
302
350
  description?: string,
351
+ userIdType: UserIdType = "open_id",
303
352
  ) {
304
353
  // Step 1: Create the chat
305
- const createResult = await createChat(client, name, userIds, description);
354
+ const createResult = await createChat(client, name, userIds, description, userIdType);
306
355
  const chatId = createResult.chat_id;
307
356
 
308
357
  if (!chatId) {
@@ -358,15 +407,21 @@ export async function runChatAction(client: ChatClient, params: FeishuChatParams
358
407
  switch (params.action) {
359
408
  case "get_announcement_info":
360
409
  case "get_announcement":
361
- return getAnnouncement(client, params.chat_id);
410
+ return getAnnouncement(client, requireString(params.chat_id, "chat_id"));
362
411
  case "list_announcement_blocks":
363
- return listAnnouncementBlocks(client, params.chat_id);
412
+ return listAnnouncementBlocks(client, requireString(params.chat_id, "chat_id"));
364
413
  case "get_announcement_block":
365
- return getAnnouncementBlock(client, params.chat_id, params.block_id);
414
+ return getAnnouncementBlock(
415
+ client,
416
+ requireString(params.chat_id, "chat_id"),
417
+ requireString(params.block_id, "block_id"),
418
+ );
366
419
  case "write_announcement": {
367
- const current = await getAnnouncement(client, params.chat_id);
420
+ const chatId = requireString(params.chat_id, "chat_id");
421
+ const content = requireString(params.content, "content");
422
+ const current = await getAnnouncement(client, chatId);
368
423
  if (current.announcement_type === "doc") {
369
- return writeDocAnnouncement(client, params.chat_id, params.content);
424
+ return writeDocAnnouncement(client, chatId, content);
370
425
  } else {
371
426
  // For docx announcements, append a text block under the Page root block.
372
427
  // Full replacement is not supported via API; use update_announcement_block to edit existing blocks.
@@ -375,15 +430,17 @@ export async function runChatAction(client: ChatClient, params: FeishuChatParams
375
430
  if (!pageBlock?.block_id) {
376
431
  return { error: "Could not find the Page root block for docx announcement. Use list_announcement_blocks to inspect the structure." };
377
432
  }
378
- return createTextBlock(client, params.chat_id, pageBlock.block_id, params.content);
433
+ return createTextBlock(client, chatId, pageBlock.block_id, content);
379
434
  }
380
435
  }
381
436
  case "append_announcement": {
382
- const current = await getAnnouncement(client, params.chat_id);
437
+ const chatId = requireString(params.chat_id, "chat_id");
438
+ const content = requireString(params.content, "content");
439
+ const current = await getAnnouncement(client, chatId);
383
440
  if (current.announcement_type === "doc") {
384
441
  const existingContent = (current as any).content || "";
385
- const newContent = existingContent + "\n" + params.content;
386
- return writeDocAnnouncement(client, params.chat_id, newContent);
442
+ const newContent = existingContent + "\n" + content;
443
+ return writeDocAnnouncement(client, chatId, newContent);
387
444
  } else {
388
445
  // For docx format, the parent block must be the Page root block (block_type: 1)
389
446
  const blocks: any[] = (current as any).blocks ?? [];
@@ -391,40 +448,52 @@ export async function runChatAction(client: ChatClient, params: FeishuChatParams
391
448
  if (!pageBlock?.block_id) {
392
449
  return { error: "Could not find the Page root block for docx announcement. Use list_announcement_blocks to inspect the structure." };
393
450
  }
394
- return createTextBlock(client, params.chat_id, pageBlock.block_id, params.content);
451
+ return createTextBlock(client, chatId, pageBlock.block_id, content);
395
452
  }
396
453
  }
397
454
  case "update_announcement_block": {
398
455
  const requests = [
399
456
  {
400
- block_id: params.block_id,
457
+ block_id: requireString(params.block_id, "block_id"),
401
458
  update_text_elements: {
402
- elements: [{ text_run: { content: params.content } }],
459
+ elements: [{ text_run: { content: requireString(params.content, "content") } }],
403
460
  },
404
461
  },
405
462
  ];
406
- return batchUpdateAnnouncementBlocks(client, params.chat_id, requests);
463
+ return batchUpdateAnnouncementBlocks(client, requireString(params.chat_id, "chat_id"), requests);
407
464
  }
408
465
  // ============== New Chat Management Actions ==============
409
466
  case "create_chat": {
410
- return createChat(client, params.name, params.user_ids, params.description);
467
+ return createChat(
468
+ client,
469
+ requireString(params.name, "name"),
470
+ params.user_ids,
471
+ params.description,
472
+ params.user_id_type,
473
+ );
411
474
  }
412
475
  case "add_members": {
413
- return addMembers(client, params.chat_id, params.user_ids);
476
+ return addMembers(
477
+ client,
478
+ requireString(params.chat_id, "chat_id"),
479
+ requireStringArray(params.user_ids, "user_ids"),
480
+ params.member_id_type,
481
+ );
414
482
  }
415
483
  case "check_bot_in_chat": {
416
- return checkBotInChat(client, params.chat_id);
484
+ return checkBotInChat(client, requireString(params.chat_id, "chat_id"));
417
485
  }
418
486
  case "delete_chat": {
419
- return deleteChat(client, params.chat_id);
487
+ return deleteChat(client, requireString(params.chat_id, "chat_id"));
420
488
  }
421
489
  case "create_session_chat": {
422
490
  return createSessionChat(
423
491
  client,
424
- params.name,
425
- params.user_ids,
492
+ requireString(params.name, "name"),
493
+ requireStringArray(params.user_ids, "user_ids"),
426
494
  params.greeting,
427
495
  params.description,
496
+ params.user_id_type,
428
497
  );
429
498
  }
430
499
  default: