mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 04:17:42 +00:00
refactor(channels): dedupe transport and gateway test scaffolds
This commit is contained in:
@@ -23,6 +23,12 @@ type AttachmentLog = {
|
||||
warn: (message: string) => void;
|
||||
};
|
||||
|
||||
type NormalizedAttachment = {
|
||||
label: string;
|
||||
mime: string;
|
||||
base64: string;
|
||||
};
|
||||
|
||||
function normalizeMime(mime?: string): string | undefined {
|
||||
if (!mime) {
|
||||
return undefined;
|
||||
@@ -40,6 +46,49 @@ function isValidBase64(value: string): boolean {
|
||||
return value.length > 0 && value.length % 4 === 0 && /^[A-Za-z0-9+/]+={0,2}$/.test(value);
|
||||
}
|
||||
|
||||
function normalizeAttachment(
|
||||
att: ChatAttachment,
|
||||
idx: number,
|
||||
opts: { stripDataUrlPrefix: boolean; requireImageMime: boolean },
|
||||
): NormalizedAttachment {
|
||||
const mime = att.mimeType ?? "";
|
||||
const content = att.content;
|
||||
const label = att.fileName || att.type || `attachment-${idx + 1}`;
|
||||
|
||||
if (typeof content !== "string") {
|
||||
throw new Error(`attachment ${label}: content must be base64 string`);
|
||||
}
|
||||
if (opts.requireImageMime && !mime.startsWith("image/")) {
|
||||
throw new Error(`attachment ${label}: only image/* supported`);
|
||||
}
|
||||
|
||||
let base64 = content.trim();
|
||||
if (opts.stripDataUrlPrefix) {
|
||||
// Strip data URL prefix if present (e.g., "data:image/jpeg;base64,...").
|
||||
const dataUrlMatch = /^data:[^;]+;base64,(.*)$/.exec(base64);
|
||||
if (dataUrlMatch) {
|
||||
base64 = dataUrlMatch[1];
|
||||
}
|
||||
}
|
||||
return { label, mime, base64 };
|
||||
}
|
||||
|
||||
function validateAttachmentBase64OrThrow(
|
||||
normalized: NormalizedAttachment,
|
||||
opts: { maxBytes: number },
|
||||
): number {
|
||||
if (!isValidBase64(normalized.base64)) {
|
||||
throw new Error(`attachment ${normalized.label}: invalid base64 content`);
|
||||
}
|
||||
const sizeBytes = estimateBase64DecodedBytes(normalized.base64);
|
||||
if (sizeBytes <= 0 || sizeBytes > opts.maxBytes) {
|
||||
throw new Error(
|
||||
`attachment ${normalized.label}: exceeds size limit (${sizeBytes} > ${opts.maxBytes} bytes)`,
|
||||
);
|
||||
}
|
||||
return sizeBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse attachments and extract images as structured content blocks.
|
||||
* Returns the message text and an array of image content blocks
|
||||
@@ -62,28 +111,12 @@ export async function parseMessageWithAttachments(
|
||||
if (!att) {
|
||||
continue;
|
||||
}
|
||||
const mime = att.mimeType ?? "";
|
||||
const content = att.content;
|
||||
const label = att.fileName || att.type || `attachment-${idx + 1}`;
|
||||
|
||||
if (typeof content !== "string") {
|
||||
throw new Error(`attachment ${label}: content must be base64 string`);
|
||||
}
|
||||
|
||||
let sizeBytes = 0;
|
||||
let b64 = content.trim();
|
||||
// Strip data URL prefix if present (e.g., "data:image/jpeg;base64,...")
|
||||
const dataUrlMatch = /^data:[^;]+;base64,(.*)$/.exec(b64);
|
||||
if (dataUrlMatch) {
|
||||
b64 = dataUrlMatch[1];
|
||||
}
|
||||
if (!isValidBase64(b64)) {
|
||||
throw new Error(`attachment ${label}: invalid base64 content`);
|
||||
}
|
||||
sizeBytes = estimateBase64DecodedBytes(b64);
|
||||
if (sizeBytes <= 0 || sizeBytes > maxBytes) {
|
||||
throw new Error(`attachment ${label}: exceeds size limit (${sizeBytes} > ${maxBytes} bytes)`);
|
||||
}
|
||||
const normalized = normalizeAttachment(att, idx, {
|
||||
stripDataUrlPrefix: true,
|
||||
requireImageMime: false,
|
||||
});
|
||||
validateAttachmentBase64OrThrow(normalized, { maxBytes });
|
||||
const { base64: b64, label, mime } = normalized;
|
||||
|
||||
const providedMime = normalizeMime(mime);
|
||||
const sniffedMime = normalizeMime(await sniffMimeFromBase64(b64));
|
||||
@@ -131,29 +164,15 @@ export function buildMessageWithAttachments(
|
||||
if (!att) {
|
||||
continue;
|
||||
}
|
||||
const mime = att.mimeType ?? "";
|
||||
const content = att.content;
|
||||
const label = att.fileName || att.type || `attachment-${idx + 1}`;
|
||||
|
||||
if (typeof content !== "string") {
|
||||
throw new Error(`attachment ${label}: content must be base64 string`);
|
||||
}
|
||||
if (!mime.startsWith("image/")) {
|
||||
throw new Error(`attachment ${label}: only image/* supported`);
|
||||
}
|
||||
|
||||
let sizeBytes = 0;
|
||||
const b64 = content.trim();
|
||||
if (!isValidBase64(b64)) {
|
||||
throw new Error(`attachment ${label}: invalid base64 content`);
|
||||
}
|
||||
sizeBytes = estimateBase64DecodedBytes(b64);
|
||||
if (sizeBytes <= 0 || sizeBytes > maxBytes) {
|
||||
throw new Error(`attachment ${label}: exceeds size limit (${sizeBytes} > ${maxBytes} bytes)`);
|
||||
}
|
||||
const normalized = normalizeAttachment(att, idx, {
|
||||
stripDataUrlPrefix: false,
|
||||
requireImageMime: true,
|
||||
});
|
||||
validateAttachmentBase64OrThrow(normalized, { maxBytes });
|
||||
const { base64, label, mime } = normalized;
|
||||
|
||||
const safeLabel = label.replace(/\s+/g, "_");
|
||||
const dataUrl = ``;
|
||||
const dataUrl = ``;
|
||||
blocks.push(dataUrl);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user