From ee93e15af8cdd2f7c43408be1632dbbdd80d3c2b Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Fri, 4 Sep 2026 15:11:15 +0200 Subject: [PATCH] Verbessere CAPTCHA-Handling im Login-Prozess: Implementiere automatische Token-Generierung und verbessere Fehlerbehandlung. --- server/utils/mytischtennis-client.js | 45 ++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/server/utils/mytischtennis-client.js b/server/utils/mytischtennis-client.js index 921c394..34626cd 100644 --- a/server/utils/mytischtennis-client.js +++ b/server/utils/mytischtennis-client.js @@ -37,17 +37,44 @@ async function loginWithBrowser(email, password) { const browser = await chromium.launch({ headless: true }) try { const page = await browser.newPage() - await page.goto(`${BASE_URL}/login?next=%2F`, { waitUntil: 'networkidle' }) + // Das myTT-CAPTCHA wird erst nach dem Laden der Seite erzeugt. Der Ablauf + // entspricht der erprobten Integration im Trainingstagebuch. + await page.goto(BASE_URL, { waitUntil: 'domcontentloaded', timeout: 30_000 }).catch(() => {}) + await page.goto(`${BASE_URL}/login?next=%2F`, { waitUntil: 'domcontentloaded', timeout: 45_000 }) await page.locator('input[name="email"]').fill(email) await page.locator('input[name="password"]').fill(password) - // Das CAPTCHA wird vom Anbieter im Browser erzeugt. Ohne erfolgreiches Token wird nicht abgesendet. - await page.waitForFunction(() => { - const input = document.querySelector('input[name="captcha"]') - return Boolean(input?.value) - }, { timeout: 25_000 }) - await page.locator('form').evaluate(form => form.requestSubmit()) - await page.waitForTimeout(1_500) - const cookie = (await page.context().cookies(BASE_URL)).find(item => item.name === 'sb-10-auth-token') + + const captchaHost = page.locator('private-captcha').first() + const hasCaptcha = await captchaHost.count() > 0 + if (hasCaptcha) { + await page.waitForTimeout(1_200) + await page.evaluate(() => { + const host = document.querySelector('private-captcha') + const checkbox = host?.shadowRoot?.querySelector('#pc-checkbox') + if (!checkbox) return + checkbox.click() + checkbox.dispatchEvent(new Event('input', { bubbles: true })) + checkbox.dispatchEvent(new Event('change', { bubbles: true })) + }) + await page.waitForFunction(() => { + const token = document.querySelector('input[name="captcha"]')?.value?.trim() || '' + const clicked = document.querySelector('input[name="captcha_clicked"]')?.value?.toLowerCase() || '' + return token.length > 80 && (clicked === 'true' || clicked === '1') + }, { timeout: 32_000 }).catch(() => {}) + const captchaReady = await page.evaluate(() => (document.querySelector('input[name="captcha"]')?.value?.trim().length || 0) > 80) + if (!captchaReady) throw new Error('CAPTCHA konnte nicht automatisch gelöst werden.') + await page.waitForTimeout(2_500) + } + + const submit = page.locator('button[type="submit"][name="intent"][value="login"], button[type="submit"], input[type="submit"]').first() + if (await submit.count()) await submit.click({ noWaitAfter: true }) + else await page.locator('form').evaluate(form => form.requestSubmit()) + let cookie = null + for (let attempt = 0; attempt < 40; attempt += 1) { + cookie = (await page.context().cookies(BASE_URL)).find(item => /^sb-\d+-auth-token$/.test(item.name)) + if (cookie) break + await page.waitForTimeout(500) + } if (!cookie) throw new Error('CAPTCHA konnte nicht automatisch gelöst werden.') return `${cookie.name}=${cookie.value}` } finally { await browser.close() }