mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 01:31:08 +00:00
refactor: dedupe bluebubbles and zalouser readers
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import crypto from "node:crypto";
|
||||
import path from "node:path";
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { resolveBlueBubblesServerAccount } from "./account-resolve.js";
|
||||
import { assertMultipartActionOk, postMultipartFormData } from "./multipart.js";
|
||||
import {
|
||||
@@ -161,7 +162,7 @@ export async function sendBlueBubblesAttachment(params: {
|
||||
const wantsVoice = asVoice === true;
|
||||
const fallbackName = wantsVoice ? "Audio Message" : "attachment";
|
||||
filename = sanitizeFilename(filename, fallbackName);
|
||||
contentType = contentType?.trim() || undefined;
|
||||
contentType = normalizeOptionalString(contentType);
|
||||
const { baseUrl, password, accountId, allowPrivateNetwork } = resolveAccount(opts);
|
||||
const privateApiStatus = getCachedBlueBubblesPrivateApiStatus(accountId);
|
||||
const privateApiEnabled = isBlueBubblesPrivateApiStatusEnabled(privateApiStatus);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { parseFiniteNumber } from "openclaw/plugin-sdk/infra-runtime";
|
||||
import { asNullableRecord, readStringField } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
asNullableRecord,
|
||||
normalizeOptionalString,
|
||||
readStringField,
|
||||
} from "openclaw/plugin-sdk/text-runtime";
|
||||
import { extractHandleFromChatGuid, normalizeBlueBubblesHandle } from "./targets.js";
|
||||
import type { BlueBubblesAttachment } from "./types.js";
|
||||
|
||||
@@ -164,8 +168,8 @@ function extractReplyMetadata(message: Record<string, unknown>): {
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
replyToId: (replyToId ?? fallbackReplyId)?.trim() || undefined,
|
||||
replyToBody: replyToBody?.trim() || undefined,
|
||||
replyToId: normalizeOptionalString(replyToId ?? fallbackReplyId),
|
||||
replyToBody: normalizeOptionalString(replyToBody),
|
||||
replyToSender: normalizedSender || undefined,
|
||||
};
|
||||
}
|
||||
@@ -336,7 +340,7 @@ function normalizeParticipantEntry(entry: unknown): BlueBubblesParticipant | nul
|
||||
if (!normalizedId) {
|
||||
return null;
|
||||
}
|
||||
const name = nameRaw?.trim() || undefined;
|
||||
const name = normalizeOptionalString(nameRaw);
|
||||
return { id: normalizedId, name };
|
||||
}
|
||||
|
||||
@@ -563,7 +567,9 @@ export function resolveTapbackContext(message: NormalizedWebhookMessage): {
|
||||
if (!hasTapbackType && !hasTapbackMarker) {
|
||||
return null;
|
||||
}
|
||||
const replyToId = message.associatedMessageGuid?.trim() || message.replyToId?.trim() || undefined;
|
||||
const replyToId =
|
||||
normalizeOptionalString(message.associatedMessageGuid) ??
|
||||
normalizeOptionalString(message.replyToId);
|
||||
const actionHint = resolveTapbackActionHint(associatedType);
|
||||
const emojiHint =
|
||||
message.associatedMessageEmoji?.trim() || REACTION_TYPE_MAP.get(associatedType ?? -1)?.emoji;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
sendMediaWithLeadingCaption,
|
||||
} from "openclaw/plugin-sdk/reply-payload";
|
||||
import { isPrivateNetworkOptInEnabled } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { downloadBlueBubblesAttachment } from "./attachments.js";
|
||||
import { markBlueBubblesChatRead, sendBlueBubblesTyping } from "./chat.js";
|
||||
import { resolveBlueBubblesConversationRoute } from "./conversation-route.js";
|
||||
@@ -92,8 +93,7 @@ const pendingOutboundMessageIds: PendingOutboundMessageId[] = [];
|
||||
let pendingOutboundMessageIdCounter = 0;
|
||||
|
||||
function trimOrUndefined(value?: string | null): string | undefined {
|
||||
const trimmed = value?.trim();
|
||||
return trimmed ? trimmed : undefined;
|
||||
return normalizeOptionalString(value);
|
||||
}
|
||||
|
||||
function normalizeSnippet(value: string): string {
|
||||
@@ -732,7 +732,7 @@ export async function processMessage(
|
||||
chatId: message.chatId ?? undefined,
|
||||
chatIdentifier: message.chatIdentifier ?? undefined,
|
||||
});
|
||||
const groupName = message.chatName?.trim() || undefined;
|
||||
const groupName = normalizeOptionalString(message.chatName);
|
||||
|
||||
if (accessDecision.decision !== "allow") {
|
||||
if (isGroup) {
|
||||
@@ -1105,11 +1105,11 @@ export async function processMessage(
|
||||
// The sender identity is included in the envelope body via formatInboundEnvelope.
|
||||
const senderLabel = message.senderName || `user:${message.senderId}`;
|
||||
const fromLabel = isGroup
|
||||
? `${message.chatName?.trim() || "Group"} id:${peerId}`
|
||||
? `${normalizeOptionalString(message.chatName) || "Group"} id:${peerId}`
|
||||
: senderLabel !== message.senderId
|
||||
? `${senderLabel} id:${message.senderId}`
|
||||
: senderLabel;
|
||||
const groupSubject = isGroup ? message.chatName?.trim() || undefined : undefined;
|
||||
const groupSubject = isGroup ? normalizeOptionalString(message.chatName) : undefined;
|
||||
const groupMembers = isGroup
|
||||
? formatGroupMembers({
|
||||
participants: message.participants,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { execFile, type ExecFileOptionsWithStringEncoding } from "node:child_pro
|
||||
import { access, readdir } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { BlueBubblesParticipant } from "./monitor-normalize.js";
|
||||
|
||||
const execFileAsync = promisify(execFile) as ExecFileRunner;
|
||||
@@ -313,7 +314,7 @@ export async function enrichBlueBubblesParticipantsWithContactNames(
|
||||
try {
|
||||
const resolved = await lookup([...pendingPhoneKeys]);
|
||||
for (const phoneKey of pendingPhoneKeys) {
|
||||
const name = resolved.get(phoneKey)?.trim() || undefined;
|
||||
const name = normalizeOptionalString(resolved.get(phoneKey));
|
||||
writeCacheEntry(phoneKey, name, nowMs);
|
||||
if (name) {
|
||||
cachedNames.set(phoneKey, name);
|
||||
|
||||
@@ -146,7 +146,7 @@ export function registerBrowserCookiesAndStorageCommands(
|
||||
method: "GET",
|
||||
path: `/storage/${kind}`,
|
||||
query: {
|
||||
key: key?.trim() || undefined,
|
||||
key: normalizeOptionalString(key),
|
||||
targetId,
|
||||
profile,
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import Ajv from "ajv";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
formatXHighModelHint,
|
||||
normalizeThinkLevel,
|
||||
@@ -96,8 +97,8 @@ export function createLlmTaskTool(api: OpenClawPluginApi) {
|
||||
const defaultsModel = api.config?.agents?.defaults?.model;
|
||||
const primary =
|
||||
typeof defaultsModel === "string"
|
||||
? defaultsModel.trim()
|
||||
: (defaultsModel?.primary?.trim() ?? undefined);
|
||||
? normalizeOptionalString(defaultsModel)
|
||||
: normalizeOptionalString(defaultsModel?.primary);
|
||||
const primaryProvider = typeof primary === "string" ? primary.split("/")[0] : undefined;
|
||||
const primaryModel =
|
||||
typeof primary === "string" ? primary.split("/").slice(1).join("/") : undefined;
|
||||
|
||||
@@ -81,7 +81,7 @@ export function renderWikiMarkdown(params: {
|
||||
|
||||
export function extractTitleFromMarkdown(body: string): string | undefined {
|
||||
const match = body.match(/^#\s+(.+?)\s*$/m);
|
||||
return match?.[1]?.trim() || undefined;
|
||||
return normalizeOptionalString(match?.[1]);
|
||||
}
|
||||
|
||||
export function normalizeSourceIds(value: unknown): string[] {
|
||||
|
||||
@@ -5,6 +5,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
|
||||
import { resolveStateDir as resolvePluginStateDir } from "openclaw/plugin-sdk/state-paths";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { normalizeZaloReactionIcon } from "./reaction.js";
|
||||
import type {
|
||||
ZaloAuthStatus,
|
||||
@@ -500,7 +501,7 @@ function resolveUploadedVoiceAsset(
|
||||
continue;
|
||||
}
|
||||
if (fileType === "others" || fileType === "video") {
|
||||
return { fileUrl, fileName: item.fileName?.trim() || undefined };
|
||||
return { fileUrl, fileName: normalizeOptionalString(item.fileName) };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@@ -963,7 +964,8 @@ export async function listZaloGroupMembers(
|
||||
continue;
|
||||
}
|
||||
currentById.set(id, {
|
||||
displayName: member.dName?.trim() || member.zaloName?.trim() || undefined,
|
||||
displayName:
|
||||
normalizeOptionalString(member.dName) ?? normalizeOptionalString(member.zaloName),
|
||||
avatar: member.avatar || undefined,
|
||||
});
|
||||
}
|
||||
@@ -990,7 +992,9 @@ export async function listZaloGroupMembers(
|
||||
continue;
|
||||
}
|
||||
profileMap.set(id, {
|
||||
displayName: profileValue.displayName?.trim() || profileValue.zaloName?.trim() || undefined,
|
||||
displayName:
|
||||
normalizeOptionalString(profileValue.displayName) ??
|
||||
normalizeOptionalString(profileValue.zaloName),
|
||||
avatar: profileValue.avatar || undefined,
|
||||
});
|
||||
}
|
||||
@@ -1024,7 +1028,7 @@ export async function resolveZaloGroupContext(
|
||||
| undefined;
|
||||
const context: ZaloGroupContext = {
|
||||
groupId: normalizedGroupId,
|
||||
name: groupInfo?.name?.trim() || undefined,
|
||||
name: normalizeOptionalString(groupInfo?.name),
|
||||
members: extractGroupMembersFromInfo(groupInfo),
|
||||
};
|
||||
writeCachedGroupContext(profile, context);
|
||||
|
||||
@@ -3,6 +3,7 @@ import crypto from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { normalizeOptionalString } from "../src/shared/string-coerce.ts";
|
||||
|
||||
type Args = {
|
||||
agentId: string;
|
||||
@@ -36,7 +37,7 @@ const parseArgs = (): Args => {
|
||||
continue;
|
||||
}
|
||||
if (arg === "--session-key" && args[i + 1]) {
|
||||
sessionKey = String(args[++i]).trim() || undefined;
|
||||
sessionKey = normalizeOptionalString(String(args[++i]));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { execFileSync } from "node:child_process";
|
||||
import { mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { normalizeOptionalString } from "../../src/shared/string-coerce.ts";
|
||||
import { parseReleaseVersion } from "../openclaw-npm-release-check.ts";
|
||||
import { resolveNpmPublishPlan } from "./npm-publish-plan.mjs";
|
||||
|
||||
@@ -270,7 +271,7 @@ export function collectPublishablePluginPackages(
|
||||
version,
|
||||
channel: parsedVersion.channel,
|
||||
publishTag: resolveNpmPublishPlan(version).publishTag,
|
||||
installNpmSpec: packageJson.openclaw?.install?.npmSpec?.trim() || undefined,
|
||||
installNpmSpec: normalizeOptionalString(packageJson.openclaw?.install?.npmSpec),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createHash, randomBytes } from "node:crypto";
|
||||
import type { OAuthCredentials } from "@mariozechner/pi-ai";
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
|
||||
export const CHUTES_OAUTH_ISSUER = "https://api.chutes.ai";
|
||||
export const CHUTES_AUTHORIZE_ENDPOINT = `${CHUTES_OAUTH_ISSUER}/idp/authorize`;
|
||||
@@ -66,8 +67,8 @@ export function parseOAuthCallbackInput(
|
||||
}
|
||||
}
|
||||
|
||||
const code = url.searchParams.get("code")?.trim();
|
||||
const state = url.searchParams.get("state")?.trim();
|
||||
const code = normalizeOptionalString(url.searchParams.get("code"));
|
||||
const state = normalizeOptionalString(url.searchParams.get("state"));
|
||||
if (!code) {
|
||||
return { error: "Missing 'code' parameter in URL" };
|
||||
}
|
||||
@@ -181,7 +182,7 @@ export async function refreshChutesTokens(params: {
|
||||
if (!clientId) {
|
||||
throw new Error("Missing CHUTES_CLIENT_ID for Chutes OAuth refresh (set env var or re-auth).");
|
||||
}
|
||||
const clientSecret = process.env.CHUTES_CLIENT_SECRET?.trim() || undefined;
|
||||
const clientSecret = normalizeOptionalString(process.env.CHUTES_CLIENT_SECRET);
|
||||
|
||||
const body = new URLSearchParams({
|
||||
grant_type: "refresh_token",
|
||||
|
||||
@@ -156,7 +156,7 @@ export function buildEmbeddedRunPayloads(params: {
|
||||
})
|
||||
: undefined;
|
||||
const rawErrorMessage = lastAssistantErrored
|
||||
? params.lastAssistant?.errorMessage?.trim() || undefined
|
||||
? normalizeOptionalString(params.lastAssistant?.errorMessage)
|
||||
: undefined;
|
||||
const rawErrorFingerprint = rawErrorMessage
|
||||
? getApiErrorPayloadFingerprint(rawErrorMessage)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
|
||||
export const testServiceAuditCodes = {
|
||||
gatewayEntrypointMismatch: "gateway-entrypoint-mismatch",
|
||||
gatewayTokenMismatch: "gateway-token-mismatch",
|
||||
@@ -11,5 +13,5 @@ export function readEmbeddedGatewayTokenForTest(
|
||||
) {
|
||||
return command?.environmentValueSources?.OPENCLAW_GATEWAY_TOKEN === "file"
|
||||
? undefined
|
||||
: command?.environment?.OPENCLAW_GATEWAY_TOKEN?.trim() || undefined;
|
||||
: normalizeOptionalString(command?.environment?.OPENCLAW_GATEWAY_TOKEN);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user