Compare commits
No commits in common. "646a417cf79fdd0a0ae359937c3642ae6d431229" and "8465e707725940c2fccf7dcdcbfe832da258003f" have entirely different histories.
646a417cf7
...
8465e70772
5 changed files with 147 additions and 104 deletions
|
|
@ -306,6 +306,12 @@ def _login_headless(credentials: dict[str, str]) -> LoginResult:
|
||||||
|
|
||||||
cookie_cache = Path.home() / ".cache" / "cp-nvim" / "atcoder-cookies.json"
|
cookie_cache = Path.home() / ".cache" / "cp-nvim" / "atcoder-cookies.json"
|
||||||
cookie_cache.parent.mkdir(parents=True, exist_ok=True)
|
cookie_cache.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
saved_cookies: list[dict[str, Any]] = []
|
||||||
|
if cookie_cache.exists():
|
||||||
|
try:
|
||||||
|
saved_cookies = json.loads(cookie_cache.read_text())
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
logged_in = False
|
logged_in = False
|
||||||
login_error: str | None = None
|
login_error: str | None = None
|
||||||
|
|
@ -334,24 +340,34 @@ def _login_headless(credentials: dict[str, str]) -> LoginResult:
|
||||||
headless=True,
|
headless=True,
|
||||||
timeout=BROWSER_SESSION_TIMEOUT,
|
timeout=BROWSER_SESSION_TIMEOUT,
|
||||||
google_search=False,
|
google_search=False,
|
||||||
|
cookies=saved_cookies if saved_cookies else [],
|
||||||
) as session:
|
) as session:
|
||||||
print(json.dumps({"status": "logging_in"}), flush=True)
|
if saved_cookies:
|
||||||
session.fetch(
|
print(json.dumps({"status": "checking_login"}), flush=True)
|
||||||
f"{BASE_URL}/login",
|
session.fetch(
|
||||||
page_action=login_action,
|
f"{BASE_URL}/home", page_action=check_login, network_idle=True
|
||||||
solve_cloudflare=True,
|
|
||||||
)
|
|
||||||
if login_error:
|
|
||||||
return LoginResult(success=False, error=f"Login failed: {login_error}")
|
|
||||||
|
|
||||||
session.fetch(
|
|
||||||
f"{BASE_URL}/home", page_action=check_login, network_idle=True
|
|
||||||
)
|
|
||||||
if not logged_in:
|
|
||||||
return LoginResult(
|
|
||||||
success=False, error="Login failed (bad credentials?)"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not logged_in:
|
||||||
|
print(json.dumps({"status": "logging_in"}), flush=True)
|
||||||
|
session.fetch(
|
||||||
|
f"{BASE_URL}/login",
|
||||||
|
page_action=login_action,
|
||||||
|
solve_cloudflare=True,
|
||||||
|
)
|
||||||
|
if login_error:
|
||||||
|
return LoginResult(
|
||||||
|
success=False, error=f"Login failed: {login_error}"
|
||||||
|
)
|
||||||
|
|
||||||
|
session.fetch(
|
||||||
|
f"{BASE_URL}/home", page_action=check_login, network_idle=True
|
||||||
|
)
|
||||||
|
if not logged_in:
|
||||||
|
return LoginResult(
|
||||||
|
success=False, error="Login failed (bad credentials?)"
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
browser_cookies = session.context.cookies()
|
browser_cookies = session.context.cookies()
|
||||||
if any(c["name"] == "REVEL_SESSION" for c in browser_cookies):
|
if any(c["name"] == "REVEL_SESSION" for c in browser_cookies):
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,12 @@ def _login_headless_codechef(credentials: dict[str, str]) -> LoginResult:
|
||||||
_ensure_browser()
|
_ensure_browser()
|
||||||
|
|
||||||
_COOKIE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
_COOKIE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
saved_cookies: list[dict[str, Any]] = []
|
||||||
|
if _COOKIE_PATH.exists():
|
||||||
|
try:
|
||||||
|
saved_cookies = json.loads(_COOKIE_PATH.read_text())
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
logged_in = False
|
logged_in = False
|
||||||
login_error: str | None = None
|
login_error: str | None = None
|
||||||
|
|
@ -94,18 +100,30 @@ def _login_headless_codechef(credentials: dict[str, str]) -> LoginResult:
|
||||||
headless=True,
|
headless=True,
|
||||||
timeout=BROWSER_SESSION_TIMEOUT,
|
timeout=BROWSER_SESSION_TIMEOUT,
|
||||||
google_search=False,
|
google_search=False,
|
||||||
|
cookies=saved_cookies if saved_cookies else [],
|
||||||
) as session:
|
) as session:
|
||||||
print(json.dumps({"status": "logging_in"}), flush=True)
|
if saved_cookies:
|
||||||
session.fetch(f"{BASE_URL}/login", page_action=login_action)
|
print(json.dumps({"status": "checking_login"}), flush=True)
|
||||||
if login_error:
|
session.fetch(
|
||||||
return LoginResult(success=False, error=f"Login failed: {login_error}")
|
f"{BASE_URL}/", page_action=check_login, network_idle=True
|
||||||
|
|
||||||
session.fetch(f"{BASE_URL}/", page_action=check_login, network_idle=True)
|
|
||||||
if not logged_in:
|
|
||||||
return LoginResult(
|
|
||||||
success=False, error="Login failed (bad credentials?)"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not logged_in:
|
||||||
|
print(json.dumps({"status": "logging_in"}), flush=True)
|
||||||
|
session.fetch(f"{BASE_URL}/login", page_action=login_action)
|
||||||
|
if login_error:
|
||||||
|
return LoginResult(
|
||||||
|
success=False, error=f"Login failed: {login_error}"
|
||||||
|
)
|
||||||
|
|
||||||
|
session.fetch(
|
||||||
|
f"{BASE_URL}/", page_action=check_login, network_idle=True
|
||||||
|
)
|
||||||
|
if not logged_in:
|
||||||
|
return LoginResult(
|
||||||
|
success=False, error="Login failed (bad credentials?)"
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
browser_cookies = session.context.cookies()
|
browser_cookies = session.context.cookies()
|
||||||
if browser_cookies:
|
if browser_cookies:
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,12 @@ def _login_headless_cf(credentials: dict[str, str]) -> LoginResult:
|
||||||
|
|
||||||
cookie_cache = Path.home() / ".cache" / "cp-nvim" / "codeforces-cookies.json"
|
cookie_cache = Path.home() / ".cache" / "cp-nvim" / "codeforces-cookies.json"
|
||||||
cookie_cache.parent.mkdir(parents=True, exist_ok=True)
|
cookie_cache.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
saved_cookies: list[dict[str, Any]] = []
|
||||||
|
if cookie_cache.exists():
|
||||||
|
try:
|
||||||
|
saved_cookies = json.loads(cookie_cache.read_text())
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
logged_in = False
|
logged_in = False
|
||||||
login_error: str | None = None
|
login_error: str | None = None
|
||||||
|
|
@ -382,26 +388,38 @@ def _login_headless_cf(credentials: dict[str, str]) -> LoginResult:
|
||||||
headless=True,
|
headless=True,
|
||||||
timeout=BROWSER_SESSION_TIMEOUT,
|
timeout=BROWSER_SESSION_TIMEOUT,
|
||||||
google_search=False,
|
google_search=False,
|
||||||
|
cookies=saved_cookies if saved_cookies else [],
|
||||||
) as session:
|
) as session:
|
||||||
print(json.dumps({"status": "logging_in"}), flush=True)
|
if saved_cookies:
|
||||||
session.fetch(
|
print(json.dumps({"status": "checking_login"}), flush=True)
|
||||||
f"{BASE_URL}/enter",
|
session.fetch(
|
||||||
page_action=login_action,
|
f"{BASE_URL}/",
|
||||||
solve_cloudflare=True,
|
page_action=check_login,
|
||||||
)
|
network_idle=True,
|
||||||
if login_error:
|
|
||||||
return LoginResult(success=False, error=f"Login failed: {login_error}")
|
|
||||||
|
|
||||||
session.fetch(
|
|
||||||
f"{BASE_URL}/",
|
|
||||||
page_action=check_login,
|
|
||||||
network_idle=True,
|
|
||||||
)
|
|
||||||
if not logged_in:
|
|
||||||
return LoginResult(
|
|
||||||
success=False, error="Login failed (bad credentials?)"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not logged_in:
|
||||||
|
print(json.dumps({"status": "logging_in"}), flush=True)
|
||||||
|
session.fetch(
|
||||||
|
f"{BASE_URL}/enter",
|
||||||
|
page_action=login_action,
|
||||||
|
solve_cloudflare=True,
|
||||||
|
)
|
||||||
|
if login_error:
|
||||||
|
return LoginResult(
|
||||||
|
success=False, error=f"Login failed: {login_error}"
|
||||||
|
)
|
||||||
|
|
||||||
|
session.fetch(
|
||||||
|
f"{BASE_URL}/",
|
||||||
|
page_action=check_login,
|
||||||
|
network_idle=True,
|
||||||
|
)
|
||||||
|
if not logged_in:
|
||||||
|
return LoginResult(
|
||||||
|
success=False, error="Login failed (bad credentials?)"
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
browser_cookies = session.context.cookies()
|
browser_cookies = session.context.cookies()
|
||||||
if any(c.get("name") == "X-User-Handle" for c in browser_cookies):
|
if any(c.get("name") == "X-User-Handle" for c in browser_cookies):
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,21 @@ class CSESScraper(BaseScraper):
|
||||||
return self._login_error("Missing username or password")
|
return self._login_error("Missing username or password")
|
||||||
|
|
||||||
async with httpx.AsyncClient(follow_redirects=True) as client:
|
async with httpx.AsyncClient(follow_redirects=True) as client:
|
||||||
|
token = credentials.get("token")
|
||||||
|
|
||||||
|
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)
|
print(json.dumps({"status": "logging_in"}), flush=True)
|
||||||
token = await self._web_login(client, username, password)
|
token = await self._web_login(client, username, password)
|
||||||
if not token:
|
if not token:
|
||||||
|
|
|
||||||
|
|
@ -423,7 +423,11 @@ class USACOScraper(BaseScraper):
|
||||||
|
|
||||||
async with httpx.AsyncClient(follow_redirects=True) as client:
|
async with httpx.AsyncClient(follow_redirects=True) as client:
|
||||||
await _load_usaco_cookies(client)
|
await _load_usaco_cookies(client)
|
||||||
if not client.cookies:
|
print(json.dumps({"status": "checking_login"}), flush=True)
|
||||||
|
logged_in = bool(client.cookies) and await _check_usaco_login(
|
||||||
|
client, username
|
||||||
|
)
|
||||||
|
if not logged_in:
|
||||||
print(json.dumps({"status": "logging_in"}), flush=True)
|
print(json.dumps({"status": "logging_in"}), flush=True)
|
||||||
try:
|
try:
|
||||||
ok = await _do_usaco_login(client, username, password)
|
ok = await _do_usaco_login(client, username, password)
|
||||||
|
|
@ -433,72 +437,44 @@ class USACOScraper(BaseScraper):
|
||||||
return self._submit_error("Login failed (bad credentials?)")
|
return self._submit_error("Login failed (bad credentials?)")
|
||||||
await _save_usaco_cookies(client)
|
await _save_usaco_cookies(client)
|
||||||
|
|
||||||
result = await self._do_submit(client, problem_id, language_id, source)
|
print(json.dumps({"status": "submitting"}), flush=True)
|
||||||
|
|
||||||
if result.success or result.error != "auth_failure":
|
|
||||||
return result
|
|
||||||
|
|
||||||
client.cookies.clear()
|
|
||||||
print(json.dumps({"status": "logging_in"}), flush=True)
|
|
||||||
try:
|
try:
|
||||||
ok = await _do_usaco_login(client, username, password)
|
page_r = await client.get(
|
||||||
|
f"{_AUTH_BASE}/index.php?page=viewproblem2&cpid={problem_id}",
|
||||||
|
headers=HEADERS,
|
||||||
|
timeout=HTTP_TIMEOUT,
|
||||||
|
)
|
||||||
|
form_url, hidden_fields, lang_val = _parse_submit_form(
|
||||||
|
page_r.text, language_id
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
form_url = _AUTH_BASE + _SUBMIT_PATH
|
||||||
|
hidden_fields = {}
|
||||||
|
lang_val = None
|
||||||
|
|
||||||
|
data: dict[str, str] = {"cpid": problem_id, **hidden_fields}
|
||||||
|
data["language"] = lang_val if lang_val is not None else language_id
|
||||||
|
ext = "py" if "python" in language_id.lower() else "cpp"
|
||||||
|
try:
|
||||||
|
r = await client.post(
|
||||||
|
form_url,
|
||||||
|
data=data,
|
||||||
|
files={"sourcefile": (f"solution.{ext}", source, "text/plain")},
|
||||||
|
headers=HEADERS,
|
||||||
|
timeout=HTTP_TIMEOUT,
|
||||||
|
)
|
||||||
|
r.raise_for_status()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self._submit_error(f"Login failed: {e}")
|
return self._submit_error(f"Submit request failed: {e}")
|
||||||
if not ok:
|
|
||||||
return self._submit_error("Login failed (bad credentials?)")
|
|
||||||
await _save_usaco_cookies(client)
|
|
||||||
|
|
||||||
return await self._do_submit(client, problem_id, language_id, source)
|
try:
|
||||||
|
resp = r.json()
|
||||||
async def _do_submit(
|
sid = str(resp.get("submission_id", resp.get("id", "")))
|
||||||
self,
|
except Exception:
|
||||||
client: httpx.AsyncClient,
|
sid = ""
|
||||||
problem_id: str,
|
return SubmitResult(
|
||||||
language_id: str,
|
success=True, error="", submission_id=sid, verdict="submitted"
|
||||||
source: bytes,
|
|
||||||
) -> SubmitResult:
|
|
||||||
print(json.dumps({"status": "submitting"}), flush=True)
|
|
||||||
try:
|
|
||||||
page_r = await client.get(
|
|
||||||
f"{_AUTH_BASE}/index.php?page=viewproblem2&cpid={problem_id}",
|
|
||||||
headers=HEADERS,
|
|
||||||
timeout=HTTP_TIMEOUT,
|
|
||||||
)
|
)
|
||||||
if "login" in page_r.url.path.lower() 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
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
form_url = _AUTH_BASE + _SUBMIT_PATH
|
|
||||||
hidden_fields = {}
|
|
||||||
lang_val = None
|
|
||||||
|
|
||||||
data: dict[str, str] = {"cpid": problem_id, **hidden_fields}
|
|
||||||
data["language"] = lang_val if lang_val is not None else language_id
|
|
||||||
ext = "py" if "python" in language_id.lower() else "cpp"
|
|
||||||
try:
|
|
||||||
r = await client.post(
|
|
||||||
form_url,
|
|
||||||
data=data,
|
|
||||||
files={"sourcefile": (f"solution.{ext}", source, "text/plain")},
|
|
||||||
headers=HEADERS,
|
|
||||||
timeout=HTTP_TIMEOUT,
|
|
||||||
)
|
|
||||||
r.raise_for_status()
|
|
||||||
except Exception as e:
|
|
||||||
return self._submit_error(f"Submit request failed: {e}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
resp = r.json()
|
|
||||||
if resp.get("code") == 0 and "login" in resp.get("message", "").lower():
|
|
||||||
return self._submit_error("auth_failure")
|
|
||||||
sid = str(resp.get("submission_id", resp.get("id", "")))
|
|
||||||
except Exception:
|
|
||||||
sid = ""
|
|
||||||
return SubmitResult(
|
|
||||||
success=True, error="", submission_id=sid, verdict="submitted"
|
|
||||||
)
|
|
||||||
|
|
||||||
async def login(self, credentials: dict[str, str]) -> LoginResult:
|
async def login(self, credentials: dict[str, str]) -> LoginResult:
|
||||||
username = credentials.get("username", "")
|
username = credentials.get("username", "")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue