@wecom/wecom-openclaw-plugin 2026.3.25 → 2026.3.26

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.
@@ -70,3 +70,5 @@ export declare const EVENT_ENTER_CHECK_UPDATE = "event.enter_check_update";
70
70
  export declare const CMD_ENTER_EVENT_REPLY = "ww_ai_robot_enter_event";
71
71
  /** WSClient scene 参数:企微 OpenClaw 场景 */
72
72
  export declare const SCENE_WECOM_OPENCLAW = 1;
73
+ /** 合法的模板卡片 card_type 列表 */
74
+ export declare const VALID_CARD_TYPES: string[];
@@ -33,6 +33,22 @@ export interface MessageState {
33
33
  deliverCalled?: boolean;
34
34
  /** 流式回复是否已过期(errcode 846608,>6分钟),需降级为主动发送 */
35
35
  streamExpired?: boolean;
36
+ /** 是否已成功发送过模板卡片 */
37
+ hasTemplateCard?: boolean;
38
+ }
39
+ /** 从文本中提取的模板卡片 */
40
+ export interface ExtractedTemplateCard {
41
+ /** 原始 JSON 对象(已验证 card_type 合法) */
42
+ cardJson: Record<string, unknown>;
43
+ /** card_type 值 */
44
+ cardType: string;
45
+ }
46
+ /** extractTemplateCards 返回值 */
47
+ export interface TemplateCardExtractionResult {
48
+ /** 提取到的合法模板卡片列表 */
49
+ cards: ExtractedTemplateCard[];
50
+ /** 移除卡片代码块后的剩余文本 */
51
+ remainingText: string;
36
52
  }
37
53
  /**
38
54
  * WebSocket 请求消息基础格式
@@ -68,20 +84,20 @@ export interface WeComSubscribeRequest extends WeComRequest {
68
84
  * 企业微信推送消息格式
69
85
  */
