fix: use Teams v2 stable selectors (#prejoin-join-button, data-tid) with 20s waitForSelector for Join button
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d8d1ffec17
commit
2c6a1f1d38
2 changed files with 70 additions and 30 deletions
|
|
@ -203,26 +203,33 @@ export class JoinProcedure {
|
||||||
// First, dismiss any "no audio/video" modal that may be blocking
|
// First, dismiss any "no audio/video" modal that may be blocking
|
||||||
await this._dismissNoAudioVideoModal();
|
await this._dismissNoAudioVideoModal();
|
||||||
|
|
||||||
// Primary selector - confirmed working by Recall.ai (Jan 2025)
|
// Teams v2 uses stable IDs for the join button. Wait with multiple selectors.
|
||||||
const primarySelector = 'button:has-text("Join now")';
|
// The button may take time to render in the Teams v2 SPA after auth redirect.
|
||||||
|
const joinSelectors = [
|
||||||
|
'#prejoin-join-button', // Teams v2 stable ID
|
||||||
|
'button[data-tid="prejoin-join-button"]', // Teams v2 data-tid
|
||||||
|
'button:has-text("Join now")', // Text-based (light-meetings)
|
||||||
|
'button:has-text("Join meeting")', // Alternative text
|
||||||
|
];
|
||||||
|
|
||||||
|
const combinedSelector = joinSelectors.join(', ');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._page.waitForSelector(primarySelector, { timeout: 15000 });
|
await this._page.waitForSelector(combinedSelector, { timeout: 20000, state: 'visible' });
|
||||||
await this._page.click(primarySelector);
|
const button = await this._page.$(combinedSelector);
|
||||||
|
if (button) {
|
||||||
|
await button.click();
|
||||||
this._logger.info('Clicked "Join now" button');
|
this._logger.info('Clicked "Join now" button');
|
||||||
|
|
||||||
// After clicking Join, Teams may show the modal again. Dismiss if present.
|
|
||||||
await this._page.waitForTimeout(2000);
|
await this._page.waitForTimeout(2000);
|
||||||
await this._dismissNoAudioVideoModal();
|
await this._dismissNoAudioVideoModal();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
this._logger.info('Primary join button selector not found, trying fallbacks...');
|
this._logger.info('Join button not found with combined selectors, trying text fallbacks...');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback selectors
|
// Last resort fallback: any button with "Join" text
|
||||||
const fallbackSelectors = [
|
const fallbackSelectors = [
|
||||||
'button[data-tid="prejoin-join-button"]',
|
|
||||||
'button:has-text("Join meeting")',
|
|
||||||
'button:has-text("Join")',
|
'button:has-text("Join")',
|
||||||
'[data-tid="joinButton"]',
|
'[data-tid="joinButton"]',
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -218,22 +218,55 @@ export class BotOrchestrator {
|
||||||
const postAuthUrl = this._page!.url();
|
const postAuthUrl = this._page!.url();
|
||||||
this._logger.info(`Post-auth URL: ${postAuthUrl.substring(0, 100)}`);
|
this._logger.info(`Post-auth URL: ${postAuthUrl.substring(0, 100)}`);
|
||||||
|
|
||||||
// After login, Microsoft may redirect to M365/Office instead of back to the meeting.
|
// After login, Microsoft redirects to M365 (m365.cloud.microsoft/chat/).
|
||||||
// We need to navigate back to the meeting URL -- now with the auth session active.
|
// The "Continue on this browser" launcher ALWAYS leads to anonymous mode.
|
||||||
// This time, Teams should recognize the auth and show the authenticated pre-join page.
|
// Instead, navigate to teams.cloud.microsoft and use the meeting join from there.
|
||||||
if (!postAuthUrl.includes('teams.microsoft.com/v2') || !postAuthUrl.includes('meet')) {
|
// teams.cloud.microsoft is the new Teams domain where the auth session lives.
|
||||||
this._logger.info('Not on Teams meeting page after auth - navigating back to meeting URL...');
|
const { parseMeetingUrl } = await import('./meetingUrlParser');
|
||||||
await this._page!.goto(this._meetingUrl, {
|
const parsed = parseMeetingUrl(this._meetingUrl);
|
||||||
waitUntil: 'domcontentloaded',
|
const meetingId = parsed.meetingId || '';
|
||||||
timeout: 30000,
|
const passcode = parsed.passcode || '';
|
||||||
});
|
|
||||||
|
|
||||||
// Handle launcher dialog if it appears again
|
// Navigate to Teams on the cloud.microsoft domain (where auth cookies are)
|
||||||
await this._joinProcedure!.handleLauncherIfPresent();
|
// and try to join the meeting from within the authenticated app
|
||||||
|
const teamsCloudUrls = [
|
||||||
|
// Direct meeting URL on new Teams domain
|
||||||
|
`https://teams.cloud.microsoft/meet/${meetingId}${passcode ? '?p=' + passcode : ''}`,
|
||||||
|
// Original meeting URL (teams.microsoft.com)
|
||||||
|
this._meetingUrl,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const url of teamsCloudUrls) {
|
||||||
|
this._logger.info(`Trying authenticated meeting URL: ${url.substring(0, 80)}`);
|
||||||
|
try {
|
||||||
|
await this._page!.goto(url, {
|
||||||
|
waitUntil: 'domcontentloaded',
|
||||||
|
timeout: 20000,
|
||||||
|
});
|
||||||
await this._page!.waitForTimeout(5000);
|
await this._page!.waitForTimeout(5000);
|
||||||
|
|
||||||
const meetingPageUrl = this._page!.url();
|
const resultUrl = this._page!.url();
|
||||||
this._logger.info(`After re-navigation to meeting: ${meetingPageUrl.substring(0, 100)}`);
|
const pageContent = await this._page!.evaluate(() => document.body?.innerText?.substring(0, 500) || '');
|
||||||
|
this._logger.info(`Result URL: ${resultUrl.substring(0, 100)}`);
|
||||||
|
this._logger.info(`Page content: ${pageContent.substring(0, 200)}`);
|
||||||
|
|
||||||
|
// Check if we have the authenticated pre-join (no "Type your name", has "Join now")
|
||||||
|
if (pageContent.includes('Join now') && !pageContent.includes('Type your name') && !pageContent.includes('Enter the name')) {
|
||||||
|
this._logger.info('Found authenticated pre-join page!');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If launcher appears, DON'T click "Continue on this browser" (leads to anon)
|
||||||
|
// Instead try the next URL
|
||||||
|
if (pageContent.includes('Continue on this browser')) {
|
||||||
|
this._logger.info('Launcher appeared - skipping (would lead to anonymous)');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (navErr) {
|
||||||
|
this._logger.warn(`Navigation to ${url.substring(0, 60)} failed: ${navErr}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify we're on the authenticated pre-join page
|
// Verify we're on the authenticated pre-join page
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue