mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-03 21:20:23 +00:00
refactor: dedupe trimmed string readers
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
||||
wrapWebContent,
|
||||
writeCachedSearchPayload,
|
||||
} from "openclaw/plugin-sdk/provider-web-search";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
isNativeMoonshotBaseUrl,
|
||||
MOONSHOT_BASE_URL,
|
||||
@@ -92,7 +93,7 @@ function resolveKimiApiKey(kimi?: KimiConfig): string | undefined {
|
||||
}
|
||||
|
||||
function resolveKimiModel(kimi?: KimiConfig): string {
|
||||
const model = typeof kimi?.model === "string" ? kimi.model.trim() : "";
|
||||
const model = normalizeOptionalString(kimi?.model) ?? "";
|
||||
return model || DEFAULT_KIMI_SEARCH_MODEL;
|
||||
}
|
||||
|
||||
@@ -101,7 +102,7 @@ function trimTrailingSlashes(url: string): string {
|
||||
}
|
||||
|
||||
function resolveKimiBaseUrl(kimi?: KimiConfig, openClawConfig?: OpenClawConfig): string {
|
||||
const explicitBaseUrl = typeof kimi?.baseUrl === "string" ? kimi.baseUrl.trim() : "";
|
||||
const explicitBaseUrl = normalizeOptionalString(kimi?.baseUrl) ?? "";
|
||||
if (explicitBaseUrl) {
|
||||
return trimTrailingSlashes(explicitBaseUrl) || DEFAULT_KIMI_BASE_URL;
|
||||
}
|
||||
@@ -141,12 +142,14 @@ function extractKimiCitations(data: KimiSearchResponse): string[] {
|
||||
search_results?: Array<{ url?: string }>;
|
||||
url?: string;
|
||||
};
|
||||
if (typeof parsed.url === "string" && parsed.url.trim()) {
|
||||
citations.push(parsed.url.trim());
|
||||
const parsedUrl = normalizeOptionalString(parsed.url);
|
||||
if (parsedUrl) {
|
||||
citations.push(parsedUrl);
|
||||
}
|
||||
for (const result of parsed.search_results ?? []) {
|
||||
if (typeof result.url === "string" && result.url.trim()) {
|
||||
citations.push(result.url.trim());
|
||||
const resultUrl = normalizeOptionalString(result.url);
|
||||
if (resultUrl) {
|
||||
citations.push(resultUrl);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
@@ -352,12 +355,10 @@ async function runKimiSearchProviderSetup(
|
||||
ctx: WebSearchProviderSetupContext,
|
||||
): Promise<WebSearchProviderSetupContext["config"]> {
|
||||
const existingPluginConfig = resolveProviderWebSearchPluginConfig(ctx.config, "moonshot");
|
||||
const existingBaseUrl =
|
||||
typeof existingPluginConfig?.baseUrl === "string" ? existingPluginConfig.baseUrl.trim() : "";
|
||||
const existingBaseUrl = normalizeOptionalString(existingPluginConfig?.baseUrl) ?? "";
|
||||
// Normalize trailing slashes so initialValue matches canonical option values.
|
||||
const normalizedBaseUrl = existingBaseUrl.replace(/\/+$/, "");
|
||||
const existingModel =
|
||||
typeof existingPluginConfig?.model === "string" ? existingPluginConfig.model.trim() : "";
|
||||
const existingModel = normalizeOptionalString(existingPluginConfig?.model) ?? "";
|
||||
|
||||
// Region selection (baseUrl)
|
||||
const isCustomBaseUrl = normalizedBaseUrl && !isNativeMoonshotBaseUrl(normalizedBaseUrl);
|
||||
|
||||
@@ -72,7 +72,7 @@ function resolveActionTarget(
|
||||
}
|
||||
|
||||
function resolveActionMessageId(params: Record<string, unknown>): string {
|
||||
return typeof params.messageId === "string" ? params.messageId.trim() : "";
|
||||
return normalizeOptionalString(params.messageId) ?? "";
|
||||
}
|
||||
|
||||
function resolveActionPinnedMessageId(params: Record<string, unknown>): string {
|
||||
@@ -84,7 +84,7 @@ function resolveActionPinnedMessageId(params: Record<string, unknown>): string {
|
||||
}
|
||||
|
||||
function resolveActionQuery(params: Record<string, unknown>): string {
|
||||
return typeof params.query === "string" ? params.query.trim() : "";
|
||||
return normalizeOptionalString(params.query) ?? "";
|
||||
}
|
||||
|
||||
function resolveActionContent(params: Record<string, unknown>): string {
|
||||
@@ -413,7 +413,7 @@ export const msteamsActionsAdapter: NonNullable<ChannelPlugin["actions"]> = {
|
||||
toolParams: ctx.params,
|
||||
currentChannelId: ctx.toolContext?.currentChannelId,
|
||||
run: async (target) => {
|
||||
const emoji = typeof ctx.params.emoji === "string" ? ctx.params.emoji.trim() : "";
|
||||
const emoji = normalizeOptionalString(ctx.params.emoji) ?? "";
|
||||
const remove = typeof ctx.params.remove === "boolean" ? ctx.params.remove : false;
|
||||
if (!emoji) {
|
||||
return {
|
||||
@@ -487,7 +487,7 @@ export const msteamsActionsAdapter: NonNullable<ChannelPlugin["actions"]> = {
|
||||
return actionError("Search requires a target (to) and query.");
|
||||
}
|
||||
const limit = typeof ctx.params.limit === "number" ? ctx.params.limit : undefined;
|
||||
const from = typeof ctx.params.from === "string" ? ctx.params.from.trim() : undefined;
|
||||
const from = normalizeOptionalString(ctx.params.from);
|
||||
const { searchMessagesMSTeams } = await loadMSTeamsChannelRuntime();
|
||||
const result = await searchMessagesMSTeams({
|
||||
cfg: ctx.cfg,
|
||||
@@ -502,7 +502,7 @@ export const msteamsActionsAdapter: NonNullable<ChannelPlugin["actions"]> = {
|
||||
}
|
||||
|
||||
if (ctx.action === "member-info") {
|
||||
const userId = typeof ctx.params.userId === "string" ? ctx.params.userId.trim() : "";
|
||||
const userId = normalizeOptionalString(ctx.params.userId) ?? "";
|
||||
if (!userId) {
|
||||
return actionError("member-info requires a userId.");
|
||||
}
|
||||
@@ -512,7 +512,7 @@ export const msteamsActionsAdapter: NonNullable<ChannelPlugin["actions"]> = {
|
||||
}
|
||||
|
||||
if (ctx.action === "channel-list") {
|
||||
const teamId = typeof ctx.params.teamId === "string" ? ctx.params.teamId.trim() : "";
|
||||
const teamId = normalizeOptionalString(ctx.params.teamId) ?? "";
|
||||
if (!teamId) {
|
||||
return actionError("channel-list requires a teamId.");
|
||||
}
|
||||
@@ -522,8 +522,8 @@ export const msteamsActionsAdapter: NonNullable<ChannelPlugin["actions"]> = {
|
||||
}
|
||||
|
||||
if (ctx.action === "channel-info") {
|
||||
const teamId = typeof ctx.params.teamId === "string" ? ctx.params.teamId.trim() : "";
|
||||
const channelId = typeof ctx.params.channelId === "string" ? ctx.params.channelId.trim() : "";
|
||||
const teamId = normalizeOptionalString(ctx.params.teamId) ?? "";
|
||||
const channelId = normalizeOptionalString(ctx.params.channelId) ?? "";
|
||||
if (!teamId || !channelId) {
|
||||
return actionError("channel-info requires teamId and channelId.");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "openclaw/plugin-sdk/text-runtime";
|
||||
import { getMSTeamsRuntime } from "../runtime.js";
|
||||
import { downloadAndStoreMSTeamsRemoteMedia } from "./remote-media.js";
|
||||
import {
|
||||
@@ -29,21 +32,20 @@ type DownloadCandidate = {
|
||||
|
||||
function resolveDownloadCandidate(att: MSTeamsAttachmentLike): DownloadCandidate | null {
|
||||
const contentType = normalizeContentType(att.contentType);
|
||||
const name = typeof att.name === "string" ? att.name.trim() : "";
|
||||
const name = normalizeOptionalString(att.name) ?? "";
|
||||
|
||||
if (contentType === "application/vnd.microsoft.teams.file.download.info") {
|
||||
if (!isRecord(att.content)) {
|
||||
return null;
|
||||
}
|
||||
const downloadUrl =
|
||||
typeof att.content.downloadUrl === "string" ? att.content.downloadUrl.trim() : "";
|
||||
const downloadUrl = normalizeOptionalString(att.content.downloadUrl) ?? "";
|
||||
if (!downloadUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fileType = typeof att.content.fileType === "string" ? att.content.fileType.trim() : "";
|
||||
const uniqueId = typeof att.content.uniqueId === "string" ? att.content.uniqueId.trim() : "";
|
||||
const fileName = typeof att.content.fileName === "string" ? att.content.fileName.trim() : "";
|
||||
const fileType = normalizeOptionalString(att.content.fileType) ?? "";
|
||||
const uniqueId = normalizeOptionalString(att.content.uniqueId) ?? "";
|
||||
const fileName = normalizeOptionalString(att.content.fileName) ?? "";
|
||||
|
||||
const fileHint = name || fileName || (uniqueId && fileType ? `${uniqueId}.${fileType}` : "");
|
||||
return {
|
||||
@@ -58,7 +60,7 @@ function resolveDownloadCandidate(att: MSTeamsAttachmentLike): DownloadCandidate
|
||||
};
|
||||
}
|
||||
|
||||
const contentUrl = typeof att.contentUrl === "string" ? att.contentUrl.trim() : "";
|
||||
const contentUrl = normalizeOptionalString(att.contentUrl) ?? "";
|
||||
if (!contentUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalLowercaseString,
|
||||
normalizeOptionalString,
|
||||
} from "openclaw/plugin-sdk/text-runtime";
|
||||
import { fetchWithSsrFGuard, type SsrFPolicy } from "../../runtime-api.js";
|
||||
import { getMSTeamsRuntime } from "../runtime.js";
|
||||
@@ -54,7 +55,7 @@ export function buildMSTeamsGraphMessageUrls(params: {
|
||||
const conversationType = normalizeLowercaseStringOrEmpty(params.conversationType ?? "");
|
||||
const messageIdCandidates = new Set<string>();
|
||||
const pushCandidate = (value: string | null | undefined) => {
|
||||
const trimmed = typeof value === "string" ? value.trim() : "";
|
||||
const trimmed = normalizeOptionalString(value) ?? "";
|
||||
if (trimmed) {
|
||||
messageIdCandidates.add(trimmed);
|
||||
}
|
||||
@@ -65,7 +66,7 @@ export function buildMSTeamsGraphMessageUrls(params: {
|
||||
pushCandidate(readNestedString(params.channelData, ["messageId"]));
|
||||
pushCandidate(readNestedString(params.channelData, ["teamsMessageId"]));
|
||||
|
||||
const replyToId = typeof params.replyToId === "string" ? params.replyToId.trim() : "";
|
||||
const replyToId = normalizeOptionalString(params.replyToId) ?? "";
|
||||
|
||||
if (conversationType === "channel") {
|
||||
const teamId =
|
||||
|
||||
@@ -325,7 +325,7 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount, ProbeMSTeamsRe
|
||||
formatCapabilitiesProbe: ({ probe }) => {
|
||||
const teamsProbe = probe as ProbeMSTeamsResult | undefined;
|
||||
const lines: Array<{ text: string; tone?: "error" }> = [];
|
||||
const appId = typeof teamsProbe?.appId === "string" ? teamsProbe.appId.trim() : "";
|
||||
const appId = normalizeOptionalString(teamsProbe?.appId) ?? "";
|
||||
if (appId) {
|
||||
lines.push({ text: `App: ${appId}` });
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
definePluginEntry,
|
||||
type GatewayRequestHandlerOptions,
|
||||
@@ -196,8 +197,8 @@ export default definePluginEntry({
|
||||
};
|
||||
|
||||
const resolveCallMessageRequest = async (params: GatewayRequestHandlerOptions["params"]) => {
|
||||
const callId = typeof params?.callId === "string" ? params.callId.trim() : "";
|
||||
const message = typeof params?.message === "string" ? params.message.trim() : "";
|
||||
const callId = normalizeOptionalString(params?.callId) ?? "";
|
||||
const message = normalizeOptionalString(params?.message) ?? "";
|
||||
if (!callId || !message) {
|
||||
return { error: "callId and message required" } as const;
|
||||
}
|
||||
@@ -257,16 +258,13 @@ export default definePluginEntry({
|
||||
"voicecall.initiate",
|
||||
async ({ params, respond }: GatewayRequestHandlerOptions) => {
|
||||
try {
|
||||
const message = typeof params?.message === "string" ? params.message.trim() : "";
|
||||
const message = normalizeOptionalString(params?.message) ?? "";
|
||||
if (!message) {
|
||||
respond(false, { error: "message required" });
|
||||
return;
|
||||
}
|
||||
const rt = await ensureRuntime();
|
||||
const to =
|
||||
typeof params?.to === "string" && params.to.trim()
|
||||
? params.to.trim()
|
||||
: rt.config.toNumber;
|
||||
const to = normalizeOptionalString(params?.to) ?? rt.config.toNumber;
|
||||
if (!to) {
|
||||
respond(false, { error: "to required" });
|
||||
return;
|
||||
@@ -323,7 +321,7 @@ export default definePluginEntry({
|
||||
"voicecall.end",
|
||||
async ({ params, respond }: GatewayRequestHandlerOptions) => {
|
||||
try {
|
||||
const callId = typeof params?.callId === "string" ? params.callId.trim() : "";
|
||||
const callId = normalizeOptionalString(params?.callId) ?? "";
|
||||
if (!callId) {
|
||||
respond(false, { error: "callId required" });
|
||||
return;
|
||||
@@ -346,11 +344,7 @@ export default definePluginEntry({
|
||||
async ({ params, respond }: GatewayRequestHandlerOptions) => {
|
||||
try {
|
||||
const raw =
|
||||
typeof params?.callId === "string"
|
||||
? params.callId.trim()
|
||||
: typeof params?.sid === "string"
|
||||
? params.sid.trim()
|
||||
: "";
|
||||
normalizeOptionalString(params?.callId) ?? normalizeOptionalString(params?.sid) ?? "";
|
||||
if (!raw) {
|
||||
respond(false, { error: "callId required" });
|
||||
return;
|
||||
@@ -372,8 +366,8 @@ export default definePluginEntry({
|
||||
"voicecall.start",
|
||||
async ({ params, respond }: GatewayRequestHandlerOptions) => {
|
||||
try {
|
||||
const to = typeof params?.to === "string" ? params.to.trim() : "";
|
||||
const message = typeof params?.message === "string" ? params.message.trim() : "";
|
||||
const to = normalizeOptionalString(params?.to) ?? "";
|
||||
const message = normalizeOptionalString(params?.message) ?? "";
|
||||
if (!to) {
|
||||
respond(false, { error: "to required" });
|
||||
return;
|
||||
@@ -408,14 +402,11 @@ export default definePluginEntry({
|
||||
if (typeof params?.action === "string") {
|
||||
switch (params.action) {
|
||||
case "initiate_call": {
|
||||
const message = String(params.message || "").trim();
|
||||
const message = normalizeOptionalString(params.message) ?? "";
|
||||
if (!message) {
|
||||
throw new Error("message required");
|
||||
}
|
||||
const to =
|
||||
typeof params.to === "string" && params.to.trim()
|
||||
? params.to.trim()
|
||||
: rt.config.toNumber;
|
||||
const to = normalizeOptionalString(params.to) ?? rt.config.toNumber;
|
||||
if (!to) {
|
||||
throw new Error("to required");
|
||||
}
|
||||
@@ -432,8 +423,8 @@ export default definePluginEntry({
|
||||
return json({ callId: result.callId, initiated: true });
|
||||
}
|
||||
case "continue_call": {
|
||||
const callId = String(params.callId || "").trim();
|
||||
const message = String(params.message || "").trim();
|
||||
const callId = normalizeOptionalString(params.callId) ?? "";
|
||||
const message = normalizeOptionalString(params.message) ?? "";
|
||||
if (!callId || !message) {
|
||||
throw new Error("callId and message required");
|
||||
}
|
||||
@@ -444,8 +435,8 @@ export default definePluginEntry({
|
||||
return json({ success: true, transcript: result.transcript });
|
||||
}
|
||||
case "speak_to_user": {
|
||||
const callId = String(params.callId || "").trim();
|
||||
const message = String(params.message || "").trim();
|
||||
const callId = normalizeOptionalString(params.callId) ?? "";
|
||||
const message = normalizeOptionalString(params.message) ?? "";
|
||||
if (!callId || !message) {
|
||||
throw new Error("callId and message required");
|
||||
}
|
||||
@@ -456,7 +447,7 @@ export default definePluginEntry({
|
||||
return json({ success: true });
|
||||
}
|
||||
case "end_call": {
|
||||
const callId = String(params.callId || "").trim();
|
||||
const callId = normalizeOptionalString(params.callId) ?? "";
|
||||
if (!callId) {
|
||||
throw new Error("callId required");
|
||||
}
|
||||
@@ -467,7 +458,7 @@ export default definePluginEntry({
|
||||
return json({ success: true });
|
||||
}
|
||||
case "get_status": {
|
||||
const callId = String(params.callId || "").trim();
|
||||
const callId = normalizeOptionalString(params.callId) ?? "";
|
||||
if (!callId) {
|
||||
throw new Error("callId required");
|
||||
}
|
||||
@@ -480,7 +471,7 @@ export default definePluginEntry({
|
||||
|
||||
const mode = params?.mode ?? "call";
|
||||
if (mode === "status") {
|
||||
const sid = typeof params.sid === "string" ? params.sid.trim() : "";
|
||||
const sid = normalizeOptionalString(params.sid) ?? "";
|
||||
if (!sid) {
|
||||
throw new Error("sid required for status");
|
||||
}
|
||||
@@ -488,18 +479,12 @@ export default definePluginEntry({
|
||||
return json(call ? { found: true, call } : { found: false });
|
||||
}
|
||||
|
||||
const to =
|
||||
typeof params.to === "string" && params.to.trim()
|
||||
? params.to.trim()
|
||||
: rt.config.toNumber;
|
||||
const to = normalizeOptionalString(params.to) ?? rt.config.toNumber;
|
||||
if (!to) {
|
||||
throw new Error("to required for call");
|
||||
}
|
||||
const result = await rt.manager.initiateCall(to, undefined, {
|
||||
message:
|
||||
typeof params.message === "string" && params.message.trim()
|
||||
? params.message.trim()
|
||||
: undefined,
|
||||
message: normalizeOptionalString(params.message),
|
||||
});
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || "initiate failed");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { VoiceCallConfig } from "./config.js";
|
||||
import type { CallManagerContext } from "./manager/context.js";
|
||||
import { processEvent as processManagerEvent } from "./manager/events.js";
|
||||
@@ -287,8 +288,7 @@ export class CallManager {
|
||||
}
|
||||
|
||||
private maybeSpeakInitialMessageOnAnswered(call: CallRecord): void {
|
||||
const initialMessage =
|
||||
typeof call.metadata?.initialMessage === "string" ? call.metadata.initialMessage.trim() : "";
|
||||
const initialMessage = normalizeOptionalString(call.metadata?.initialMessage) ?? "";
|
||||
|
||||
if (!initialMessage) {
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import crypto from "node:crypto";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { PlivoConfig, WebhookSecurityConfig } from "../config.js";
|
||||
import { getHeader } from "../http-headers.js";
|
||||
import type {
|
||||
@@ -130,7 +133,7 @@ export class PlivoProvider implements VoiceCallProvider {
|
||||
ctx: WebhookContext,
|
||||
options?: WebhookParseOptions,
|
||||
): ProviderWebhookParseResult {
|
||||
const flow = typeof ctx.query?.flow === "string" ? ctx.query.flow.trim() : "";
|
||||
const flow = normalizeOptionalString(ctx.query?.flow) ?? "";
|
||||
|
||||
const parsed = this.parseBody(ctx.rawBody);
|
||||
if (!parsed) {
|
||||
@@ -518,10 +521,7 @@ export class PlivoProvider implements VoiceCallProvider {
|
||||
}
|
||||
|
||||
private getCallIdFromQuery(ctx: WebhookContext): string | undefined {
|
||||
const callId =
|
||||
typeof ctx.query?.callId === "string" && ctx.query.callId.trim()
|
||||
? ctx.query.callId.trim()
|
||||
: undefined;
|
||||
const callId = normalizeOptionalString(ctx.query?.callId);
|
||||
return callId || undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import crypto from "node:crypto";
|
||||
import { safeEqualSecret } from "openclaw/plugin-sdk/browser-security-runtime";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { TwilioConfig, WebhookSecurityConfig } from "../config.js";
|
||||
import { getHeader } from "../http-headers.js";
|
||||
import type { MediaStreamHandler } from "../media-stream.js";
|
||||
@@ -46,9 +47,9 @@ function createTwilioRequestDedupeKey(ctx: WebhookContext, verifiedRequestKey?:
|
||||
const callSid = params.get("CallSid") ?? "";
|
||||
const callStatus = params.get("CallStatus") ?? "";
|
||||
const direction = params.get("Direction") ?? "";
|
||||
const callId = typeof ctx.query?.callId === "string" ? ctx.query.callId.trim() : "";
|
||||
const flow = typeof ctx.query?.flow === "string" ? ctx.query.flow.trim() : "";
|
||||
const turnToken = typeof ctx.query?.turnToken === "string" ? ctx.query.turnToken.trim() : "";
|
||||
const callId = normalizeOptionalString(ctx.query?.callId) ?? "";
|
||||
const flow = normalizeOptionalString(ctx.query?.flow) ?? "";
|
||||
const turnToken = normalizeOptionalString(ctx.query?.turnToken) ?? "";
|
||||
return `twilio:fallback:${crypto
|
||||
.createHash("sha256")
|
||||
.update(
|
||||
@@ -265,14 +266,8 @@ export class TwilioProvider implements VoiceCallProvider {
|
||||
): ProviderWebhookParseResult {
|
||||
try {
|
||||
const params = new URLSearchParams(ctx.rawBody);
|
||||
const callIdFromQuery =
|
||||
typeof ctx.query?.callId === "string" && ctx.query.callId.trim()
|
||||
? ctx.query.callId.trim()
|
||||
: undefined;
|
||||
const turnTokenFromQuery =
|
||||
typeof ctx.query?.turnToken === "string" && ctx.query.turnToken.trim()
|
||||
? ctx.query.turnToken.trim()
|
||||
: undefined;
|
||||
const callIdFromQuery = normalizeOptionalString(ctx.query?.callId);
|
||||
const turnTokenFromQuery = normalizeOptionalString(ctx.query?.turnToken);
|
||||
const dedupeKey = createTwilioRequestDedupeKey(ctx, options?.verifiedRequestKey);
|
||||
const event = this.normalizeEvent(params, {
|
||||
callIdOverride: callIdFromQuery,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { WebhookContext } from "../../types.js";
|
||||
|
||||
export type TwimlResponseKind = "empty" | "pause" | "queue" | "stored" | "stream";
|
||||
@@ -40,11 +41,8 @@ function isOutboundDirection(direction: string | null): boolean {
|
||||
|
||||
export function readTwimlRequestView(ctx: WebhookContext): TwimlRequestView {
|
||||
const params = new URLSearchParams(ctx.rawBody);
|
||||
const type = typeof ctx.query?.type === "string" ? ctx.query.type.trim() : undefined;
|
||||
const callIdFromQuery =
|
||||
typeof ctx.query?.callId === "string" && ctx.query.callId.trim()
|
||||
? ctx.query.callId.trim()
|
||||
: undefined;
|
||||
const type = normalizeOptionalString(ctx.query?.type);
|
||||
const callIdFromQuery = normalizeOptionalString(ctx.query?.callId);
|
||||
|
||||
return {
|
||||
callStatus: params.get("CallStatus"),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { VoiceCallTtsConfig } from "./config.js";
|
||||
|
||||
function resolveProviderVoiceSetting(providerConfig: unknown): string | undefined {
|
||||
@@ -8,13 +9,7 @@ function resolveProviderVoiceSetting(providerConfig: unknown): string | undefine
|
||||
voice?: unknown;
|
||||
voiceId?: unknown;
|
||||
};
|
||||
if (typeof candidate.voice === "string" && candidate.voice.trim()) {
|
||||
return candidate.voice;
|
||||
}
|
||||
if (typeof candidate.voiceId === "string" && candidate.voiceId.trim()) {
|
||||
return candidate.voiceId;
|
||||
}
|
||||
return undefined;
|
||||
return normalizeOptionalString(candidate.voice) ?? normalizeOptionalString(candidate.voiceId);
|
||||
}
|
||||
|
||||
export function resolvePreferredTtsVoice(config: { tts?: VoiceCallTtsConfig }): string | undefined {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import http from "node:http";
|
||||
import { URL } from "node:url";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
createWebhookInFlightLimiter,
|
||||
WEBHOOK_BODY_READ_DEFAULTS,
|
||||
@@ -152,8 +153,7 @@ export class VoiceCallWebhookServer {
|
||||
return false;
|
||||
}
|
||||
|
||||
const initialMessage =
|
||||
typeof call.metadata?.initialMessage === "string" ? call.metadata.initialMessage.trim() : "";
|
||||
const initialMessage = normalizeOptionalString(call.metadata?.initialMessage) ?? "";
|
||||
return initialMessage.length > 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user