fix: sendPayload now handles payload.mediaUrls (iterate all items, extraContent only on first)

This commit is contained in:
OpenClaw Agent
2026-04-28 14:08:21 +08:00
committed by Peter Steinberger
parent ada2ff35c0
commit c5ff4d40ae
2 changed files with 120 additions and 1 deletions

View File

@@ -257,4 +257,97 @@ describe("matrixOutbound cfg threading", () => {
}),
);
});
it("sends all media URLs via sendPayload", async () => {
const cfg = {
channels: {
matrix: {
accessToken: "resolved-token",
},
},
} as OpenClawConfig;
await matrixOutbound.sendPayload!({
cfg,
to: "room:!room:example",
text: "caption",
payload: {
text: "caption",
mediaUrls: ["file:///tmp/a.png", "file:///tmp/b.png"],
},
accountId: "default",
threadId: "$thread",
});
expect(mocks.sendMessageMatrix).toHaveBeenCalledTimes(2);
// First call: caption + media
expect(mocks.sendMessageMatrix).toHaveBeenNthCalledWith(
1,
"room:!room:example",
"caption",
expect.objectContaining({
mediaUrl: "file:///tmp/a.png",
threadId: "$thread",
}),
);
// Second call: no text, just media
expect(mocks.sendMessageMatrix).toHaveBeenNthCalledWith(
2,
"room:!room:example",
"",
expect.objectContaining({
mediaUrl: "file:///tmp/b.png",
threadId: "$thread",
}),
);
});
it("sends mediaUrls with extraContent only on first item", async () => {
const cfg = {
channels: {
matrix: {
accessToken: "resolved-token",
},
},
} as OpenClawConfig;
await matrixOutbound.sendPayload!({
cfg,
to: "room:!room:example",
text: "caption",
payload: {
text: "caption",
mediaUrls: ["file:///tmp/a.png", "file:///tmp/b.png"],
channelData: {
matrix: {
extraContent: {
"com.openclaw.presentation": { version: 1 },
},
},
},
},
accountId: "default",
threadId: "$thread",
});
expect(mocks.sendMessageMatrix).toHaveBeenCalledTimes(2);
// First call gets extraContent
expect(mocks.sendMessageMatrix).toHaveBeenNthCalledWith(
1,
"room:!room:example",
"caption",
expect.objectContaining({
extraContent: { "com.openclaw.presentation": { version: 1 } },
}),
);
// Second call does NOT get extraContent
expect(mocks.sendMessageMatrix).toHaveBeenNthCalledWith(
2,
"room:!room:example",
"",
expect.not.objectContaining({
extraContent: expect.anything(),
}),
);
});
});

View File

@@ -2,6 +2,7 @@ import {
renderMessagePresentationFallbackText,
type MessagePresentation,
} from "openclaw/plugin-sdk/interactive-runtime";
import { resolvePayloadMediaUrls } from "openclaw/plugin-sdk/reply-payload";
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
import { sendMessageMatrix, sendPollMatrix } from "./matrix/send.js";
import type { MatrixExtraContentFields } from "./matrix/send/types.js";
@@ -95,13 +96,38 @@ export const matrixOutbound: ChannelOutboundAdapter = {
resolveOutboundSendDep<typeof sendMessageMatrix>(deps, "matrix") ?? sendMessageMatrix;
const resolvedThreadId =
threadId !== undefined && threadId !== null ? String(threadId) : undefined;
const resolvedReplyToId = replyToId ?? undefined;
const urls = resolvePayloadMediaUrls(payload);
if (urls.length > 0) {
let lastResult: Awaited<ReturnType<typeof send>> | undefined;
for (let i = 0; i < urls.length; i++) {
const isFirst = i === 0;
lastResult = await send(to, isFirst ? (payload.text ?? "") : "", {
cfg,
mediaUrl: urls[i],
mediaAccess,
mediaLocalRoots,
mediaReadFile,
replyToId: resolvedReplyToId,
threadId: resolvedThreadId,
accountId: accountId ?? undefined,
audioAsVoice,
extraContent: isFirst ? resolveMatrixExtraContent(payload) : undefined,
});
}
return {
channel: "matrix",
messageId: lastResult!.messageId,
roomId: lastResult!.roomId,
};
}
const result = await send(to, payload.text ?? "", {
cfg,
mediaUrl: payload.mediaUrl,
mediaAccess,
mediaLocalRoots,
mediaReadFile,
replyToId: replyToId ?? undefined,
replyToId: resolvedReplyToId,
threadId: resolvedThreadId,
accountId: accountId ?? undefined,
audioAsVoice,