From 537975723e7010837b0d1e6608723f0e91e12e5a Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Mon, 16 Feb 2026 16:01:10 +0100
Subject: [PATCH] fix: fallback manual navigation to /v2/ URL when headless
browser does not auto-redirect after auth
Co-authored-by: Cursor
---
src/bot/joinProcedure.ts | 46 ++++++++++++++++++++++++++++++++++++++--
src/bot/orchestrator.ts | 4 ++--
2 files changed, 46 insertions(+), 4 deletions(-)
diff --git a/src/bot/joinProcedure.ts b/src/bot/joinProcedure.ts
index 28f29ec..a9ea7c0 100644
--- a/src/bot/joinProcedure.ts
+++ b/src/bot/joinProcedure.ts
@@ -15,12 +15,14 @@ export class JoinProcedure {
private _logger: Logger;
private _botName: string;
private _isAuthenticated: boolean;
+ private _meetingUrl: string;
- constructor(page: Page, logger: Logger, botName: string, isAuthenticated: boolean = false) {
+ constructor(page: Page, logger: Logger, botName: string, isAuthenticated: boolean = false, meetingUrl: string = '') {
this._page = page;
this._logger = logger;
this._botName = botName;
this._isAuthenticated = isAuthenticated;
+ this._meetingUrl = meetingUrl;
}
/**
@@ -175,7 +177,47 @@ export class JoinProcedure {
}
}
- this._logger.warn('Could not confirm authenticated pre-join page after all retries. Proceeding anyway...');
+ // Fallback: The automatic redirect to /v2/ did not happen (common in headless browsers).
+ // Manually navigate to the /v2/ authenticated pre-join URL with auth cookies.
+ // Format: https://teams.microsoft.com/v2/?meetingjoin=true#/meet/{meetingId}?p={passcode}
+ if (this._meetingUrl) {
+ const v2Url = this._buildV2MeetingUrl(this._meetingUrl);
+ this._logger.info(`Redirect to /v2/ did not happen. Navigating manually to: ${v2Url}`);
+ await this._page.goto(v2Url, { waitUntil: 'domcontentloaded', timeout: 30000 });
+
+ // Wait for the /v2/ page to load and check again
+ for (let attempt = 1; attempt <= 3; attempt++) {
+ await this._page.waitForTimeout(5000);
+ const hasNameInput = await this._page.$('input[placeholder="Type your name"]');
+ const hasJoinButton = await this._page.$('#prejoin-join-button, button[data-tid="prejoin-join-button"], button:has-text("Join now")');
+ const url = this._page.url();
+
+ if (hasJoinButton && !hasNameInput) {
+ this._logger.info(`Authenticated pre-join page confirmed after manual nav (attempt ${attempt}/3). URL: ${url.substring(0, 100)}`);
+ return;
+ }
+
+ this._logger.info(`After manual nav (attempt ${attempt}/3): hasJoin=${!!hasJoinButton}, hasName=${!!hasNameInput}. URL: ${url.substring(0, 100)}`);
+ }
+ }
+
+ this._logger.warn('Could not confirm authenticated pre-join page. Proceeding anyway...');
+ }
+
+ /**
+ * Build the authenticated /v2/ meeting URL from the original meeting URL.
+ * Input: https://teams.microsoft.com/meet/36438888781520?p=5fGqrujxzewPFjJacW
+ * Output: https://teams.microsoft.com/v2/?meetingjoin=true#/meet/36438888781520?p=5fGqrujxzewPFjJacW
+ */
+ private _buildV2MeetingUrl(meetingUrl: string): string {
+ try {
+ const url = new URL(meetingUrl);
+ const pathAndQuery = url.pathname + url.search;
+ return `https://teams.microsoft.com/v2/?meetingjoin=true#${pathAndQuery}`;
+ } catch {
+ this._logger.warn(`Could not parse meeting URL: ${meetingUrl}`);
+ return `https://teams.microsoft.com/v2/?meetingjoin=true`;
+ }
}
/**
diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts
index d2225fb..7a4f8f7 100644
--- a/src/bot/orchestrator.ts
+++ b/src/bot/orchestrator.ts
@@ -133,8 +133,8 @@ export class BotOrchestrator {
// Launch browser
await this._launchBrowser();
- // Update JoinProcedure with correct auth state
- this._joinProcedure = new JoinProcedure(this._page!, this._logger, this._botName, authenticate);
+ // Update JoinProcedure with correct auth state and meeting URL
+ this._joinProcedure = new JoinProcedure(this._page!, this._logger, this._botName, authenticate, this._meetingUrl);
this._setState('navigating');