/** Escapa texto pra uso seguro dentro de atributos/elementos XML. */ function xmlEscape(value: string): string { return value .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } export interface DirectoryUserParams { domain: string; extensionNumber: string; extensionName: string; sipPassword: string; context: string; callerIdName?: string; callerIdNumber?: string; tenantId: string; extensionId: string; /** Grupo de captura (secao 178) — vira a variable `callgroup` (sem * hífen: é o nome que o `mod_dptools`/convenção FreeSWITCH usa, * confirmado contra a documentação oficial — `${user_data(@ * var callgroup)}` no dialplan lê exatamente esse nome). O * pickup em si não é automático por variável: o dialplan que atende a * ligação pro ramal precisa forkar um leg `pickup/${...essa var...}` * no `bridge`, e uma extension separada de feature code (`*8`) chama a * application `pickup` com o grupo de quem discou. Ver * docs/EXTENSIONS.md — achado real reportado pelo usuário, verificado * contra a documentação do FreeSWITCH antes de close (a primeira versão * usava `call-group`, que não é lido por nada). */ callGroup?: string | null; } /** * Monta o XML de directory que o FreeSWITCH espera do mod_xml_curl pra * autenticar/rotear um ramal (agente.md secao 26). `b2bcall_tenant_id` e * `b2bcall_extension_id` viram channel variables em toda chamada desse * ramal — é assim que o resto do sistema volta a saber de qual tenant uma * chamada é (secao 81). */ export function buildDirectoryUserXml(params: DirectoryUserParams): string { const callerIdName = xmlEscape(params.callerIdName ?? params.extensionName); const callerIdNumber = xmlEscape(params.callerIdNumber ?? params.extensionNumber); return `
${params.callGroup ? `` : ""}
`; } export const NOT_FOUND_XML = `
`;