mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-08 06:54:24 +00:00
feishu: forward outbound reply target context
This commit is contained in:
@@ -136,6 +136,25 @@ describe("feishuOutbound.sendText local-image auto-convert", () => {
|
||||
expect(sendMessageFeishuMock).not.toHaveBeenCalled();
|
||||
expect(result).toEqual(expect.objectContaining({ channel: "feishu", messageId: "card_msg" }));
|
||||
});
|
||||
|
||||
it("forwards replyToId as replyToMessageId on sendText", async () => {
|
||||
await sendText({
|
||||
cfg: {} as any,
|
||||
to: "chat_1",
|
||||
text: "hello",
|
||||
replyToId: "om_reply_1",
|
||||
accountId: "main",
|
||||
} as any);
|
||||
|
||||
expect(sendMessageFeishuMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
to: "chat_1",
|
||||
text: "hello",
|
||||
replyToMessageId: "om_reply_1",
|
||||
accountId: "main",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("feishuOutbound.sendMedia renderMode", () => {
|
||||
@@ -178,4 +197,24 @@ describe("feishuOutbound.sendMedia renderMode", () => {
|
||||
expect(sendMessageFeishuMock).not.toHaveBeenCalled();
|
||||
expect(result).toEqual(expect.objectContaining({ channel: "feishu", messageId: "media_msg" }));
|
||||
});
|
||||
|
||||
it("uses threadId fallback as replyToMessageId on sendMedia", async () => {
|
||||
await feishuOutbound.sendMedia?.({
|
||||
cfg: {} as any,
|
||||
to: "chat_1",
|
||||
text: "caption",
|
||||
mediaUrl: "https://example.com/image.png",
|
||||
threadId: "om_thread_1",
|
||||
accountId: "main",
|
||||
} as any);
|
||||
|
||||
expect(sendMediaFeishuMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
to: "chat_1",
|
||||
mediaUrl: "https://example.com/image.png",
|
||||
replyToMessageId: "om_thread_1",
|
||||
accountId: "main",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,21 +43,34 @@ function shouldUseCard(text: string): boolean {
|
||||
return /```[\s\S]*?```/.test(text) || /\|.+\|[\r\n]+\|[-:| ]+\|/.test(text);
|
||||
}
|
||||
|
||||
function resolveReplyToMessageId(params: {
|
||||
replyToId?: string | null;
|
||||
threadId?: string | number | null;
|
||||
}): string | undefined {
|
||||
const replyTarget = params.replyToId ?? params.threadId;
|
||||
if (replyTarget == null) {
|
||||
return undefined;
|
||||
}
|
||||
const trimmed = String(replyTarget).trim();
|
||||
return trimmed || undefined;
|
||||
}
|
||||
|
||||
async function sendOutboundText(params: {
|
||||
cfg: Parameters<typeof sendMessageFeishu>[0]["cfg"];
|
||||
to: string;
|
||||
text: string;
|
||||
accountId?: string;
|
||||
replyToMessageId?: string;
|
||||
}) {
|
||||
const { cfg, to, text, accountId } = params;
|
||||
const { cfg, to, text, accountId, replyToMessageId } = params;
|
||||
const account = resolveFeishuAccount({ cfg, accountId });
|
||||
const renderMode = account.config?.renderMode ?? "auto";
|
||||
|
||||
if (renderMode === "card" || (renderMode === "auto" && shouldUseCard(text))) {
|
||||
return sendMarkdownCardFeishu({ cfg, to, text, accountId });
|
||||
return sendMarkdownCardFeishu({ cfg, to, text, accountId, replyToMessageId });
|
||||
}
|
||||
|
||||
return sendMessageFeishu({ cfg, to, text, accountId });
|
||||
return sendMessageFeishu({ cfg, to, text, accountId, replyToMessageId });
|
||||
}
|
||||
|
||||
export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
@@ -65,7 +78,8 @@ export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
chunker: (text, limit) => getFeishuRuntime().channel.text.chunkMarkdownText(text, limit),
|
||||
chunkerMode: "markdown",
|
||||
textChunkLimit: 4000,
|
||||
sendText: async ({ cfg, to, text, accountId }) => {
|
||||
sendText: async ({ cfg, to, text, accountId, replyToId, threadId }) => {
|
||||
const replyToMessageId = resolveReplyToMessageId({ replyToId, threadId });
|
||||
// Scheme A compatibility shim:
|
||||
// when upstream accidentally returns a local image path as plain text,
|
||||
// auto-upload and send as Feishu image message instead of leaking path text.
|
||||
@@ -77,6 +91,7 @@ export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
to,
|
||||
mediaUrl: localImagePath,
|
||||
accountId: accountId ?? undefined,
|
||||
replyToMessageId,
|
||||
});
|
||||
return { channel: "feishu", ...result };
|
||||
} catch (err) {
|
||||
@@ -90,10 +105,21 @@ export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
to,
|
||||
text,
|
||||
accountId: accountId ?? undefined,
|
||||
replyToMessageId,
|
||||
});
|
||||
return { channel: "feishu", ...result };
|
||||
},
|
||||
sendMedia: async ({ cfg, to, text, mediaUrl, accountId, mediaLocalRoots }) => {
|
||||
sendMedia: async ({
|
||||
cfg,
|
||||
to,
|
||||
text,
|
||||
mediaUrl,
|
||||
accountId,
|
||||
mediaLocalRoots,
|
||||
replyToId,
|
||||
threadId,
|
||||
}) => {
|
||||
const replyToMessageId = resolveReplyToMessageId({ replyToId, threadId });
|
||||
// Send text first if provided
|
||||
if (text?.trim()) {
|
||||
await sendOutboundText({
|
||||
@@ -101,6 +127,7 @@ export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
to,
|
||||
text,
|
||||
accountId: accountId ?? undefined,
|
||||
replyToMessageId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -113,6 +140,7 @@ export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
mediaUrl,
|
||||
accountId: accountId ?? undefined,
|
||||
mediaLocalRoots,
|
||||
replyToMessageId,
|
||||
});
|
||||
return { channel: "feishu", ...result };
|
||||
} catch (err) {
|
||||
@@ -125,6 +153,7 @@ export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
to,
|
||||
text: fallbackText,
|
||||
accountId: accountId ?? undefined,
|
||||
replyToMessageId,
|
||||
});
|
||||
return { channel: "feishu", ...result };
|
||||
}
|
||||
@@ -136,6 +165,7 @@ export const feishuOutbound: ChannelOutboundAdapter = {
|
||||
to,
|
||||
text: text ?? "",
|
||||
accountId: accountId ?? undefined,
|
||||
replyToMessageId,
|
||||
});
|
||||
return { channel: "feishu", ...result };
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user