fix: address 4 PR review issues in feishu cross-bot relay
Issue 2: card path skipped relay in outbound.ts - Extract triggerRelay() helper to avoid duplication - Card branch now calls triggerRelay() before returning instead of early-returning without relay - Also passes threadId to relay so topic threads are preserved Issue 4: streaming messageId any cast + closeStreaming timing - Add getMessageId() typed accessor to FeishuStreamingSession - Save messageId via streaming.getMessageId() BEFORE calling closeStreaming() (closeStreaming sets streaming=null, making post-close access always undefined) - Replace (streaming as any)?.state?.messageId with the typed accessor Issue 5: sendChunk callbacks missing return value - sendChunkedTextReply callbacks for both card and text paths now use 'return sendStructuredCardFeishu(...)' / 'return sendMessageFeishu(...)' so result?.messageId is correctly captured in lastMessageId Issue 8: relay missing topic/thread metadata - Add threadId field to RelayOutboundParams - Populate root_id/thread_id in synthetic event from threadId - Pass rootId from reply-dispatcher relay calls - Pass threadId from outbound.ts sendText relay calls
This commit is contained in:
parent
9a8a666880
commit
c0cead3063
@ -48,6 +48,8 @@ export type RelayOutboundParams = {
|
||||
text: string;
|
||||
/** Message ID returned by Feishu API after sending */
|
||||
messageId?: string;
|
||||
/** Thread/topic ID for topic group messages (root_id in Feishu events) */
|
||||
threadId?: string;
|
||||
/** Bot's open_id (sender identity) */
|
||||
senderBotOpenId?: string;
|
||||
/** Bot's display name */
|
||||
@ -55,7 +57,8 @@ export type RelayOutboundParams = {
|
||||
};
|
||||
|
||||
export async function relayOutboundToOtherBots(params: RelayOutboundParams): Promise<void> {
|
||||
const { senderAccountId, chatId, text, messageId, senderBotOpenId, senderBotName } = params;
|
||||
const { senderAccountId, chatId, text, messageId, threadId, senderBotOpenId, senderBotName } =
|
||||
params;
|
||||
|
||||
// Only relay to group chats
|
||||
if (!chatId) return;
|
||||
@ -123,6 +126,8 @@ export async function relayOutboundToOtherBots(params: RelayOutboundParams): Pro
|
||||
content: JSON.stringify({ text }),
|
||||
create_time: String(Date.now()),
|
||||
mentions,
|
||||
// Preserve thread/topic metadata so relay messages stay in the correct topic
|
||||
...(threadId ? { root_id: threadId, thread_id: threadId } : {}),
|
||||
},
|
||||
};
|
||||
const log = target.runtime?.log ?? console.log;
|
||||
|
||||
@ -120,6 +120,25 @@ export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
const account = resolveFeishuAccount({ cfg, accountId: accountId ?? undefined });
|
||||
const renderMode = account.config?.renderMode ?? "auto";
|
||||
const useCard = renderMode === "card" || (renderMode === "auto" && shouldUseCard(text));
|
||||
// Cross-bot relay helper — extracted to avoid duplication between card and text paths
|
||||
const triggerRelay = (result: { messageId?: string }) => {
|
||||
const feishuCfg = resolveFeishuAccount({ cfg, accountId: accountId ?? undefined }).config;
|
||||
const effectiveAccountId = accountId ?? undefined;
|
||||
if (feishuCfg?.crossBotRelay && to.startsWith("oc_") && text?.trim()) {
|
||||
void relayOutboundToOtherBots({
|
||||
senderAccountId: effectiveAccountId ?? "default",
|
||||
chatId: to,
|
||||
text,
|
||||
messageId: result.messageId,
|
||||
threadId: threadId != null ? String(threadId) : undefined,
|
||||
senderBotOpenId: botOpenIds.get(effectiveAccountId ?? "default"),
|
||||
senderBotName: botNames.get(effectiveAccountId ?? "default"),
|
||||
}).catch((err) => {
|
||||
console.error(`[feishu] cross-bot relay failed:`, err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (useCard) {
|
||||
const header = identity
|
||||
? {
|
||||
@ -129,7 +148,7 @@ export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
template: "blue" as const,
|
||||
}
|
||||
: undefined;
|
||||
return await sendStructuredCardFeishu({
|
||||
const cardResult = await sendStructuredCardFeishu({
|
||||
cfg,
|
||||
to,
|
||||
text,
|
||||
@ -138,6 +157,9 @@ export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
accountId: accountId ?? undefined,
|
||||
header: header?.title ? header : undefined,
|
||||
});
|
||||
// Relay card messages too — previously skipped by early return
|
||||
triggerRelay(cardResult);
|
||||
return cardResult;
|
||||
}
|
||||
const result = await sendOutboundText({
|
||||
cfg,
|
||||
@ -147,21 +169,7 @@ export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
replyToMessageId,
|
||||
});
|
||||
|
||||
// Cross-bot relay: notify other bots in the same group
|
||||
const feishuCfg = resolveFeishuAccount({ cfg, accountId: accountId ?? undefined }).config;
|
||||
if (feishuCfg?.crossBotRelay && to.startsWith("oc_") && text?.trim()) {
|
||||
const effectiveAccountId = accountId ?? undefined;
|
||||
void relayOutboundToOtherBots({
|
||||
senderAccountId: effectiveAccountId ?? "default",
|
||||
chatId: to,
|
||||
text,
|
||||
messageId: result.messageId,
|
||||
senderBotOpenId: botOpenIds.get(effectiveAccountId ?? "default"),
|
||||
senderBotName: botNames.get(effectiveAccountId ?? "default"),
|
||||
}).catch((err) => {
|
||||
console.error(`[feishu] cross-bot relay failed:`, err);
|
||||
});
|
||||
}
|
||||
triggerRelay(result);
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
@ -344,6 +344,7 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
|
||||
chatId,
|
||||
text: params.text,
|
||||
messageId: lastMessageId,
|
||||
threadId: rootId,
|
||||
senderBotOpenId: botOpenIds.get(accountId ?? "default"),
|
||||
senderBotName: botNames.get(accountId ?? "default"),
|
||||
});
|
||||
@ -426,18 +427,20 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
|
||||
}
|
||||
if (info?.kind === "final") {
|
||||
streamText = mergeStreamingText(streamText, text);
|
||||
// Save messageId before closeStreaming() clears the streaming object
|
||||
const streamingMsgId = streaming?.getMessageId();
|
||||
await closeStreaming();
|
||||
deliveredFinalTexts.add(text);
|
||||
// Cross-bot relay: trigger after streaming final
|
||||
const feishuCfg = resolveFeishuAccount({ cfg, accountId }).config;
|
||||
if (feishuCfg?.crossBotRelay && chatId?.startsWith("oc_") && text?.trim()) {
|
||||
try {
|
||||
const streamingMsgId = (streaming as any)?.state?.messageId;
|
||||
await relayOutboundToOtherBots({
|
||||
senderAccountId: accountId ?? "default",
|
||||
chatId,
|
||||
text,
|
||||
messageId: streamingMsgId,
|
||||
threadId: rootId,
|
||||
senderBotOpenId: botOpenIds.get(accountId ?? "default"),
|
||||
senderBotName: botNames.get(accountId ?? "default"),
|
||||
});
|
||||
@ -461,7 +464,7 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
|
||||
useCard: true,
|
||||
infoKind: info?.kind,
|
||||
sendChunk: async ({ chunk, isFirst }) => {
|
||||
await sendStructuredCardFeishu({
|
||||
return sendStructuredCardFeishu({
|
||||
cfg,
|
||||
to: chatId,
|
||||
text: chunk,
|
||||
@ -480,7 +483,7 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
|
||||
useCard: false,
|
||||
infoKind: info?.kind,
|
||||
sendChunk: async ({ chunk, isFirst }) => {
|
||||
await sendMessageFeishu({
|
||||
return sendMessageFeishu({
|
||||
cfg,
|
||||
to: chatId,
|
||||
text: chunk,
|
||||
|
||||
@ -447,4 +447,9 @@ export class FeishuStreamingSession {
|
||||
isActive(): boolean {
|
||||
return this.state !== null && !this.closed;
|
||||
}
|
||||
|
||||
/** Returns the Feishu message_id of the streaming card, or undefined if not started. */
|
||||
getMessageId(): string | undefined {
|
||||
return this.state?.messageId;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user