fix(scripts): resolve UPN to OID, fix transcript headers, move aiInsights to /copilot path
- OnlineMeetings API + Application Access Policy match users by OID;
UPN routing returns 404. Resolve UPN -> OID before all Graph calls.
- Transcript content endpoint rejects default Accept '*/*';
add ?$format=text/vtt + Accept header + responseType('text').
- aiInsights moved to /copilot/users/{oid}/onlineMeetings/{mid}/aiInsights (Beta).
List call returns metadata only; fetch detail per insight for
meetingNotes/actionItems/mentions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
18e7737db5
commit
69c7f86fac
|
|
@ -99,8 +99,12 @@ async function main() {
|
|||
process.exit(2);
|
||||
}
|
||||
const env = loadEnv();
|
||||
const userId = opts.user || env.AZURE_USER_EMAIL;
|
||||
const upn = opts.user || env.AZURE_USER_EMAIL;
|
||||
const client = await buildGraphClient(env);
|
||||
// Resolve UPN -> Object ID. OnlineMeetings API + Application Access Policy
|
||||
// match users by OID; UPN routing returns 404 even with correct policy.
|
||||
const me = await client.api('/users/' + upn).select('id').get();
|
||||
const userId = me.id;
|
||||
const result = await runFetch(client, userId, opts);
|
||||
const json = JSON.stringify(result, null, 2);
|
||||
if (opts.out) writeFileSync(opts.out, json, 'utf-8');
|
||||
|
|
|
|||
|
|
@ -62,14 +62,37 @@ async function fetchTranscriptVtt(client, userId, meetingId) {
|
|||
new Date(b.createdDateTime) - new Date(a.createdDateTime)
|
||||
)[0];
|
||||
const contentPath = `/users/${userId}/onlineMeetings/${meetingId}/transcripts/${latest.id}/content`;
|
||||
return await safeGet(client, contentPath);
|
||||
// Transcript content endpoint returns text/vtt; default Accept */* is rejected.
|
||||
try {
|
||||
let req = client.api(contentPath).query({ $format: 'text/vtt' });
|
||||
if (typeof req.header === 'function') req = req.header('Accept', 'text/vtt');
|
||||
if (typeof req.responseType === 'function') req = req.responseType('text');
|
||||
const resp = await req.get();
|
||||
if (typeof resp === 'string') return resp;
|
||||
if (resp && typeof resp.text === 'function') return await resp.text();
|
||||
return resp;
|
||||
} catch (err) {
|
||||
if (err.statusCode === 404 || err.statusCode === 403) return null;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// Copilot recap moved under /copilot/... (beta) ca. 2025-2026.
|
||||
// List call returns metadata only; detail call required for meetingNotes/actionItems.
|
||||
const listPath = `/copilot/users/${userId}/onlineMeetings/${meetingId}/aiInsights`;
|
||||
const list = await safeGet(client, listPath, { version: 'beta' });
|
||||
const items = list?.value || [];
|
||||
if (items.length === 0) return null;
|
||||
const details = [];
|
||||
for (const item of items) {
|
||||
const detail = await safeGet(client, `${listPath}/${item.id}`, { version: 'beta' });
|
||||
if (detail) details.push(detail);
|
||||
}
|
||||
if (details.length === 0) return null;
|
||||
// Return single object if one insight, otherwise wrap.
|
||||
if (details.length === 1) return details[0];
|
||||
return { id: details[0].id, insights: details };
|
||||
}
|
||||
|
||||
async function fetchRecordingUrl(client, userId, meetingId) {
|
||||
|
|
|
|||
|
|
@ -71,8 +71,11 @@ describe('fetchTranscriptVtt', () => {
|
|||
describe('fetchAiInsights', () => {
|
||||
it('returns insights when available', async () => {
|
||||
const client = fakeClient({
|
||||
"/users/u/onlineMeetings/M1/aiInsights": {
|
||||
value: [{ id: 'I1', actionItems: [], meetingNotes: [], mentions: [] }]
|
||||
"/copilot/users/u/onlineMeetings/M1/aiInsights": {
|
||||
value: [{ id: 'I1' }]
|
||||
},
|
||||
"/copilot/users/u/onlineMeetings/M1/aiInsights/I1": {
|
||||
id: 'I1', actionItems: [], meetingNotes: [], mentions: []
|
||||
}
|
||||
});
|
||||
const out = await fetchAiInsights(client, 'u', 'M1');
|
||||
|
|
|
|||
Loading…
Reference in New Issue