mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 12:18:37 +00:00
refactor: share bluebubbles multipart helpers
This commit is contained in:
@@ -2,7 +2,7 @@ import crypto from "node:crypto";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/bluebubbles";
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/bluebubbles";
|
||||||
import { resolveBlueBubblesServerAccount } from "./account-resolve.js";
|
import { resolveBlueBubblesServerAccount } from "./account-resolve.js";
|
||||||
import { postMultipartFormData } from "./multipart.js";
|
import { assertMultipartActionOk, postMultipartFormData } from "./multipart.js";
|
||||||
import {
|
import {
|
||||||
getCachedBlueBubblesPrivateApiStatus,
|
getCachedBlueBubblesPrivateApiStatus,
|
||||||
isBlueBubblesPrivateApiStatusEnabled,
|
isBlueBubblesPrivateApiStatusEnabled,
|
||||||
@@ -262,12 +262,7 @@ export async function sendBlueBubblesAttachment(params: {
|
|||||||
timeoutMs: opts.timeoutMs ?? 60_000, // longer timeout for file uploads
|
timeoutMs: opts.timeoutMs ?? 60_000, // longer timeout for file uploads
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
await assertMultipartActionOk(res, "attachment send");
|
||||||
const errorText = await res.text();
|
|
||||||
throw new Error(
|
|
||||||
`BlueBubbles attachment send failed (${res.status}): ${errorText || "unknown"}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseBody = await res.text();
|
const responseBody = await res.text();
|
||||||
if (!responseBody) {
|
if (!responseBody) {
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ describe("chat", () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mockTwoOkTextResponses() {
|
||||||
|
mockOkTextResponse();
|
||||||
|
mockOkTextResponse();
|
||||||
|
}
|
||||||
|
|
||||||
async function expectCalledUrlIncludesPassword(params: {
|
async function expectCalledUrlIncludesPassword(params: {
|
||||||
password: string;
|
password: string;
|
||||||
invoke: () => Promise<void>;
|
invoke: () => Promise<void>;
|
||||||
@@ -198,15 +203,7 @@ describe("chat", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("uses POST for start and DELETE for stop", async () => {
|
it("uses POST for start and DELETE for stop", async () => {
|
||||||
mockFetch
|
mockTwoOkTextResponses();
|
||||||
.mockResolvedValueOnce({
|
|
||||||
ok: true,
|
|
||||||
text: () => Promise.resolve(""),
|
|
||||||
})
|
|
||||||
.mockResolvedValueOnce({
|
|
||||||
ok: true,
|
|
||||||
text: () => Promise.resolve(""),
|
|
||||||
});
|
|
||||||
|
|
||||||
await sendBlueBubblesTyping("iMessage;-;+15551234567", true, {
|
await sendBlueBubblesTyping("iMessage;-;+15551234567", true, {
|
||||||
serverUrl: "http://localhost:1234",
|
serverUrl: "http://localhost:1234",
|
||||||
@@ -442,15 +439,7 @@ describe("chat", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("adds and removes participant using matching endpoint", async () => {
|
it("adds and removes participant using matching endpoint", async () => {
|
||||||
mockFetch
|
mockTwoOkTextResponses();
|
||||||
.mockResolvedValueOnce({
|
|
||||||
ok: true,
|
|
||||||
text: () => Promise.resolve(""),
|
|
||||||
})
|
|
||||||
.mockResolvedValueOnce({
|
|
||||||
ok: true,
|
|
||||||
text: () => Promise.resolve(""),
|
|
||||||
});
|
|
||||||
|
|
||||||
await addBlueBubblesParticipant("chat-guid", "+15551234567", {
|
await addBlueBubblesParticipant("chat-guid", "+15551234567", {
|
||||||
serverUrl: "http://localhost:1234",
|
serverUrl: "http://localhost:1234",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import crypto from "node:crypto";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/bluebubbles";
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/bluebubbles";
|
||||||
import { resolveBlueBubblesServerAccount } from "./account-resolve.js";
|
import { resolveBlueBubblesServerAccount } from "./account-resolve.js";
|
||||||
import { postMultipartFormData } from "./multipart.js";
|
import { assertMultipartActionOk, postMultipartFormData } from "./multipart.js";
|
||||||
import { getCachedBlueBubblesPrivateApiStatus } from "./probe.js";
|
import { getCachedBlueBubblesPrivateApiStatus } from "./probe.js";
|
||||||
import { blueBubblesFetchWithTimeout, buildBlueBubblesApiUrl } from "./types.js";
|
import { blueBubblesFetchWithTimeout, buildBlueBubblesApiUrl } from "./types.js";
|
||||||
|
|
||||||
@@ -26,14 +26,6 @@ function assertPrivateApiEnabled(accountId: string, feature: string): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function assertBlueBubblesActionOk(response: Response, action: string): Promise<void> {
|
|
||||||
if (response.ok) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const errorText = await response.text().catch(() => "");
|
|
||||||
throw new Error(`BlueBubbles ${action} failed (${response.status}): ${errorText || "unknown"}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolvePartIndex(partIndex: number | undefined): number {
|
function resolvePartIndex(partIndex: number | undefined): number {
|
||||||
return typeof partIndex === "number" ? partIndex : 0;
|
return typeof partIndex === "number" ? partIndex : 0;
|
||||||
}
|
}
|
||||||
@@ -63,7 +55,7 @@ async function sendBlueBubblesChatEndpointRequest(params: {
|
|||||||
{ method: params.method },
|
{ method: params.method },
|
||||||
params.opts.timeoutMs,
|
params.opts.timeoutMs,
|
||||||
);
|
);
|
||||||
await assertBlueBubblesActionOk(res, params.action);
|
await assertMultipartActionOk(res, params.action);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendPrivateApiJsonRequest(params: {
|
async function sendPrivateApiJsonRequest(params: {
|
||||||
@@ -89,7 +81,7 @@ async function sendPrivateApiJsonRequest(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = await blueBubblesFetchWithTimeout(url, request, params.opts.timeoutMs);
|
const res = await blueBubblesFetchWithTimeout(url, request, params.opts.timeoutMs);
|
||||||
await assertBlueBubblesActionOk(res, params.action);
|
await assertMultipartActionOk(res, params.action);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function markBlueBubblesChatRead(
|
export async function markBlueBubblesChatRead(
|
||||||
@@ -327,8 +319,5 @@ export async function setGroupIconBlueBubbles(
|
|||||||
timeoutMs: opts.timeoutMs ?? 60_000, // longer timeout for file uploads
|
timeoutMs: opts.timeoutMs ?? 60_000, // longer timeout for file uploads
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
await assertMultipartActionOk(res, "setGroupIcon");
|
||||||
const errorText = await res.text().catch(() => "");
|
|
||||||
throw new Error(`BlueBubbles setGroupIcon failed (${res.status}): ${errorText || "unknown"}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,3 +30,11 @@ export async function postMultipartFormData(params: {
|
|||||||
params.timeoutMs,
|
params.timeoutMs,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function assertMultipartActionOk(response: Response, action: string): Promise<void> {
|
||||||
|
if (response.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const errorText = await response.text().catch(() => "");
|
||||||
|
throw new Error(`BlueBubbles ${action} failed (${response.status}): ${errorText || "unknown"}`);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user