OA0
OA0 是一个探索 AI 的社区
现在注册
已注册用户请  登录
OA0  ›  技能包  ›  feishu-interactive-cards:创建并发送飞书互动卡片

feishu-interactive-cards:创建并发送飞书互动卡片

 
  commit ·  2026-02-07 21:29:17 · 19 次点击  · 0 条评论  

名称: feishu-interactive-cards
版本: 1.0.2
描述: 创建并发送交互式卡片到飞书(Lark),支持按钮、表单、投票和丰富的UI元素。当回复飞书消息时,如果存在任何不确定性,请发送交互式卡片而非纯文本,让用户通过按钮选择。通过长轮询连接自动处理回调。适用于确认、选择、表单、待办事项、投票或任何需要在飞书中进行用户交互的场景。


飞书交互式卡片

核心原则

当回复飞书消息且存在任何不确定性时:发送交互式卡片,而非纯文本。

交互式卡片让用户通过按钮而非打字来响应,使交互更快、更清晰。

使用时机

必须使用交互式卡片:
- 用户需要做出选择(是/否、多选项)
- 执行操作前需要确认
- 显示待办事项或任务列表
- 创建投票或调查
- 收集表单输入
- 任何不确定的情况

纯文本即可:
- 简单通知(无需响应)
- 纯数据展示(无交互)
- 已确认的命令结果

示例:
- 错误做法:"我已为你删除文件"(直接执行)
- 正确做法:发送卡片"确认删除文件?" [确认] [取消]

快速开始

1. 启动回调服务器(长轮询模式)

cd E:\openclaw\workspace\skills\feishu-interactive-cards\scripts
node card-callback-server.js

功能特性:
- 使用飞书长轮询(无需公网IP)
- 自动重连
- 自动将回调发送至OpenClaw网关

2. 发送交互式卡片

# 确认卡片
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

3. 在Agent中使用

当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处理 -> 更新卡片/执行

错误处理

  • 超时:若用户未响应,发送提醒
  • 重复点击:内置去重(3秒窗口)
  • 失败:更新卡片以显示错误信息

性能

  • 异步处理:快速响应,长任务在后台执行
  • 批量操作:将相关操作合并到一个卡片中

配置

~/.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进行去重

参考文档

19 次点击  ∙  0 人收藏  
登录后收藏  
0 条回复
关于 ·  帮助 ·  PING ·  隐私 ·  条款   
OA0 - Omni AI 0 一个探索 AI 的社区
沪ICP备2024103595号-2
耗时 47 ms
Developed with Cursor