agentleFS
Aquarius-muAquarius-mu/skillManager/skills/feishu-card/SKILL.md

feishu-card

Skill1 starsChanged 51 days agoMITView on GitHub

飞书交互卡片(schema 2.0):构建和发送卡片消息,含 @提及、流式思考态、原地更新、按钮/选择器交互。通过 lark-cli --as bot 发送,无需手动管理 token。

5 other projects use a version of this, 2 of them edited.

Adopted by

Most recently taken up by

The file

Files
SKILL.md

feishu-card(schema 2.0)

关键规则

  • 所有卡片使用 schema 2.0,元素放在 body.elements[],不是顶层 elements[]
  • 发送卡片必须 --as bot,--msg-type interactive,--content 传 JSON 字符串
  • 卡片内 @人 用 <at id="ou_xxx">姓名</at>(不是 <at user_id>)
  • JSON 2.0 按钮不再有 action 容器,button 直接放进 body.elements,交互用 behaviors 数组
  • 先发"思考中"卡片拿到 message_id,Claude 回答后 PATCH 原地更新,避免刷屏
  • note tag 在 schema 2.0 中已不支持(报错 200861),备注改用 div + lark_md(<font color=grey>备注文字</font>)
  • 构建卡片 JSON 必须用 Python heredoc 或写文件,禁止用 $(python3 -c "...") ——内容含反引号或特殊字符时 bash 会报语法错误

顶层结构完整字段

{
  "schema": "2.0",
  "config": {
    "streaming_mode": false,
    "enable_forward": true,
    "update_multi": true,
    "width_mode": "default"
  },
  "card_link": {
    "url": "https://...",
    "pc_url": "https://...",
    "ios_url": "https://...",
    "android_url": "https://..."
  },
  "header": { "...": "..." },
  "body": {
    "direction": "vertical",
    "padding": "0px",
    "elements": []
  }
}

config 字段

字段类型默认说明
streaming_modeboolfalse流式更新态(思考中占位用)
enable_forwardbooltrue允许转发卡片
update_multiboolfalseJSON 2.0 共享卡片模式(多人同步更新需设 true)
width_modestringdefault(600px)compact(400px)/ fill(全宽响应)/ default

Header 字段

{
  "header": {
    "title":    {"tag": "plain_text", "content": "标题"},
    "subtitle": {"tag": "plain_text", "content": "副标题(可选)"},
    "template": "blue",
    "icon": {
      "tag": "standard_icon",
      "token": "robot-outlined",
      "color": "blue"
    },
    "text_tag_list": [
      {"tag": "text_tag", "text": {"tag": "plain_text", "content": "New"}, "color": "blue"}
    ]
  }
}

template 颜色:blue / wathet / turquoise / green / yellow / orange / red / carmine / violet / purple / indigo / grey


飞书颜色枚举速查

所有支持颜色枚举的字段(background_color、text_color、icon color 等)均使用以下格式,不能只写基础色名(如 "blue" 单独使用在某些组件会报错),需带层级后缀:

色系浅色(背景推荐)中色深色(文字推荐)
蓝 blueblue-50 blue-100 blue-200blue-300~blue-500blue-600~blue-900
水蓝 wathetwathet-50 wathet-100 wathet-200wathet-300~wathet-500wathet-600~wathet-900
青 turquoiseturquoise-50 turquoise-100turquoise-300~turquoise-500turquoise-600~turquoise-900
绿 greengreen-50 green-100 green-200green-300~green-500green-600~green-900
黄绿 limelime-50 lime-100lime-300~lime-500lime-600~lime-900
黄 yellowyellow-50 yellow-100yellow-300~yellow-500yellow-600~yellow-900
橙 orangeorange-50 orange-100orange-300~orange-500orange-600~orange-900
红 redred-50 red-100red-300~red-500red-600~red-900
粉红 carminecarmine-50 carmine-100carmine-300~carmine-500carmine-600~carmine-900
紫 purplepurple-50 purple-100purple-300~purple-500purple-600~purple-900
蓝紫 violetviolet-50 violet-100violet-300~violet-500violet-600~violet-900
靛 indigoindigo-50 indigo-100indigo-300~indigo-500indigo-600~indigo-900
灰 greygrey-00 grey-50 grey-100grey-300~grey-500grey-600~grey-1000
白white bg-white——

