From bb304f3db186e2d122c1cc6b86532edce6b8da56 Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Wed, 18 Feb 2026 22:10:27 +0100 Subject: [PATCH] fix: add suppressPrompt/msLaunch params to auth join URL, add joinOnWeb selector Co-authored-by: Cursor --- src/bot/orchestrator.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts index 324a7fb..8749100 100644 --- a/src/bot/orchestrator.ts +++ b/src/bot/orchestrator.ts @@ -15,7 +15,7 @@ import { AudioCaptureProcedure } from './audioCaptureProcedure'; import { ChatProcedure, ChatMessageEntry } from './chatProcedure'; import { AuthProcedure } from './authProcedure'; import { TeamsActionsService } from './teamsActionsService'; -import { isValidMeetingUrl } from './meetingUrlParser'; +import { isValidMeetingUrl, getMeetingLaunchUrl } from './meetingUrlParser'; // Camera / fake video injection is disabled for now to focus on stability. // The Y4M fake video file was causing browser crashes when audio started flowing. @@ -264,9 +264,19 @@ export class BotOrchestrator { } await this._takeScreenshot('step3-teams-loaded', true); - // STEP 4: Navigate to the meeting URL - this._logger.info(`STEP 4: navigating to meeting URL: ${this._meetingUrl.substring(0, 80)}...`); - await this._page!.goto(this._meetingUrl, { + // STEP 4: Navigate to the meeting URL with launch params + // Add suppressPrompt, msLaunch=false, directDl=true to skip the native + // "Open in Teams app?" dialog that blocks Playwright (invisible native prompt). + // For auth join we strip anon=true since the user is authenticated. + let launchUrl = getMeetingLaunchUrl(this._meetingUrl); + try { + const urlObj = new URL(launchUrl); + urlObj.searchParams.delete('anon'); + launchUrl = urlObj.toString(); + } catch { /* keep as-is */ } + + this._logger.info(`STEP 4: navigating to launch URL: ${launchUrl.substring(0, 120)}...`); + await this._page!.goto(launchUrl, { waitUntil: 'domcontentloaded', timeout: 30000, }); @@ -275,9 +285,12 @@ export class BotOrchestrator { // STEP 4a: Poll for first actionable button (interstitial OR pre-join) const interstitialSelectors = [ + 'button[data-tid="joinOnWeb"]', 'button:has-text("Continue on this browser")', 'button:has-text("In diesem Browser fortfahren")', 'button:has-text("Weiter in diesem Browser")', + 'button:has-text("Join on the web instead")', + 'button:has-text("Use web app instead")', 'button[data-tid="chat-join-button"]', 'button[data-tid="join-call-button"]', ]; @@ -303,7 +316,7 @@ export class BotOrchestrator { if (!isPreJoin) { await firstBtn.click(); - this._logger.info(`STEP 4a: clicked interstitial: "${btnText}"`); + this._logger.info(`STEP 4a: clicked interstitial: "${btnText}" (data-tid="${btnTid}")`); await this._takeScreenshot('step4a-after-interstitial', true); } else { this._logger.info(`STEP 4a: pre-join button already visible: "${btnText}"`);