Outbound: avoid empty multi-media fallback sends
This commit is contained in:
@@ -918,9 +918,59 @@ describe("deliverOutboundPayloads", () => {
|
||||
text: "caption",
|
||||
}),
|
||||
);
|
||||
expect(logMocks.warn).toHaveBeenCalledWith(
|
||||
"Plugin outbound adapter does not implement sendMedia; media URLs will be dropped and text fallback will be used",
|
||||
expect.objectContaining({
|
||||
channel: "matrix",
|
||||
mediaCount: 1,
|
||||
}),
|
||||
);
|
||||
expect(results).toEqual([{ channel: "matrix", messageId: "mx-1" }]);
|
||||
});
|
||||
|
||||
it("falls back to one sendText call for multi-media payloads when sendMedia is omitted", async () => {
|
||||
const sendText = vi.fn().mockResolvedValue({ channel: "matrix", messageId: "mx-2" });
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{
|
||||
pluginId: "matrix",
|
||||
source: "test",
|
||||
plugin: createOutboundTestPlugin({
|
||||
id: "matrix",
|
||||
outbound: { deliveryMode: "direct", sendText },
|
||||
}),
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
const results = await deliverOutboundPayloads({
|
||||
cfg: {},
|
||||
channel: "matrix",
|
||||
to: "!room:1",
|
||||
payloads: [
|
||||
{
|
||||
text: "caption",
|
||||
mediaUrls: ["https://example.com/a.png", "https://example.com/b.png"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(sendText).toHaveBeenCalledTimes(1);
|
||||
expect(sendText).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
text: "caption",
|
||||
}),
|
||||
);
|
||||
expect(logMocks.warn).toHaveBeenCalledWith(
|
||||
"Plugin outbound adapter does not implement sendMedia; media URLs will be dropped and text fallback will be used",
|
||||
expect.objectContaining({
|
||||
channel: "matrix",
|
||||
mediaCount: 2,
|
||||
}),
|
||||
);
|
||||
expect(results).toEqual([{ channel: "matrix", messageId: "mx-2" }]);
|
||||
});
|
||||
|
||||
it("emits message_sent failure when delivery errors", async () => {
|
||||
hookMocks.runner.hasHooks.mockReturnValue(true);
|
||||
const sendWhatsApp = vi.fn().mockRejectedValue(new Error("downstream failed"));
|
||||
|
||||
@@ -97,6 +97,7 @@ type ChannelHandler = {
|
||||
chunker: Chunker | null;
|
||||
chunkerMode?: "text" | "markdown";
|
||||
textChunkLimit?: number;
|
||||
supportsMedia: boolean;
|
||||
sendPayload?: (
|
||||
payload: ReplyPayload,
|
||||
overrides?: {
|
||||
@@ -169,6 +170,7 @@ function createPluginHandler(
|
||||
chunker,
|
||||
chunkerMode,
|
||||
textChunkLimit: outbound.textChunkLimit,
|
||||
supportsMedia: Boolean(sendMedia),
|
||||
sendPayload: outbound.sendPayload
|
||||
? async (payload, overrides) =>
|
||||
outbound.sendPayload!({
|
||||
@@ -737,6 +739,30 @@ async function deliverOutboundPayloadsCore(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!handler.supportsMedia) {
|
||||
log.warn(
|
||||
"Plugin outbound adapter does not implement sendMedia; media URLs will be dropped and text fallback will be used",
|
||||
{
|
||||
channel,
|
||||
to,
|
||||
mediaCount: payloadSummary.mediaUrls.length,
|
||||
},
|
||||
);
|
||||
const fallbackText = payloadSummary.text.trim();
|
||||
if (!fallbackText) {
|
||||
continue;
|
||||
}
|
||||
const beforeCount = results.length;
|
||||
await sendTextChunks(fallbackText, sendOverrides);
|
||||
const messageId = results.at(-1)?.messageId;
|
||||
emitMessageSent({
|
||||
success: results.length > beforeCount,
|
||||
content: payloadSummary.text,
|
||||
messageId,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
let first = true;
|
||||
let lastMessageId: string | undefined;
|
||||
for (const url of payloadSummary.mediaUrls) {
|
||||
|
||||
Reference in New Issue
Block a user