常用浅色背景推荐(collapsible_panel、column 背景等):

blue-100    wathet-100    green-100    yellow-100
orange-100  red-100       purple-100   carmine-100
turquoise-100    violet-100    indigo-100    lime-100

组件速查表

tag类型说明
div内容文本块(plain_text / lark_md),支持前缀 icon
markdown内容纯 Markdown 文本(不支持 @,@用 div+lark_md)
img内容图片
table内容数据表格(最多 5 个/卡片,不可嵌套)
hr布局分割线
note布局❌ schema 2.0 已废弃,改用 div + lark_md + <font color=grey>
column_set容器多列布局
collapsible_panel容器折叠面板
button交互按钮(JSON 2.0 直接放 elements,不用 action 容器)
input交互文本输入框(须在 form 内使用)
select_static交互静态下拉选择器

div(文本块)

{
  "tag": "div",
  "text": {
    "tag": "lark_md",
    "content": "**加粗** _斜体_ `代码`",
    "text_size": "normal",
    "text_color": "default",
    "text_align": "left",
    "lines": 3
  },
  "icon": {"tag": "standard_icon", "token": "robot-outlined", "color": "blue"}
}

text.tag:plain_text(纯文本)或 lark_md(Markdown + @) text_size:heading-0 / heading / normal(默认)/ notation / x-small text_align:left / center / right


img(图片)

{
  "tag": "img",
  "img_key": "img_xxx",
  "alt": {"tag": "plain_text", "content": "悬停说明"},
  "title": {"tag": "plain_text", "content": "图片标题"},
  "scale_type": "crop_center",
  "size": "medium",
  "corner_radius": "4px",
  "preview": true
}

scale_type:crop_center / crop_top / fit_horizontal size:stretch(全宽)/ large / medium / small / tiny / "300px 200px"(自定义)

JSON 2.0 不支持 stretch_without_padding,全宽效果改用负 margin


column_set(多列布局)

{
  "tag": "column_set",
  "flex_mode": "bisect",
  "horizontal_spacing": "medium",
  "background_style": "default",
  "columns": [
    {
      "tag": "column",
      "width": "weighted",
      "weight": 1,
      "vertical_align": "top",
      "elements": [{"tag": "div", "text": {"tag": "plain_text", "content": "左列"}}]
    },
    {
      "tag": "column",
      "width": "weighted",
      "weight": 1,
      "elements": [{"tag": "div", "text": {"tag": "plain_text", "content": "右列"}}]
    }
  ]
}

flex_mode:none(按比例压缩)/ stretch(窄屏纵排)/ flow(自动换行)/ bisect(等宽两列)/ trisect(等宽三列) horizontal_spacing:small(4px) / medium(8px) / large(12px) / extra_large(16px) column.width:auto / weighted(配合 weight 1-5)/ "200px"

最大嵌套 5 层,列内不可放 form 容器和 table


collapsible_panel(折叠面板)

{
  "tag": "collapsible_panel",
  "expanded": false,
  "background_color": "default",
  "header": {
    "title": {"tag": "plain_text", "content": "点击展开"},
    "background_color": "grey",
    "icon": {"tag": "standard_icon", "token": "arrow-down-outlined"},
    "icon_expanded_angle": -180
  },
  "elements": [
    {"tag": "div", "text": {"tag": "plain_text", "content": "折叠内容"}}
  ]
}

table(数据表格)

