mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 05:01:15 +00:00
fix: canonicalize legacy whatsapp group sessions
This commit is contained in:
@@ -3,7 +3,9 @@ type UnsupportedSecretRefConfigCandidate = {
|
||||
value: unknown;
|
||||
};
|
||||
|
||||
export { normalizeCompatibilityConfig } from "./src/doctor-contract.js";
|
||||
import { hasAnyWhatsAppAuth } from "./src/accounts.js";
|
||||
export { canonicalizeLegacySessionKey, isLegacyGroupSessionKey } from "./src/session-contract.js";
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null;
|
||||
|
||||
21
extensions/whatsapp/src/session-contract.test.ts
Normal file
21
extensions/whatsapp/src/session-contract.test.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { canonicalizeLegacySessionKey, isLegacyGroupSessionKey } from "./session-contract.js";
|
||||
|
||||
describe("whatsapp legacy session contract", () => {
|
||||
it("canonicalizes legacy WhatsApp group keys to channel-qualified agent keys", () => {
|
||||
expect(canonicalizeLegacySessionKey({ key: "group:123@g.us", agentId: "main" })).toBe(
|
||||
"agent:main:whatsapp:group:123@g.us",
|
||||
);
|
||||
expect(canonicalizeLegacySessionKey({ key: "123@g.us", agentId: "main" })).toBe(
|
||||
"agent:main:whatsapp:group:123@g.us",
|
||||
);
|
||||
expect(canonicalizeLegacySessionKey({ key: "whatsapp:123@g.us", agentId: "main" })).toBe(
|
||||
"agent:main:whatsapp:group:123@g.us",
|
||||
);
|
||||
});
|
||||
|
||||
it("does not claim generic non-WhatsApp group keys", () => {
|
||||
expect(isLegacyGroupSessionKey("group:abc")).toBe(false);
|
||||
expect(canonicalizeLegacySessionKey({ key: "group:abc", agentId: "main" })).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,42 +1,37 @@
|
||||
export function isLegacyGroupSessionKey(key: string): boolean {
|
||||
function extractLegacyWhatsAppGroupId(key: string): string | null {
|
||||
const trimmed = key.trim();
|
||||
if (!trimmed) {
|
||||
return false;
|
||||
}
|
||||
if (trimmed.startsWith("group:")) {
|
||||
return true;
|
||||
return null;
|
||||
}
|
||||
const lower = trimmed.toLowerCase();
|
||||
if (trimmed.startsWith("group:")) {
|
||||
const id = trimmed.slice("group:".length).trim();
|
||||
return id.toLowerCase().includes("@g.us") ? id : null;
|
||||
}
|
||||
if (!lower.includes("@g.us")) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
if (!trimmed.includes(":")) {
|
||||
return true;
|
||||
return trimmed;
|
||||
}
|
||||
return lower.startsWith("whatsapp:") && !trimmed.includes(":group:");
|
||||
if (lower.startsWith("whatsapp:") && !trimmed.includes(":group:")) {
|
||||
const remainder = trimmed.slice("whatsapp:".length).trim();
|
||||
const cleaned = remainder.replace(/^group:/i, "").trim();
|
||||
return cleaned || null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isLegacyGroupSessionKey(key: string): boolean {
|
||||
return extractLegacyWhatsAppGroupId(key) !== null;
|
||||
}
|
||||
|
||||
export function canonicalizeLegacySessionKey(params: {
|
||||
key: string;
|
||||
agentId: string;
|
||||
}): string | null {
|
||||
const trimmed = params.key.trim();
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
if (trimmed.startsWith("group:")) {
|
||||
const id = trimmed.slice("group:".length).trim();
|
||||
return id ? `agent:${params.agentId}:whatsapp:group:${id}`.toLowerCase() : null;
|
||||
}
|
||||
if (!trimmed.includes(":") && trimmed.toLowerCase().includes("@g.us")) {
|
||||
return `agent:${params.agentId}:whatsapp:group:${trimmed}`.toLowerCase();
|
||||
}
|
||||
if (trimmed.toLowerCase().startsWith("whatsapp:") && trimmed.toLowerCase().includes("@g.us")) {
|
||||
const remainder = trimmed.slice("whatsapp:".length).trim();
|
||||
const cleaned = remainder.replace(/^group:/i, "").trim();
|
||||
if (cleaned && !trimmed.includes(":group:")) {
|
||||
return `agent:${params.agentId}:whatsapp:group:${cleaned}`.toLowerCase();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
const legacyGroupId = extractLegacyWhatsAppGroupId(params.key);
|
||||
return legacyGroupId
|
||||
? `agent:${params.agentId}:whatsapp:group:${legacyGroupId}`.toLowerCase()
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -99,17 +99,15 @@ describe("state migrations", () => {
|
||||
expect(detected.agentDir.hasLegacy).toBe(true);
|
||||
expect(detected.channelPlans.hasLegacy).toBe(true);
|
||||
expect(detected.channelPlans.plans.map((plan) => plan.targetPath)).toEqual([
|
||||
path.join(stateDir, "credentials", "whatsapp", "default", "creds.json"),
|
||||
path.join(stateDir, "credentials", "whatsapp", "default", "pre-key-1.json"),
|
||||
resolveChannelAllowFromPath("telegram", env, "alpha"),
|
||||
path.join(stateDir, "credentials", "whatsapp", "default", "creds.json"),
|
||||
]);
|
||||
expect(detected.preview).toEqual([
|
||||
`- Sessions: ${path.join(stateDir, "sessions")} → ${path.join(stateDir, "agents", "worker-1", "sessions")}`,
|
||||
`- Sessions: canonicalize legacy keys in ${path.join(stateDir, "agents", "worker-1", "sessions", "sessions.json")}`,
|
||||
`- Agent dir: ${path.join(stateDir, "agent")} → ${path.join(stateDir, "agents", "worker-1", "agent")}`,
|
||||
`- WhatsApp auth creds.json: ${path.join(stateDir, "credentials", "creds.json")} → ${path.join(stateDir, "credentials", "whatsapp", "default", "creds.json")}`,
|
||||
`- WhatsApp auth pre-key-1.json: ${path.join(stateDir, "credentials", "pre-key-1.json")} → ${path.join(stateDir, "credentials", "whatsapp", "default", "pre-key-1.json")}`,
|
||||
`- Telegram pairing allowFrom: ${resolveChannelAllowFromPath("telegram", env)} → ${resolveChannelAllowFromPath("telegram", env, "alpha")}`,
|
||||
`- WhatsApp auth creds.json: ${path.join(stateDir, "credentials", "creds.json")} → ${path.join(stateDir, "credentials", "whatsapp", "default", "creds.json")}`,
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -133,9 +131,9 @@ describe("state migrations", () => {
|
||||
"Canonicalized 1 legacy session key(s)",
|
||||
"Moved trace.jsonl → agents/worker-1/sessions",
|
||||
"Moved agent file settings.json → agents/worker-1/agent",
|
||||
`Copied Telegram pairing allowFrom → ${resolveChannelAllowFromPath("telegram", env, "alpha")}`,
|
||||
`Moved WhatsApp auth creds.json → ${path.join(stateDir, "credentials", "whatsapp", "default", "creds.json")}`,
|
||||
`Moved WhatsApp auth pre-key-1.json → ${path.join(stateDir, "credentials", "whatsapp", "default", "pre-key-1.json")}`,
|
||||
`Copied Telegram pairing allowFrom → ${resolveChannelAllowFromPath("telegram", env, "alpha")}`,
|
||||
]);
|
||||
|
||||
const mergedStore = JSON.parse(
|
||||
|
||||
@@ -93,6 +93,10 @@ function isLegacyGroupKey(key: string): boolean {
|
||||
if (!trimmed) {
|
||||
return false;
|
||||
}
|
||||
const lower = trimmed.toLowerCase();
|
||||
if (lower.startsWith("group:") || lower.startsWith("channel:")) {
|
||||
return true;
|
||||
}
|
||||
for (const surface of getLegacySessionSurfaces()) {
|
||||
if (surface.isLegacyGroupSessionKey?.(trimmed)) {
|
||||
return true;
|
||||
@@ -215,6 +219,10 @@ function canonicalizeSessionKeyForAgent(params: {
|
||||
return canonicalized.trim().toLowerCase();
|
||||
}
|
||||
}
|
||||
const lower = raw.toLowerCase();
|
||||
if (lower.startsWith("group:") || lower.startsWith("channel:")) {
|
||||
return `agent:${agentId}:unknown:${raw}`.toLowerCase();
|
||||
}
|
||||
if (isSurfaceGroupKey(raw)) {
|
||||
return `agent:${agentId}:${raw}`.toLowerCase();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user