mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 00:38:28 +00:00
bluebubbles: gracefully handle disabled private API with action/tool filtering and fallbacks (#16002)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 243cc0cc9a
Co-authored-by: tyler6204 <243?+tyler6204@users.noreply.github.com>
Co-authored-by: tyler6204 <64381258+tyler6204@users.noreply.github.com>
Reviewed-by: @tyler6204
This commit is contained in:
@@ -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 { getCachedBlueBubblesPrivateApiStatus } from "./probe.js";
|
||||
import { blueBubblesFetchWithTimeout, buildBlueBubblesApiUrl } from "./types.js";
|
||||
|
||||
export type BlueBubblesChatOpts = {
|
||||
@@ -25,7 +26,15 @@ function resolveAccount(params: BlueBubblesChatOpts) {
|
||||
if (!password) {
|
||||
throw new Error("BlueBubbles password is required");
|
||||
}
|
||||
return { baseUrl, password };
|
||||
return { baseUrl, password, accountId: account.accountId };
|
||||
}
|
||||
|
||||
function assertPrivateApiEnabled(accountId: string, feature: string): void {
|
||||
if (getCachedBlueBubblesPrivateApiStatus(accountId) === false) {
|
||||
throw new Error(
|
||||
`BlueBubbles ${feature} requires Private API, but it is disabled on the BlueBubbles server.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function markBlueBubblesChatRead(
|
||||
@@ -36,7 +45,10 @@ export async function markBlueBubblesChatRead(
|
||||
if (!trimmed) {
|
||||
return;
|
||||
}
|
||||
const { baseUrl, password } = resolveAccount(opts);
|
||||
const { baseUrl, password, accountId } = resolveAccount(opts);
|
||||
if (getCachedBlueBubblesPrivateApiStatus(accountId) === false) {
|
||||
return;
|
||||
}
|
||||
const url = buildBlueBubblesApiUrl({
|
||||
baseUrl,
|
||||
path: `/api/v1/chat/${encodeURIComponent(trimmed)}/read`,
|
||||
@@ -58,7 +70,10 @@ export async function sendBlueBubblesTyping(
|
||||
if (!trimmed) {
|
||||
return;
|
||||
}
|
||||
const { baseUrl, password } = resolveAccount(opts);
|
||||
const { baseUrl, password, accountId } = resolveAccount(opts);
|
||||
if (getCachedBlueBubblesPrivateApiStatus(accountId) === false) {
|
||||
return;
|
||||
}
|
||||
const url = buildBlueBubblesApiUrl({
|
||||
baseUrl,
|
||||
path: `/api/v1/chat/${encodeURIComponent(trimmed)}/typing`,
|
||||
@@ -93,7 +108,8 @@ export async function editBlueBubblesMessage(
|
||||
throw new Error("BlueBubbles edit requires newText");
|
||||
}
|
||||
|
||||
const { baseUrl, password } = resolveAccount(opts);
|
||||
const { baseUrl, password, accountId } = resolveAccount(opts);
|
||||
assertPrivateApiEnabled(accountId, "edit");
|
||||
const url = buildBlueBubblesApiUrl({
|
||||
baseUrl,
|
||||
path: `/api/v1/message/${encodeURIComponent(trimmedGuid)}/edit`,
|
||||
@@ -135,7 +151,8 @@ export async function unsendBlueBubblesMessage(
|
||||
throw new Error("BlueBubbles unsend requires messageGuid");
|
||||
}
|
||||
|
||||
const { baseUrl, password } = resolveAccount(opts);
|
||||
const { baseUrl, password, accountId } = resolveAccount(opts);
|
||||
assertPrivateApiEnabled(accountId, "unsend");
|
||||
const url = buildBlueBubblesApiUrl({
|
||||
baseUrl,
|
||||
path: `/api/v1/message/${encodeURIComponent(trimmedGuid)}/unsend`,
|
||||
@@ -175,7 +192,8 @@ export async function renameBlueBubblesChat(
|
||||
throw new Error("BlueBubbles rename requires chatGuid");
|
||||
}
|
||||
|
||||
const { baseUrl, password } = resolveAccount(opts);
|
||||
const { baseUrl, password, accountId } = resolveAccount(opts);
|
||||
assertPrivateApiEnabled(accountId, "renameGroup");
|
||||
const url = buildBlueBubblesApiUrl({
|
||||
baseUrl,
|
||||
path: `/api/v1/chat/${encodeURIComponent(trimmedGuid)}`,
|
||||
@@ -215,7 +233,8 @@ export async function addBlueBubblesParticipant(
|
||||
throw new Error("BlueBubbles addParticipant requires address");
|
||||
}
|
||||
|
||||
const { baseUrl, password } = resolveAccount(opts);
|
||||
const { baseUrl, password, accountId } = resolveAccount(opts);
|
||||
assertPrivateApiEnabled(accountId, "addParticipant");
|
||||
const url = buildBlueBubblesApiUrl({
|
||||
baseUrl,
|
||||
path: `/api/v1/chat/${encodeURIComponent(trimmedGuid)}/participant`,
|
||||
@@ -255,7 +274,8 @@ export async function removeBlueBubblesParticipant(
|
||||
throw new Error("BlueBubbles removeParticipant requires address");
|
||||
}
|
||||
|
||||
const { baseUrl, password } = resolveAccount(opts);
|
||||
const { baseUrl, password, accountId } = resolveAccount(opts);
|
||||
assertPrivateApiEnabled(accountId, "removeParticipant");
|
||||
const url = buildBlueBubblesApiUrl({
|
||||
baseUrl,
|
||||
path: `/api/v1/chat/${encodeURIComponent(trimmedGuid)}/participant`,
|
||||
@@ -292,7 +312,8 @@ export async function leaveBlueBubblesChat(
|
||||
throw new Error("BlueBubbles leaveChat requires chatGuid");
|
||||
}
|
||||
|
||||
const { baseUrl, password } = resolveAccount(opts);
|
||||
const { baseUrl, password, accountId } = resolveAccount(opts);
|
||||
assertPrivateApiEnabled(accountId, "leaveGroup");
|
||||
const url = buildBlueBubblesApiUrl({
|
||||
baseUrl,
|
||||
path: `/api/v1/chat/${encodeURIComponent(trimmedGuid)}/leave`,
|
||||
@@ -325,7 +346,8 @@ export async function setGroupIconBlueBubbles(
|
||||
throw new Error("BlueBubbles setGroupIcon requires image buffer");
|
||||
}
|
||||
|
||||
const { baseUrl, password } = resolveAccount(opts);
|
||||
const { baseUrl, password, accountId } = resolveAccount(opts);
|
||||
assertPrivateApiEnabled(accountId, "setGroupIcon");
|
||||
const url = buildBlueBubblesApiUrl({
|
||||
baseUrl,
|
||||
path: `/api/v1/chat/${encodeURIComponent(trimmedGuid)}/icon`,
|
||||
|
||||
Reference in New Issue
Block a user