diff --git a/src/auto-reply/reply/reply-elevated.ts b/src/auto-reply/reply/reply-elevated.ts index 2550997e53..43727aa9e8 100644 --- a/src/auto-reply/reply/reply-elevated.ts +++ b/src/auto-reply/reply/reply-elevated.ts @@ -3,6 +3,7 @@ import { getChannelDock } from "../../channels/dock.js"; import { normalizeChannelId } from "../../channels/plugins/index.js"; import { CHAT_CHANNEL_ORDER } from "../../channels/registry.js"; import type { AgentElevatedAllowFromConfig, OpenClawConfig } from "../../config/config.js"; +import { normalizeAtHashSlug } from "../../shared/string-normalization.js"; import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js"; import type { MsgContext } from "../templating.js"; export { formatElevatedUnavailableMessage } from "./elevated-unavailable.js"; @@ -15,17 +16,7 @@ function normalizeAllowToken(value?: string) { } function slugAllowToken(value?: string) { - if (!value) { - return ""; - } - let text = value.trim().toLowerCase(); - if (!text) { - return ""; - } - text = text.replace(/^[@#]+/, ""); - text = text.replace(/[\s_]+/g, "-"); - text = text.replace(/[^a-z0-9-]+/g, "-"); - return text.replace(/-{2,}/g, "-").replace(/^-+|-+$/g, ""); + return normalizeAtHashSlug(value); } const SENDER_PREFIXES = [ diff --git a/src/channels/plugins/group-mentions.ts b/src/channels/plugins/group-mentions.ts index 994f457ce1..7194097015 100644 --- a/src/channels/plugins/group-mentions.ts +++ b/src/channels/plugins/group-mentions.ts @@ -9,7 +9,7 @@ import type { GroupToolPolicyBySenderConfig, GroupToolPolicyConfig, } from "../../config/types.tools.js"; -import { normalizeHyphenSlug } from "../../shared/string-normalization.js"; +import { normalizeAtHashSlug, normalizeHyphenSlug } from "../../shared/string-normalization.js"; import { resolveSlackAccount } from "../../slack/accounts.js"; type GroupMentionParams = { @@ -25,18 +25,7 @@ type GroupMentionParams = { }; function normalizeDiscordSlug(value?: string | null) { - if (!value) { - return ""; - } - let text = value.trim().toLowerCase(); - if (!text) { - return ""; - } - text = text.replace(/^[@#]+/, ""); - text = text.replace(/[\s_]+/g, "-"); - text = text.replace(/[^a-z0-9-]+/g, "-"); - text = text.replace(/-{2,}/g, "-").replace(/^-+|-+$/g, ""); - return text; + return normalizeAtHashSlug(value); } function parseTelegramGroupId(value?: string | null) { diff --git a/src/shared/string-normalization.test.ts b/src/shared/string-normalization.test.ts index 20327f3907..15e5ee5fc7 100644 --- a/src/shared/string-normalization.test.ts +++ b/src/shared/string-normalization.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { + normalizeAtHashSlug, normalizeHyphenSlug, normalizeStringEntries, normalizeStringEntriesLower, @@ -22,4 +23,11 @@ describe("shared/string-normalization", () => { expect(normalizeHyphenSlug(undefined)).toBe(""); expect(normalizeHyphenSlug(null)).toBe(""); }); + + it("normalizes @/# prefixed slugs used by channel allowlists", () => { + expect(normalizeAtHashSlug(" #My_Channel + Alerts ")).toBe("my-channel-alerts"); + expect(normalizeAtHashSlug("@@Room___Name")).toBe("room-name"); + expect(normalizeAtHashSlug(undefined)).toBe(""); + expect(normalizeAtHashSlug(null)).toBe(""); + }); }); diff --git a/src/shared/string-normalization.ts b/src/shared/string-normalization.ts index 9147b1b508..67a191a8bf 100644 --- a/src/shared/string-normalization.ts +++ b/src/shared/string-normalization.ts @@ -15,3 +15,14 @@ export function normalizeHyphenSlug(raw?: string | null) { const cleaned = dashed.replace(/[^a-z0-9#@._+-]+/g, "-"); return cleaned.replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, ""); } + +export function normalizeAtHashSlug(raw?: string | null) { + const trimmed = raw?.trim().toLowerCase() ?? ""; + if (!trimmed) { + return ""; + } + const withoutPrefix = trimmed.replace(/^[@#]+/, ""); + const dashed = withoutPrefix.replace(/[\s_]+/g, "-"); + const cleaned = dashed.replace(/[^a-z0-9-]+/g, "-"); + return cleaned.replace(/-{2,}/g, "-").replace(/^-+|-+$/g, ""); +}