fix(scrapers): login fast paths and re-auth hardening for httpx platforms (#357)
## Problem On CSES, Kattis, and USACO, `:CP <platform> login` always prompted for credentials and ran a full web login even when a valid session was already cached. Submit also had weak stale-session detection. ## Solution `credentials.lua` now tries cached credentials first before prompting, delegating fast-path detection to each scraper. CSES `login()` checks the cached API token and returns immediately if valid. USACO `login()` and `submit()` call `_check_usaco_login()` upfront. Kattis `submit()` emits `checking_login` consistently and also triggers re-auth on HTTP 400/403, not just on the `"Request validation failed"` text match. The premature `Submitting...` log emitted by Lua before the scraper started is removed — Python's own status events are sufficient.
This commit is contained in:
parent
6e9829a115
commit
eb0dea777e
6 changed files with 88 additions and 48 deletions
|
|
@ -248,7 +248,21 @@ class CSESScraper(BaseScraper):
|
|||
if not username or not password:
|
||||
return self._login_error("Missing username or password")
|
||||
|
||||
token = credentials.get("token")
|
||||
async with httpx.AsyncClient(follow_redirects=True) as client:
|
||||
if token:
|
||||
print(json.dumps({"status": "checking_login"}), flush=True)
|
||||
if await self._check_token(client, token):
|
||||
return LoginResult(
|
||||
success=True,
|
||||
error="",
|
||||
credentials={
|
||||
"username": username,
|
||||
"password": password,
|
||||
"token": token,
|
||||
},
|
||||
)
|
||||
|
||||
print(json.dumps({"status": "logging_in"}), flush=True)
|
||||
token = await self._web_login(client, username, password)
|
||||
if not token:
|
||||
|
|
@ -460,7 +474,8 @@ class CSESScraper(BaseScraper):
|
|||
|
||||
if r.status_code not in range(200, 300):
|
||||
try:
|
||||
err = r.json().get("message", r.text)
|
||||
body = r.json()
|
||||
err = body.get("error") or body.get("message") or r.text
|
||||
except Exception:
|
||||
err = r.text
|
||||
return self._submit_error(f"Submit request failed: {err}")
|
||||
|
|
|
|||
|
|
@ -329,6 +329,7 @@ class KattisScraper(BaseScraper):
|
|||
return self._submit_error("Missing credentials. Use :CP kattis login")
|
||||
|
||||
async with httpx.AsyncClient(follow_redirects=True) as client:
|
||||
print(json.dumps({"status": "checking_login"}), flush=True)
|
||||
await _load_kattis_cookies(client)
|
||||
if not client.cookies:
|
||||
print(json.dumps({"status": "logging_in"}), flush=True)
|
||||
|
|
@ -366,7 +367,7 @@ class KattisScraper(BaseScraper):
|
|||
except Exception as e:
|
||||
return self._submit_error(f"Submit request failed: {e}")
|
||||
|
||||
if r.text == "Request validation failed":
|
||||
if r.status_code in (400, 403) or r.text == "Request validation failed":
|
||||
_COOKIE_PATH.unlink(missing_ok=True)
|
||||
print(json.dumps({"status": "logging_in"}), flush=True)
|
||||
ok = await _do_kattis_login(client, username, password)
|
||||
|
|
|
|||
|
|
@ -429,7 +429,19 @@ class USACOScraper(BaseScraper):
|
|||
|
||||
async with httpx.AsyncClient(follow_redirects=True) as client:
|
||||
await _load_usaco_cookies(client)
|
||||
if not client.cookies:
|
||||
if client.cookies:
|
||||
print(json.dumps({"status": "checking_login"}), flush=True)
|
||||
if not await _check_usaco_login(client, username):
|
||||
client.cookies.clear()
|
||||
print(json.dumps({"status": "logging_in"}), flush=True)
|
||||
try:
|
||||
ok = await _do_usaco_login(client, username, password)
|
||||
except Exception as e:
|
||||
return self._submit_error(f"Login failed: {e}")
|
||||
if not ok:
|
||||
return self._submit_error("Login failed (bad credentials?)")
|
||||
await _save_usaco_cookies(client)
|
||||
else:
|
||||
print(json.dumps({"status": "logging_in"}), flush=True)
|
||||
try:
|
||||
ok = await _do_usaco_login(client, username, password)
|
||||
|
|
@ -470,7 +482,8 @@ class USACOScraper(BaseScraper):
|
|||
headers=HEADERS,
|
||||
timeout=HTTP_TIMEOUT,
|
||||
)
|
||||
if "login" in page_r.url.path.lower() or "Login" in page_r.text[:2000]:
|
||||
page_url = str(page_r.url)
|
||||
if "/login" in page_url or "Login" in page_r.text[:2000]:
|
||||
return self._submit_error("auth_failure")
|
||||
form_url, hidden_fields, lang_val = _parse_submit_form(
|
||||
page_r.text, language_id
|
||||
|
|
@ -513,6 +526,16 @@ class USACOScraper(BaseScraper):
|
|||
return self._login_error("Missing username or password")
|
||||
|
||||
async with httpx.AsyncClient(follow_redirects=True) as client:
|
||||
await _load_usaco_cookies(client)
|
||||
if client.cookies:
|
||||
print(json.dumps({"status": "checking_login"}), flush=True)
|
||||
if await _check_usaco_login(client, username):
|
||||
return LoginResult(
|
||||
success=True,
|
||||
error="",
|
||||
credentials={"username": username, "password": password},
|
||||
)
|
||||
|
||||
print(json.dumps({"status": "logging_in"}), flush=True)
|
||||
try:
|
||||
ok = await _do_usaco_login(client, username, password)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue