名称: feishu-interactive-cards
版本: 1.0.2
描述: 创建并发送交互式卡片到飞书(Lark),支持按钮、表单、投票和丰富的UI元素。当回复飞书消息时,如果存在任何不确定性,请发送交互式卡片而非纯文本,让用户通过按钮选择。通过长轮询连接自动处理回调。适用于确认、选择、表单、待办事项、投票或任何需要在飞书中进行用户交互的场景。
当回复飞书消息且存在任何不确定性时:发送交互式卡片,而非纯文本。
交互式卡片让用户通过按钮而非打字来响应,使交互更快、更清晰。
必须使用交互式卡片:
- 用户需要做出选择(是/否、多选项)
- 执行操作前需要确认
- 显示待办事项或任务列表
- 创建投票或调查
- 收集表单输入
- 任何不确定的情况
纯文本即可:
- 简单通知(无需响应)
- 纯数据展示(无交互)
- 已确认的命令结果
示例:
- 错误做法:"我已为你删除文件"(直接执行)
- 正确做法:发送卡片"确认删除文件?" [确认] [取消]
cd E:\openclaw\workspace\skills\feishu-interactive-cards\scripts
node card-callback-server.js
功能特性:
- 使用飞书长轮询(无需公网IP)
- 自动重连
- 自动将回调发送至OpenClaw网关
# 确认卡片
node scripts/send-card.js confirmation "确认删除文件?" --chat-id oc_xxx
# 待办列表
node scripts/send-card.js todo --chat-id oc_xxx
# 投票
node scripts/send-card.js poll "团队活动" --options "保龄球,看电影,聚餐" --chat-id oc_xxx
# 自定义卡片
node scripts/send-card.js custom --template examples/custom-card.json --chat-id oc_xxx
当Agent需要发送飞书消息时:
// 错误:发送纯文本
await message({
action: "send",
channel: "feishu",
message: "确认删除?"
});
// 正确:发送交互式卡片
await exec({
command: `node E:\\openclaw\\workspace\\skills\\feishu-interactive-cards\\scripts\\send-card.js confirmation "确认删除文件 test.txt?" --chat-id ${chatId}`
});
查看 examples/ 目录获取完整卡片模板:
- confirmation-card.json - 确认对话框
- todo-card.json - 带复选框的任务列表
- poll-card.json - 投票和调查
- form-card.json - 带输入字段的表单
有关详细的卡片设计模式和最佳实践,请参阅 references/card-design-guide.md。
回调服务器自动将所有卡片交互发送到OpenClaw网关。详细的集成指南请参阅 references/gateway-integration.md。
快速示例:
// 处理确认
if (callback.data.action.value.action === "confirm") {
const file = callback.data.action.value.file;
// ⚠️ 安全:使用前验证并清理文件路径
// 使用OpenClaw内置的文件操作,而非shell命令
const fs = require('fs').promises;
const path = require('path');
try {
// 验证文件路径(防止目录遍历攻击)
const safePath = path.resolve(file);
if (!safePath.startsWith(process.cwd())) {
throw new Error('无效的文件路径');
}
// 使用fs API而非shell命令
await fs.unlink(safePath);
// 更新卡片
await updateCard(callback.context.open_message_id, {
header: { title: "完成", template: "green" },
elements: [
{ tag: "div", text: { content: `文件 ${path.basename(safePath)} 已删除`, tag: "lark_md" } }
]
});
} catch (error) {
// 处理错误
await updateCard(callback.context.open_message_id, {
header: { title: "错误", template: "red" },
elements: [
{ tag: "div", text: { content: `删除文件失败: ${error.message}`, tag: "lark_md" } }
]
});
}
}
danger 类型value 中携带完整状态,避免额外查询用户请求 -> Agent决策 -> 发送卡片 -> 用户点击按钮
-> 回调服务器 -> 网关 -> Agent处理 -> 更新卡片/执行
在 ~/.openclaw/openclaw.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 中的令牌
卡片显示问题:
- 使用提供的模板作为基础
- 验证JSON格式
- 检查必填字段
⚠️ 关键:切勿将用户输入直接传递给shell命令!
本技能包含全面的安全指南。在实现回调处理程序之前,请务必阅读 references/security-best-practices.md。
关键安全原则:
- 始终验证并清理用户输入
- 使用Node.js内置API而非shell命令
- 实施适当的权限检查
- 防止命令注入漏洞
- 使用event_id进行去重