mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
refactor: dedupe bluebubbles readers
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
} from "openclaw/plugin-sdk/account-resolution";
|
||||
import { resolveChannelStreamingChunkMode } from "openclaw/plugin-sdk/channel-streaming";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { hasConfiguredSecretInput, normalizeSecretInputString } from "./secret-input.js";
|
||||
import { normalizeBlueBubblesServerUrl, type BlueBubblesAccountConfig } from "./types.js";
|
||||
|
||||
@@ -58,7 +59,7 @@ export function resolveBlueBubblesAccount(params: {
|
||||
return {
|
||||
accountId,
|
||||
enabled: baseEnabled !== false && accountEnabled,
|
||||
name: merged.name?.trim() || undefined,
|
||||
name: normalizeOptionalString(merged.name),
|
||||
config: merged,
|
||||
configured,
|
||||
baseUrl,
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
createComputedAccountStatusAdapter,
|
||||
createDefaultChannelRuntimeState,
|
||||
} from "openclaw/plugin-sdk/status-helpers";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { type ResolvedBlueBubblesAccount } from "./accounts.js";
|
||||
import { bluebubblesMessageActions } from "./actions.js";
|
||||
import {
|
||||
@@ -135,7 +136,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount, BlueBu
|
||||
looksLikeId: looksLikeBlueBubblesExplicitTargetId,
|
||||
hint: "<handle|chat_guid:GUID|chat_id:ID|chat_identifier:ID>",
|
||||
resolveTarget: async ({ normalized }) => {
|
||||
const to = normalized?.trim();
|
||||
const to = normalizeOptionalString(normalized);
|
||||
if (!to) {
|
||||
return null;
|
||||
}
|
||||
@@ -160,7 +161,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount, BlueBu
|
||||
|
||||
// Helper to extract a clean handle from any BlueBubbles target format
|
||||
const extractCleanDisplay = (value: string | undefined): string | null => {
|
||||
const trimmed = value?.trim();
|
||||
const trimmed = normalizeOptionalString(value);
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
@@ -196,7 +197,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount, BlueBu
|
||||
};
|
||||
|
||||
// Try to get a clean display from the display parameter first
|
||||
const trimmedDisplay = display?.trim();
|
||||
const trimmedDisplay = normalizeOptionalString(display);
|
||||
if (trimmedDisplay) {
|
||||
if (!shouldParseDisplay(trimmedDisplay)) {
|
||||
return trimmedDisplay;
|
||||
@@ -214,7 +215,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount, BlueBu
|
||||
}
|
||||
|
||||
// Last resort: return display or target as-is
|
||||
return display?.trim() || target?.trim() || "";
|
||||
return normalizeOptionalString(display) || normalizeOptionalString(target) || "";
|
||||
},
|
||||
},
|
||||
setup: blueBubblesSetupAdapter,
|
||||
@@ -286,7 +287,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount, BlueBu
|
||||
},
|
||||
threading: {
|
||||
buildToolContext: ({ context, hasRepliedRef }) => ({
|
||||
currentChannelId: context.To?.trim() || undefined,
|
||||
currentChannelId: normalizeOptionalString(context.To),
|
||||
currentThreadTs: context.ReplyToIdFull ?? context.ReplyToId,
|
||||
hasRepliedRef,
|
||||
}),
|
||||
@@ -314,7 +315,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount, BlueBu
|
||||
deliveryMode: "direct",
|
||||
textChunkLimit: 4000,
|
||||
resolveTarget: ({ to }) => {
|
||||
const trimmed = to?.trim();
|
||||
const trimmed = normalizeOptionalString(to);
|
||||
if (!trimmed) {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -328,7 +329,7 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount, BlueBu
|
||||
channel: "bluebubbles",
|
||||
sendText: async ({ cfg, to, text, accountId, replyToId }) => {
|
||||
const runtime = await loadBlueBubblesChannelRuntime();
|
||||
const rawReplyToId = typeof replyToId === "string" ? replyToId.trim() : "";
|
||||
const rawReplyToId = normalizeOptionalString(replyToId) ?? "";
|
||||
const replyToMessageGuid = rawReplyToId
|
||||
? runtime.resolveBlueBubblesMessageId(rawReplyToId, { requireKnownShortId: true })
|
||||
: "";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { BaseProbeResult } from "./runtime-api.js";
|
||||
import { normalizeSecretInputString } from "./secret-input.js";
|
||||
import { buildBlueBubblesApiUrl, blueBubblesFetchWithTimeout } from "./types.js";
|
||||
@@ -24,7 +25,7 @@ const serverInfoCache = new Map<string, { info: BlueBubblesServerInfo; expires:
|
||||
const CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
||||
|
||||
function buildCacheKey(accountId?: string): string {
|
||||
return accountId?.trim() || "default";
|
||||
return normalizeOptionalString(accountId) || "default";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import crypto from "node:crypto";
|
||||
import { stripMarkdown } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { normalizeOptionalString, stripMarkdown } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { resolveBlueBubblesServerAccount } from "./account-resolve.js";
|
||||
import {
|
||||
getCachedBlueBubblesPrivateApiStatus,
|
||||
@@ -137,8 +137,9 @@ function extractChatGuid(chat: BlueBubblesChatRecord): string | null {
|
||||
chat.chat_identifier,
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (typeof candidate === "string" && candidate.trim()) {
|
||||
return candidate.trim();
|
||||
const value = normalizeOptionalString(candidate);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -159,8 +160,7 @@ function extractChatIdentifierFromChatGuid(chatGuid: string): string | null {
|
||||
if (parts.length < 3) {
|
||||
return null;
|
||||
}
|
||||
const identifier = parts[2]?.trim();
|
||||
return identifier ? identifier : null;
|
||||
return normalizeOptionalString(parts[2]) ?? null;
|
||||
}
|
||||
|
||||
function extractParticipantAddresses(chat: BlueBubblesChatRecord): string[] {
|
||||
@@ -479,7 +479,7 @@ export async function sendMessageBlueBubbles(
|
||||
);
|
||||
}
|
||||
const effectId = resolveEffectId(opts.effectId);
|
||||
const wantsReplyThread = Boolean(opts.replyToMessageGuid?.trim());
|
||||
const wantsReplyThread = normalizeOptionalString(opts.replyToMessageGuid) !== undefined;
|
||||
const wantsEffect = Boolean(effectId);
|
||||
const privateApiDecision = resolvePrivateApiDecision({
|
||||
privateApiStatus,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
type ChannelSetupWizard,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/setup";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { resolveBlueBubblesAccount, resolveDefaultBlueBubblesAccountId } from "./accounts.js";
|
||||
import { applyBlueBubblesConnectionConfig } from "./config-apply.js";
|
||||
import { hasConfiguredSecretInput, normalizeSecretInputString } from "./secret-input.js";
|
||||
@@ -39,7 +40,7 @@ function validateBlueBubblesAllowFromEntry(value: string): string | null {
|
||||
if (parsed.kind === "handle" && !parsed.handle) {
|
||||
return null;
|
||||
}
|
||||
return value.trim() || null;
|
||||
return normalizeOptionalString(value) ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
@@ -76,7 +77,7 @@ const promptBlueBubblesAllowFrom = createPromptParsedAllowFromForAccount({
|
||||
});
|
||||
|
||||
function validateBlueBubblesServerUrlInput(value: unknown): string | undefined {
|
||||
const trimmed = typeof value === "string" ? value.trim() : "";
|
||||
const trimmed = normalizeOptionalString(value) ?? "";
|
||||
if (!trimmed) {
|
||||
return "Required";
|
||||
}
|
||||
@@ -108,11 +109,11 @@ function applyBlueBubblesSetupPatch(
|
||||
}
|
||||
|
||||
function resolveBlueBubblesServerUrl(cfg: OpenClawConfig, accountId: string): string | undefined {
|
||||
return resolveBlueBubblesAccount({ cfg, accountId }).config.serverUrl?.trim() || undefined;
|
||||
return normalizeOptionalString(resolveBlueBubblesAccount({ cfg, accountId }).config.serverUrl);
|
||||
}
|
||||
|
||||
function resolveBlueBubblesWebhookPath(cfg: OpenClawConfig, accountId: string): string | undefined {
|
||||
return resolveBlueBubblesAccount({ cfg, accountId }).config.webhookPath?.trim() || undefined;
|
||||
return normalizeOptionalString(resolveBlueBubblesAccount({ cfg, accountId }).config.webhookPath);
|
||||
}
|
||||
|
||||
function validateBlueBubblesWebhookPath(value: string): string | undefined {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
resolveServicePrefixedAllowTarget,
|
||||
resolveServicePrefixedTarget,
|
||||
} from "openclaw/plugin-sdk/channel-targets";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
|
||||
export type BlueBubblesService = "imessage" | "sms" | "auto";
|
||||
|
||||
@@ -29,7 +30,7 @@ const CHAT_IDENTIFIER_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4
|
||||
const CHAT_IDENTIFIER_HEX_RE = /^[0-9a-f]{24,64}$/i;
|
||||
|
||||
function parseRawChatGuid(value: string): string | null {
|
||||
const trimmed = value.trim();
|
||||
const trimmed = normalizeOptionalString(value);
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
@@ -37,9 +38,9 @@ function parseRawChatGuid(value: string): string | null {
|
||||
if (parts.length !== 3) {
|
||||
return null;
|
||||
}
|
||||
const service = parts[0]?.trim();
|
||||
const separator = parts[1]?.trim();
|
||||
const identifier = parts[2]?.trim();
|
||||
const service = normalizeOptionalString(parts[0]);
|
||||
const separator = normalizeOptionalString(parts[1]);
|
||||
const identifier = normalizeOptionalString(parts[2]);
|
||||
if (!service || !identifier) {
|
||||
return null;
|
||||
}
|
||||
@@ -54,7 +55,7 @@ function stripPrefix(value: string, prefix: string): string {
|
||||
}
|
||||
|
||||
function stripBlueBubblesPrefix(value: string): string {
|
||||
const trimmed = value.trim();
|
||||
const trimmed = normalizeOptionalString(value) ?? "";
|
||||
if (!trimmed) {
|
||||
return "";
|
||||
}
|
||||
@@ -65,7 +66,7 @@ function stripBlueBubblesPrefix(value: string): string {
|
||||
}
|
||||
|
||||
function looksLikeRawChatIdentifier(value: string): boolean {
|
||||
const trimmed = value.trim();
|
||||
const trimmed = normalizeOptionalString(value);
|
||||
if (!trimmed) {
|
||||
return false;
|
||||
}
|
||||
@@ -139,7 +140,7 @@ export function extractHandleFromChatGuid(chatGuid: string): string | null {
|
||||
const parts = chatGuid.split(";");
|
||||
// DM format: service;-;handle (3 parts, middle is "-")
|
||||
if (parts.length === 3 && parts[1] === "-") {
|
||||
const handle = parts[2]?.trim();
|
||||
const handle = normalizeOptionalString(parts[2]);
|
||||
if (handle) {
|
||||
return normalizeBlueBubblesHandle(handle);
|
||||
}
|
||||
@@ -222,7 +223,7 @@ export function looksLikeBlueBubblesTargetId(raw: string, normalized?: string):
|
||||
return true;
|
||||
}
|
||||
if (normalized) {
|
||||
const normalizedTrimmed = normalized.trim();
|
||||
const normalizedTrimmed = normalizeOptionalString(normalized);
|
||||
if (!normalizedTrimmed) {
|
||||
return false;
|
||||
}
|
||||
@@ -346,7 +347,7 @@ export function parseBlueBubblesTarget(raw: string): BlueBubblesTarget {
|
||||
}
|
||||
|
||||
export function parseBlueBubblesAllowTarget(raw: string): BlueBubblesAllowTarget {
|
||||
const trimmed = raw.trim();
|
||||
const trimmed = normalizeOptionalString(raw) ?? "";
|
||||
if (!trimmed) {
|
||||
return { kind: "handle", handle: "" };
|
||||
}
|
||||
@@ -412,11 +413,11 @@ export function formatBlueBubblesChatTarget(params: {
|
||||
if (params.chatId && Number.isFinite(params.chatId)) {
|
||||
return `chat_id:${params.chatId}`;
|
||||
}
|
||||
const guid = params.chatGuid?.trim();
|
||||
const guid = normalizeOptionalString(params.chatGuid);
|
||||
if (guid) {
|
||||
return `chat_guid:${guid}`;
|
||||
}
|
||||
const identifier = params.chatIdentifier?.trim();
|
||||
const identifier = normalizeOptionalString(params.chatIdentifier);
|
||||
if (identifier) {
|
||||
return `chat_identifier:${identifier}`;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { normalizeWebhookPath } from "openclaw/plugin-sdk/webhook-path";
|
||||
import type { BlueBubblesAccountConfig } from "./types.js";
|
||||
|
||||
@@ -6,7 +7,7 @@ export { normalizeWebhookPath };
|
||||
export const DEFAULT_WEBHOOK_PATH = "/bluebubbles-webhook";
|
||||
|
||||
export function resolveWebhookPathFromConfig(config?: BlueBubblesAccountConfig): string {
|
||||
const raw = config?.webhookPath?.trim();
|
||||
const raw = normalizeOptionalString(config?.webhookPath);
|
||||
if (raw) {
|
||||
return normalizeWebhookPath(raw);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user