mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:49:33 +00:00
113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
import type { MsgContext } from "../../auto-reply/templating.js";
|
|
import type { GroupKeyResolution } from "./types.js";
|
|
import { listDeliverableMessageChannels } from "../../utils/message-channel.js";
|
|
|
|
const getGroupSurfaces = () => new Set<string>([...listDeliverableMessageChannels(), "webchat"]);
|
|
|
|
function normalizeGroupLabel(raw?: string) {
|
|
const trimmed = raw?.trim().toLowerCase() ?? "";
|
|
if (!trimmed) {
|
|
return "";
|
|
}
|
|
const dashed = trimmed.replace(/\s+/g, "-");
|
|
const cleaned = dashed.replace(/[^a-z0-9#@._+-]+/g, "-");
|
|
return cleaned.replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
|
}
|
|
|
|
function shortenGroupId(value?: string) {
|
|
const trimmed = value?.trim() ?? "";
|
|
if (!trimmed) {
|
|
return "";
|
|
}
|
|
if (trimmed.length <= 14) {
|
|
return trimmed;
|
|
}
|
|
return `${trimmed.slice(0, 6)}...${trimmed.slice(-4)}`;
|
|
}
|
|
|
|
export function buildGroupDisplayName(params: {
|
|
provider?: string;
|
|
subject?: string;
|
|
groupChannel?: string;
|
|
space?: string;
|
|
id?: string;
|
|
key: string;
|
|
}) {
|
|
const providerKey = (params.provider?.trim().toLowerCase() || "group").trim();
|
|
const groupChannel = params.groupChannel?.trim();
|
|
const space = params.space?.trim();
|
|
const subject = params.subject?.trim();
|
|
const detail =
|
|
(groupChannel && space
|
|
? `${space}${groupChannel.startsWith("#") ? "" : "#"}${groupChannel}`
|
|
: groupChannel || subject || space || "") || "";
|
|
const fallbackId = params.id?.trim() || params.key;
|
|
const rawLabel = detail || fallbackId;
|
|
let token = normalizeGroupLabel(rawLabel);
|
|
if (!token) {
|
|
token = normalizeGroupLabel(shortenGroupId(rawLabel));
|
|
}
|
|
if (!params.groupChannel && token.startsWith("#")) {
|
|
token = token.replace(/^#+/, "");
|
|
}
|
|
if (token && !/^[@#]/.test(token) && !token.startsWith("g-") && !token.includes("#")) {
|
|
token = `g-${token}`;
|
|
}
|
|
return token ? `${providerKey}:${token}` : providerKey;
|
|
}
|
|
|
|
export function resolveGroupSessionKey(ctx: MsgContext): GroupKeyResolution | null {
|
|
const from = typeof ctx.From === "string" ? ctx.From.trim() : "";
|
|
const chatType = ctx.ChatType?.trim().toLowerCase();
|
|
const normalizedChatType =
|
|
chatType === "channel" ? "channel" : chatType === "group" ? "group" : undefined;
|
|
|
|
const isWhatsAppGroupId = from.toLowerCase().endsWith("@g.us");
|
|
const looksLikeGroup =
|
|
normalizedChatType === "group" ||
|
|
normalizedChatType === "channel" ||
|
|
from.includes(":group:") ||
|
|
from.includes(":channel:") ||
|
|
isWhatsAppGroupId;
|
|
if (!looksLikeGroup) {
|
|
return null;
|
|
}
|
|
|
|
const providerHint = ctx.Provider?.trim().toLowerCase();
|
|
|
|
const parts = from.split(":").filter(Boolean);
|
|
const head = parts[0]?.trim().toLowerCase() ?? "";
|
|
const headIsSurface = head ? getGroupSurfaces().has(head) : false;
|
|
|
|
const provider = headIsSurface
|
|
? head
|
|
: (providerHint ?? (isWhatsAppGroupId ? "whatsapp" : undefined));
|
|
if (!provider) {
|
|
return null;
|
|
}
|
|
|
|
const second = parts[1]?.trim().toLowerCase();
|
|
const secondIsKind = second === "group" || second === "channel";
|
|
const kind = secondIsKind
|
|
? second
|
|
: from.includes(":channel:") || normalizedChatType === "channel"
|
|
? "channel"
|
|
: "group";
|
|
const id = headIsSurface
|
|
? secondIsKind
|
|
? parts.slice(2).join(":")
|
|
: parts.slice(1).join(":")
|
|
: from;
|
|
const finalId = id.trim().toLowerCase();
|
|
if (!finalId) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
key: `${provider}:${kind}:${finalId}`,
|
|
channel: provider,
|
|
id: finalId,
|
|
chatType: kind === "channel" ? "channel" : "group",
|
|
};
|
|
}
|