介绍
# Feishu Interactive Cards
## 核心原则
**在回复飞书且存在任何不确定性时:发送交互卡片而不是纯文本。**
交互卡片允许用户通过按钮进行响应,而无需手动输入,从而使交互更快、更清晰。
## 使用场景
**必须使用交互卡片:** - 用户需要做出选择(是/否,多项选择) - 执行操作前需要确认 - 显示待办事项或任务列表 - 创建投票或调查 - 收集表单输入 - 任何不确定的情况
**纯文本即可:** - 简单通知(无需回复) - 纯数据展示(无交互) - 已确认的命令结果
**示例:** - 错误:“我已为您删除文件”(直接执行) - 正确:发送卡片“确认删除文件?” [确认] [取消]
## 快速开始
### 1. 启动回调服务(长轮询模式)
```bash cd E:\openclaw\workspace\skills\feishu-interactive-cards\scripts node card-callback-server.js ```
**特性:** - 使用飞书长轮询(无需公网 IP) - 自动重连 - 自动将回调发送至 OpenClaw 网关
### 2. 发送交互卡片
```bash # Confirmation card node scripts/send-card.js confirmation "Confirm delete file?" --chat-id oc_xxx
# Todo list node scripts/send-card.js todo --chat-id oc_xxx
# Poll node scripts/send-card.js poll "Team activity" --options "Bowling,Movie,Dinner" --chat-id oc_xxx
# Custom card node scripts/send-card.js custom --template examples/custom-card.json --chat-id oc_xxx ```
### 3. 在 Agent 中使用
当 Agent 需要发送飞书消息时:
```javascript // Wrong: Send plain text await message({ action: "send", channel: "feishu", message: "Confirm delete?" });
// Right: Send interactive card await exec({ command: `node E:\\openclaw\\workspace\\skills\\feishu-interactive-cards\\scripts\\send-card.js confirmation "Confirm delete file test.txt?" --chat-id ${chatId}` }); ```
## 卡片模板
请参阅 `examples/` 目录获取完整的卡片模板: - `confirmation-card.json` - 确认对话框 - `todo-card.json` - 带复选框的任务列表 - `poll-card.json` - 投票和调查 - `form-card.json` - 带输入字段的表单
有关详细的卡片设计模式和最佳实践,请参阅 [references/card-design-guide.md](references/card-design-guide.md)。
## 回调处理
回调服务器会自动将所有卡片交互发送至 OpenClaw 网关。有关详细的集成指南,请参阅 [references/gateway-integration.md](references/gateway-integration.md)。
**快速示例:**
```javascript // Handle confirmation if (callback.data.action.value.action === "confirm") { const file = callback.data.action.value.file; // ⚠️ SECURITY: Validate and sanitize file path before use // Use OpenClaw's built-in file operations instead of shell commands const fs = require('fs').promises; const path = require('path'); try { // Validate file path (prevent directory traversal) const safePath = path.resolve(file); if (!safePath.startsWith(process.cwd())) { throw new Error('Invalid file path'); } // Use fs API instead of shell command await fs.unlink(safePath); // Update card await updateCard(callback.context.open_message_id, { header: { title: "Done", template: "green" }, elements: [ { tag: "div", text: { content: `File ${path.basename(safePath)} deleted`, tag: "lark_md" } } ] }); } catch (error) { // Handle error await updateCard(callback.context.open_message_id, { header: { title: "Error", template: "red" }, elements: [ { tag: "div", text: { content: `Failed to delete file: ${error.message}`, tag: "lark_md" } } ] }); } } ```
## 最佳实践
### 卡片设计 - 标题和内容清晰 - 按钮操作明显 - 对破坏性操作使用 `danger` 类型 - 在按钮 `value` 中携带完整状态以避免额外查询
### 交互流程 ``` User request -> Agent decides -> Send card -> User clicks button -> Callback server -> Gateway -> Agent handles -> Update card/execute ```
### 错误处理 - 超时:如果用户未响应,发送提醒 - 重复点击:内置去重(3秒窗口) - 失败:更新卡片以显示错误消息
### 性能 - 异步处理:快速响应,后台处理长任务 - 批量操作:将相关操作合并在一张卡片中
## 配置
在 `~/.openclaw/openclaw.json` 中进行配置:
```json { "channels": { "feishu": { "accounts": { "main": { "appId": "YOUR_APP_ID", "appSecret": "YOUR_APP_SECRET" } } } }, "gateway": { "enabled": true, "port": 18789, "token": "YOUR_GATEWAY_TOKEN" } } ```
回调服务器会自动读取配置。
## 故障排除
**按钮点击无效:** - 检查回调服务器是否正在运行 - 验证飞书后端使用的是“长轮询”模式 - 确保已订阅 `card.action.trigger` 事件
**网关未收到回调:** - 启动网关:`E:\openclaw\workspace\scripts\gateway.cmd` - 检查 `~/.openclaw\openclaw.json` 中的 token
**卡片显示问题:** - 使用提供的模板作为基础 - 验证 JSON 格式 - 检查必填字段
## 安全性
**⚠️ 关键:切勿将用户输入直接传递给 Shell 命令!**
此技能包含全面的安全指南。请在实现回调处理程序之前阅读 [references/security-best-practices.md](references/security-best-practices.md)。
主要安全原则: - 始终验证并清理用户输入 - 使用 Node.js 内置 API 代替 Shell 命令 - 实施适当的权限检查 - 防止命令注入漏洞 - 使用 event_id 进行去重
## 参考资料
- [安全最佳实践](references/security-best-practices.md) - **请首先阅读本文!** - [飞书卡片文档](https://open.feishu.cn/document/ukTMukTMukTM/uczM3QjL3MzN04yNzcDN) - [OpenClaw 文档](https://docs.openclaw.ai)