115 lines
3.5 KiB
JavaScript
115 lines
3.5 KiB
JavaScript
// scripts/fetch-meeting-artifacts.js
|
|
const { writeFileSync } = require('node:fs');
|
|
const {
|
|
loadEnv,
|
|
buildGraphClient,
|
|
resolveOnlineMeeting,
|
|
fetchTranscriptVtt,
|
|
fetchAiInsights,
|
|
fetchRecordingUrl
|
|
} = require('./lib/graph-meetings.js');
|
|
const { extractJoinUrlFromBody } = require('./lib/o365-calendar.js');
|
|
const { parseVtt, formatTranscript } = require('./lib/vtt-parser.js');
|
|
|
|
function parseArgs(argv) {
|
|
const out = {};
|
|
for (let i = 2; i < argv.length; i++) {
|
|
const a = argv[i];
|
|
if (a === '--o365-id') out.o365Id = argv[++i];
|
|
else if (a === '--join-url') out.joinUrl = argv[++i];
|
|
else if (a === '--out') out.out = argv[++i];
|
|
else if (a === '--user') out.user = argv[++i];
|
|
}
|
|
return out;
|
|
}
|
|
|
|
async function runFetch(client, userId, opts) {
|
|
const warnings = [];
|
|
let event = null;
|
|
let joinUrl = opts.joinUrl || null;
|
|
let seriesMasterId = null;
|
|
|
|
if (opts.o365Id) {
|
|
event = await client.api(`/users/${userId}/events/${opts.o365Id}`)
|
|
.select('id,subject,start,end,body,attendees,seriesMasterId,onlineMeeting')
|
|
.get();
|
|
seriesMasterId = event.seriesMasterId || null;
|
|
if (!joinUrl) {
|
|
joinUrl = event.onlineMeeting?.joinUrl
|
|
|| extractJoinUrlFromBody(event.body?.content || '');
|
|
}
|
|
}
|
|
|
|
if (!joinUrl) {
|
|
warnings.push('no teams join url on event');
|
|
return {
|
|
meeting: event ? eventSummary(event) : null,
|
|
transcript: null, recap: null, recordingUrl: null, warnings
|
|
};
|
|
}
|
|
|
|
const om = await resolveOnlineMeeting(client, userId, joinUrl);
|
|
if (!om) {
|
|
warnings.push(`onlineMeeting not found for joinUrl=${joinUrl}`);
|
|
return {
|
|
meeting: event ? eventSummary(event) : null,
|
|
transcript: null, recap: null, recordingUrl: null, warnings
|
|
};
|
|
}
|
|
|
|
const [vtt, recap, recordingUrl] = await Promise.all([
|
|
fetchTranscriptVtt(client, userId, om.id).catch(e => { warnings.push(`transcript: ${e.message}`); return null; }),
|
|
fetchAiInsights(client, userId, om.id).catch(e => { warnings.push(`aiInsights: ${e.message}`); return null; }),
|
|
fetchRecordingUrl(client, userId, om.id).catch(e => { warnings.push(`recording: ${e.message}`); return null; })
|
|
]);
|
|
|
|
let transcript = null;
|
|
if (vtt) {
|
|
const cues = parseVtt(vtt, { mergeConsecutive: true });
|
|
transcript = formatTranscript(cues);
|
|
}
|
|
|
|
return {
|
|
meeting: event ? { ...eventSummary(event), onlineMeetingId: om.id, seriesMasterId } : { onlineMeetingId: om.id },
|
|
transcript,
|
|
recap,
|
|
recordingUrl,
|
|
warnings
|
|
};
|
|
}
|
|
|
|
function eventSummary(event) {
|
|
return {
|
|
id: event.id,
|
|
subject: event.subject,
|
|
start: event.start?.dateTime,
|
|
end: event.end?.dateTime,
|
|
seriesMasterId: event.seriesMasterId || null,
|
|
attendees: (event.attendees || []).map(a => ({
|
|
name: a.emailAddress?.name,
|
|
email: (a.emailAddress?.address || '').toLowerCase()
|
|
}))
|
|
};
|
|
}
|
|
|
|
async function main() {
|
|
const opts = parseArgs(process.argv);
|
|
if (!opts.o365Id && !opts.joinUrl) {
|
|
console.error('Usage: node fetch-meeting-artifacts.js --o365-id <id> [--out file.json]');
|
|
process.exit(2);
|
|
}
|
|
const env = loadEnv();
|
|
const userId = opts.user || env.AZURE_USER_EMAIL;
|
|
const client = await buildGraphClient(env);
|
|
const result = await runFetch(client, userId, opts);
|
|
const json = JSON.stringify(result, null, 2);
|
|
if (opts.out) writeFileSync(opts.out, json, 'utf-8');
|
|
else process.stdout.write(json + '\n');
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main().catch(err => { console.error(err.stack || err.message); process.exit(1); });
|
|
}
|
|
|
|
module.exports = { runFetch, parseArgs };
|