Spaces:
Paused
Paused
| import type { OpenClawConfig } from "../../config/config.js"; | |
| import { normalizeChatChannelId } from "../../channels/registry.js"; | |
| import { loadSessionStore, resolveStorePath } from "../../config/sessions.js"; | |
| import { normalizeE164 } from "../../utils.js"; | |
| type HeartbeatRecipientsResult = { recipients: string[]; source: string }; | |
| type HeartbeatRecipientsOpts = { to?: string; all?: boolean }; | |
| function getSessionRecipients(cfg: OpenClawConfig) { | |
| const sessionCfg = cfg.session; | |
| const scope = sessionCfg?.scope ?? "per-sender"; | |
| if (scope === "global") { | |
| return []; | |
| } | |
| const storePath = resolveStorePath(cfg.session?.store); | |
| const store = loadSessionStore(storePath); | |
| const isGroupKey = (key: string) => | |
| key.includes(":group:") || key.includes(":channel:") || key.includes("@g.us"); | |
| const isCronKey = (key: string) => key.startsWith("cron:"); | |
| const recipients = Object.entries(store) | |
| .filter(([key]) => key !== "global" && key !== "unknown") | |
| .filter(([key]) => !isGroupKey(key) && !isCronKey(key)) | |
| .map(([_, entry]) => ({ | |
| to: | |
| normalizeChatChannelId(entry?.lastChannel) === "whatsapp" && entry?.lastTo | |
| ? normalizeE164(entry.lastTo) | |
| : "", | |
| updatedAt: entry?.updatedAt ?? 0, | |
| })) | |
| .filter(({ to }) => to.length > 1) | |
| .toSorted((a, b) => b.updatedAt - a.updatedAt); | |
| // Dedupe while preserving recency ordering. | |
| const seen = new Set<string>(); | |
| return recipients.filter((r) => { | |
| if (seen.has(r.to)) { | |
| return false; | |
| } | |
| seen.add(r.to); | |
| return true; | |
| }); | |
| } | |
| export function resolveWhatsAppHeartbeatRecipients( | |
| cfg: OpenClawConfig, | |
| opts: HeartbeatRecipientsOpts = {}, | |
| ): HeartbeatRecipientsResult { | |
| if (opts.to) { | |
| return { recipients: [normalizeE164(opts.to)], source: "flag" }; | |
| } | |
| const sessionRecipients = getSessionRecipients(cfg); | |
| const allowFrom = | |
| Array.isArray(cfg.channels?.whatsapp?.allowFrom) && cfg.channels.whatsapp.allowFrom.length > 0 | |
| ? cfg.channels.whatsapp.allowFrom.filter((v) => v !== "*").map(normalizeE164) | |
| : []; | |
| const unique = (list: string[]) => [...new Set(list.filter(Boolean))]; | |
| if (opts.all) { | |
| const all = unique([...sessionRecipients.map((s) => s.to), ...allowFrom]); | |
| return { recipients: all, source: "all" }; | |
| } | |
| if (sessionRecipients.length === 1) { | |
| return { recipients: [sessionRecipients[0].to], source: "session-single" }; | |
| } | |
| if (sessionRecipients.length > 1) { | |
| return { | |
| recipients: sessionRecipients.map((s) => s.to), | |
| source: "session-ambiguous", | |
| }; | |
| } | |
| return { recipients: allowFrom, source: "allowFrom" }; | |
| } | |