Skip to content

Commit d353baa

Browse files
Abel Xiaoclaude
authored andcommitted
fix(feishu): preserve sender identity and resolve mentions in merge_forward messages
Prepend each sub-message with its sender open_id and resolve @_user_N mention placeholders back to real names via the mentions array in the Feishu API response. - Sort mentions by key length descending to prevent partial-match collisions (e.g. @_user_1 matching @_user_10) - Normalize mention IDs for both string and object shapes, with union_id fallback - Use callback replacement to avoid $ substitution in display names Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1b81ed6 commit d353baa

2 files changed

Lines changed: 29 additions & 2 deletions

File tree

extensions/feishu/src/bot-content.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ export function parseMergeForwardContent(params: { content: string; log?: Feishu
214214
msg_type?: string;
215215
body?: { content?: string };
216216
sender?: { id?: string };
217+
mentions?: Array<{
218+
key?: string;
219+
id?: string | { open_id?: string; user_id?: string; union_id?: string };
220+
name?: string;
221+
}>;
217222
upper_message_id?: string;
218223
create_time?: string;
219224
}>;
@@ -238,7 +243,27 @@ export function parseMergeForwardContent(params: { content: string; log?: Feishu
238243

239244
const lines = ["[Merged and Forwarded Messages]"];
240245
for (const item of subMessages.slice(0, maxMessages)) {
241-
lines.push(`- ${formatSubMessageContent(item.body?.content || "", item.msg_type || "text")}`);
246+
const formatted = formatSubMessageContent(item.body?.content || "", item.msg_type || "text");
247+
const senderId = item.sender?.id || "unknown";
248+
249+
// Resolve @_user_N placeholders using item.mentions from Feishu API
250+
let resolved = formatted;
251+
if (item.mentions && item.mentions.length > 0) {
252+
// Sort by key length descending to avoid @_user_1 matching inside @_user_10
253+
const sorted = [...item.mentions].sort((a, b) => (b.key?.length ?? 0) - (a.key?.length ?? 0));
254+
for (const m of sorted) {
255+
if (m.key) {
256+
const mId =
257+
typeof m.id === "string"
258+
? m.id
259+
: m.id?.open_id || m.id?.user_id || m.id?.union_id || "";
260+
const display = m.name && mId ? `${m.name}(${mId})` : m.name || mId || m.key;
261+
resolved = resolved.replaceAll(m.key, () => `@${display}`);
262+
}
263+
}
264+
}
265+
266+
lines.push(`- [${senderId}] ${resolved}`);
242267
}
243268
if (subMessages.length > maxMessages) {
244269
lines.push(`... and ${subMessages.length - maxMessages} more messages`);

extensions/feishu/src/bot.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1898,13 +1898,15 @@ describe("handleFeishuMessage command authorization", () => {
18981898
upper_message_id: "container",
18991899
msg_type: "file",
19001900
body: { content: JSON.stringify({ file_name: "report.pdf" }) },
1901+
sender: { id: "ou-bob" },
19011902
create_time: "2000",
19021903
},
19031904
{
19041905
message_id: "sub-1",
19051906
upper_message_id: "container",
19061907
msg_type: "text",
19071908
body: { content: JSON.stringify({ text: "alpha" }) },
1909+
sender: { id: "ou-alice" },
19081910
create_time: "1000",
19091911
},
19101912
],
@@ -1954,7 +1956,7 @@ describe("handleFeishuMessage command authorization", () => {
19541956
expect(mockFinalizeInboundContext).toHaveBeenCalledWith(
19551957
expect.objectContaining({
19561958
BodyForAgent: expect.stringContaining(
1957-
"[Merged and Forwarded Messages]\n- alpha\n- [File: report.pdf]",
1959+
"[Merged and Forwarded Messages]\n- [ou-alice] alpha\n- [ou-bob] [File: report.pdf]",
19581960
),
19591961
}),
19601962
);

0 commit comments

Comments
 (0)