openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
72 lines (71 loc) • 3.69 kB
JavaScript
import { c as normalizeOptionalLowercaseString } from "./string-coerce-CIXf7egm.js";
import { n as normalizeMessageChannel } from "./message-channel-core-AaEWic-A.js";
import { r as listRuntimeVisibleChannelPlugins } from "./runtime-visible-channels-CaZJZcDe.js";
//#region src/infra/outbound/channel-target-prefix.ts
const TARGET_KIND_PREFIXES = /* @__PURE__ */ new Set([
"channel",
"conversation",
"dm",
"group",
"room",
"thread",
"user"
]);
const DEFAULT_TARGET_KINDS = [...TARGET_KIND_PREFIXES];
const TARGET_KIND_PATTERN = new RegExp(`^(${DEFAULT_TARGET_KINDS.join("|")}):`, "i");
/** Removes a selected channel/provider prefix from an outbound target string. */
function stripTargetProviderPrefix(raw, ...providers) {
const trimmed = raw.trim();
const lower = normalizeOptionalLowercaseString(trimmed) ?? "";
for (const provider of providers) {
const normalizedProvider = normalizeOptionalLowercaseString(provider);
if (normalizedProvider && lower.startsWith(`${normalizedProvider}:`)) return trimmed.slice(normalizedProvider.length + 1).trim();
}
return trimmed;
}
/** Removes generic target-kind prefixes such as room:, thread:, or user:. */
function stripOutboundTargetKindPrefix(raw, kinds = DEFAULT_TARGET_KINDS) {
if (kinds === DEFAULT_TARGET_KINDS) return raw.replace(TARGET_KIND_PATTERN, "").trim();
const kindPattern = kinds.map((kind) => normalizeOptionalLowercaseString(kind)).filter((kind) => Boolean(kind)).join("|");
return kindPattern ? raw.replace(new RegExp(`^(${kindPattern}):`, "i"), "").trim() : raw.trim();
}
/** Strips plugin topic suffixes while preserving ordinary colon-containing targets. */
function stripTargetTopicSuffix(raw, options = {}) {
const trimmed = raw.trim();
const numericTopicMatch = options.allowNumericShorthand ? /^(-?\d+):(\d+)$/.exec(trimmed) : null;
if (numericTopicMatch?.[1]) return numericTopicMatch[1];
return trimmed.replace(/:topic:.*$/i, "").trim();
}
function resolvePluginTargetPrefix(prefix) {
const normalizedPrefix = normalizeOptionalLowercaseString(prefix);
if (!normalizedPrefix) return;
for (const plugin of listRuntimeVisibleChannelPlugins()) {
const channelId = normalizeOptionalLowercaseString(plugin.id);
const candidates = plugin.messaging?.targetPrefixes ?? [];
if (channelId && candidates.some((candidate) => normalizeOptionalLowercaseString(candidate) === normalizedPrefix)) return channelId;
}
}
function resolveChannelTargetProviderPrefix(raw) {
const match = /^\s*([a-z][a-z0-9_-]*):/i.exec(raw ?? "");
const prefix = normalizeOptionalLowercaseString(match?.[1]);
if (!prefix || TARGET_KIND_PREFIXES.has(prefix)) return;
const channel = resolvePluginTargetPrefix(prefix);
return channel ? {
prefix,
channel
} : void 0;
}
/** Resolves the channel implied by a plugin-owned target prefix, if any. */
function resolveTargetPrefixedChannel(raw) {
return resolveChannelTargetProviderPrefix(raw)?.channel;
}
/** Rejects targets whose plugin-owned prefix belongs to a different selected channel. */
function validateTargetProviderPrefix(params) {
const selectedChannel = normalizeMessageChannel(params.channel) ?? normalizeOptionalLowercaseString(params.channel);
if (!selectedChannel || selectedChannel === "last") return;
const prefixed = resolveChannelTargetProviderPrefix(params.to);
if (!prefixed || prefixed.channel === selectedChannel) return;
return /* @__PURE__ */ new Error(`Target prefix "${prefixed.prefix}:" belongs to ${prefixed.channel}, not ${selectedChannel}.`);
}
//#endregion
export { validateTargetProviderPrefix as a, stripTargetTopicSuffix as i, stripOutboundTargetKindPrefix as n, stripTargetProviderPrefix as r, resolveTargetPrefixedChannel as t };