diff --git a/src/infra/outbound/message-action-params.ts b/src/infra/outbound/message-action-params.ts index 4fc9ddb7fb8..bb57a12bb99 100644 --- a/src/infra/outbound/message-action-params.ts +++ b/src/infra/outbound/message-action-params.ts @@ -168,6 +168,58 @@ function normalizeBase64Payload(params: { base64?: string; contentType?: string }; } +async function hydrateAttachmentPayload(params: { + cfg: OpenClawConfig; + channel: ChannelId; + accountId?: string | null; + args: Record; + dryRun?: boolean; + contentTypeParam?: string | null; + mediaHint?: string | null; + fileHint?: string | null; +}) { + const contentTypeParam = params.contentTypeParam ?? undefined; + const rawBuffer = readStringParam(params.args, "buffer", { trim: false }); + const normalized = normalizeBase64Payload({ + base64: rawBuffer, + contentType: contentTypeParam ?? undefined, + }); + if (normalized.base64 !== rawBuffer && normalized.base64) { + params.args.buffer = normalized.base64; + if (normalized.contentType && !contentTypeParam) { + params.args.contentType = normalized.contentType; + } + } + + const filename = readStringParam(params.args, "filename"); + const mediaSource = (params.mediaHint ?? undefined) || (params.fileHint ?? undefined); + + if (!params.dryRun && !readStringParam(params.args, "buffer", { trim: false }) && mediaSource) { + const maxBytes = resolveAttachmentMaxBytes({ + cfg: params.cfg, + channel: params.channel, + accountId: params.accountId, + }); + // localRoots: "any" — media paths are already validated by normalizeSandboxMediaList above. + const media = await loadWebMedia(mediaSource, maxBytes, { localRoots: "any" }); + params.args.buffer = media.buffer.toString("base64"); + if (!contentTypeParam && media.contentType) { + params.args.contentType = media.contentType; + } + if (!filename) { + params.args.filename = inferAttachmentFilename({ + mediaHint: media.fileName ?? mediaSource, + contentType: media.contentType ?? contentTypeParam ?? undefined, + }); + } + } else if (!filename) { + params.args.filename = inferAttachmentFilename({ + mediaHint: mediaSource, + contentType: contentTypeParam ?? undefined, + }); + } +} + export async function normalizeSandboxMediaParams(params: { args: Record; sandboxRoot?: string; @@ -233,46 +285,16 @@ export async function hydrateSetGroupIconParams(params: { readStringParam(params.args, "filePath", { trim: false }); const contentTypeParam = readStringParam(params.args, "contentType") ?? readStringParam(params.args, "mimeType"); - - const rawBuffer = readStringParam(params.args, "buffer", { trim: false }); - const normalized = normalizeBase64Payload({ - base64: rawBuffer, - contentType: contentTypeParam ?? undefined, + await hydrateAttachmentPayload({ + cfg: params.cfg, + channel: params.channel, + accountId: params.accountId, + args: params.args, + dryRun: params.dryRun, + contentTypeParam, + mediaHint, + fileHint, }); - if (normalized.base64 !== rawBuffer && normalized.base64) { - params.args.buffer = normalized.base64; - if (normalized.contentType && !contentTypeParam) { - params.args.contentType = normalized.contentType; - } - } - - const filename = readStringParam(params.args, "filename"); - const mediaSource = mediaHint ?? fileHint; - - if (!params.dryRun && !readStringParam(params.args, "buffer", { trim: false }) && mediaSource) { - const maxBytes = resolveAttachmentMaxBytes({ - cfg: params.cfg, - channel: params.channel, - accountId: params.accountId, - }); - // localRoots: "any" — media paths are already validated by normalizeSandboxMediaList above. - const media = await loadWebMedia(mediaSource, maxBytes, { localRoots: "any" }); - params.args.buffer = media.buffer.toString("base64"); - if (!contentTypeParam && media.contentType) { - params.args.contentType = media.contentType; - } - if (!filename) { - params.args.filename = inferAttachmentFilename({ - mediaHint: media.fileName ?? mediaSource, - contentType: media.contentType ?? contentTypeParam ?? undefined, - }); - } - } else if (!filename) { - params.args.filename = inferAttachmentFilename({ - mediaHint: mediaSource, - contentType: contentTypeParam ?? undefined, - }); - } } export async function hydrateSendAttachmentParams(params: { @@ -298,46 +320,16 @@ export async function hydrateSendAttachmentParams(params: { if (!caption && message) { params.args.caption = message; } - - const rawBuffer = readStringParam(params.args, "buffer", { trim: false }); - const normalized = normalizeBase64Payload({ - base64: rawBuffer, - contentType: contentTypeParam ?? undefined, + await hydrateAttachmentPayload({ + cfg: params.cfg, + channel: params.channel, + accountId: params.accountId, + args: params.args, + dryRun: params.dryRun, + contentTypeParam, + mediaHint, + fileHint, }); - if (normalized.base64 !== rawBuffer && normalized.base64) { - params.args.buffer = normalized.base64; - if (normalized.contentType && !contentTypeParam) { - params.args.contentType = normalized.contentType; - } - } - - const filename = readStringParam(params.args, "filename"); - const mediaSource = mediaHint ?? fileHint; - - if (!params.dryRun && !readStringParam(params.args, "buffer", { trim: false }) && mediaSource) { - const maxBytes = resolveAttachmentMaxBytes({ - cfg: params.cfg, - channel: params.channel, - accountId: params.accountId, - }); - // localRoots: "any" — media paths are already validated by normalizeSandboxMediaList above. - const media = await loadWebMedia(mediaSource, maxBytes, { localRoots: "any" }); - params.args.buffer = media.buffer.toString("base64"); - if (!contentTypeParam && media.contentType) { - params.args.contentType = media.contentType; - } - if (!filename) { - params.args.filename = inferAttachmentFilename({ - mediaHint: media.fileName ?? mediaSource, - contentType: media.contentType ?? contentTypeParam ?? undefined, - }); - } - } else if (!filename) { - params.args.filename = inferAttachmentFilename({ - mediaHint: mediaSource, - contentType: contentTypeParam ?? undefined, - }); - } } export function parseButtonsParam(params: Record): void {