Spaces:
Running
Running
| import type { OpenClawConfig } from "../../config/types.js"; | |
| import type { ChannelDirectoryEntry } from "./types.js"; | |
| import { resolveDiscordAccount } from "../../discord/accounts.js"; | |
| import { resolveSlackAccount } from "../../slack/accounts.js"; | |
| import { resolveTelegramAccount } from "../../telegram/accounts.js"; | |
| import { resolveWhatsAppAccount } from "../../web/accounts.js"; | |
| import { isWhatsAppGroupJid, normalizeWhatsAppTarget } from "../../whatsapp/normalize.js"; | |
| import { normalizeSlackMessagingTarget } from "./normalize/slack.js"; | |
| export type DirectoryConfigParams = { | |
| cfg: OpenClawConfig; | |
| accountId?: string | null; | |
| query?: string | null; | |
| limit?: number | null; | |
| }; | |
| export async function listSlackDirectoryPeersFromConfig( | |
| params: DirectoryConfigParams, | |
| ): Promise<ChannelDirectoryEntry[]> { | |
| const account = resolveSlackAccount({ cfg: params.cfg, accountId: params.accountId }); | |
| const q = params.query?.trim().toLowerCase() || ""; | |
| const ids = new Set<string>(); | |
| for (const entry of account.dm?.allowFrom ?? []) { | |
| const raw = String(entry).trim(); | |
| if (!raw || raw === "*") { | |
| continue; | |
| } | |
| ids.add(raw); | |
| } | |
| for (const id of Object.keys(account.config.dms ?? {})) { | |
| const trimmed = id.trim(); | |
| if (trimmed) { | |
| ids.add(trimmed); | |
| } | |
| } | |
| for (const channel of Object.values(account.config.channels ?? {})) { | |
| for (const user of channel.users ?? []) { | |
| const raw = String(user).trim(); | |
| if (raw) { | |
| ids.add(raw); | |
| } | |
| } | |
| } | |
| return Array.from(ids) | |
| .map((raw) => raw.trim()) | |
| .filter(Boolean) | |
| .map((raw) => { | |
| const mention = raw.match(/^<@([A-Z0-9]+)>$/i); | |
| const normalizedUserId = (mention?.[1] ?? raw).replace(/^(slack|user):/i, "").trim(); | |
| if (!normalizedUserId) { | |
| return null; | |
| } | |
| const target = `user:${normalizedUserId}`; | |
| return normalizeSlackMessagingTarget(target) ?? target.toLowerCase(); | |
| }) | |
| .filter((id): id is string => Boolean(id)) | |
| .filter((id) => id.startsWith("user:")) | |
| .filter((id) => (q ? id.toLowerCase().includes(q) : true)) | |
| .slice(0, params.limit && params.limit > 0 ? params.limit : undefined) | |
| .map((id) => ({ kind: "user", id }) as const); | |
| } | |
| export async function listSlackDirectoryGroupsFromConfig( | |
| params: DirectoryConfigParams, | |
| ): Promise<ChannelDirectoryEntry[]> { | |
| const account = resolveSlackAccount({ cfg: params.cfg, accountId: params.accountId }); | |
| const q = params.query?.trim().toLowerCase() || ""; | |
| return Object.keys(account.config.channels ?? {}) | |
| .map((raw) => raw.trim()) | |
| .filter(Boolean) | |
| .map((raw) => normalizeSlackMessagingTarget(raw) ?? raw.toLowerCase()) | |
| .filter((id) => id.startsWith("channel:")) | |
| .filter((id) => (q ? id.toLowerCase().includes(q) : true)) | |
| .slice(0, params.limit && params.limit > 0 ? params.limit : undefined) | |
| .map((id) => ({ kind: "group", id }) as const); | |
| } | |
| export async function listDiscordDirectoryPeersFromConfig( | |
| params: DirectoryConfigParams, | |
| ): Promise<ChannelDirectoryEntry[]> { | |
| const account = resolveDiscordAccount({ cfg: params.cfg, accountId: params.accountId }); | |
| const q = params.query?.trim().toLowerCase() || ""; | |
| const ids = new Set<string>(); | |
| for (const entry of account.config.dm?.allowFrom ?? []) { | |
| const raw = String(entry).trim(); | |
| if (!raw || raw === "*") { | |
| continue; | |
| } | |
| ids.add(raw); | |
| } | |
| for (const id of Object.keys(account.config.dms ?? {})) { | |
| const trimmed = id.trim(); | |
| if (trimmed) { | |
| ids.add(trimmed); | |
| } | |
| } | |
| for (const guild of Object.values(account.config.guilds ?? {})) { | |
| for (const entry of guild.users ?? []) { | |
| const raw = String(entry).trim(); | |
| if (raw) { | |
| ids.add(raw); | |
| } | |
| } | |
| for (const channel of Object.values(guild.channels ?? {})) { | |
| for (const user of channel.users ?? []) { | |
| const raw = String(user).trim(); | |
| if (raw) { | |
| ids.add(raw); | |
| } | |
| } | |
| } | |
| } | |
| return Array.from(ids) | |
| .map((raw) => raw.trim()) | |
| .filter(Boolean) | |
| .map((raw) => { | |
| const mention = raw.match(/^<@!?(\d+)>$/); | |
| const cleaned = (mention?.[1] ?? raw).replace(/^(discord|user):/i, "").trim(); | |
| if (!/^\d+$/.test(cleaned)) { | |
| return null; | |
| } | |
| return `user:${cleaned}`; | |
| }) | |
| .filter((id): id is string => Boolean(id)) | |
| .filter((id) => (q ? id.toLowerCase().includes(q) : true)) | |
| .slice(0, params.limit && params.limit > 0 ? params.limit : undefined) | |
| .map((id) => ({ kind: "user", id }) as const); | |
| } | |
| export async function listDiscordDirectoryGroupsFromConfig( | |
| params: DirectoryConfigParams, | |
| ): Promise<ChannelDirectoryEntry[]> { | |
| const account = resolveDiscordAccount({ cfg: params.cfg, accountId: params.accountId }); | |
| const q = params.query?.trim().toLowerCase() || ""; | |
| const ids = new Set<string>(); | |
| for (const guild of Object.values(account.config.guilds ?? {})) { | |
| for (const channelId of Object.keys(guild.channels ?? {})) { | |
| const trimmed = channelId.trim(); | |
| if (trimmed) { | |
| ids.add(trimmed); | |
| } | |
| } | |
| } | |
| return Array.from(ids) | |
| .map((raw) => raw.trim()) | |
| .filter(Boolean) | |
| .map((raw) => { | |
| const mention = raw.match(/^<#(\d+)>$/); | |
| const cleaned = (mention?.[1] ?? raw).replace(/^(discord|channel|group):/i, "").trim(); | |
| if (!/^\d+$/.test(cleaned)) { | |
| return null; | |
| } | |
| return `channel:${cleaned}`; | |
| }) | |
| .filter((id): id is string => Boolean(id)) | |
| .filter((id) => (q ? id.toLowerCase().includes(q) : true)) | |
| .slice(0, params.limit && params.limit > 0 ? params.limit : undefined) | |
| .map((id) => ({ kind: "group", id }) as const); | |
| } | |
| export async function listTelegramDirectoryPeersFromConfig( | |
| params: DirectoryConfigParams, | |
| ): Promise<ChannelDirectoryEntry[]> { | |
| const account = resolveTelegramAccount({ cfg: params.cfg, accountId: params.accountId }); | |
| const q = params.query?.trim().toLowerCase() || ""; | |
| const raw = [ | |
| ...(account.config.allowFrom ?? []).map((entry) => String(entry)), | |
| ...Object.keys(account.config.dms ?? {}), | |
| ]; | |
| return Array.from( | |
| new Set( | |
| raw | |
| .map((entry) => entry.trim()) | |
| .filter(Boolean) | |
| .map((entry) => entry.replace(/^(telegram|tg):/i, "")), | |
| ), | |
| ) | |
| .map((entry) => { | |
| const trimmed = entry.trim(); | |
| if (!trimmed) { | |
| return null; | |
| } | |
| if (/^-?\d+$/.test(trimmed)) { | |
| return trimmed; | |
| } | |
| const withAt = trimmed.startsWith("@") ? trimmed : `@${trimmed}`; | |
| return withAt; | |
| }) | |
| .filter((id): id is string => Boolean(id)) | |
| .filter((id) => (q ? id.toLowerCase().includes(q) : true)) | |
| .slice(0, params.limit && params.limit > 0 ? params.limit : undefined) | |
| .map((id) => ({ kind: "user", id }) as const); | |
| } | |
| export async function listTelegramDirectoryGroupsFromConfig( | |
| params: DirectoryConfigParams, | |
| ): Promise<ChannelDirectoryEntry[]> { | |
| const account = resolveTelegramAccount({ cfg: params.cfg, accountId: params.accountId }); | |
| const q = params.query?.trim().toLowerCase() || ""; | |
| return Object.keys(account.config.groups ?? {}) | |
| .map((id) => id.trim()) | |
| .filter((id) => Boolean(id) && id !== "*") | |
| .filter((id) => (q ? id.toLowerCase().includes(q) : true)) | |
| .slice(0, params.limit && params.limit > 0 ? params.limit : undefined) | |
| .map((id) => ({ kind: "group", id }) as const); | |
| } | |
| export async function listWhatsAppDirectoryPeersFromConfig( | |
| params: DirectoryConfigParams, | |
| ): Promise<ChannelDirectoryEntry[]> { | |
| const account = resolveWhatsAppAccount({ cfg: params.cfg, accountId: params.accountId }); | |
| const q = params.query?.trim().toLowerCase() || ""; | |
| return (account.allowFrom ?? []) | |
| .map((entry) => String(entry).trim()) | |
| .filter((entry) => Boolean(entry) && entry !== "*") | |
| .map((entry) => normalizeWhatsAppTarget(entry) ?? "") | |
| .filter(Boolean) | |
| .filter((id) => !isWhatsAppGroupJid(id)) | |
| .filter((id) => (q ? id.toLowerCase().includes(q) : true)) | |
| .slice(0, params.limit && params.limit > 0 ? params.limit : undefined) | |
| .map((id) => ({ kind: "user", id }) as const); | |
| } | |
| export async function listWhatsAppDirectoryGroupsFromConfig( | |
| params: DirectoryConfigParams, | |
| ): Promise<ChannelDirectoryEntry[]> { | |
| const account = resolveWhatsAppAccount({ cfg: params.cfg, accountId: params.accountId }); | |
| const q = params.query?.trim().toLowerCase() || ""; | |
| return Object.keys(account.groups ?? {}) | |
| .map((id) => id.trim()) | |
| .filter((id) => Boolean(id) && id !== "*") | |
| .filter((id) => (q ? id.toLowerCase().includes(q) : true)) | |
| .slice(0, params.limit && params.limit > 0 ? params.limit : undefined) | |
| .map((id) => ({ kind: "group", id }) as const); | |
| } | |