From 2ddb63b932d2e3c6c3deca2d386d56975e9849d0 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 11 Mar 2026 15:36:24 +0100 Subject: [PATCH] feat(clickTtPlayerRegistrationService): enhance login flow and consent handling - Added a new constant for the Click-TT login URL to streamline navigation during the login process. - Improved the login flow by directly navigating to the login URL if no direct login fields are found, enhancing user experience. - Introduced a method to dismiss consent overlays, improving the automation of the registration process by handling consent banners effectively. --- .../clickTtPlayerRegistrationService.js | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/backend/services/clickTtPlayerRegistrationService.js b/backend/services/clickTtPlayerRegistrationService.js index 402a0ec2..6a824838 100644 --- a/backend/services/clickTtPlayerRegistrationService.js +++ b/backend/services/clickTtPlayerRegistrationService.js @@ -5,6 +5,7 @@ import { checkAccess } from '../utils/userUtils.js'; import HttpError from '../exceptions/HttpError.js'; const CLICKTT_ENTRY_URL = 'https://httv.click-tt.de/'; +const CLICKTT_LOGIN_URL = 'https://httv.click-tt.de/cgi-bin/WebObjects/nuLigaTTDE.woa/wa/login?federation=HeTTV®ion=DE'; const TRACE_LIMIT = 250; function formatGermanDate(value) { @@ -219,20 +220,13 @@ class ClickTtPlayerRegistrationService { this._trace(trace, 'step', { name: 'open-entry', url: CLICKTT_ENTRY_URL }); await page.goto(CLICKTT_ENTRY_URL, { waitUntil: 'domcontentloaded', timeout: 45000 }); + await this._dismissConsentOverlays(page, trace); + const directLoginOrAuth = page.locator('input[name="email"], input[name="password"]').first(); - const loginLinkCandidates = [ - page.getByRole('link', { name: /login|anmelden/i }).first(), - page.locator('a[href*="oauth2/authz"], a[href*="oAuthLogin"], a[href*="login"]').first() - ]; if (!(await directLoginOrAuth.count())) { - for (const locator of loginLinkCandidates) { - if (await locator.count()) { - this._trace(trace, 'step', { name: 'click-login-entry' }); - await locator.click(); - await page.waitForLoadState('domcontentloaded'); - break; - } - } + this._trace(trace, 'step', { name: 'open-login-entry', url: CLICKTT_LOGIN_URL }); + await page.goto(CLICKTT_LOGIN_URL, { waitUntil: 'domcontentloaded', timeout: 45000 }); + await this._dismissConsentOverlays(page, trace); } const needsLogin = await page.locator('input[name="email"], input[name="password"]').count(); @@ -256,6 +250,30 @@ class ClickTtPlayerRegistrationService { await page.waitForURL(/click-tt\.de/, { timeout: 60000 }); } + async _dismissConsentOverlays(page, trace) { + const candidates = [ + page.getByRole('button', { name: /akzeptieren|accept|zustimmen/i }).first(), + page.locator('#cmpwelcomebtnyes').first(), + page.locator('[id*="cmp"][class*="button"]:has-text("Akzeptieren")').first(), + page.locator('button:has-text("Akzeptieren")').first() + ]; + + for (const locator of candidates) { + try { + if (await locator.count()) { + this._trace(trace, 'step', { name: 'dismiss-consent-banner' }); + await locator.click({ timeout: 2000 }); + await page.waitForTimeout(300); + return true; + } + } catch (_error) { + // ignore and continue with next selector + } + } + + return false; + } + async _fillSearchForm(page, member) { await this._fillFirstAvailable(page, [ 'input[name*=".1"]',