fix(scrapers): bad credentials detection and error message cleanup

Problem: Wrong credentials during login produced two bugs: scrapers
wrapped the `bad_credentials` error code in `"Login failed: ..."`,
causing double-prefixed messages in the UI; and `credentials.lua`
did not clear cached credentials before re-prompting or on failure,
leaving stale bad creds in the cache.

Solution: Standardize all scrapers to emit `"bad_credentials"` as
the raw error code. Add `LOGIN_ERRORS` map in `constants.lua` to
translate it to a human-readable string in both `credentials.lua`
and `submit.lua`. Fix `credentials.lua` to clear credentials on
failure in both the fresh-prompt and cached-creds-fail paths.
For AtCoder and Codeforces, replace `wait_for_url` with
`wait_for_function` to detect the login error element immediately
rather than waiting the full 10s navigation timeout. Also add
"Remember me" checkbox check on Codeforces login.
This commit is contained in:
Barrett Ruth 2026-03-07 17:35:20 -05:00
parent 573b335646
commit 0ad7a9614f
Signed by: barrett
GPG key ID: A6C96C9349D2FC81
9 changed files with 55 additions and 25 deletions

View file

@ -353,10 +353,15 @@ def _cf_login_action(credentials: dict[str, str]):
page.wait_for_selector('input[name="handleOrEmail"]', timeout=60000)
page.fill('input[name="handleOrEmail"]', credentials.get("username", ""))
page.fill('input[name="password"]', credentials.get("password", ""))
page.locator('#enterForm input[name="remember"]').check()
page.locator('#enterForm input[type="submit"]').click()
page.wait_for_url(
lambda url: "/enter" not in url, timeout=BROWSER_NAV_TIMEOUT
page.wait_for_function(
"() => !window.location.href.includes('/enter') || !!document.querySelector('#enterForm span.error')",
timeout=BROWSER_NAV_TIMEOUT,
)
if "/enter" in page.url:
login_error = "bad_credentials"
return
except Exception as e:
login_error = str(e)
@ -416,7 +421,9 @@ def _login_headless_cf(credentials: dict[str, str]) -> LoginResult:
solve_cloudflare=True,
)
login_error = get_error()
if login_error:
if login_error == "bad_credentials":
return LoginResult(success=False, error="bad_credentials")
elif login_error:
return LoginResult(success=False, error=f"Login failed: {login_error}")
logged_in = False
@ -428,7 +435,7 @@ def _login_headless_cf(credentials: dict[str, str]) -> LoginResult:
session.fetch(f"{BASE_URL}/", page_action=verify_action, network_idle=True)
if not logged_in:
return LoginResult(
success=False, error="Login failed (bad credentials?)"
success=False, error="bad_credentials"
)
try:
@ -540,7 +547,9 @@ def _submit_headless(
solve_cloudflare=True,
)
login_error = _get_login_error()
if login_error:
if login_error == "bad_credentials":
return SubmitResult(success=False, error="bad_credentials")
elif login_error:
return SubmitResult(
success=False, error=f"Login failed: {login_error}"
)