From b8dd6548aa309fa92aa66953a056c68578158e9a Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Wed, 18 Mar 2026 04:03:47 +0000 Subject: [PATCH] Zalo User: move outbound session routing behind plugin boundary --- extensions/zalouser/src/channel.ts | 2 + extensions/zalouser/src/session-route.ts | 70 ++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 extensions/zalouser/src/session-route.ts diff --git a/extensions/zalouser/src/channel.ts b/extensions/zalouser/src/channel.ts index f0170af4aa1..b86b3ef8156 100644 --- a/extensions/zalouser/src/channel.ts +++ b/extensions/zalouser/src/channel.ts @@ -33,6 +33,7 @@ import { probeZalouser } from "./probe.js"; import { writeQrDataUrlToTempFile } from "./qr-temp-file.js"; import { getZalouserRuntime } from "./runtime.js"; import { sendMessageZalouser, sendReactionZalouser } from "./send.js"; +import { resolveZalouserOutboundSessionRoute } from "./session-route.js"; import { zalouserSetupAdapter } from "./setup-core.js"; import { zalouserSetupWizard } from "./setup-surface.js"; import { createZalouserPluginBase } from "./shared.js"; @@ -312,6 +313,7 @@ export const zalouserPlugin: ChannelPlugin = { actions: zalouserMessageActions, messaging: { normalizeTarget: (raw) => normalizePrefixedTarget(raw), + resolveOutboundSessionRoute: (params) => resolveZalouserOutboundSessionRoute(params), targetResolver: { looksLikeId: (raw) => { const normalized = normalizePrefixedTarget(raw); diff --git a/extensions/zalouser/src/session-route.ts b/extensions/zalouser/src/session-route.ts new file mode 100644 index 00000000000..c6a1761818d --- /dev/null +++ b/extensions/zalouser/src/session-route.ts @@ -0,0 +1,70 @@ +import { + buildChannelOutboundSessionRoute, + type ChannelOutboundSessionRouteParams, +} from "openclaw/plugin-sdk/core"; + +function stripZalouserTargetPrefix(raw: string): string { + return raw + .trim() + .replace(/^(zalouser|zlu):/i, "") + .trim(); +} + +function normalizePrefixedTarget(raw: string): string | undefined { + const trimmed = stripZalouserTargetPrefix(raw); + if (!trimmed) { + return undefined; + } + + const lower = trimmed.toLowerCase(); + if (lower.startsWith("group:")) { + const id = trimmed.slice("group:".length).trim(); + return id ? `group:${id}` : undefined; + } + if (lower.startsWith("g:")) { + const id = trimmed.slice("g:".length).trim(); + return id ? `group:${id}` : undefined; + } + if (lower.startsWith("user:")) { + const id = trimmed.slice("user:".length).trim(); + return id ? `user:${id}` : undefined; + } + if (lower.startsWith("dm:")) { + const id = trimmed.slice("dm:".length).trim(); + return id ? `user:${id}` : undefined; + } + if (lower.startsWith("u:")) { + const id = trimmed.slice("u:".length).trim(); + return id ? `user:${id}` : undefined; + } + if (/^g-\S+$/i.test(trimmed)) { + return `group:${trimmed}`; + } + if (/^u-\S+$/i.test(trimmed)) { + return `user:${trimmed}`; + } + + return trimmed; +} + +export function resolveZalouserOutboundSessionRoute(params: ChannelOutboundSessionRouteParams) { + const normalized = normalizePrefixedTarget(params.target); + if (!normalized) { + return null; + } + const isGroup = normalized.toLowerCase().startsWith("group:"); + const peerId = normalized.replace(/^(group|user):/i, "").trim(); + return buildChannelOutboundSessionRoute({ + cfg: params.cfg, + agentId: params.agentId, + channel: "zalouser", + accountId: params.accountId, + peer: { + kind: isGroup ? "group" : "direct", + id: peerId, + }, + chatType: isGroup ? "group" : "direct", + from: isGroup ? `zalouser:group:${peerId}` : `zalouser:${peerId}`, + to: `zalouser:${peerId}`, + }); +}