Files

5.6 KiB

飞书群发消息 + @人(已验证配方 2026-06-16)

目标:把消息发到正确的飞书群@ 正确的人,一次做对。依赖 lark-cli(lark-im 技能)。

步骤

1. 找出 bot 实际所在的群(不要猜 chat_id)

cd ~ && lark-cli im +chat-list --as bot

返回每个群的 chat_idnamedescriptionowner_id。挑出你和目标人共处的那个群。

  • 本环境已知群:"Doro, Maggie, 魏玮"(描述"小Maggie工作群")= oc_a927f86118216c36cb9394b0e95f2a11
  • 若有多个候选群,按成员和描述确认,仍不确定就问用户,别赌。

2. @人的格式(两种已验证写法,均触发真实可点击 @ + 通知)

  • text 模式--msg-type text,content 形如 {"text":"<at>…</at> 正文"},@ 标签 <at user_id="ou_xxx">显示名</at>(用 open_id;嵌进 JSON 字符串时内层引号按 JSON 规则转义)。@所有人 <at user_id="all"></at>
  • post 模式(多行/结构化汇报推荐,2026-06-16 已验证):post JSON 里 @ 用 at 元素 {"tag":"at","user_id":"ou_xxx"},与文本元素 {"tag":"text","text":"…"} 同行拼接,配 --msg-type post。结构: {"zh_cn":{"content":[[{"tag":"at","user_id":"ou_757f053c9d7aff6c73b18aa60c337756"},{"tag":"text","text":" 进展汇报…"}],[{"tag":"text","text":"第二行"}]]}}
  • 不要用 --markdown 发 @:会强制转 post 且 @ 处理不可靠,用显式 post JSON 自己控制。

3. 先 dry-run 验证 payload

lark-cli im +messages-send --chat-id oc_xxx --as bot \
  --content '{"text":"<at user_id=\"ou_757f053c9d7aff6c73b18aa60c337756\">Doro</at> 正文…"}' \
  --msg-type text --dry-run

检查 body 里 chat_id、msg_type、@标签是否正确。

4. 去掉 --dry-run 正式发送

成功返回 message_id + create_time。把 message_id 和北京时间报给用户便于核对。

发送前如何核实「这个 open_id 确实是目标本人」

@错人是红线。理想是查通讯录,但本 bot 常缺 contact / chat 读权限,按可靠性从高到低:

  1. gateway.log 取地面真值(最可靠,无需任何 scope)——平台事件原始数据,比 API、比记忆都硬:
    grep "oc_<群id>" ~/.hermes/logs/gateway.log | grep -oE "ou_[a-z0-9]{20,}" | sort | uniq -c | sort -rn
    
    再看「当前这条消息」的入站行确认发送者:
    grep "inbound message: platform=feishu" ~/.hermes/logs/gateway.log | tail
    # → user=ou_xxx chat=oc_xxx msg='…' 即是谁在这个群说了这句话
    
    把刚收到那句话的 user=ou_xxx 与已知映射比对,一致才发。
  2. 已知 open_id 映射(下方表 + memory),ID 没映射到人 → 先问不猜。
  3. 通讯录 API(常被 scope 挡)lark-cli contact +get-user --user-id ou_xxx --user-id-type open_id --as user。本环境 2026-06-16 报缺 contact:user.basic_profile:readonlyim chat.members get 也缺 im:chat.members:read 等。缺权限是正常状态,别卡在这里——退回方法 1。

已知 open_id(核对身份用,新增/纠正后同步到 memory)

  • Doro = ou_757f053c9d7aff6c73b18aa60c337756(与 bot 私聊 chat=oc_9292e11bd98ddb5a69ea2c2da10d4f12
  • Scott(魏玮) = ou_04fade9a9335c09ad09846da2051b3c0

注意

  • --as bot:消息以应用 bot 名义发出,bot 必须已在目标群里。
  • lark-cli 是 API 工具,不是消息网关;bot 身份不代理用户(Scott 已纠正)。
  • 终端里 lark-cli 输出常带 HTTPS_PROXY 的 WARN 和版本更新 notice,是噪音,不影响结果;可 grep -v "WARN\|proxy" 过滤。

发文件附件到群里(md/pdf/docx 报告,已验证)

要把一份文件(错误分析 md、合同 docx、报告 pdf)发到群里,用 --file。常见组合:先发文件附件,再发一条 @某人 的 post 说明(文件本身不能 @ 人,说明消息负责 @)。

# 先发文件(--file 接 cwd 相对路径),成功返回 message_id 并打印 "uploading file: xxx"
cd ~/lark_send_tmp && lark-cli im +messages-send \
  --chat-id oc_a927f86118216c36cb9394b0e95f2a11 \
  --as bot --file "./报告.md" \
  --dry-run 2>&1 | grep -v "WARN\|proxy"   # 先 dry-run,确认后去掉 --dry-run
# 再发 @某人 的 post 说明(见上「@人的格式」post 模式),把文件背景+要点写清楚

⚠️ 关键坑:--file 只接 cwd 相对路径,绝对路径被拒

lark-cli 安全限制:--file(及 --image/--video/--audio拒绝绝对路径(如 /tmp/x.md),路径解析 ../symlink 后必须仍在 cwd 内。 解法:把文件复制到干净工作目录,cd 进去用 ./文件名 发:

mkdir -p ~/lark_send_tmp && cp "/tmp/报告.md" ~/lark_send_tmp/
cd ~/lark_send_tmp && lark-cli im +messages-send --chat-id oc_xxx --as bot --file "./报告.md"
rm -rf ~/lark_send_tmp   # 发完清理
  • 本地文件 lark-cli 会先自动上传再发 file 消息,无需手动 images.create/拿 file_key。
  • dry-run 时 file_key 显示占位符 file_dryrun_upload 是正常的,正式发送才真上传。
  • 同理发图片用 --image ./x.png,视频 --video ./x.mp4 --video-cover ./cover.png

已知群与人 ID(核对身份用)

对象 ID
飞书工作群"Doro, Maggie, 魏玮"(小Maggie工作群) oc_a927f86118216c36cb9394b0e95f2a11
Doro ou_757f053c9d7aff6c73b18aa60c337756(私聊 chat oc_9292e11bd98ddb5a69ea2c2da10d4f12,别和群混)
Scott / 魏玮 ou_04fade9a9335c09ad09846da2051b3c0
@所有人 all