diff --git a/src/bot/authTestProcedure.ts b/src/bot/authTestProcedure.ts index 60cbaae..e10c0f3 100644 --- a/src/bot/authTestProcedure.ts +++ b/src/bot/authTestProcedure.ts @@ -363,37 +363,105 @@ async function _runVariant( _log('warn', '"Join a meeting" button not found on landing page'); } - // Wait for the resulting page to FULLY load - _log('info', 'Waiting for resulting page to fully load...'); + // Wait for the "Join a meeting" form to FULLY load + _log('info', 'Waiting for Join a meeting form to load...'); await page.waitForTimeout(5000); try { await page.waitForLoadState('networkidle', { timeout: 20000 }); } catch { _log('warn', 'networkidle timeout, continuing'); } - await page.waitForTimeout(5000); - await _screenshotStep('3 - Nach "Join a meeting"'); + await page.waitForTimeout(3000); + await _screenshotStep('3 - Join a meeting Formular'); - // If there's an input for meeting URL, enter it - _log('info', 'Looking for meeting URL input...'); - const meetingInputSelectors = [ - 'input[placeholder*="meeting" i]', - 'input[placeholder*="code" i]', - 'input[placeholder*="link" i]', - 'input[placeholder*="ID" i]', - 'input[type="text"]', - 'input[type="url"]', + // Parse meeting URL to extract Meeting ID and Passcode + // URL format: https://teams.microsoft.com/meet/36438888781520?p=5fGqrujxzewPFjJacW + const { meetingId, passcode } = _parseMeetingUrl(meetingUrl); + _log('info', `Parsed meeting URL: ID="${meetingId}", Passcode="${passcode ? passcode.substring(0, 5) + '...' : 'none'}"`); + + // Fill Meeting ID field + if (meetingId) { + _log('info', 'Looking for Meeting ID input...'); + const idInputSelectors = [ + 'input[placeholder*="meeting" i]', + 'input[placeholder*="Meeting ID" i]', + 'input[placeholder*="ID" i]', + 'input[aria-label*="Meeting ID" i]', + 'input[type="text"]:first-of-type', + ]; + + let idFilled = false; + for (const selector of idInputSelectors) { + try { + const input = await page.waitForSelector(selector, { timeout: 5000, state: 'visible' }); + if (input) { + await input.fill(meetingId); + _log('info', `Entered Meeting ID in: ${selector}`); + idFilled = true; + await page.waitForTimeout(1000); + break; + } + } catch { + // Try next + } + } + if (!idFilled) { + _log('warn', 'Meeting ID input not found'); + } + } + + // Fill Passcode field + if (passcode) { + _log('info', 'Looking for Passcode input...'); + const passcodeSelectors = [ + 'input[placeholder*="passcode" i]', + 'input[placeholder*="Passcode" i]', + 'input[placeholder*="password" i]', + 'input[aria-label*="passcode" i]', + 'input[aria-label*="Passcode" i]', + ]; + + let passcodeFilled = false; + for (const selector of passcodeSelectors) { + try { + const input = await page.waitForSelector(selector, { timeout: 5000, state: 'visible' }); + if (input) { + await input.fill(passcode); + _log('info', `Entered Passcode in: ${selector}`); + passcodeFilled = true; + await page.waitForTimeout(1000); + break; + } + } catch { + // Try next + } + } + if (!passcodeFilled) { + _log('warn', 'Passcode input not found'); + } + } + + await _screenshotStep('4 - Meeting-Daten eingegeben'); + + // Click "Join meeting" button + _log('info', 'Looking for "Join meeting" button...'); + const joinBtnSelectors = [ + 'button:has-text("Join meeting")', + 'button:has-text("Besprechung beitreten")', + 'button:has-text("Join now")', + 'button:has-text("Jetzt teilnehmen")', + 'button[data-tid="joinMeetingButton"]', + 'button[data-tid="join-meeting-button"]', ]; - let meetingInputFound = false; - for (const selector of meetingInputSelectors) { + let joinClicked = false; + for (const selector of joinBtnSelectors) { try { - const input = await page.waitForSelector(selector, { timeout: 5000, state: 'visible' }); - if (input) { - await input.fill(meetingUrl); - _log('info', `Entered meeting URL in: ${selector}`); - meetingInputFound = true; - await page.waitForTimeout(3000); + const btn = await page.waitForSelector(selector, { timeout: 5000, state: 'visible' }); + if (btn) { + await btn.click(); + _log('info', `Clicked "Join meeting": ${selector}`); + joinClicked = true; break; } } catch { @@ -401,12 +469,36 @@ async function _runVariant( } } - if (meetingInputFound) { - await page.waitForTimeout(5000); - await _screenshotStep('4 - Meeting URL eingegeben'); - } else { - _log('warn', 'No meeting URL input field found'); + if (!joinClicked) { + _log('warn', '"Join meeting" button not found — logging all visible buttons'); + try { + const buttons = await page.evaluate(() => { + const btns = document.querySelectorAll('button'); + return Array.from(btns).slice(0, 20).map(b => { + const text = (b.textContent || '').trim().substring(0, 80); + const tid = b.getAttribute('data-tid') || ''; + const disabled = b.disabled ? ' DISABLED' : ''; + return `[BUTTON tid="${tid}"${disabled}] ${text}`; + }); + }); + buttons.forEach(b => _log('info', ` ${b}`)); + } catch { + // Ignore + } } + + // Wait for resulting page after clicking Join + if (joinClicked) { + _log('info', 'Waiting for page after Join meeting...'); + await page.waitForTimeout(5000); + try { + await page.waitForLoadState('networkidle', { timeout: 30000 }); + } catch { + _log('warn', 'networkidle timeout after Join, continuing'); + } + await page.waitForTimeout(10000); + } + await _screenshotStep('5 - Nach Join meeting'); } // ===================================================================== @@ -753,6 +845,39 @@ function _getDeviceSpoofScript(): void { } } +// ============================================================================ +// MEETING URL PARSER +// ============================================================================ + +/** + * Parse a Teams meeting URL to extract Meeting ID and Passcode. + * URL format: https://teams.microsoft.com/meet/36438888781520?p=5fGqrujxzewPFjJacW + * Meeting ID: 36438888781520 (from path) + * Passcode: 5fGqrujxzewPFjJacW (from ?p= parameter) + */ +function _parseMeetingUrl(meetingUrl: string): { meetingId: string; passcode: string | null } { + try { + const parsed = new URL(meetingUrl.trim()); + + // Extract meeting ID from path: /meet/36438888781520 + let meetingId = ''; + if (parsed.pathname.includes('/meet/')) { + meetingId = parsed.pathname.split('/meet/')[1] || ''; + // Remove trailing slashes + meetingId = meetingId.replace(/\/$/, ''); + } + + // Extract passcode from ?p= parameter + const passcode = parsed.searchParams.get('p') || null; + + return { meetingId, passcode }; + } catch { + // If URL parsing fails, try to extract digits as meeting ID + const digits = meetingUrl.replace(/\D/g, ''); + return { meetingId: digits, passcode: null }; + } +} + // ============================================================================ // URL RESOLUTION // ============================================================================