const { ConfidentialClientApplication } = require('@azure/msal-node'); const { Client } = require('@microsoft/microsoft-graph-client'); const { readFileSync } = require('node:fs'); const { resolve } = require('node:path'); function loadEnv() { const envPath = resolve(__dirname, '..', '.env'); const content = readFileSync(envPath, 'utf-8'); const vars = {}; for (const line of content.split('\n')) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; const [key, ...rest] = trimmed.split('='); vars[key.trim()] = rest.join('=').trim(); } return vars; } async function buildGraphClient(env = loadEnv()) { const cca = new ConfidentialClientApplication({ auth: { clientId: env.AZURE_CLIENT_ID, clientSecret: env.AZURE_CLIENT_SECRET, authority: `https://login.microsoftonline.com/${env.AZURE_TENANT_ID}` } }); const tokenResponse = await cca.acquireTokenByClientCredential({ scopes: ['https://graph.microsoft.com/.default'] }); return Client.init({ authProvider: (done) => done(null, tokenResponse.accessToken), defaultVersion: 'v1.0' }); } async function safeGet(client, path, options = {}) { try { let req = client.api(path); if (options.version && typeof req.version === 'function') req = req.version(options.version); if (options.query && typeof req.query === 'function') req = req.query(options.query); return await req.get(); } catch (err) { if (err.statusCode === 404 || err.statusCode === 403) return null; throw err; } } async function resolveOnlineMeeting(client, userId, joinWebUrl) { const path = `/users/${userId}/onlineMeetings`; const resp = await safeGet(client, path, { query: { $filter: `JoinWebUrl eq '${joinWebUrl}'` } }); const list = resp?.value || []; return list[0] || null; } async function fetchTranscriptVtt(client, userId, meetingId) { const list = await safeGet(client, `/users/${userId}/onlineMeetings/${meetingId}/transcripts`); const items = list?.value || []; if (items.length === 0) return null; const latest = [...items].sort((a, b) => new Date(b.createdDateTime) - new Date(a.createdDateTime) )[0]; const contentPath = `/users/${userId}/onlineMeetings/${meetingId}/transcripts/${latest.id}/content`; return await safeGet(client, contentPath); } async function fetchAiInsights(client, userId, meetingId) { const path = `/users/${userId}/onlineMeetings/${meetingId}/aiInsights`; const resp = await safeGet(client, path, { version: 'beta' }); const items = resp?.value || []; return items[0] || null; } async function fetchRecordingUrl(client, userId, meetingId) { const list = await safeGet(client, `/users/${userId}/onlineMeetings/${meetingId}/recordings`); const items = list?.value || []; if (items.length === 0) return null; const latest = [...items].sort((a, b) => new Date(b.createdDateTime) - new Date(a.createdDateTime) )[0]; return latest.recordingContentUrl || latest.contentCorrelationId || null; } module.exports = { loadEnv, buildGraphClient, resolveOnlineMeeting, fetchTranscriptVtt, fetchAiInsights, fetchRecordingUrl };