Skip to content

feat(im): 消息读取带上服务端发送者名称(with_sender_name=true) - #480

Merged
deepcoldy merged 1 commit into
masterfrom
feat/with-sender-name
Jul 16, 2026
Merged

feat(im): 消息读取带上服务端发送者名称(with_sender_name=true)#480
deepcoldy merged 1 commit into
masterfrom
feat/with-sender-name

Conversation

@deepcoldy

Copy link
Copy Markdown
Owner

改了什么

lark-cli v1.0.69 的 #1829 揭示了一个服务端 API 能力:消息 list/get 带 with_sender_name=true 时,服务端直接返回发送者显示名(user 和 bot 都给)。本 PR 让 botmux 的 API 读消息路径全部用上:

  • src/im/lark/client.ts:5 个消息读取调用点(listByThread / listChatMessages / listChatMessagesUntil / listByChatFilter / getMessageDetail)统一带上 with_sender_name=true
  • src/im/lark/message-parser.tsparseApiMessage 读取 sender.sender_name,透出为可选字段 senderName(空白视为无)
  • src/types.tsLarkMessage 增加可选 senderName
  • src/im/lark/merge-forward.ts:合并转发展开时把 sender_name 填进 ForwardedNode.senderName —— 渲染器的 <p name=…> 支持早就存在,之前一直没有数据源
  • dashboard 会话历史(dashboard-ipc-server.ts + history-senders.ts):服务端名字随消息透传,本地花名册 / contact 解析不到时兜底用它;本地解析到的名字优先级不变...message 展开在前,本地名仅在存在时覆盖)

为什么

之前 botmux history / quoted / 合并转发展开的输出里发送者只有 open_id + sender_type:

  • 人类名字要靠额外调 contact API(依赖 contact scope,不在可见范围还查不到)
  • bot 名字只有 bots.json / 观察名册里认识的自家 bot 有,第三方 bot 是一串裸 id——多 bot 群里 agent 读上下文分不清谁说的

现在服务端一把给全,第三方 bot 也有名字,还省 contact 往返。

影响面

API 读消息路径(history / quoted / 合并转发展开 / dashboard 会话历史)。实时事件路径(receive_v1)不带 sender_name,不受影响;daemon 现有 contact / 花名册解析逻辑原样保留,服务端名字只作补充。senderName 为可选字段,纯增量,旧消费方无感。

跨 CLI / 跨后端无涉——改动都在 Lark API 读链路和纯函数渲染层。

测试验证

  • pnpm build
  • 新增单测 6 个,全绿:
    • parseApiMessage 透出 sender_name / 空白时省略(message-parser.test.ts)
    • 合并转发 participants 带 name 属性(merge-forward.test.ts)
    • dashboard enrich:本地解析不到时保留服务端名、本地名优先(dashboard-history-senders.test.ts)
  • npx vitest run 定向 4 个受影响文件:94/94 通过
  • 全量 pnpm test:8261 passed / 5 failed——失败的 5 个(scheduler 时区 ×3、dashboard popover、restore limit session)在干净 master 上同样失败,与本 PR 无关(已用 git stash 对照验证)
  • live 实测:用本分支构建直接跑 node dist/cli.js history --limit 4 打真实 Lark API——bot 消息(sender id 为 cli_a9f66d03be385ceb,本地花名册无法匹配的形态)返回 "senderName": "Premium(Claude)",用户消息返回 "senderName": "申晗",未调用 contact API

消息 list/get 全部读路径带上 with_sender_name=true,服务端直接返回
sender_name(user 和 bot 都给),parseApiMessage 透出为 senderName:

- botmux history / quoted 输出发送者名字,多 bot 群里 agent 不用再猜
  哪条消息是谁发的;第三方 bot(不在 bots.json / 观察名册里)也有名字
- 合并转发展开的 <participants> 带 name 属性(渲染器本就支持,之前
  没有数据源)
- dashboard 会话历史:本地花名册/contact 解析不到时兜底用服务端名字;
  本地解析到的名字优先级不变

影响面:仅 API 读消息路径(history/quoted/合并转发/dashboard 历史)。
实时事件路径(receive_v1)不带 sender_name,不受影响;daemon 现有的
contact/花名册解析逻辑保持原样,服务端名字只作补充兜底。
@deepcoldy
deepcoldy merged commit 098ba1a into master Jul 16, 2026
1 check passed
deepcoldy added a commit that referenced this pull request Jul 26, 2026
PR #480 只给「API 读消息路径」(history/quoted/合并转发/dashboard 历史)
加了 with_sender_name=true,但实时事件路径 receive_v1 注入进 CLI prompt
的 <sender type=... open_id=... /> 完全没受影响——那条路径的名字只靠
identity-cache 的 contact.v3.user.get 现补,contact 缺权限/可见范围/超时
或发送方是 bot 时就降级成没有 name 属性(用户实测反馈的正是这个)。

本 PR 在 resolveSender 增加最后一层兜底:cache/hint、contact API 都没拿到
名字、且调用方带了 messageId 时,用 getMessageDetail(with_sender_name=true)
反查一次 sender_name。它同时覆盖 user 和 bot,且不依赖 contact scope
(服务端直接返回这条消息发送方的显示名),解析到的名字写回 identity 缓存,
后续同发送方的消息命中缓存不再重复请求。

- src/im/lark/identity-cache.ts:新增 resolveNameViaMessage(复用 #480 已
  带 with_sender_name 的 getMessageDetail,套同一 RESOLVE_BUDGET_MS 预算,
  失败静默降级 undefined);resolveSender 的 hint 增加可选 messageId,仅在
  前两步无名时触发;IdentityRecord.source 增加 'message_api'
- src/daemon.ts:4 个 <sender> 注入点(p2p 冷启动/命令、新话题、话题回复)
  把入站 messageId 透给 resolveSender;订阅/定时等无入站消息的调用点不传,
  行为不变

影响面:仅 Lark 实时事件的 <sender> 名字解析链路。名字优先级不变(hint >
cache > contact > message.get 兜底),纯增量:不带 messageId 时行为与之前
完全一致。跨 CLI/跨后端无涉。

测试:
- 新增 test/identity-cache-message-fallback.test.ts,7 例全绿(bot 兜底、
  user contact miss 后兜底、缓存命中不再请求、无 messageId 不请求、hint 已
  有名字不请求、无 sender_name/抛错静默降级)
- 回归 message-parser/merge-forward/dashboard-history-senders 共 86 例全绿
- pnpm build:改动文件 tsc 无报错(src/desktop 的 electron 报错在干净
  master 上同样存在,与本 PR 无关)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant