mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-20 21:23:23 +00:00
refactor: dedupe auto-reply lowercase readers
This commit is contained in:
@@ -2,7 +2,10 @@ import { getChannelPlugin, listChannelPlugins } from "../channels/plugins/index.
|
||||
import type { ChannelId, ChannelPlugin } from "../channels/plugins/types.js";
|
||||
import { normalizeAnyChannelId } from "../channels/registry.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalLowercaseString,
|
||||
} from "../shared/string-coerce.js";
|
||||
import { normalizeStringEntries } from "../shared/string-normalization.js";
|
||||
import {
|
||||
INTERNAL_MESSAGE_CHANNEL,
|
||||
@@ -446,7 +449,7 @@ function resolveCommandSenderAuthorization(params: {
|
||||
}
|
||||
|
||||
function isConversationLikeIdentity(value: string): boolean {
|
||||
const normalized = value.trim().toLowerCase();
|
||||
const normalized = normalizeOptionalLowercaseString(value);
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
@@ -570,7 +573,7 @@ function resolveFallbackAccountConfig(
|
||||
| undefined,
|
||||
accountId?: string | null,
|
||||
) {
|
||||
const normalizedAccountId = accountId?.trim().toLowerCase();
|
||||
const normalizedAccountId = normalizeOptionalLowercaseString(accountId);
|
||||
if (!accounts || !normalizedAccountId) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -579,7 +582,7 @@ function resolveFallbackAccountConfig(
|
||||
return direct;
|
||||
}
|
||||
const matchKey = Object.keys(accounts).find(
|
||||
(key) => key.trim().toLowerCase() === normalizedAccountId,
|
||||
(key) => normalizeOptionalLowercaseString(key) === normalizedAccountId,
|
||||
);
|
||||
return matchKey ? accounts[matchKey] : undefined;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import type { SkillCommandSpec } from "../agents/skills.js";
|
||||
import { getChannelPlugin } from "../channels/plugins/index.js";
|
||||
import { isCommandFlagEnabled } from "../config/commands.js";
|
||||
import type { OpenClawConfig } from "../config/types.js";
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalLowercaseString,
|
||||
} from "../shared/string-coerce.js";
|
||||
import { escapeRegExp } from "../utils.js";
|
||||
import { getChatCommands, getNativeCommandSurfaces } from "./commands-registry.data.js";
|
||||
import type {
|
||||
@@ -395,7 +398,7 @@ export function normalizeCommandBody(raw: string, options?: CommandNormalizeOpti
|
||||
})()
|
||||
: singleLine;
|
||||
|
||||
const normalizedBotUsername = options?.botUsername?.trim().toLowerCase();
|
||||
const normalizedBotUsername = normalizeOptionalLowercaseString(options?.botUsername);
|
||||
const mentionMatch = normalizedBotUsername
|
||||
? normalized.match(/^\/([^\s@]+)@([^\s]+)(.*)$/)
|
||||
: null;
|
||||
@@ -404,7 +407,7 @@ export function normalizeCommandBody(raw: string, options?: CommandNormalizeOpti
|
||||
? `/${mentionMatch[1]}${mentionMatch[3] ?? ""}`
|
||||
: normalized;
|
||||
|
||||
const lowered = commandBody.toLowerCase();
|
||||
const lowered = normalizeLowercaseStringOrEmpty(commandBody);
|
||||
const textAliasMap = getTextAliasMap();
|
||||
const exact = textAliasMap.get(lowered);
|
||||
if (exact) {
|
||||
@@ -442,7 +445,7 @@ export function getCommandDetection(_cfg?: OpenClawConfig): CommandDetection {
|
||||
const patterns: string[] = [];
|
||||
for (const cmd of commands) {
|
||||
for (const alias of cmd.textAliases) {
|
||||
const normalized = alias.trim().toLowerCase();
|
||||
const normalized = normalizeOptionalLowercaseString(alias);
|
||||
if (!normalized) {
|
||||
continue;
|
||||
}
|
||||
@@ -472,7 +475,7 @@ export function maybeResolveTextAlias(raw: string, cfg?: OpenClawConfig) {
|
||||
return null;
|
||||
}
|
||||
const detection = getCommandDetection(cfg);
|
||||
const normalized = trimmed.toLowerCase();
|
||||
const normalized = normalizeLowercaseStringOrEmpty(trimmed);
|
||||
if (detection.exact.has(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
@@ -518,7 +521,7 @@ export function isNativeCommandSurface(surface?: string): boolean {
|
||||
if (!surface) {
|
||||
return false;
|
||||
}
|
||||
return getNativeCommandSurfaces().has(surface.toLowerCase());
|
||||
return getNativeCommandSurfaces().has(normalizeLowercaseStringOrEmpty(surface));
|
||||
}
|
||||
|
||||
export function shouldHandleTextCommands(params: ShouldHandleTextCommandsParams): boolean {
|
||||
|
||||
@@ -6,7 +6,10 @@ import { isCommandFlagEnabled } from "../../config/commands.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { formatErrorMessage } from "../../infra/errors.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "../../shared/string-coerce.js";
|
||||
import { clampInt } from "../../utils.js";
|
||||
import type { MsgContext } from "../templating.js";
|
||||
import type { ReplyPayload } from "../types.js";
|
||||
@@ -63,7 +66,7 @@ function formatOutputBlock(text: string) {
|
||||
function parseBashRequest(raw: string): BashRequest | null {
|
||||
const trimmed = raw.trimStart();
|
||||
let restSource = "";
|
||||
if (trimmed.toLowerCase().startsWith("/bash")) {
|
||||
if (normalizeLowercaseStringOrEmpty(trimmed).startsWith("/bash")) {
|
||||
const match = trimmed.match(/^\/bash(?:\s*:\s*|\s+|$)([\s\S]*)$/i);
|
||||
if (!match) {
|
||||
return null;
|
||||
@@ -85,7 +88,7 @@ function parseBashRequest(raw: string): BashRequest | null {
|
||||
const tokenMatch = rest.match(/^(\S+)(?:\s+([\s\S]+))?$/);
|
||||
const token = normalizeOptionalString(tokenMatch?.[1]) ?? "";
|
||||
const remainder = normalizeOptionalString(tokenMatch?.[2]) ?? "";
|
||||
const lowered = token.toLowerCase();
|
||||
const lowered = normalizeLowercaseStringOrEmpty(token);
|
||||
if (lowered === "poll") {
|
||||
return { action: "poll", sessionId: remainder || undefined };
|
||||
}
|
||||
|
||||
@@ -78,7 +78,8 @@ function resolveAllowlistAccountId(params: {
|
||||
|
||||
function parseAllowlistCommand(raw: string): AllowlistCommand | null {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed.toLowerCase().startsWith("/allowlist")) {
|
||||
const trimmedLower = normalizeOptionalLowercaseString(trimmed) ?? "";
|
||||
if (!trimmedLower.startsWith("/allowlist")) {
|
||||
return null;
|
||||
}
|
||||
const rest = trimmed.slice("/allowlist".length).trim();
|
||||
@@ -96,18 +97,20 @@ function parseAllowlistCommand(raw: string): AllowlistCommand | null {
|
||||
const entryTokens: string[] = [];
|
||||
|
||||
let i = 0;
|
||||
if (tokens[i] && ACTIONS.has(tokens[i].toLowerCase())) {
|
||||
action = tokens[i].toLowerCase() as AllowlistAction;
|
||||
const firstAction = normalizeOptionalLowercaseString(tokens[i]);
|
||||
if (firstAction && ACTIONS.has(firstAction)) {
|
||||
action = firstAction as AllowlistAction;
|
||||
i += 1;
|
||||
}
|
||||
if (tokens[i] && SCOPES.has(tokens[i].toLowerCase() as AllowlistScope)) {
|
||||
scope = tokens[i].toLowerCase() as AllowlistScope;
|
||||
const firstScope = normalizeOptionalLowercaseString(tokens[i]);
|
||||
if (firstScope && SCOPES.has(firstScope as AllowlistScope)) {
|
||||
scope = firstScope as AllowlistScope;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
for (; i < tokens.length; i += 1) {
|
||||
const token = tokens[i];
|
||||
const lowered = token.toLowerCase();
|
||||
const lowered = normalizeOptionalLowercaseString(token) ?? "";
|
||||
if (lowered === "--resolve" || lowered === "resolve") {
|
||||
resolve = true;
|
||||
continue;
|
||||
@@ -146,8 +149,9 @@ function parseAllowlistCommand(raw: string): AllowlistCommand | null {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (key === "scope" && value && SCOPES.has(value.toLowerCase() as AllowlistScope)) {
|
||||
scope = value.toLowerCase() as AllowlistScope;
|
||||
const normalizedValue = normalizeOptionalLowercaseString(value);
|
||||
if (key === "scope" && normalizedValue && SCOPES.has(normalizedValue as AllowlistScope)) {
|
||||
scope = normalizedValue as AllowlistScope;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
|
||||
|
||||
export type SlashCommandParseResult =
|
||||
| { kind: "no-match" }
|
||||
| { kind: "empty" }
|
||||
@@ -10,8 +12,8 @@ export type ParsedSlashCommand =
|
||||
|
||||
export function parseSlashCommandActionArgs(raw: string, slash: string): SlashCommandParseResult {
|
||||
const trimmed = raw.trim();
|
||||
const slashLower = slash.toLowerCase();
|
||||
if (!trimmed.toLowerCase().startsWith(slashLower)) {
|
||||
const slashLower = normalizeLowercaseStringOrEmpty(slash);
|
||||
if (!normalizeLowercaseStringOrEmpty(trimmed).startsWith(slashLower)) {
|
||||
return { kind: "no-match" };
|
||||
}
|
||||
const rest = trimmed.slice(slash.length).trim();
|
||||
@@ -22,7 +24,7 @@ export function parseSlashCommandActionArgs(raw: string, slash: string): SlashCo
|
||||
if (!match) {
|
||||
return { kind: "invalid" };
|
||||
}
|
||||
const action = match[1]?.toLowerCase() ?? "";
|
||||
const action = normalizeLowercaseStringOrEmpty(match[1]);
|
||||
const args = (match[2] ?? "").trim();
|
||||
return { kind: "parsed", action, args };
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ import type { MessagingToolSend } from "../../agents/pi-embedded-runner.js";
|
||||
import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js";
|
||||
import { normalizeTargetForProvider } from "../../infra/outbound/target-normalization.js";
|
||||
import { normalizeOptionalAccountId } from "../../routing/account-id.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "../../shared/string-coerce.js";
|
||||
import type { ReplyPayload } from "../types.js";
|
||||
|
||||
export function filterMessagingToolDuplicates(params: {
|
||||
@@ -26,7 +29,7 @@ export function filterMessagingToolMediaDuplicates(params: {
|
||||
if (!trimmed) {
|
||||
return "";
|
||||
}
|
||||
if (!trimmed.toLowerCase().startsWith("file://")) {
|
||||
if (!normalizeLowercaseStringOrEmpty(trimmed).startsWith("file://")) {
|
||||
return trimmed;
|
||||
}
|
||||
try {
|
||||
@@ -66,7 +69,7 @@ function normalizeProviderForComparison(value?: string): string | undefined {
|
||||
if (!trimmed) {
|
||||
return undefined;
|
||||
}
|
||||
const lowered = trimmed.toLowerCase();
|
||||
const lowered = normalizeLowercaseStringOrEmpty(trimmed);
|
||||
const normalizedChannel = normalizeChannelId(trimmed);
|
||||
if (normalizedChannel) {
|
||||
return normalizedChannel;
|
||||
@@ -82,7 +85,7 @@ function normalizeThreadIdForComparison(value?: string): string | undefined {
|
||||
if (/^-?\d+$/.test(trimmed)) {
|
||||
return String(Number.parseInt(trimmed, 10));
|
||||
}
|
||||
return trimmed.toLowerCase();
|
||||
return normalizeLowercaseStringOrEmpty(trimmed);
|
||||
}
|
||||
|
||||
function resolveTargetProviderForComparison(params: {
|
||||
|
||||
@@ -16,6 +16,7 @@ import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { formatErrorMessage } from "../../infra/errors.js";
|
||||
import { buildOutboundSessionContext } from "../../infra/outbound/session-context.js";
|
||||
import { hasReplyPayloadContent } from "../../interactive/payload.js";
|
||||
import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js";
|
||||
import { INTERNAL_MESSAGE_CHANNEL, normalizeMessageChannel } from "../../utils/message-channel.js";
|
||||
import type { OriginatingChannelType } from "../templating.js";
|
||||
import type { ReplyPayload } from "../types.js";
|
||||
@@ -83,8 +84,7 @@ export async function routeReply(params: RouteReplyParams): Promise<RouteReplyRe
|
||||
}
|
||||
const normalizedChannel = normalizeMessageChannel(channel);
|
||||
const channelId =
|
||||
normalizeChannelId(channel) ??
|
||||
(typeof channel === "string" ? channel.trim().toLowerCase() : null);
|
||||
normalizeChannelId(channel) ?? normalizeOptionalLowercaseString(channel) ?? null;
|
||||
const loadedPlugin = channelId ? getLoadedChannelPlugin(channelId) : undefined;
|
||||
const bundledPlugin = channelId ? getBundledChannelPlugin(channelId) : undefined;
|
||||
const messaging = loadedPlugin?.messaging ?? bundledPlugin?.messaging;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { buildAgentMainSessionKey } from "../../routing/session-key.js";
|
||||
import { parseAgentSessionKey } from "../../sessions/session-key-utils.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalLowercaseString,
|
||||
normalizeOptionalString,
|
||||
} from "../../shared/string-coerce.js";
|
||||
import {
|
||||
@@ -27,7 +28,7 @@ function resolveSessionKeyChannelHint(sessionKey?: string): string | undefined {
|
||||
if (!parsed?.rest) {
|
||||
return undefined;
|
||||
}
|
||||
const head = normalizeOptionalString(parsed.rest.split(":")[0])?.toLowerCase();
|
||||
const head = normalizeOptionalLowercaseString(parsed.rest.split(":")[0]);
|
||||
if (!head || head === "main" || head === "cron" || head === "subagent" || head === "acp") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { buildWorkspaceSkillCommandSpecs, type SkillCommandSpec } from "../agent
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { logVerbose } from "../globals.js";
|
||||
import { getRemoteSkillEligibility } from "../infra/skills-remote.js";
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
import { listReservedChatSlashCommandNames } from "./skill-commands-base.js";
|
||||
export {
|
||||
listReservedChatSlashCommandNames,
|
||||
@@ -41,7 +42,7 @@ function dedupeBySkillName(commands: SkillCommandSpec[]): SkillCommandSpec[] {
|
||||
const seen = new Set<string>();
|
||||
const out: SkillCommandSpec[] = [];
|
||||
for (const cmd of commands) {
|
||||
const key = cmd.skillName.trim().toLowerCase();
|
||||
const key = normalizeOptionalLowercaseString(cmd.skillName);
|
||||
if (key && seen.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
resolveProviderDefaultThinkingLevel,
|
||||
resolveProviderXHighThinking,
|
||||
} from "../plugins/provider-thinking.js";
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
|
||||
export function isBinaryThinkingProvider(provider?: string | null, model?: string | null): boolean {
|
||||
const normalizedProvider = provider?.trim() ? normalizeProviderId(provider) : "";
|
||||
@@ -54,7 +55,7 @@ export function isBinaryThinkingProvider(provider?: string | null, model?: strin
|
||||
}
|
||||
|
||||
export function supportsXHighThinking(provider?: string | null, model?: string | null): boolean {
|
||||
const modelKey = model?.trim().toLowerCase();
|
||||
const modelKey = normalizeOptionalLowercaseString(model);
|
||||
if (!modelKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
|
||||
export type {
|
||||
AllowlistMatch,
|
||||
AllowlistMatchSource,
|
||||
@@ -36,7 +38,8 @@ export function formatAllowFromLowercase(params: {
|
||||
.map((entry) => String(entry).trim())
|
||||
.filter(Boolean)
|
||||
.map((entry) => (params.stripPrefixRe ? entry.replace(params.stripPrefixRe, "") : entry))
|
||||
.map((entry) => entry.toLowerCase());
|
||||
.map((entry) => normalizeOptionalLowercaseString(entry))
|
||||
.filter((entry): entry is string => Boolean(entry));
|
||||
}
|
||||
|
||||
/** Normalize allowlist entries through a channel-provided parser or canonicalizer. */
|
||||
@@ -67,7 +70,7 @@ export function isNormalizedSenderAllowed(params: {
|
||||
if (normalizedAllow.includes("*")) {
|
||||
return true;
|
||||
}
|
||||
const sender = String(params.senderId).trim().toLowerCase();
|
||||
const sender = normalizeOptionalLowercaseString(String(params.senderId));
|
||||
return normalizedAllow.includes(sender);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,10 @@ import { matchesApprovalRequestFilters } from "../infra/approval-request-filters
|
||||
import { getExecApprovalReplyMetadata } from "../infra/exec-approval-reply.js";
|
||||
import type { ExecApprovalRequest } from "../infra/exec-approvals.js";
|
||||
import type { PluginApprovalRequest } from "../infra/plugin-approvals.js";
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
import {
|
||||
normalizeOptionalLowercaseString,
|
||||
normalizeOptionalString,
|
||||
} from "../shared/string-coerce.js";
|
||||
import type { OpenClawConfig } from "./config-runtime.js";
|
||||
import { normalizeAccountId } from "./routing.js";
|
||||
|
||||
@@ -58,7 +61,7 @@ export function isChannelExecApprovalTargetRecipient(params: {
|
||||
}): boolean {
|
||||
const normalizeSenderId = params.normalizeSenderId ?? normalizeOptionalString;
|
||||
const normalizedSenderId = params.senderId ? normalizeSenderId(params.senderId) : undefined;
|
||||
const normalizedChannel = params.channel.trim().toLowerCase();
|
||||
const normalizedChannel = normalizeOptionalLowercaseString(params.channel);
|
||||
if (!normalizedSenderId || !isApprovalTargetsMode(params.cfg)) {
|
||||
return false;
|
||||
}
|
||||
@@ -68,7 +71,7 @@ export function isChannelExecApprovalTargetRecipient(params: {
|
||||
}
|
||||
const normalizedAccountId = params.accountId ? normalizeAccountId(params.accountId) : undefined;
|
||||
return targets.some((target) => {
|
||||
if (target.channel?.trim().toLowerCase() !== normalizedChannel) {
|
||||
if (normalizeOptionalLowercaseString(target.channel) !== normalizedChannel) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
|
||||
@@ -15,6 +15,7 @@ import type { ChannelConfigAdapter } from "../channels/plugins/types.adapters.js
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
import { normalizeStringEntries } from "../shared/string-normalization.js";
|
||||
|
||||
const INTERNAL_MESSAGE_CHANNEL = "webchat";
|
||||
@@ -113,7 +114,7 @@ export function canBypassConfigWritePolicy(params: {
|
||||
return canBypassConfigWritePolicyShared({
|
||||
...params,
|
||||
isInternalMessageChannel: (channel) =>
|
||||
channel?.trim().toLowerCase() === INTERNAL_MESSAGE_CHANNEL,
|
||||
normalizeOptionalLowercaseString(channel) === INTERNAL_MESSAGE_CHANNEL,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user