From ce19b6bf6a3d8472d443b79e4b265fa73bee12b8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 7 Apr 2026 07:45:05 +0100 Subject: [PATCH] refactor: dedupe channel extension readers --- .../cloudflare-ai-gateway/catalog-provider.ts | 18 ++++++++++-------- extensions/cloudflare-ai-gateway/index.ts | 19 ++++++++++++------- extensions/googlechat/src/accounts.ts | 7 ++++--- extensions/googlechat/src/monitor-access.ts | 3 ++- extensions/slack/src/approval-native.ts | 4 +++- extensions/slack/src/channel.ts | 5 +++-- .../src/monitor/message-handler/prepare.ts | 5 +++-- extensions/slack/src/outbound-adapter.ts | 7 ++++--- extensions/slack/src/resolve-users.ts | 13 ++++++++----- extensions/slack/src/setup-core.ts | 5 +++-- .../slack/src/threading-tool-context.ts | 3 ++- extensions/telegram/src/approval-native.ts | 4 +++- extensions/telegram/src/bot.ts | 3 ++- extensions/telegram/src/bot/body-helpers.ts | 10 +++++----- extensions/telegram/src/channel.ts | 3 ++- extensions/telegram/src/send.ts | 6 +++--- .../telegram/src/threading-tool-context.ts | 5 +++-- .../voice-call/src/provider-selection.ts | 4 +++- 18 files changed, 75 insertions(+), 49 deletions(-) diff --git a/extensions/cloudflare-ai-gateway/catalog-provider.ts b/extensions/cloudflare-ai-gateway/catalog-provider.ts index 47732e1129c..93fbf00f7a7 100644 --- a/extensions/cloudflare-ai-gateway/catalog-provider.ts +++ b/extensions/cloudflare-ai-gateway/catalog-provider.ts @@ -4,6 +4,7 @@ import { resolveNonEnvSecretRefApiKeyMarker, } from "openclaw/plugin-sdk/provider-auth"; import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { buildCloudflareAiGatewayModelDefinition, resolveCloudflareAiGatewayBaseUrl, @@ -21,12 +22,11 @@ export function resolveCloudflareAiGatewayApiKey( } const keyRef = coerceSecretRef(cred.keyRef); - if (keyRef && keyRef.id.trim()) { - return keyRef.source === "env" - ? keyRef.id.trim() - : resolveNonEnvSecretRefApiKeyMarker(keyRef.source); + const keyRefId = normalizeOptionalString(keyRef?.id); + if (keyRef && keyRefId) { + return keyRef.source === "env" ? keyRefId : resolveNonEnvSecretRefApiKeyMarker(keyRef.source); } - return cred.key?.trim() || undefined; + return normalizeOptionalString(cred.key); } export function resolveCloudflareAiGatewayMetadata(cred: CloudflareAiGatewayCredential): { @@ -37,8 +37,8 @@ export function resolveCloudflareAiGatewayMetadata(cred: CloudflareAiGatewayCred return {}; } return { - accountId: cred.metadata?.accountId?.trim() || undefined, - gatewayId: cred.metadata?.gatewayId?.trim() || undefined, + accountId: normalizeOptionalString(cred.metadata?.accountId), + gatewayId: normalizeOptionalString(cred.metadata?.gatewayId), }; } @@ -46,7 +46,9 @@ export function buildCloudflareAiGatewayCatalogProvider(params: { credential: CloudflareAiGatewayCredential; envApiKey?: string; }): ModelProviderConfig | null { - const apiKey = params.envApiKey?.trim() || resolveCloudflareAiGatewayApiKey(params.credential); + const apiKey = + normalizeOptionalString(params.envApiKey) ?? + resolveCloudflareAiGatewayApiKey(params.credential); if (!apiKey) { return null; } diff --git a/extensions/cloudflare-ai-gateway/index.ts b/extensions/cloudflare-ai-gateway/index.ts index 0582a87cd42..569edde6ed9 100644 --- a/extensions/cloudflare-ai-gateway/index.ts +++ b/extensions/cloudflare-ai-gateway/index.ts @@ -11,6 +11,7 @@ import { upsertAuthProfile, validateApiKeyInput, } from "openclaw/plugin-sdk/provider-auth"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { buildCloudflareAiGatewayCatalogProvider } from "./catalog-provider.js"; import { CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF } from "./models.js"; import { applyCloudflareAiGatewayConfig, buildCloudflareAiGatewayConfigPatch } from "./onboard.js"; @@ -33,8 +34,8 @@ async function resolveCloudflareGatewayMetadataInteractive(ctx: { }) => Promise; }; }) { - let accountId = ctx.accountId?.trim() ?? ""; - let gatewayId = ctx.gatewayId?.trim() ?? ""; + let accountId = normalizeOptionalString(ctx.accountId) ?? ""; + let gatewayId = normalizeOptionalString(ctx.gatewayId) ?? ""; if (!accountId) { const value = await ctx.prompter.text({ message: "Enter Cloudflare Account ID", @@ -136,10 +137,12 @@ export default definePluginEntry({ const storedMetadata = authStore.profiles[PROFILE_ID]?.type === "api_key" ? { - accountId: - authStore.profiles[PROFILE_ID]?.metadata?.accountId?.trim() || undefined, - gatewayId: - authStore.profiles[PROFILE_ID]?.metadata?.gatewayId?.trim() || undefined, + accountId: normalizeOptionalString( + authStore.profiles[PROFILE_ID]?.metadata?.accountId, + ), + gatewayId: normalizeOptionalString( + authStore.profiles[PROFILE_ID]?.metadata?.gatewayId, + ), } : {}; const accountId = @@ -194,7 +197,9 @@ export default definePluginEntry({ const authStore = ensureAuthProfileStore(ctx.agentDir, { allowKeychainPrompt: false, }); - const envManagedApiKey = ctx.env[PROVIDER_ENV_VAR]?.trim() ? PROVIDER_ENV_VAR : undefined; + const envManagedApiKey = normalizeOptionalString(ctx.env[PROVIDER_ENV_VAR]) + ? PROVIDER_ENV_VAR + : undefined; for (const profileId of listProfilesForProvider(authStore, PROVIDER_ID)) { const provider = buildCloudflareAiGatewayCatalogProvider({ credential: authStore.profiles[profileId], diff --git a/extensions/googlechat/src/accounts.ts b/extensions/googlechat/src/accounts.ts index e2eb0e64024..4bf9112cc32 100644 --- a/extensions/googlechat/src/accounts.ts +++ b/extensions/googlechat/src/accounts.ts @@ -8,6 +8,7 @@ import { } from "openclaw/plugin-sdk/account-resolution"; import { safeParseJsonWithSchema, safeParseWithSchema } from "openclaw/plugin-sdk/extension-shared"; import { isSecretRef } from "openclaw/plugin-sdk/secret-input"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { z } from "zod"; import type { GoogleChatAccountConfig } from "./types.config.js"; @@ -103,7 +104,7 @@ function resolveCredentialsFromConfig(params: { ); } - const file = account.serviceAccountFile?.trim(); + const file = normalizeOptionalString(account.serviceAccountFile); if (file) { return { credentialsFile: file, source: "file" }; } @@ -114,7 +115,7 @@ function resolveCredentialsFromConfig(params: { if (envInline) { return { credentials: envInline, source: "env" }; } - const envFile = process.env[ENV_SERVICE_ACCOUNT_FILE]?.trim(); + const envFile = normalizeOptionalString(process.env[ENV_SERVICE_ACCOUNT_FILE]); if (envFile) { return { credentialsFile: envFile, source: "env" }; } @@ -138,7 +139,7 @@ export function resolveGoogleChatAccount(params: { return { accountId, - name: merged.name?.trim() || undefined, + name: normalizeOptionalString(merged.name), enabled, config: merged, credentialSource: credentials.source, diff --git a/extensions/googlechat/src/monitor-access.ts b/extensions/googlechat/src/monitor-access.ts index 2c8f2d84a8b..bb386dc9d9d 100644 --- a/extensions/googlechat/src/monitor-access.ts +++ b/extensions/googlechat/src/monitor-access.ts @@ -1,4 +1,5 @@ import { resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-inbound"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { GROUP_POLICY_BLOCKED_LABEL, createChannelPairingController, @@ -397,6 +398,6 @@ export async function applyGoogleChatInboundAccessPolicy(params: { ok: true, commandAuthorized, effectiveWasMentioned, - groupSystemPrompt: groupEntry?.systemPrompt?.trim() || undefined, + groupSystemPrompt: normalizeOptionalString(groupEntry?.systemPrompt), }; } diff --git a/extensions/slack/src/approval-native.ts b/extensions/slack/src/approval-native.ts index 3c872d8839c..392e82a5e7f 100644 --- a/extensions/slack/src/approval-native.ts +++ b/extensions/slack/src/approval-native.ts @@ -7,6 +7,7 @@ import { createChannelNativeOriginTargetResolver, } from "openclaw/plugin-sdk/approval-native-runtime"; import type { ExecApprovalRequest, PluginApprovalRequest } from "openclaw/plugin-sdk/infra-runtime"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { listSlackAccountIds } from "./accounts.js"; import { isSlackApprovalAuthorizedSender } from "./approval-auth.js"; import { @@ -138,7 +139,8 @@ export const slackApprovalCapability = createApproverRestrictedNativeApprovalCap resolveSlackExecApprovalTarget({ cfg, accountId }), requireMatchingTurnSourceChannel: true, resolveSuppressionAccountId: ({ target, request }) => - target.accountId?.trim() || request.request.turnSourceAccountId?.trim() || undefined, + normalizeOptionalString(target.accountId) ?? + normalizeOptionalString(request.request.turnSourceAccountId), resolveOriginTarget: resolveSlackOriginTarget, resolveApproverDmTargets: resolveSlackApproverDmTargets, notifyOriginWhenDmOnly: true, diff --git a/extensions/slack/src/channel.ts b/extensions/slack/src/channel.ts index 05342fdf49e..5926bb618b2 100644 --- a/extensions/slack/src/channel.ts +++ b/extensions/slack/src/channel.ts @@ -28,6 +28,7 @@ import { createDefaultChannelRuntimeState, } from "openclaw/plugin-sdk/status-helpers"; import { resolveTargetsWithOptionalToken } from "openclaw/plugin-sdk/target-resolver-runtime"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { resolveDefaultSlackAccountId, resolveSlackAccount, @@ -97,8 +98,8 @@ function getTokenForOperation( account: ResolvedSlackAccount, operation: "read" | "write", ): string | undefined { - const userToken = account.config.userToken?.trim() || undefined; - const botToken = account.botToken?.trim(); + const userToken = normalizeOptionalString(account.config.userToken); + const botToken = normalizeOptionalString(account.botToken); const allowUserWrites = account.config.userTokenReadOnly === false; if (operation === "read") { return userToken ?? botToken; diff --git a/extensions/slack/src/monitor/message-handler/prepare.ts b/extensions/slack/src/monitor/message-handler/prepare.ts index e743598ec5a..a345be7e178 100644 --- a/extensions/slack/src/monitor/message-handler/prepare.ts +++ b/extensions/slack/src/monitor/message-handler/prepare.ts @@ -25,6 +25,7 @@ import { resolveAgentRoute } from "openclaw/plugin-sdk/routing"; import { resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing"; import { logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env"; import { resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { resolveSlackReplyToMode, type ResolvedSlackAccount } from "../../accounts.js"; import { reactSlackMessage } from "../../actions.js"; import { hasSlackThreadParticipation } from "../../sent-thread-cache.js"; @@ -395,14 +396,14 @@ export async function prepareSlackMessage(params: { ), ]; - let resolvedSenderName = message.username?.trim() || undefined; + let resolvedSenderName = normalizeOptionalString(message.username); const resolveSenderName = async (): Promise => { if (resolvedSenderName) { return resolvedSenderName; } if (message.user) { const sender = await ctx.resolveUserName(message.user); - const normalized = sender?.name?.trim(); + const normalized = normalizeOptionalString(sender?.name); if (normalized) { resolvedSenderName = normalized; return resolvedSenderName; diff --git a/extensions/slack/src/outbound-adapter.ts b/extensions/slack/src/outbound-adapter.ts index 338d89a8032..603df7343dc 100644 --- a/extensions/slack/src/outbound-adapter.ts +++ b/extensions/slack/src/outbound-adapter.ts @@ -17,6 +17,7 @@ import { sendPayloadMediaSequenceAndFinalize, sendTextMediaPayload, } from "openclaw/plugin-sdk/reply-payload"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { resolveSlackAccount } from "./accounts.js"; import { parseSlackBlocksInput } from "./blocks-input.js"; import { buildSlackInteractiveBlocks, type SlackBlock } from "./blocks-render.js"; @@ -48,9 +49,9 @@ function resolveSlackSendIdentity(identity?: OutboundIdentity): SlackSendIdentit if (!identity) { return undefined; } - const username = identity.name?.trim() || undefined; - const iconUrl = identity.avatarUrl?.trim() || undefined; - const rawEmoji = identity.emoji?.trim(); + const username = normalizeOptionalString(identity.name); + const iconUrl = normalizeOptionalString(identity.avatarUrl); + const rawEmoji = normalizeOptionalString(identity.emoji); const iconEmoji = !iconUrl && rawEmoji && /^:[^:\s]+:$/.test(rawEmoji) ? rawEmoji : undefined; if (!username && !iconUrl && !iconEmoji) { return undefined; diff --git a/extensions/slack/src/resolve-users.ts b/extensions/slack/src/resolve-users.ts index 340bfa0d6bb..9f41b9d6571 100644 --- a/extensions/slack/src/resolve-users.ts +++ b/extensions/slack/src/resolve-users.ts @@ -1,4 +1,5 @@ import type { WebClient } from "@slack/web-api"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { createSlackWebClient } from "./client.js"; import { collectSlackCursorItems, @@ -74,8 +75,8 @@ async function listSlackUsers(client: WebClient): Promise { collectPageItems: (res) => (res.members ?? []) .map((member) => { - const id = member.id?.trim(); - const name = member.name?.trim(); + const id = normalizeOptionalString(member.id); + const name = normalizeOptionalString(member.name); if (!id || !name) { return null; } @@ -83,9 +84,11 @@ async function listSlackUsers(client: WebClient): Promise { return { id, name, - displayName: profile.display_name?.trim() || undefined, - realName: profile.real_name?.trim() || member.real_name?.trim() || undefined, - email: profile.email?.trim()?.toLowerCase() || undefined, + displayName: normalizeOptionalString(profile.display_name), + realName: + normalizeOptionalString(profile.real_name) ?? + normalizeOptionalString(member.real_name), + email: normalizeOptionalString(profile.email)?.toLowerCase(), deleted: Boolean(member.deleted), isBot: Boolean(member.is_bot), isAppUser: Boolean(member.is_app_user), diff --git a/extensions/slack/src/setup-core.ts b/extensions/slack/src/setup-core.ts index a06101a69bc..36a26cee171 100644 --- a/extensions/slack/src/setup-core.ts +++ b/extensions/slack/src/setup-core.ts @@ -16,6 +16,7 @@ import { type OpenClawConfig, } from "openclaw/plugin-sdk/setup-runtime"; import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { inspectSlackAccount } from "./account-inspect.js"; import { resolveSlackAccount } from "./accounts.js"; import { @@ -97,10 +98,10 @@ function createSlackTokenCredential(params: { return { accountConfigured: Boolean(resolvedValue) || hasConfiguredSecretInput(configuredValue), hasConfiguredValue: hasConfiguredSecretInput(configuredValue), - resolvedValue: resolvedValue?.trim() || undefined, + resolvedValue: normalizeOptionalString(resolvedValue), envValue: accountId === DEFAULT_ACCOUNT_ID - ? process.env[params.preferredEnvVar]?.trim() + ? normalizeOptionalString(process.env[params.preferredEnvVar]) : undefined, }; }, diff --git a/extensions/slack/src/threading-tool-context.ts b/extensions/slack/src/threading-tool-context.ts index a6b59189dee..a10f5604b64 100644 --- a/extensions/slack/src/threading-tool-context.ts +++ b/extensions/slack/src/threading-tool-context.ts @@ -3,6 +3,7 @@ import type { ChannelThreadingToolContext, } from "openclaw/plugin-sdk/channel-contract"; import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { resolveSlackAccount, resolveSlackReplyToMode } from "./accounts.js"; export function buildSlackThreadingToolContext(params: { @@ -24,7 +25,7 @@ export function buildSlackThreadingToolContext(params: { // to NativeChannelId (the raw Slack channel id, e.g. "D…"). const currentChannelId = params.context.To?.startsWith("channel:") ? params.context.To.slice("channel:".length) - : params.context.NativeChannelId?.trim() || undefined; + : normalizeOptionalString(params.context.NativeChannelId); return { currentChannelId, currentThreadTs: threadId != null ? String(threadId) : undefined, diff --git a/extensions/telegram/src/approval-native.ts b/extensions/telegram/src/approval-native.ts index a4101c4551d..0808eb9ec8f 100644 --- a/extensions/telegram/src/approval-native.ts +++ b/extensions/telegram/src/approval-native.ts @@ -8,6 +8,7 @@ import { } from "openclaw/plugin-sdk/approval-native-runtime"; import type { ChannelApprovalCapability } from "openclaw/plugin-sdk/channel-contract"; import type { ExecApprovalRequest, PluginApprovalRequest } from "openclaw/plugin-sdk/infra-runtime"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { listTelegramAccountIds } from "./accounts.js"; import { getTelegramExecApprovalApprovers, @@ -110,7 +111,8 @@ const telegramNativeApprovalCapability = createApproverRestrictedNativeApprovalC resolveTelegramExecApprovalTarget({ cfg, accountId }), requireMatchingTurnSourceChannel: true, resolveSuppressionAccountId: ({ target, request }) => - target.accountId?.trim() || request.request.turnSourceAccountId?.trim() || undefined, + normalizeOptionalString(target.accountId) ?? + normalizeOptionalString(request.request.turnSourceAccountId), resolveOriginTarget: resolveTelegramOriginTarget, resolveApproverDmTargets: resolveTelegramApproverDmTargets, }); diff --git a/extensions/telegram/src/bot.ts b/extensions/telegram/src/bot.ts index ba39dedde52..399ea7214fa 100644 --- a/extensions/telegram/src/bot.ts +++ b/extensions/telegram/src/bot.ts @@ -20,6 +20,7 @@ import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtim import { getChildLogger } from "openclaw/plugin-sdk/runtime-env"; import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env"; import { createNonExitingRuntime, type RuntimeEnv } from "openclaw/plugin-sdk/runtime-env"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { resolveTelegramAccount } from "./accounts.js"; import { defaultTelegramBotDeps, type TelegramBotDeps } from "./bot-deps.js"; import { registerTelegramHandlers } from "./bot-handlers.js"; @@ -258,7 +259,7 @@ export function createTelegramBot(opts: TelegramBotOptions): TelegramBotInstance typeof telegramCfg?.timeoutSeconds === "number" && Number.isFinite(telegramCfg.timeoutSeconds) ? Math.max(1, Math.floor(telegramCfg.timeoutSeconds)) : undefined; - const apiRoot = telegramCfg.apiRoot?.trim() || undefined; + const apiRoot = normalizeOptionalString(telegramCfg.apiRoot); const client: ApiClientOptions | undefined = finalFetch || timeoutSeconds || apiRoot ? { diff --git a/extensions/telegram/src/bot/body-helpers.ts b/extensions/telegram/src/bot/body-helpers.ts index 815ac828895..6dbc2da9451 100644 --- a/extensions/telegram/src/bot/body-helpers.ts +++ b/extensions/telegram/src/bot/body-helpers.ts @@ -186,7 +186,7 @@ export type TelegramForwardedContext = { function normalizeForwardedUserLabel(user: User) { const name = [user.first_name, user.last_name].filter(Boolean).join(" ").trim(); - const username = user.username?.trim() || undefined; + const username = normalizeOptionalString(user.username); const id = String(user.id); const display = (name && username @@ -196,8 +196,8 @@ function normalizeForwardedUserLabel(user: User) { } function normalizeForwardedChatLabel(chat: Chat, fallbackKind: "chat" | "channel") { - const title = chat.title?.trim() || undefined; - const username = chat.username?.trim() || undefined; + const title = normalizeOptionalString(chat.title); + const username = normalizeOptionalString(chat.username); const id = String(chat.id); const display = title || (username ? `@${username}` : undefined) || `${fallbackKind}:${id}`; return { display, title, username, id }; @@ -251,9 +251,9 @@ function buildForwardedContextFromChat(params: { if (!display) { return null; } - const signature = params.signature?.trim() || undefined; + const signature = normalizeOptionalString(params.signature); const from = signature ? `${display} (${signature})` : display; - const chatType = (params.chat.type?.trim() || undefined) as Chat["type"] | undefined; + const chatType = normalizeOptionalString(params.chat.type) as Chat["type"] | undefined; return { from, date: params.date, diff --git a/extensions/telegram/src/channel.ts b/extensions/telegram/src/channel.ts index bbac5494be2..9b568fa370e 100644 --- a/extensions/telegram/src/channel.ts +++ b/extensions/telegram/src/channel.ts @@ -32,6 +32,7 @@ import { createComputedAccountStatusAdapter, createDefaultChannelRuntimeState, } from "openclaw/plugin-sdk/status-helpers"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { resolveTelegramAccount, type ResolvedTelegramAccount } from "./accounts.js"; import { resolveTelegramAutoThreadId } from "./action-threading.js"; import { lookupTelegramChatId } from "./api-fetch.js"; @@ -331,7 +332,7 @@ function resolveTelegramInboundConversation(params: { parsedTarget.messageThreadId != null ? String(parsedTarget.messageThreadId) : params.threadId != null - ? String(params.threadId).trim() || undefined + ? normalizeOptionalString(String(params.threadId)) : undefined; if (threadId) { const parsedTopic = parseTelegramTopicConversation({ diff --git a/extensions/telegram/src/send.ts b/extensions/telegram/src/send.ts index 7db986639aa..862e11c3ee7 100644 --- a/extensions/telegram/src/send.ts +++ b/extensions/telegram/src/send.ts @@ -7,7 +7,7 @@ import { recordChannelActivity } from "openclaw/plugin-sdk/infra-runtime"; import { createTelegramRetryRunner, type RetryConfig } from "openclaw/plugin-sdk/retry-runtime"; import { createSubsystemLogger, logVerbose } from "openclaw/plugin-sdk/runtime-env"; import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime"; -import { redactSensitiveText } from "openclaw/plugin-sdk/text-runtime"; +import { normalizeOptionalString, redactSensitiveText } from "openclaw/plugin-sdk/text-runtime"; import { type ResolvedTelegramAccount, resolveTelegramAccount } from "./accounts.js"; import { withTelegramApiErrorLogging } from "./api-logging.js"; import { buildTelegramThreadParams, buildTypingThreadParams } from "./bot/helpers.js"; @@ -265,9 +265,9 @@ function resolveTelegramClientOptions( return telegramClientOptionsCache.get(cacheKey); } - const proxyUrl = account.config.proxy?.trim(); + const proxyUrl = normalizeOptionalString(account.config.proxy); const proxyFetch = proxyUrl ? makeProxyFetch(proxyUrl) : undefined; - const apiRoot = account.config.apiRoot?.trim() || undefined; + const apiRoot = normalizeOptionalString(account.config.apiRoot); const fetchImpl = resolveTelegramFetch(proxyFetch, { network: account.config.network, }); diff --git a/extensions/telegram/src/threading-tool-context.ts b/extensions/telegram/src/threading-tool-context.ts index 1358dd4bf73..dc9b248686e 100644 --- a/extensions/telegram/src/threading-tool-context.ts +++ b/extensions/telegram/src/threading-tool-context.ts @@ -3,13 +3,14 @@ import type { ChannelThreadingToolContext, } from "openclaw/plugin-sdk/channel-contract"; import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { parseTelegramTarget } from "./targets.js"; function resolveTelegramToolContextThreadId(context: ChannelThreadingContext): string | undefined { if (context.MessageThreadId != null) { return String(context.MessageThreadId); } - const currentChannelId = context.To?.trim(); + const currentChannelId = normalizeOptionalString(context.To); if (!currentChannelId) { return undefined; } @@ -27,7 +28,7 @@ export function buildTelegramThreadingToolContext(params: { void params.accountId; return { - currentChannelId: params.context.To?.trim() || undefined, + currentChannelId: normalizeOptionalString(params.context.To), currentThreadTs: resolveTelegramToolContextThreadId(params.context), hasRepliedRef: params.hasRepliedRef, }; diff --git a/extensions/voice-call/src/provider-selection.ts b/extensions/voice-call/src/provider-selection.ts index c1eafdd423c..eec5cadfdf0 100644 --- a/extensions/voice-call/src/provider-selection.ts +++ b/extensions/voice-call/src/provider-selection.ts @@ -1,3 +1,5 @@ +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; + type AutoSelectableProvider = { autoSelectOrder?: number; }; @@ -11,7 +13,7 @@ export function selectConfiguredOrAutoProvider