UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

221 lines (220 loc) 7.58 kB
import { a as normalizeLowercaseStringOrEmpty, s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js"; import "./string-coerce-runtime-CEGJWkQ_.js"; import { a as mapAllowlistResolutionInputs } from "./allow-from-DCaV3hzI.js"; import { c as normalizeQuery, f as resolveGraphToken, o as listChannelsForTeam, s as listTeamsByName, t as searchGraphUsers } from "./graph-users-DhhsJ2VT.js"; //#region extensions/msteams/src/resolve-allowlist.ts function stripProviderPrefix(raw) { return raw.replace(/^(msteams|teams):/i, ""); } function normalizeMSTeamsMessagingTarget(raw) { let trimmed = raw.trim(); if (!trimmed) return; trimmed = stripProviderPrefix(trimmed).trim(); if (/^conversation:/i.test(trimmed)) { const id = trimmed.slice(13).trim(); return id ? `conversation:${id}` : void 0; } if (/^user:/i.test(trimmed)) { const id = trimmed.slice(5).trim(); return id ? `user:${id}` : void 0; } return trimmed || void 0; } function normalizeMSTeamsUserInput(raw) { return stripProviderPrefix(raw).replace(/^(user|conversation):/i, "").trim(); } function parseMSTeamsConversationId(raw) { const trimmed = stripProviderPrefix(raw).trim(); if (!/^conversation:/i.test(trimmed)) return null; return trimmed.slice(13).trim(); } /** * Detect whether a raw target string looks like a Microsoft Teams conversation * or user id that cron announce delivery and other explicit-target paths can * forward verbatim to the channel adapter. * * Accepts both prefixed and bare formats: * - `conversation:<id>` — explicit conversation prefix * - `user:<aad-guid>` — user id (16+ hex chars, UUID-like) * - `19:abc@thread.tacv2` / `19:abc@thread.skype` — channel / legacy group * - `19:{userId}_{appId}@unq.gbl.spaces` — Graph 1:1 chat thread format * - `a:1xxx` — Bot Framework personal (1:1) chat id * - `8:orgid:xxx` — Bot Framework org-scoped personal chat id * - `29:xxx` — Bot Framework user id * * Display-name user targets such as `user:John Smith` intentionally return * false so that the Graph API directory lookup still runs for them. */ function looksLikeMSTeamsTargetId(raw) { const trimmed = raw.trim(); if (!trimmed) return false; if (/^conversation:/i.test(trimmed)) return true; if (/^user:/i.test(trimmed)) { const id = trimmed.slice(5).trim(); return /^[0-9a-fA-F-]{16,}$/.test(id); } if (/^19:.+@thread\.(tacv2|skype)$/i.test(trimmed)) return true; if (/^19:.+@unq\.gbl\.spaces$/i.test(trimmed)) return true; if (/^a:1[A-Za-z0-9_-]+$/i.test(trimmed)) return true; if (/^8:orgid:[A-Za-z0-9-]+$/i.test(trimmed)) return true; if (/^29:[A-Za-z0-9_-]+$/i.test(trimmed)) return true; return /@thread\b/i.test(trimmed); } function normalizeMSTeamsTeamKey(raw) { return stripProviderPrefix(raw).replace(/^team:/i, "").trim() || void 0; } function normalizeMSTeamsChannelKey(raw) { return (raw?.trim().replace(/^#/, "").trim() ?? "") || void 0; } function normalizeMSTeamsConversationTargetId(raw) { const trimmed = stripProviderPrefix(raw).trim(); return parseMSTeamsConversationId(trimmed) ?? trimmed; } function looksLikeMSTeamsThreadConversationId(raw) { const normalized = normalizeMSTeamsConversationTargetId(raw); return /^19:.+@thread\./i.test(normalized); } function parseMSTeamsTeamChannelInput(raw) { const trimmed = stripProviderPrefix(raw).trim(); if (!trimmed) return {}; const parts = trimmed.split("/"); const team = normalizeMSTeamsTeamKey(parts[0] ?? ""); const channel = parts.length > 1 ? normalizeMSTeamsChannelKey(parts.slice(1).join("/")) : void 0; return { ...team ? { team } : {}, ...channel ? { channel } : {} }; } function parseMSTeamsTeamEntry(raw) { const { team, channel } = parseMSTeamsTeamChannelInput(raw); if (!team) return null; return { teamKey: team, ...channel ? { channelKey: channel } : {} }; } async function resolveMSTeamsChannelAllowlist(params) { let tokenPromise; const getToken = () => { tokenPromise ??= resolveGraphToken(params.cfg); return tokenPromise; }; return await mapAllowlistResolutionInputs({ inputs: params.entries, mapInput: async (input) => { const { team, channel } = parseMSTeamsTeamChannelInput(input); if (!team) return { input, resolved: false }; if (looksLikeMSTeamsThreadConversationId(team)) { const teamId = normalizeMSTeamsConversationTargetId(team); if (!channel) return { input, resolved: true, teamId, teamName: teamId }; if (!looksLikeMSTeamsThreadConversationId(channel)) return { input, resolved: false, teamId, teamName: teamId, note: "channel id required for conversation-id team" }; const channelId = normalizeMSTeamsConversationTargetId(channel); return { input, resolved: true, teamId, teamName: teamId, channelId, channelName: channelId }; } const token = await getToken(); const teams = /^[0-9a-fA-F-]{16,}$/.test(team) ? [{ id: team, displayName: team }] : await listTeamsByName(token, team); if (teams.length === 0) return { input, resolved: false, note: "team not found" }; const teamMatch = teams[0]; const graphTeamId = teamMatch.id?.trim(); const teamName = teamMatch.displayName?.trim() || team; if (!graphTeamId) return { input, resolved: false, note: "team id missing" }; let teamChannels = []; try { teamChannels = await listChannelsForTeam(token, graphTeamId); } catch {} const teamId = teamChannels.find((ch) => normalizeOptionalLowercaseString(ch.displayName) === "general")?.id?.trim() || graphTeamId; if (!channel) return { input, resolved: true, teamId, teamName, note: teams.length > 1 ? "multiple teams; chose first" : void 0 }; const normalizedChannel = normalizeOptionalLowercaseString(channel); const channelMatch = teamChannels.find((item) => item.id === channel) ?? teamChannels.find((item) => normalizeOptionalLowercaseString(item.displayName) === normalizedChannel) ?? teamChannels.find((item) => normalizeLowercaseStringOrEmpty(item.displayName ?? "").includes(normalizedChannel ?? "")); if (!channelMatch?.id) return { input, resolved: false, note: "channel not found" }; return { input, resolved: true, teamId, teamName, channelId: channelMatch.id, channelName: channelMatch.displayName ?? channel, note: teamChannels.length > 1 ? "multiple channels; chose first" : void 0 }; } }); } async function resolveMSTeamsUserAllowlist(params) { const token = await resolveGraphToken(params.cfg); return await mapAllowlistResolutionInputs({ inputs: params.entries, mapInput: async (input) => { const query = normalizeQuery(normalizeMSTeamsUserInput(input)); if (!query) return { input, resolved: false }; if (/^[0-9a-fA-F-]{16,}$/.test(query)) return { input, resolved: true, id: query }; const users = await searchGraphUsers({ token, query, top: 10 }); const match = users[0]; if (!match?.id) return { input, resolved: false }; return { input, resolved: true, id: match.id, name: match.displayName ?? void 0, note: users.length > 1 ? "multiple matches; chose first" : void 0 }; } }); } //#endregion export { parseMSTeamsTeamChannelInput as a, resolveMSTeamsUserAllowlist as c, parseMSTeamsConversationId as i, normalizeMSTeamsMessagingTarget as n, parseMSTeamsTeamEntry as o, normalizeMSTeamsUserInput as r, resolveMSTeamsChannelAllowlist as s, looksLikeMSTeamsTargetId as t };