70
86
  export interface WeComCallbackMessage {
71
- cmd: WeComCommand.AIBOT_CALLBACK;
87
+ cmd: WeComCommand.AIBOT_CALLBACK | "aibot_event_callback";
72
88
  headers: {
73
89
  req_id: string;
74
90
  };
75
91
  body: {
76
92
  msgid: string;
77
93
  aibotid: string;
78
- chatid: string;
94
+ chatid?: string;
79
95
  chattype: "single" | "group";
80
96
  from: {
81
97
  userid: string;
82
98
  };
83
99
  response_url: string;
84
- msgtype: "text" | "image" | "voice" | "video" | "file" | "stream" | "mixed";
100
+ msgtype: "text" | "image" | "voice" | "video" | "file" | "stream" | "mixed" | "event";
85
101
  text?: {
86
102
  content: string;
87
103
  };
@@ -123,6 +139,22 @@ export interface WeComCallbackMessage {
123
139
  stream?: {
124
140
  id: string;
125
141
  };
142
+ event?: {
143
+ eventtype: string;
144
+ template_card_event?: {
145
+ card_type?: string;
146
+ event_key?: string;
147
+ task_id?: string;
148
+ selected_items?: {
149
+ selected_item?: Array<{
150
+ question_key?: string;
151
+ option_ids?: {
152
+ option_id?: string[];
153
+ };
154
+ }>;
155
+ };
156
+ };
157
+ };
126
158
  };
127
159
  }
128
160
  /**
@@ -9,6 +9,7 @@ export interface MessageBody {
9
9
  chatid?: string;
10
10
  chattype: "single" | "group";
11
11
  from: {
12
+ corpid?: string;
12
13
  userid: string;
13
14
  };
14
15
  response_url?: string;
@@ -56,6 +57,22 @@ export interface MessageBody {
56
57
  aeskey?: string;
57
58
  };
58
59
  };
60
+ event?: {
61
+ eventtype?: string;
62
+ template_card_event?: {
63
+ card_type?: string;
64
+ event_key?: string;
65
+ task_id?: string;
66
+ selected_items?: {
67
+ selected_item?: Array<{
68
+ question_key?: string;
69
+ option_ids?: {
70
+ option_id?: string[];
71
+ };
72
+ }>;
73
+ };
74
+ };
75
+ };
59
76
  }
60
77
  export interface ParsedMessageContent {
61
78
  textParts: string[];
@@ -66,7 +83,7 @@ export interface ParsedMessageContent {
66
83
  quoteContent: string | undefined;
67
84
  }
68
85
  /**
69
- * 解析消息内容(支持单条消息、图文混排和引用消息)
86
+ * 解析消息内容(支持单条消息、图文混排、事件回调和引用消息)
70
87
  * @returns 提取的文本数组、图片URL数组和引用消息内容
71
88
  */
72
89
  export declare function parseMessageContent(body: MessageBody): ParsedMessageContent;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 模板卡片解析器
3
+ *
4
+ * 从 LLM 回复文本中提取 markdown JSON 代码块,验证其是否为合法的企业微信模板卡片,
5
+ * 返回提取到的卡片列表和剩余文本。
6
+ *
7
+ * 同时提供 maskTemplateCardBlocks 函数,用于在流式中间帧中隐藏正在构建的卡片代码块,
8
+ * 避免 JSON 源码暴露给终端用户。
9
+ */
10
+ import type { TemplateCardExtractionResult } from "./interface.js";
11
+ /**
12
+ * 从文本中提取模板卡片 JSON 代码块
13
+ *
14
+ * 匹配规则:
15
+ * 1. 匹配所有 ```json ... ``` 或 ``` ... ``` 格式的代码块
16
+ * 2. 尝试 JSON.parse 解析代码块内容
17
+ * 3. 检查解析结果中是否包含合法的 card_type 字段
18
+ * 4. 合法的卡片从原文中移除,不合法的保留
19
+ *
20
+ * @param text - LLM 回复的完整文本
21
+ * @returns 提取结果,包含卡片列表和剩余文本
22
+ */
23
+ export declare function extractTemplateCards(text: string, log?: (...args: any[]) => void): TemplateCardExtractionResult;
24
+ /**
25
+ * 遮罩文本中的模板卡片代码块(用于流式中间帧展示)
26
+ *
27
+ * 在 LLM 流式输出过程中,累积文本可能包含:
28
+ * 1. 已闭合的模板卡片 JSON 代码块 → 替换为友好提示文本
29
+ * 2. 正在构建中的未闭合代码块 → 截断隐藏,避免 JSON 源码暴露
30
+ *
31
+ * 此函数仅做文本替换,不做 JSON 解析验证(中间帧性能优先)。
32
+ * 只要代码块内容中出现 "card_type" 关键词就认为是模板卡片并遮罩。
33
+ *
34
+ * @param text - 当前累积文本
35
+ * @returns 遮罩后的展示文本
36
+ */
37
+ export declare function maskTemplateCardBlocks(text: string, log?: (...args: any[]) => void): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wecom/wecom-openclaw-plugin",
3
- "version": "2026.3.25",
3
+ "version": "2026.3.26",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: wecom-send-template-card
3
+ description: 当需要通过企业微信发送结构化卡片消息时使用,包括:通知提醒、投票表决、方案选择、多维度选择等需要用户交互或查看结构化信息的场景。即使用户未明确要求"发卡片",只要语境适合(如"帮我问一下选A还是B"),也应主动激活。仅限 wecom 通道。
4
+ metadata:
5
+ {
6
+ "openclaw": { "emoji": "🃏", "requires": { "config": ["channels.wecom"] } },
7
+ }
8
+ ---
9
+
10
+ ## 使用方式
11
+
12
+ 在回复中输出 ` ```json ``` ` 代码块,内容为符合 [JSON Schema](./references/api-template-card-types.md) 的模板卡片 JSON。插件自动提取并发送。
13
+
14
+ ````
15
+ 你的回复文本...
16
+
17
+ ```json
18
+ {
19
+ "card_type": "...",
20
+ ...
21
+ }
22
+ ```
23
+ ````
24
+
25
+ ## 生成规则
26
+
27
+ 1. **严格按 schema 生成**:字段名、类型、嵌套结构必须与 [./references/api-template-card-types.md](./references/api-template-card-types.md) 中的 JSON Schema 定义完全一致
28
+ 2. **每张卡片必须有 `task_id`**:格式 `task_{场景}_{时间戳}`,只能包含数字、字母、`_-@`
29
+ 3. 不要使用 MCP 工具调用,直接在回复中输出 JSON 代码块
30
+ 4. 代码块外的文本会作为普通回复发送
31
+
32
+ ## 卡片类型选择
33
+
34
+ ### 决策流程
35
+
36
+ ```
37
+ 用户需求是否需要交互?
38
+ ├─ 否(纯通知/信息推送)
39
+ │ ├─ 需要图片展示? → news_notice
40
+ │ └─ 不需要图片 → text_notice
41
+ │
42
+ └─ 是(需要用户操作)
43
+ ├─ 从有限选项中做一个操作(≤6个按钮)→ button_interaction
44
+ ├─ 从一组选项中投票/选择(可多选) → vote_interaction
45
+ └─ 提供多个维度的选择(≤3个下拉框) → multiple_interaction
46
+ ```
47
+
48
+ ### 场景速查
49
+
50
+ | card_type | 典型场景 |
51
+ |-----------|---------|
52
+ | `text_notice` | 系统告警、部署通知、数据日报、审批结果、放假公告 |
53
+ | `news_notice` | 新闻推送、活动宣传、产品发布、带图表的周报、新员工介绍 |
54
+ | `button_interaction` | 审批(同意/驳回)、工单处理(接单/转派)、确认操作、满意度评价、二次确认(是/否) |
55
+ | `vote_interaction` | 团建投票、会议时间协调、问卷单题、意见征集、方案评选 |
56
+ | `multiple_interaction` | 多维度信息收集(部门+楼层+时间)、报障工单(类型+紧急程度)、会议室预定(日期+时间+房间) |
57
+
58
+ ### 易混淆卡片区分
59
+
60
+ - **button_interaction vs vote_interaction**:按钮型适合"少量选项+即时操作"(如确认/取消),投票型适合"选项多+可多选+统一提交"(如投票)
61
+ - **vote_interaction vs multiple_interaction**:投票型是"一个问题选多项",多项选择型是"多个独立问题各选一项"(如同时选时间和地点)
62
+
63
+ ## 必填字段速查
64
+
65
+ | card_type | required |
66
+ |-----------|----------|
67
+ | `text_notice` | `card_type`, `task_id`, `card_action`, (`main_title` 或 `sub_title_text` 至少一个) |
68
+ | `news_notice` | `card_type`, `task_id`, `main_title`, `card_action` |
69
+ | `button_interaction` | `card_type`, `task_id`, `main_title`, `button_list` |
70
+ | `vote_interaction` | `card_type`, `task_id`, `title`, `options` |
71
+ | `multiple_interaction` | `card_type`, `task_id`, `title`, `selectors` |
72
+
73
+ ## 最小示例
74
+
75
+ ### text_notice
76
+
77
+ ```json
78
+ {
79
+ "card_type": "text_notice",
80
+ "main_title": { "title": "项目进度更新", "desc": "进度已达 80%" },
81
+ "emphasis_content": { "title": "80%", "desc": "完成度" },
82
+ "card_action": { "type": 1, "url": "https://work.weixin.qq.com" },
83
+ "task_id": "task_progress_1711234567"
84
+ }
85
+ ```
86
+
87
+ ### news_notice
88
+
89
+ ```json
90
+ {
91
+ "card_type": "news_notice",
92
+ "main_title": { "title": "新功能上线", "desc": "v5.0 已发布" },
93
+ "card_image": { "url": "https://wework.qpic.cn/wwpic/xxx/0" },
94
+ "card_action": { "type": 1, "url": "https://work.weixin.qq.com" },
95
+ "task_id": "task_release_1711234568"
96
+ }
97
+ ```
98
+
99
+ ### button_interaction
100
+
101
+ ```json
102
+ {
103
+ "card_type": "button_interaction",
104
+ "main_title": { "title": "方案选择", "desc": "请选择技术方案" },
105
+ "button_list": [
106
+ { "text": "A方案-重构", "style": 1, "key": "plan_a" },
107
+ { "text": "B方案-兼容", "style": 4, "key": "plan_b" }
108
+ ],
109
+ "task_id": "task_plan_select_1711234569"
110
+ }
111
+ ```
112
+
113
+ ### vote_interaction(简化格式)
114
+
115
+ ```json
116
+ {
117
+ "card_type": "vote_interaction",
118
+ "title": "团建投票",
119
+ "description": "选择你想参加的活动",
120
+ "options": [
121
+ { "id": "hiking", "text": "爬山" },
122
+ { "id": "dinner", "text": "聚餐" },
123
+ { "id": "escape", "text": "密室逃脱" }
124
+ ],
125
+ "mode": 1,
126
+ "submit_text": "提交投票",
127
+ "task_id": "task_team_vote_1711234570"
128
+ }
129
+ ```
130
+
131
+ ### multiple_interaction(简化格式)
132
+
133
+ ```json
134
+ {
135
+ "card_type": "multiple_interaction",
136
+ "title": "团建安排",
137
+ "description": "分别选择时间和地点",
138
+ "selectors": [
139
+ {
140
+ "title": "时间",
141
+ "options": [
142
+ { "id": "sat", "text": "周六" },
143
+ { "id": "sun", "text": "周日" }
144
+ ]
145
+ },
146
+ {
147
+ "title": "地点",
148
+ "options": [
149
+ { "id": "park", "text": "公园" },
150
+ { "id": "resort", "text": "度假村" }
151
+ ]
152
+ }
153
+ ],
154
+ "submit_text": "提交选择",
155
+ "task_id": "task_arrange_1711234571"
156
+ }
157
+ ```
@@ -0,0 +1,358 @@
1
+ # 模板卡片类型 - JSON Schema 定义
2
+
3
+ > 来源:企业微信开发者文档 https://developer.work.weixin.qq.com/document/path/101032
4
+ > 格式:JSON Schema,所有字段名、类型、嵌套层级必须严格遵循
5
+
6
+ ---
7
+
8
+ ## 公共结构体 Schema
9
+
10
+ 以下结构体在多种卡片类型中复用,**字段名必须完全一致**。
11
+
12
+ ### MainTitle
13
+
14
+ ```json
15
+ {
16
+ "type": "object",
17
+ "properties": {
18
+ "title": { "type": "string", "description": "一级标题,建议不超过26字" },
19
+ "desc": { "type": "string", "description": "标题辅助信息,建议不超过30字" }
20
+ }
21
+ }
22
+ ```
23
+
24
+ ### Source
25
+
26
+ ```json
27
+ {
28
+ "type": "object",
29
+ "properties": {
30
+ "icon_url": { "type": "string", "description": "来源图片URL" },
31
+ "desc": { "type": "string", "description": "来源描述,建议不超过13字" },
32
+ "desc_color": { "type": "integer", "enum": [0, 1, 2, 3], "description": "来源文字颜色,只能是0(灰)、1(黑)、2(红)、3(绿)" }
33
+ }
34
+ }
35
+ ```
36
+
37
+ ### CardAction
38
+
39
+ ```json
40
+ {
41
+ "type": "object",
42
+ "required": ["type"],
43
+ "properties": {
44
+ "type": { "type": "integer", "enum": [1, 2], "description": "1=跳转url 2=打开小程序(严格只能是1或2)" },
45
+ "url": { "type": "string", "description": "跳转url,type=1时必填" },
46
+ "appid": { "type": "string", "description": "小程序appid,type=2时必填" },
47
+ "pagepath": { "type": "string", "description": "小程序pagepath" }
48
+ }
49
+ }
50
+ ```
51
+
52
+ ### EmphasisContent
53
+
54
+ ```json
55
+ {
56
+ "type": "object",
57
+ "properties": {
58
+ "title": { "type": "string", "description": "关键数据内容,建议不超过10字" },
59
+ "desc": { "type": "string", "description": "关键数据描述,建议不超过15字" }
60
+ }
61
+ }
62
+ ```
63
+
64
+ ### SubmitButton
65
+
66
+ ```json
67
+ {
68
+ "type": "object",
69
+ "required": ["text", "key"],
70
+ "properties": {
71
+ "text": { "type": "string", "description": "按钮文案,建议不超过10字" },
72
+ "key": { "type": "string", "description": "提交按钮的key,用于回调事件" }
73
+ }
74
+ }
75
+ ```
76
+
77
+ ### Button
78
+
79
+ ```json
80
+ {
81
+ "type": "object",
82
+ "required": ["text", "key"],
83
+ "properties": {
84
+ "text": { "type": "string", "description": "按钮文案,建议不超过10字" },
85
+ "style": { "type": "integer", "enum": [1, 2, 3, 4], "description": "按钮样式,只能是1、2、3、4,默认1" },
86
+ "key": { "type": "string", "description": "按钮key,用于回调事件" }
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Checkbox(投票选择题)
92
+
93
+ ```json
94
+ {
95
+ "type": "object",
96
+ "required": ["question_key", "option_list"],
97
+ "properties": {
98
+ "question_key": { "type": "string", "description": "选择题key,用于回调事件" },
99
+ "mode": { "type": "integer", "enum": [0, 1], "description": "0=单选 1=多选(严格只能是0或1,不允许2或其他值),默认0" },
100
+ "option_list": {
101
+ "type": "array",
102
+ "minItems": 1,
103
+ "maxItems": 20,
104
+ "items": {
105
+ "type": "object",
106
+ "required": ["id", "text"],
107
+ "properties": {
108
+ "id": { "type": "string", "description": "选项id,不可重复" },
109
+ "text": { "type": "string", "description": "选项文案,建议不超过11字" },
110
+ "is_checked": { "type": "boolean", "description": "是否默认选中" }
111
+ }
112
+ }
113
+ }
114
+ }
115
+ }
116
+ ```
117
+
118
+ ### SelectionItem(下拉选择器)
119
+
120
+ ```json
121
+ {
122
+ "type": "object",
123
+ "required": ["question_key", "option_list"],
124
+ "properties": {
125
+ "question_key": { "type": "string", "description": "选择器key,用于回调事件,不可重复" },
126
+ "title": { "type": "string", "description": "选择器标题,建议不超过13字" },
127
+ "selected_id": { "type": "string", "description": "默认选定的option id" },
128
+ "option_list": {
129
+ "type": "array",
130
+ "minItems": 1,
131
+ "maxItems": 10,
132
+ "items": {
133
+ "type": "object",
134
+ "required": ["id", "text"],
135
+ "properties": {
136
+ "id": { "type": "string", "description": "选项id,不可重复" },
137
+ "text": { "type": "string", "description": "选项文案,建议不超过10字" }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
145
+ ### HorizontalContent
146
+
147
+ ```json
148
+ {
149
+ "type": "object",
150
+ "required": ["keyname"],
151
+ "properties": {
152
+ "type": { "type": "integer", "enum": [0, 1, 3], "description": "只能是0(普通文本)、1(跳转url)、3(成员详情),注意没有2" },
153
+ "keyname": { "type": "string", "description": "二级标题,建议不超过5字" },
154
+ "value": { "type": "string", "description": "二级文本,建议不超过26字" },
155
+ "url": { "type": "string", "description": "跳转url,type=1时必填" },
156
+ "userid": { "type": "string", "description": "成员userid,type=3时必填" }
157
+ }
158
+ }
159
+ ```
160
+
161
+ ### JumpAction
162
+
163
+ ```json
164
+ {
165
+ "type": "object",
166
+ "required": ["title"],
167
+ "properties": {
168
+ "type": { "type": "integer", "enum": [0, 1, 2, 3], "description": "只能是0(无链接)、1(url)、2(小程序)、3(智能回复)" },
169
+ "title": { "type": "string", "description": "文案内容,建议不超过13字" },
170
+ "url": { "type": "string", "description": "跳转url,type=1时必填" },
171
+ "appid": { "type": "string", "description": "小程序appid,type=2时必填" },
172
+ "pagepath": { "type": "string", "description": "小程序pagepath" },
173
+ "question": { "type": "string", "description": "智能问答问题,type=3时必填" }
174
+ }
175
+ }
176
+ ```
177
+
178
+ ### QuoteArea
179
+
180
+ ```json
181
+ {
182
+ "type": "object",
183
+ "properties": {
184
+ "type": { "type": "integer", "enum": [0, 1, 2], "description": "只能是0(无点击)、1(url)、2(小程序)" },
185
+ "url": { "type": "string" },
186
+ "title": { "type": "string", "description": "引用标题" },
187
+ "quote_text": { "type": "string", "description": "引用文案" }
188
+ }
189
+ }
190
+ ```
191
+
192
+ ### CardImage
193
+
194
+ ```json
195
+ {
196
+ "type": "object",
197
+ "required": ["url"],
198
+ "properties": {
199
+ "url": { "type": "string", "description": "图片url" },
200
+ "aspect_ratio": { "type": "number", "description": "宽高比,1.3~2.25,默认1.3" }
201
+ }
202
+ }
203
+ ```
204
+
205
+ ---
206
+
207
+ ## 1. text_notice Schema
208
+
209
+ ```json
210
+ {
211
+ "type": "object",
212
+ "required": ["card_type", "card_action", "task_id"],
213
+ "properties": {
214
+ "card_type": { "const": "text_notice" },
215
+ "source": { "$ref": "#Source" },
216
+ "main_title": { "$ref": "#MainTitle", "description": "main_title.title 和 sub_title_text 至少填一个" },
217
+ "emphasis_content": { "$ref": "#EmphasisContent" },
218
+ "sub_title_text": { "type": "string", "description": "二级文本,建议不超过112字" },
219
+ "horizontal_content_list": { "type": "array", "maxItems": 6, "items": { "$ref": "#HorizontalContent" } },
220
+ "jump_list": { "type": "array", "maxItems": 3, "items": { "$ref": "#JumpAction" } },
221
+ "card_action": { "$ref": "#CardAction", "description": "必填" },
222
+ "task_id": { "type": "string", "description": "必填,格式:task_{描述}_{时间戳}" }
223
+ }
224
+ }
225
+ ```
226
+
227
+ ---
228
+
229
+ ## 2. news_notice Schema
230
+
231
+ ```json
232
+ {
233
+ "type": "object",
234
+ "required": ["card_type", "main_title", "card_action", "task_id"],
235
+ "properties": {
236
+ "card_type": { "const": "news_notice" },
237
+ "source": { "$ref": "#Source" },
238
+ "main_title": { "$ref": "#MainTitle", "description": "必填" },
239
+ "card_image": { "$ref": "#CardImage", "description": "card_image 和 image_text_area 至少填一个" },
240
+ "vertical_content_list": { "type": "array", "maxItems": 4, "items": { "type": "object", "required": ["title"], "properties": { "title": { "type": "string" }, "desc": { "type": "string" } } } },
241
+ "horizontal_content_list": { "type": "array", "maxItems": 6, "items": { "$ref": "#HorizontalContent" } },
242
+ "jump_list": { "type": "array", "maxItems": 3, "items": { "$ref": "#JumpAction" } },
243
+ "card_action": { "$ref": "#CardAction", "description": "必填" },
244
+ "task_id": { "type": "string", "description": "必填" }
245
+ }
246
+ }
247
+ ```
248
+
249
+ ---
250
+
251
+ ## 3. button_interaction Schema
252
+
253
+ ```json
254
+ {
255
+ "type": "object",
256
+ "required": ["card_type", "main_title", "button_list", "task_id"],
257
+ "properties": {
258
+ "card_type": { "const": "button_interaction" },
259
+ "source": { "$ref": "#Source" },
260
+ "main_title": { "$ref": "#MainTitle", "description": "必填" },
261
+ "sub_title_text": { "type": "string", "description": "二级文本" },
262
+ "horizontal_content_list": { "type": "array", "maxItems": 6, "items": { "$ref": "#HorizontalContent" } },
263
+ "button_selection": {
264
+ "$ref": "#SelectionItem",
265
+ "description": "可选的下拉选择器,与按钮配合使用"
266
+ },
267
+ "button_list": {
268
+ "type": "array",
269
+ "minItems": 1,
270
+ "maxItems": 6,
271
+ "items": { "$ref": "#Button" },
272
+ "description": "必填,按钮列表"
273
+ },
274
+ "task_id": { "type": "string", "description": "必填" }
275
+ }
276
+ }
277
+ ```
278
+
279
+ ---
280
+
281
+ ## 4. vote_interaction Schema(简化格式,代码自动转换为 API 格式)
282
+
283
+ > **不要使用** `checkbox`、`question_key`、`option_list` 等 API 原始字段名。使用以下简化字段,代码会自动组装。
284
+
285
+ ```json
286
+ {
287
+ "type": "object",
288
+ "required": ["card_type", "title", "options", "task_id"],
289
+ "properties": {
290
+ "card_type": { "const": "vote_interaction" },
291
+ "title": { "type": "string", "description": "投票标题" },
292
+ "description": { "type": "string", "description": "标题描述,可选" },
293
+ "options": {
294
+ "type": "array",
295
+ "minItems": 1,
296
+ "maxItems": 20,
297
+ "items": {
298
+ "type": "object",
299
+ "required": ["id", "text"],
300
+ "properties": {
301
+ "id": { "type": "string", "description": "选项id,不可重复" },
302
+ "text": { "type": "string", "description": "选项文案" }
303
+ }
304
+ },
305
+ "description": "投票选项列表"
306
+ },
307
+ "mode": { "type": "integer", "enum": [0, 1], "description": "0=单选 1=多选(严格只能是0或1),默认0" },
308
+ "submit_text": { "type": "string", "description": "提交按钮文案,可选,默认\"提交\"" },
309
+ "task_id": { "type": "string", "description": "必填,格式:task_{描述}_{时间戳}" }
310
+ }
311
+ }
312
+ ```
313
+
314
+ ---
315
+
316
+ ## 5. multiple_interaction Schema(简化格式,代码自动转换为 API 格式)
317
+
318
+ > **不要使用** `select_list`、`question_key`、`option_list` 等 API 原始字段名。使用以下简化字段,代码会自动组装。
319
+
320
+ ```json
321
+ {
322
+ "type": "object",
323
+ "required": ["card_type", "title", "selectors", "task_id"],
324
+ "properties": {
325
+ "card_type": { "const": "multiple_interaction" },
326
+ "title": { "type": "string", "description": "卡片标题" },
327
+ "description": { "type": "string", "description": "标题描述,可选" },
328
+ "selectors": {
329
+ "type": "array",
330
+ "minItems": 1,
331
+ "maxItems": 3,
332
+ "items": {
333
+ "type": "object",
334
+ "required": ["title", "options"],
335
+ "properties": {
336
+ "title": { "type": "string", "description": "此选择器的标题" },
337
+ "options": {
338
+ "type": "array",
339
+ "minItems": 1,
340
+ "maxItems": 10,
341
+ "items": {
342
+ "type": "object",
343
+ "required": ["id", "text"],
344
+ "properties": {
345
+ "id": { "type": "string", "description": "选项id,不可重复" },
346
+ "text": { "type": "string", "description": "选项文案" }
347
+ }
348
+ }
349
+ }
350
+ }
351
+ },
352
+ "description": "下拉选择器列表,每个选择器包含标题和选项"
353
+ },
354
+ "submit_text": { "type": "string", "description": "提交按钮文案,可选,默认\"提交\"" },
355
+ "task_id": { "type": "string", "description": "必填,格式:task_{描述}_{时间戳}" }
356
+ }
357
+ }
358
+ ```