refactor(bluebubbles): share multipart helpers

This commit is contained in:
Peter Steinberger
2026-02-15 19:24:03 +00:00
parent de103773c7
commit 719280d737
3 changed files with 44 additions and 39 deletions

View File

@@ -2,11 +2,11 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk";
import crypto from "node:crypto";
import path from "node:path";
import { resolveBlueBubblesAccount } from "./accounts.js";
import { postMultipartFormData } from "./multipart.js";
import { getCachedBlueBubblesPrivateApiStatus } from "./probe.js";
import { extractBlueBubblesMessageId, resolveBlueBubblesSendTarget } from "./send-helpers.js";
import { resolveChatGuidForTarget } from "./send.js";
import {
blueBubblesFetchWithTimeout,
buildBlueBubblesApiUrl,
type BlueBubblesAttachment,
type BlueBubblesSendTarget,
@@ -219,26 +219,12 @@ export async function sendBlueBubblesAttachment(params: {
// Close the multipart body
parts.push(encoder.encode(`--${boundary}--\r\n`));
// Combine all parts into a single buffer
const totalLength = parts.reduce((acc, part) => acc + part.length, 0);
const body = new Uint8Array(totalLength);
let offset = 0;
for (const part of parts) {
body.set(part, offset);
offset += part.length;
}
const res = await blueBubblesFetchWithTimeout(
const res = await postMultipartFormData({
url,
{
method: "POST",
headers: {
"Content-Type": `multipart/form-data; boundary=${boundary}`,
},
body,
},
opts.timeoutMs ?? 60_000, // longer timeout for file uploads
);
boundary,
parts,
timeoutMs: opts.timeoutMs ?? 60_000, // longer timeout for file uploads
});
if (!res.ok) {
const errorText = await res.text();

View File

@@ -2,6 +2,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk";
import crypto from "node:crypto";
import path from "node:path";
import { resolveBlueBubblesAccount } from "./accounts.js";
import { postMultipartFormData } from "./multipart.js";
import { getCachedBlueBubblesPrivateApiStatus } from "./probe.js";
import { blueBubblesFetchWithTimeout, buildBlueBubblesApiUrl } from "./types.js";
@@ -376,26 +377,12 @@ export async function setGroupIconBlueBubbles(
// Close multipart body
parts.push(encoder.encode(`--${boundary}--\r\n`));
// Combine into single buffer
const totalLength = parts.reduce((acc, part) => acc + part.length, 0);
const body = new Uint8Array(totalLength);
let offset = 0;
for (const part of parts) {
body.set(part, offset);
offset += part.length;
}
const res = await blueBubblesFetchWithTimeout(
const res = await postMultipartFormData({
url,
{
method: "POST",
headers: {
"Content-Type": `multipart/form-data; boundary=${boundary}`,
},
body,
},
opts.timeoutMs ?? 60_000, // longer timeout for file uploads
);
boundary,
parts,
timeoutMs: opts.timeoutMs ?? 60_000, // longer timeout for file uploads
});
if (!res.ok) {
const errorText = await res.text().catch(() => "");

View File

@@ -0,0 +1,32 @@
import { blueBubblesFetchWithTimeout } from "./types.js";
export function concatUint8Arrays(parts: Uint8Array[]): Uint8Array {
const totalLength = parts.reduce((acc, part) => acc + part.length, 0);
const body = new Uint8Array(totalLength);
let offset = 0;
for (const part of parts) {
body.set(part, offset);
offset += part.length;
}
return body;
}
export async function postMultipartFormData(params: {
url: string;
boundary: string;
parts: Uint8Array[];
timeoutMs: number;
}): Promise<Response> {
const body = concatUint8Arrays(params.parts);
return await blueBubblesFetchWithTimeout(
params.url,
{
method: "POST",
headers: {
"Content-Type": `multipart/form-data; boundary=${params.boundary}`,
},
body,
},
params.timeoutMs,
);
}