Skip to content

feat(feishu): split DM/group display names with members-only DM lookup#38378

Closed
futuremind2026 wants to merge 6 commits intoopenclaw:mainfrom
MaGonglei:ops/stable-v2026.3.2-feishu-resolvegroupnames
Closed

feat(feishu): split DM/group display names with members-only DM lookup#38378
futuremind2026 wants to merge 6 commits intoopenclaw:mainfrom
MaGonglei:ops/stable-v2026.3.2-feishu-resolvegroupnames

Conversation

@futuremind2026
Copy link
Copy Markdown
Contributor

Summary

  • What changed:
    • Split Feishu display-name resolution into independent DM vs Group paths.
    • Added explicit config gates for DM/group display-name behavior (resolveDmDisplayNames, resolveGroupNames) while preserving resolveSenderNames semantics for sender-name resolution.
    • Switched DM name lookup to im.chat.members.get members-only path and removed DM dependence on contact.user.get.
    • Improved DM lookup fallback behavior and response-code handling.
  • Why:
    • Avoid cross-coupling between DM and group naming behavior.
    • Reduce permission/surface mismatch for DM lookups by using chat-members API consistently.
    • Keep route/session behavior unchanged while improving display metadata quality.
  • Scope:
    • Feishu extension only (extensions/feishu/src/{bot.ts,bot.test.ts,channel.ts,config-schema.ts,config-schema.test.ts,types.ts}).

Behavior

  • Before:
    • DM/group display-name behavior was coupled; DM resolution could still depend on broader sender-name switches and mixed lookup paths.
    • DM lookup fallback behavior had weaker error-code handling.
  • After:
    • DM and group display-name resolution are explicitly split.
    • DM lookup uses members-only path (im.chat.members.get) with improved fallback/response handling.
    • Routing inputs (peer/parentPeer) are unchanged.

Risk

  • Runtime impact:
    • Low-to-medium; Feishu display labels/metadata path changed for DM/group name resolution.
  • Backward compatibility:
    • Config semantics changed: DM display names are now controlled by resolveDmDisplayNames rather than implicitly by resolveSenderNames.
    • In DM events with only user_id (no open_id), display name may fall back to ID when member matching is not possible.
  • Rollback plan:
    • Fast config rollback:
      • resolveDmDisplayNames: false
      • resolveGroupNames: false
    • Commit-level rollback (if needed): revert
      • 43a01f49cd, ea3a8163f7, edac6cd35a, 1351950d81, b87d0b37fc.

Validation

  • Tests run:
    • pnpm exec vitest run extensions/feishu/src/bot.test.ts extensions/feishu/src/config-schema.test.ts ✅ (73 passed)
    • pnpm exec vitest run extensions/feishu/src/*.test.ts ⚠️ (287 passed, 1 failed)
      • Known pre-existing unrelated failure: extensions/feishu/src/targets.test.ts > treats explicit channel targets as chat_id (expected chat_id, got user_id).
  • Lint/build checks:
    • pnpm build
  • Manual verification:
    • N/A in this PR (no live environment verification attached).

Notes for Reviewers

  • Key files:
    • extensions/feishu/src/bot.ts
    • extensions/feishu/src/bot.test.ts
    • extensions/feishu/src/config-schema.ts
    • extensions/feishu/src/config-schema.test.ts
    • extensions/feishu/src/channel.ts
    • extensions/feishu/src/types.ts
  • Non-goals:
    • No route/session-key algorithm changes.
    • No fix included for the unrelated targets.test.ts baseline failure.

@openclaw-barnacle openclaw-barnacle Bot added channel: feishu Channel integration: feishu size: L labels Mar 6, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 6, 2026

Greptile Summary

This PR refactors Feishu display-name resolution by splitting DM and group paths into independent code flows, adding resolveDmDisplayNames and resolveGroupNames config gates (both defaulting to true), and switching DM lookup to the im.chat.members.get API.

Key finding:

  • Logic issue in DM lookup guard: When a Feishu event contains only user_id (no open_id—common in some mobile deliveries), ctx.senderOpenId becomes a user_id string. The call to resolveFeishuDirectNameFromChatMember at line 1078 should be guarded to only invoke when a real open_id is confirmed. Currently, the API is called with a user_id while querying with member_id_type: "open_id", causing the member match at line 301 to silently fail and waste an API call. The suggested guard ensures this scenario is handled cleanly without spurious requests.

The caching logic, display-name builders, config schema additions, and test coverage are otherwise well-structured.

Confidence Score: 3/5

  • Generally safe, but has a logic bug in DM name lookup that causes wasted API calls in edge cases.
  • The PR introduces a correctness issue where DM name resolution attempts API calls with mismatched ID types when open_id is unavailable, resulting in silent failures and unnecessary quota consumption. This is a low-severity runtime issue but represents a code quality concern that should be addressed before merging. The rest of the refactoring (config gates, caching, test coverage) is sound.
  • extensions/feishu/src/bot.ts — lines 1078–1088 (DM lookup guard needed)

Last reviewed commit: 43a01f4

Comment on lines +1078 to +1088
if (isDirect && (feishuCfg?.resolveDmDisplayNames ?? true)) {
const directName = await resolveFeishuDirectNameFromChatMember({
account,
chatId: ctx.chatId,
senderOpenId: ctx.senderOpenId,
log,
});
if (directName) {
ctx = { ...ctx, senderName: directName };
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ctx.senderOpenId may contain a user_id, causing silent no-match and wasted API call

ctx.senderOpenId is set to senderFallbackId (from parseFeishuMessageEvent line 938), which falls back to user_id when open_id is unavailable:

const senderFallbackId = senderOpenId || senderUserId || "";

However, resolveFeishuDirectNameFromChatMember always calls the Feishu API with member_id_type: "open_id" (line 284), so all returned member_id values are in open_id format. The find() comparison at line 301 then tries to match those open_id values against a possibly-user_id-valued normalizedSenderOpenId — the match will always fail silently, making a wasted API call before returning undefined.

Guard the call with a confirmed open_id to avoid the needless API request:

const senderOpenId = event.sender.sender_id.open_id?.trim();

if (isDirect && senderOpenId && (feishuCfg?.resolveDmDisplayNames ?? true)) {
  const directName = await resolveFeishuDirectNameFromChatMember({
    account,
    chatId: ctx.chatId,
    senderOpenId,
    log,
  });
  if (directName) {
    ctx = { ...ctx, senderName: directName };
  }
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/bot.ts
Line: 1078-1088

Comment:
`ctx.senderOpenId` may contain a `user_id`, causing silent no-match and wasted API call

`ctx.senderOpenId` is set to `senderFallbackId` (from `parseFeishuMessageEvent` line 938), which falls back to `user_id` when `open_id` is unavailable:
```ts
const senderFallbackId = senderOpenId || senderUserId || "";
```

However, `resolveFeishuDirectNameFromChatMember` always calls the Feishu API with `member_id_type: "open_id"` (line 284), so all returned `member_id` values are in `open_id` format. The `find()` comparison at line 301 then tries to match those `open_id` values against a possibly-`user_id`-valued `normalizedSenderOpenId` — the match will always fail silently, making a wasted API call before returning `undefined`.

Guard the call with a confirmed `open_id` to avoid the needless API request:

```ts
const senderOpenId = event.sender.sender_id.open_id?.trim();

if (isDirect && senderOpenId && (feishuCfg?.resolveDmDisplayNames ?? true)) {
  const directName = await resolveFeishuDirectNameFromChatMember({
    account,
    chatId: ctx.chatId,
    senderOpenId,
    log,
  });
  if (directName) {
    ctx = { ...ctx, senderName: directName };
  }
}
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 43a01f49cd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread extensions/feishu/src/bot.ts Outdated
Comment on lines +276 to +278
const getChatMembers = client?.im?.chat?.members?.get;
if (typeof getChatMembers !== "function") {
return undefined;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Call the chat members endpoint exposed by the SDK

The new DM display-name path reads client?.im?.chat?.members?.get and returns early when that function is missing, but elsewhere in this Feishu extension the same SDK client is accessed as client.im.chatMembers.get (extensions/feishu/src/chat.ts). With the current SDK accessor shape, this makes resolveDmDisplayNames effectively a no-op in production and DM sender labels fall back to raw IDs.

Useful? React with 👍 / 👎.

Comment on lines +283 to +285
params: {
member_id_type: "open_id",
page_size: 50,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Derive member_id_type from the sender identifier

This lookup hardcodes member_id_type: "open_id" even when the event only provides user_id (the parser explicitly falls back to user_id when open_id is absent). In those DM events, member entries are returned in a different ID namespace, so entry.member_id === normalizedSenderOpenId never matches and display names always degrade to the raw ID.

Useful? React with 👍 / 👎.

@futuremind2026
Copy link
Copy Markdown
Contributor Author

Superseded by #38958, which carries the same Feishu DM/group display-name line with updated SDK-compatible DM member lookup ( with compatibility fallback), refreshed validation, and current branch/process conventions. Closing this to keep review focus on the latest patch set.

@futuremind2026
Copy link
Copy Markdown
Contributor Author

Closing as superseded by #38958. The new PR keeps the same Feishu DM/group display-name direction, updates DM member lookup to SDK-compatible chatMembers path with compatibility fallback, and carries refreshed validation under the new branch/process conventions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: feishu Channel integration: feishu size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant