Skip to content

Commit 4221b5f

Browse files
SidSid-QinTakhoffman
authored
fix: pass rootId to streaming card in Feishu topic groups (#28346) thanks @Sid-Qin
Verified: - pnpm check - pnpm test extensions/feishu/src/reply-dispatcher.test.ts Co-authored-by: Sid-Qin <[email protected]> Co-authored-by: Tak Hoffman <[email protected]>
1 parent da00ead commit 4221b5f

File tree

5 files changed

+36
-8
lines changed

5 files changed

+36
-8
lines changed

extensions/feishu/src/bot.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,7 @@ export async function handleFeishuMessage(params: {
10191019
chatId: ctx.chatId,
10201020
replyToMessageId: ctx.messageId,
10211021
replyInThread,
1022+
rootId: ctx.rootId,
10221023
mentionTargets: ctx.mentionTargets,
10231024
accountId: account.accountId,
10241025
});

extensions/feishu/src/reply-dispatcher.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,19 @@ describe("createFeishuReplyDispatcher streaming behavior", () => {
105105
agentId: "agent",
106106
runtime: { log: vi.fn(), error: vi.fn() } as never,
107107
chatId: "oc_chat",
108+
rootId: "om_root_topic",
108109
});
109110

110111
const options = createReplyDispatcherWithTypingMock.mock.calls[0]?.[0];
111112
await options.deliver({ text: "```ts\nconst x = 1\n```" }, { kind: "final" });
112113

113114
expect(streamingInstances).toHaveLength(1);
114115
expect(streamingInstances[0].start).toHaveBeenCalledTimes(1);
116+
expect(streamingInstances[0].start).toHaveBeenCalledWith("oc_chat", "chat_id", {
117+
replyToMessageId: undefined,
118+
replyInThread: undefined,
119+
rootId: "om_root_topic",
120+
});
115121
expect(streamingInstances[0].close).toHaveBeenCalledTimes(1);
116122
expect(sendMessageFeishuMock).not.toHaveBeenCalled();
117123
expect(sendMarkdownCardFeishuMock).not.toHaveBeenCalled();

extensions/feishu/src/reply-dispatcher.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,23 @@ export type CreateFeishuReplyDispatcherParams = {
2929
chatId: string;
3030
replyToMessageId?: string;
3131
replyInThread?: boolean;
32+
rootId?: string;
3233
mentionTargets?: MentionTarget[];
3334
accountId?: string;
3435
};
3536

3637
export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherParams) {
3738
const core = getFeishuRuntime();
38-
const { cfg, agentId, chatId, replyToMessageId, replyInThread, mentionTargets, accountId } =
39-
params;
39+
const {
40+
cfg,
41+
agentId,
42+
chatId,
43+
replyToMessageId,
44+
replyInThread,
45+
rootId,
46+
mentionTargets,
47+
accountId,
48+
} = params;
4049
const account = resolveFeishuAccount({ cfg, accountId });
4150
const prefixContext = createReplyPrefixContext({ cfg, agentId });
4251

@@ -105,6 +114,7 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
105114
await streaming.start(chatId, resolveReceiveIdType(chatId), {
106115
replyToMessageId,
107116
replyInThread,
117+
rootId,
108118
});
109119
} catch (error) {
110120
params.runtime.error?.(`feishu: streaming start failed: ${String(error)}`);

extensions/feishu/src/streaming-card.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export class FeishuStreamingSession {
9999
async start(
100100
receiveId: string,
101101
receiveIdType: "open_id" | "user_id" | "union_id" | "email" | "chat_id" = "chat_id",
102-
options?: { replyToMessageId?: string; replyInThread?: boolean },
102+
options?: { replyToMessageId?: string; replyInThread?: boolean; rootId?: string },
103103
): Promise<void> {
104104
if (this.state) {
105105
return;
@@ -144,9 +144,20 @@ export class FeishuStreamingSession {
144144
const cardId = createData.data.card_id;
145145
const cardContent = JSON.stringify({ type: "card", data: { card_id: cardId } });
146146

147-
// Send card message — reply into thread when configured
147+
// Topic-group replies require root_id routing. Prefer create+root_id when available.
148148
let sendRes;
149-
if (options?.replyToMessageId) {
149+
if (options?.rootId) {
150+
const createData = {
151+
receive_id: receiveId,
152+
msg_type: "interactive",
153+
content: cardContent,
154+
root_id: options.rootId,
155+
};
156+
sendRes = await this.client.im.message.create({
157+
params: { receive_id_type: receiveIdType },
158+
data: createData,
159+
});
160+
} else if (options?.replyToMessageId) {
150161
sendRes = await this.client.im.message.reply({
151162
path: { message_id: options.replyToMessageId },
152163
data: {

scripts/check-no-raw-channel-fetch.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ const sourceRoots = [
2424
const allowedRawFetchCallsites = new Set([
2525
"extensions/bluebubbles/src/types.ts:131",
2626
"extensions/feishu/src/streaming-card.ts:31",
27-
"extensions/feishu/src/streaming-card.ts:100",
28-
"extensions/feishu/src/streaming-card.ts:141",
29-
"extensions/feishu/src/streaming-card.ts:197",
27+
"extensions/feishu/src/streaming-card.ts:101",
28+
"extensions/feishu/src/streaming-card.ts:143",
29+
"extensions/feishu/src/streaming-card.ts:199",
3030
"extensions/google-gemini-cli-auth/oauth.ts:372",
3131
"extensions/google-gemini-cli-auth/oauth.ts:408",
3232
"extensions/google-gemini-cli-auth/oauth.ts:447",

0 commit comments

Comments
 (0)