{
  "tag": "table",
  "page_size": 5,
  "row_height": "low",
  "freeze_first_column": false,
  "header_style": {
    "text_align": "left",
    "text_size": "normal",
    "background_style": "grey",
    "bold": true
  },
  "columns": [
    {"name": "name", "display_name": "姓名", "data_type": "text", "width": "auto"},
    {"name": "status", "display_name": "状态", "data_type": "options", "width": "120px"},
    {"name": "count", "display_name": "数量", "data_type": "number",
     "format": {"precision": 0, "separator": true}},
    {"name": "ts", "display_name": "时间", "data_type": "date",
     "format": "YYYY/MM/DD HH:mm"}
  ],
  "rows": [
    {"name": "张三", "status": [{"name": "进行中", "color": "blue"}], "count": 42, "ts": 1700000000000}
  ]
}

data_type:text / lark_md / markdown / options / number / persons / date row_height:low / middle / high / auto / "48px"(32-124px)

每张卡片最多 5 个 table,不可嵌套


button(JSON 2.0)

{
  "tag": "button",
  "text": {"tag": "plain_text", "content": "确认"},
  "type": "primary",
  "size": "medium",
  "width": "default",
  "disabled": false,
  "behaviors": [
    {
      "type": "callback",
      "value": {"action": "confirm", "data": "payload"}
    }
  ],
  "confirm": {
    "title": {"tag": "plain_text", "content": "二次确认"},
    "text":  {"tag": "plain_text", "content": "确认执行此操作?"}
  }
}

type:default(灰)/ primary(蓝)/ danger(红)/ text / primary_text / danger_text / primary_filled / danger_filled / laser size:tiny / small / medium / large behaviors[].type:callback(触发事件)/ open_url(跳转链接)

JSON 2.0 变化:不再有 action 容器,button 直接放 body.elements 或 column.elements;交互用 behaviors 替代旧的 value


input(文本输入)

{
  "tag": "input",
  "name": "field_name",
  "placeholder": {"tag": "plain_text", "content": "请输入..."},
  "default_value": "",
  "input_type": "text",
  "max_length": 200,
  "required": false,
  "disabled": false,
  "label": {"tag": "plain_text", "content": "标签"},
  "label_position": "top",
  "width": "fill"
}

input_type:text / multiline_text / password 用户提交触发 card.action.trigger 事件,事件含 input_value


@提及语法

消息类型@人语法@全体
卡片 interactive lark_md<at id="ou_xxx">姓名</at><at id="all">所有人</at>
文本 text / post<at user_id="ou_xxx">姓名</at><at user_id="all"></at>

卡片内 @mention 经验证可用的元素结构(div + lark_md,不要用裸 markdown tag):

{
  "tag": "div",
  "text": {
    "tag": "lark_md",
    "content": "<at id=\"ou_xxx\">姓名</at> 消息内容"
  }
}

Markdown 语法速查(lark_md / markdown tag 内)

文字样式

**加粗**    *斜体*    ~~删除线~~    `行内代码`

颜色文字

<font color=red>红色</font>
<font color=green>绿色</font>
<font color=grey>灰色</font>
<font color=blue>蓝色</font>
<font color=orange>橙色</font>
<font color=wathet>水蓝</font>
<font color=purple>紫色</font>

完整颜色:red / green / grey / blue / orange / wathet / purple / yellow / turquoise / indigo / violet / lime

标签 / 数字徽章

<text_tag color='red'>标签</text_tag>
<number_tag>99</number_tag>

本地化时间

<local_datetime millisecond='1700000000000' format_type='date_num'></local_datetime>

format_type:date_num(2023/11/15)/ date_full(2023年11月15日)/ datetime(含时分秒)

标题 / 列表 / 引用

# H1  ## H2  ### H3  #### H4  ##### H5  ###### H6

- 无序  1. 有序  > 引用

代码块

```python
print("hello")
```

图片(Markdown 内)

![悬停文字](img_xxx)

表格(最多 5 行数据)

| 列1 | 列2 |
|-----|-----|
| A   | B   |

链接

[文字](https://url)    [电话](tel://13800138000)

分割线 / 换行

---   或   <hr>
<br/>  或  两次回车(段落换行)

Emoji

⚠️ 严格规则

  1. 只能使用官方列表中的 key,未收录的 key(如 :WAITING:、:PROCESSING:)会渲染成纯文本,禁止使用。
  2. key 大小写必须与官方完全一致(如 Fire 不是 FIRE,Trophy 不是 TROPHY,Shrug 不是 SHRUG)。
  3. <text_tag> 内部不渲染 :KEY:,须改用 Unicode emoji(如 ✅)。
  4. :KEY: 仅在 lark_md 普通文本区域生效。

发版通知常用

:DONE:        :OK:           :THUMBSUP:     :APPLAUSE:
:PARTY:       :FIREWORKS:    :MUSCLE:       :THANKS:
:Trophy:      :Fire:         :Hundred:      :CheckMark:

工作 & 状态

:LGTM:        :OnIt:         :OneSecond:    :Get:
:ERROR:        :CrossMark:    :Pin:          :Alarm:
:Loudspeaker: :OKR:          :Yes:          :No:
:HEADSET:     :Typing:       :MeMeMe:       :Sigh:
:Lemon:       :AWESOMEN:     :VRHeadset:    :YouAreTheBest:
:SALUTE:      :HIGHFIVE:     :FISTBUMP:     :FINGERHEART:
:WAVE:        :ThumbsDown:

表情 - 正面

:SMILE:       :LAUGH:        :LOL:          :LOVE:
:WINK:        :PROUD:        :WITTY:        :JOYFUL:
:WOW:         :BLUSH:        :HUG:          :BeamingFace:
:Delighted:   :ThanksFace:   :SaluteFace:   :PRAISE:
:CLAP:        :JIAYI:        :FACEPALM:     :YEAH:

表情 - 负面 / 搞怪

:CRY:         :SOB:          :ANGRY:        :SHOCKED:
:Shrug:       :THINKING:     :DIZZY:        :SLEEP:
:YAWN:        :SICK:         :SWEAT:        :EMBARRASSED:
:SKULL:       :RAINBOWPUKE:  :PUKE:         :ClownFace:
:ColdSweat:   :SMUG:         :SILENT:       :TERROR:
:PETRIFIED:   :BETRAYED:     :WRONGED:

庆祝 & 节日

:PARTY:       :FIREWORKS:    :REDPACKET:    :FORTUNE:
:FIRECRACKER: :XmasTree:     :XmasHat:      :Snowman:
:Pumpkin:     :Mooncake:     :MoonRabbit:   :Partying:
:StickyRiceBalls:            :JubilantRabbit:  :LUCK:
:FullMoonFace: :GoGoGo:      :HappyDragon:

物品 & 食物

:ROSE:        :HEART:        :BEER:         :CAKE:
:GIFT:        :BOMB:         :Fire:         :Trophy:
:Coffee:      :BubbleTea:    :Drumstick:    :Pepper:
:CUCUMBER:    :CANDIEDHAWS:  :Soccer:       :Basketball:
:Music:       :CLEAVER:      :EatingFood:   :LIPS:

状态指示(勿频繁使用,仅状态场景)

:GeneralDoNotDisturb:        :GeneralInMeetingBusy:
:StatusReading:              :GeneralWorkFromHome:
:GeneralBusinessTrip:        :StatusInFlight:
:GeneralSun:                 :GeneralMoonRest:
:StatusFlashOfInspiration:   :Status_PrivateMessage:
:GeneralTravellingCar:       :StatusBus:

Unicode Emoji(标题、text_tag 内、header subtitle 中使用)

🎉 ✅ ❌ ⚠️ 🚀 📦 💬 ⏳ 🔔 📝 🛠️ 🔍 👋 🎯 💡 🌟
👥 📅 🤖 🏆 🔥 💯 📣 🎁 ☕ 🍺

推荐风格:标题用 Unicode emoji 增强视觉,状态/反馈用飞书 :DONE: :LGTM: 等专属语法


发送 / 回复 / 更新

发送到群(新消息)

CARD=$(python3 -c "
import json
card = {
  'schema': '2.0',
  'header': {'title': {'tag': 'plain_text', 'content': '标题'}, 'template': 'blue'},
  'body': {'elements': [{'tag': 'markdown', 'content': '内容'}]}
}
print(json.dumps(card))
")
lark-cli im +messages-send \
  --chat-id oc_xxx \
  --msg-type interactive \
  --content "$CARD" \
  --as bot

回复指定消息(获取 message_id 供后续更新)

MSG_ID=$(lark-cli im +messages-reply \
  --message-id om_xxx \
  --msg-type interactive \
  --content "$CARD" \
  --as bot \
  --jq '.data.message_id')

回复到 thread(不出现在主聊天流)

lark-cli im +messages-reply \
  --message-id om_xxx \
  --msg-type interactive \
  --content "$CARD" \
  --reply-in-thread \
  --as bot

防重复发送(1小时内相同 key 只发一次)

lark-cli im +messages-send \
  --chat-id oc_xxx \
  --msg-type interactive \
  --content "$CARD" \
  --idempotency-key "deploy-654.0-notify" \
  --as bot

原地更新卡片(PATCH)

PAYLOAD=$(jq -n --argjson card "$CARD" '{"msg_type":"interactive","content":($card|tojson)}')
lark-cli api PATCH "/open-apis/im/v1/messages/${MSG_ID}" \
  --data "$PAYLOAD" \
  --as bot

卡片模板

思考中(流式态,先发占位)

make_thinking_card() {
  local name="$1" question="$2"
  jq -n --arg name "$name" --arg q "$question" '{
    schema:"2.0",
    header:{title:{tag:"plain_text",content:("💬 "+$name+" 问")},template:"grey"},
    config:{streaming_mode:true},
    body:{elements:[
      {tag:"markdown",content:("> "+$q)},
      {tag:"hr"},
      {tag:"markdown",content:"⏳ **思考中…**"}
    ]}
  }'
}

回答完成(更新思考中卡片)

make_reply_card() {
  local name="$1" question="$2" answer="$3" duration="$4"
  jq -n --arg name "$name" --arg q "$question" --arg ans "$answer" --arg dur "$duration" '{
    schema:"2.0",
    header:{title:{tag:"plain_text",content:("💬 "+$name+" 问")},template:"blue"},
    config:{streaming_mode:false},
    body:{elements:[
      {tag:"markdown",content:("> "+$q)},
      {tag:"hr"},
      {tag:"markdown",content:$ans},
      {tag:"hr"},
      {tag:"markdown",content:("✅ 耗时 **"+$dur+"**")}
    ]}
  }'
}

错误提示

make_error_card() {
  local msg="${1:-抱歉,我暂时无法回答,请稍后再试。}" hint="${2:-}"
  local content="$msg"
  [[ -n "$hint" ]] && content+="\\n\\n💡 _${hint}_"
  jq -n --arg content "$content" '{
    schema:"2.0",
    header:{title:{tag:"plain_text",content:"❌ 出错了"},template:"red"},
    config:{streaming_mode:false},
    body:{elements:[{tag:"markdown",content:$content}]}
  }'
}

成功通知

make_success_card() {
  jq -n --arg title "$1" --arg content "$2" '{
    schema:"2.0",
    header:{title:{tag:"plain_text",content:$title},template:"green"},
    config:{streaming_mode:false},
    body:{elements:[{tag:"markdown",content:$content}]}
  }'
}

权限不足

make_noperm_card() {
  jq -n '{
    schema:"2.0",
    header:{title:{tag:"plain_text",content:"⛔ 权限不足"},template:"red"},
    config:{streaming_mode:false},
    body:{elements:[{tag:"markdown",content:"抱歉,你没有使用该 Bot 的权限。"}]}
  }'
}

发版通知(含 @人)

布局:顶部三列信息栏(版本号/环境/状态)+ 版本负责人 + 活动负责人。 图标一律用 Unicode emoji 写在 lark_md content 里,禁止用 icon.token(token 名称不可靠,会静默消失)。

send_deploy_notify() {
  local chat_id="$1" version="$2" env="$3"
  shift 3
  # 剩余参数格式:open_id1 name1 open_id2 name2 ...
  local at_owners=""
  while [[ $# -ge 2 ]]; do
    at_owners+="<at id=\"$1\">$2</at>  "
    shift 2
  done

  local now; now=$(date '+%Y-%m-%d %H:%M')

  python3 /tmp/_deploy_card.py "$version" "$env" "$now" "$at_owners" > /tmp/_deploy_card.json
  lark-cli im +messages-send \
    --chat-id "$chat_id" \
    --msg-type interactive \
    --content "$(cat /tmp/_deploy_card.json)" \
    --as bot
}

/tmp/_deploy_card.py(构建脚本,避免 heredoc 内变量展开问题):

import json, sys
version, env, now, at_owners = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]

card = {
    "schema": "2.0",
    "config": {"wide_screen_mode": True},
    "header": {
        "title": {"tag": "plain_text", "content": f"🚀 {version} · {env} 发版通知"},
        "template": "wathet",
        "subtitle": {"tag": "plain_text", "content": f"MLA · {env} · {now}"}
    },
    "body": {"elements": [
        {
            "tag": "column_set", "flex_mode": "trisect",
            "columns": [
                {"tag": "column", "elements": [{"tag": "div", "text": {"tag": "lark_md", "content": f"<font color=grey>版本号</font>\n**{version}**"}}]},
                {"tag": "column", "elements": [{"tag": "div", "text": {"tag": "lark_md", "content": f"<font color=grey>环境</font>\n<text_tag color='blue'>{env}</text_tag>"}}]},
                {"tag": "column", "elements": [{"tag": "div", "text": {"tag": "lark_md", "content": "<font color=grey>状态</font>\n<text_tag color='green'>:DONE: 已发好</text_tag>"}}]},
            ]
        },
        {"tag": "hr"},
        {"tag": "div", "text": {"tag": "lark_md",
            "content": '👥 **版本负责人知悉**\n<at id="ou_xxx">负责人甲</at>  <at id="ou_yyy">负责人乙</at>\n请知悉~'}},
        {"tag": "hr"},
        {"tag": "div", "text": {"tag": "lark_md",
            "content": f"📅 **活动配置请跟进**\n{at_owners}\n麻烦配置活动,辛苦了!:THUMBSUP:"}},
        {"tag": "hr"},
        {"tag": "div", "text": {"tag": "lark_md", "content": f"<font color=grey>:DONE: {version} · {env} · 由 后端AI 自动发送</font>"}}
    ]}
}
print(json.dumps(card, ensure_ascii=False))

按钮确认(JSON 2.0)

jq -n '{
  schema:"2.0",
  header:{title:{tag:"plain_text",content:"请确认"},template:"orange"},
  body:{elements:[
    {tag:"markdown",content:"是否执行此操作?"},
    {tag:"button",
     text:{tag:"plain_text",content:"确认"},
     type:"primary",
     behaviors:[{type:"callback",value:{action:"confirm"}}]},
    {tag:"button",
     text:{tag:"plain_text",content:"取消"},
     type:"default",
     behaviors:[{type:"callback",value:{action:"cancel"}}]}
  ]}
}'

错误处理

result=$(lark-cli api PATCH "/open-apis/im/v1/messages/${MSG_ID}" \
  --data "$PAYLOAD" --as bot 2>/dev/null)
code=$(echo "$result" | jq -r '.code // 0')
if [[ "$code" != "0" ]]; then
  # PATCH 失败(消息过期/权限问题),降级为发新消息
  lark-cli im +messages-send --chat-id "$CHAT_ID" --msg-type interactive \
    --content "$CARD" --as bot
fi

常见错误码:

code含义处理
230002没有操作权限检查 bot 是否在群内
10012消息不存在或已过期降级发新消息
99991663token 过期重新 lark-cli auth login --as bot

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.