diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts index 6027084..43d21b3 100644 --- a/src/bot/orchestrator.ts +++ b/src/bot/orchestrator.ts @@ -179,7 +179,7 @@ export class BotOrchestrator { /** * Join a meeting as authenticated user (System Bot or User Account). - * Flow: teams.microsoft.com → MS Login → Teams Chat → Join → Pre-Join → Join now + * Flow: teams.microsoft.com → MS Login → Navigate to meeting URL → Pre-Join → Join now */ private async _attemptAuthJoin(): Promise { // Launch browser in headful mode with minimal args (Chromium Minimal) @@ -187,7 +187,7 @@ export class BotOrchestrator { this._setState('navigating'); - // STEP 1: Navigate to teams.microsoft.com + // STEP 1: Navigate to teams.microsoft.com to trigger authentication this._logger.info('Auth join: navigating to teams.microsoft.com'); await this._page!.goto('https://teams.microsoft.com', { waitUntil: 'domcontentloaded', @@ -225,51 +225,65 @@ export class BotOrchestrator { } this._logger.info('Authentication successful'); - // STEP 3: Wait for Teams chat page (landing page after auth) + // STEP 3: Wait for Teams to finish loading after auth + this._logger.info('Waiting for Teams to load after auth...'); try { await this._page!.waitForURL('**/teams.microsoft.com/**', { timeout: 30000 }); } catch { - // Also accept teams.cloud.microsoft try { await this._page!.waitForURL('**/teams.cloud.microsoft/**', { timeout: 10000 }); } catch { this._logger.warn(`Unexpected URL after auth: ${this._page!.url().substring(0, 150)}`); } } + // Give Teams a moment to initialize (session cookies, service workers) + await this._page!.waitForTimeout(3000); - // Wait for "Join" button in chat header + // STEP 4: Navigate directly to the meeting URL (authenticated session) + // This is the key step: the previous flow waited for a "Join" button in + // the Teams chat header, which only works if the meeting chat happens to + // be visible. Navigating directly to the meeting URL reliably shows the + // pre-join screen for authenticated users. + this._logger.info(`Auth join: navigating to meeting URL: ${this._meetingUrl.substring(0, 80)}...`); + await this._page!.goto(this._meetingUrl, { + waitUntil: 'domcontentloaded', + timeout: 30000, + }); + + // Teams might show a "Continue on this browser" interstitial or redirect + // Wait for the page to settle + await this._page!.waitForTimeout(3000); + const currentUrl = this._page!.url(); + this._logger.info(`Auth join: URL after meeting navigation: ${currentUrl.substring(0, 150)}`); + + // Handle "Continue on this browser" if shown (same as anonymous flow) try { - await this._page!.waitForSelector( - 'button[data-tid="chat-join-button"], button[data-tid="join-call-button"]', - { timeout: 30000, state: 'visible' }, - ); - this._logger.info('Teams chat page loaded, "Join" button found'); - } catch { - this._logger.warn('"Join" button not found in chat header'); - await this._takeScreenshot('auth-no-join-button'); - } + const continueBtn = await this._page!.$('button:has-text("Continue on this browser"), button:has-text("In diesem Browser fortfahren")'); + if (continueBtn) { + await continueBtn.click(); + this._logger.info('Clicked "Continue on this browser"'); + await this._page!.waitForTimeout(3000); + } + } catch { /* not shown for auth users — expected */ } - // STEP 4: Click "Join" in chat header → Pre-Join screen - this._logger.info('Clicking "Join" in chat header'); + // Handle "Join" button if Teams shows the chat view first + // (some meeting URLs redirect to the chat thread with a Join button) const chatJoinSelectors = [ 'button[data-tid="chat-join-button"]', 'button[data-tid="join-call-button"]', ]; - let chatJoinClicked = false; for (const selector of chatJoinSelectors) { try { - const btn = await this._page!.waitForSelector(selector, { timeout: 5000, state: 'visible' }); + const btn = await this._page!.$(selector); if (btn) { await btn.click(); chatJoinClicked = true; + this._logger.info(`Clicked chat "Join" button: ${selector}`); + await this._page!.waitForTimeout(3000); break; } - } catch { /* try next */ } - } - - if (!chatJoinClicked) { - throw new Error('"Join" button in chat header not found'); + } catch { /* continue */ } } // STEP 5: Pre-Join screen → Click "Join now" @@ -284,7 +298,6 @@ export class BotOrchestrator { await this._takeScreenshot('auth-no-join-now'); } - // Camera stays OFF — no video injection, focus on communication stability this._logger.info('Camera left OFF (video disabled for stability)'); // Ensure microphone is ON (required for voice playback) @@ -311,6 +324,7 @@ export class BotOrchestrator { } if (!joinNowClicked) { + await this._takeScreenshot('auth-no-join-now-final'); throw new Error('"Join now" button not found on pre-join screen'); }