From 72f57f062d354ee6df631367100e3dedfdbd46ee Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Wed, 18 Feb 2026 20:58:40 +0100
Subject: [PATCH] fix: navigate to meeting URL after auth instead of waiting
for chat Join button
Co-authored-by: Cursor
---
src/bot/orchestrator.ts | 62 +++++++++++++++++++++++++----------------
1 file changed, 38 insertions(+), 24 deletions(-)
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');
}