parent
baaaa95b27
commit
18a60da2d8
2 changed files with 68 additions and 21 deletions
|
|
@ -5,21 +5,21 @@ local logger = require('cp.log')
|
||||||
local utils = require('cp.utils')
|
local utils = require('cp.utils')
|
||||||
|
|
||||||
local function syshandle(result)
|
local function syshandle(result)
|
||||||
|
local ok, data = pcall(vim.json.decode, result.stdout or '')
|
||||||
|
if ok then
|
||||||
|
return { success = true, data = data }
|
||||||
|
end
|
||||||
|
|
||||||
if result.code ~= 0 then
|
if result.code ~= 0 then
|
||||||
local msg = 'Scraper failed: ' .. (result.stderr or 'Unknown error')
|
local msg = 'Scraper failed: ' .. (result.stderr or 'Unknown error')
|
||||||
return { success = false, error = msg }
|
return { success = false, error = msg }
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, data = pcall(vim.json.decode, result.stdout)
|
|
||||||
if not ok then
|
|
||||||
local msg = 'Failed to parse scraper output: ' .. tostring(data)
|
local msg = 'Failed to parse scraper output: ' .. tostring(data)
|
||||||
logger.log(msg, vim.log.levels.ERROR)
|
logger.log(msg, vim.log.levels.ERROR)
|
||||||
return { success = false, error = msg }
|
return { success = false, error = msg }
|
||||||
end
|
end
|
||||||
|
|
||||||
return { success = true, data = data }
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param env_map table<string, string>
|
---@param env_map table<string, string>
|
||||||
---@return string[]
|
---@return string[]
|
||||||
local function spawn_env_list(env_map)
|
local function spawn_env_list(env_map)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
@ -15,6 +16,7 @@ from requests.adapters import HTTPAdapter
|
||||||
from urllib3.util.retry import Retry
|
from urllib3.util.retry import Retry
|
||||||
|
|
||||||
from .base import BaseScraper, extract_precision
|
from .base import BaseScraper, extract_precision
|
||||||
|
from .language_ids import get_language_id
|
||||||
from .models import (
|
from .models import (
|
||||||
CombinedTest,
|
CombinedTest,
|
||||||
ContestListResult,
|
ContestListResult,
|
||||||
|
|
@ -378,10 +380,12 @@ class AtcoderScraper(BaseScraper):
|
||||||
credentials: dict[str, str],
|
credentials: dict[str, str],
|
||||||
) -> SubmitResult:
|
) -> SubmitResult:
|
||||||
def _submit_sync() -> SubmitResult:
|
def _submit_sync() -> SubmitResult:
|
||||||
|
from curl_cffi import requests as curl_requests
|
||||||
|
|
||||||
try:
|
try:
|
||||||
login_page = _session.get(
|
session = curl_requests.Session(impersonate="chrome")
|
||||||
f"{BASE_URL}/login", headers=HEADERS, timeout=TIMEOUT_SECONDS
|
|
||||||
)
|
login_page = session.get(f"{BASE_URL}/login", timeout=TIMEOUT_SECONDS)
|
||||||
login_page.raise_for_status()
|
login_page.raise_for_status()
|
||||||
soup = BeautifulSoup(login_page.text, "html.parser")
|
soup = BeautifulSoup(login_page.text, "html.parser")
|
||||||
csrf_input = soup.find("input", {"name": "csrf_token"})
|
csrf_input = soup.find("input", {"name": "csrf_token"})
|
||||||
|
|
@ -391,21 +395,29 @@ class AtcoderScraper(BaseScraper):
|
||||||
)
|
)
|
||||||
csrf_token = csrf_input.get("value", "") or "" # type: ignore[union-attr]
|
csrf_token = csrf_input.get("value", "") or "" # type: ignore[union-attr]
|
||||||
|
|
||||||
login_resp = _session.post(
|
login_resp = session.post(
|
||||||
f"{BASE_URL}/login",
|
f"{BASE_URL}/login",
|
||||||
data={
|
data={
|
||||||
"username": credentials.get("username", ""),
|
"username": credentials.get("username", ""),
|
||||||
"password": credentials.get("password", ""),
|
"password": credentials.get("password", ""),
|
||||||
"csrf_token": csrf_token,
|
"csrf_token": csrf_token,
|
||||||
},
|
},
|
||||||
headers=HEADERS,
|
|
||||||
timeout=TIMEOUT_SECONDS,
|
timeout=TIMEOUT_SECONDS,
|
||||||
|
allow_redirects=False,
|
||||||
)
|
)
|
||||||
|
if login_resp.status_code in (301, 302):
|
||||||
|
location = login_resp.headers.get("Location", "")
|
||||||
|
if "/login" in location:
|
||||||
|
return SubmitResult(
|
||||||
|
success=False,
|
||||||
|
error="Login failed: incorrect username or password",
|
||||||
|
)
|
||||||
|
session.get(BASE_URL + location, timeout=TIMEOUT_SECONDS)
|
||||||
|
else:
|
||||||
login_resp.raise_for_status()
|
login_resp.raise_for_status()
|
||||||
|
|
||||||
submit_page = _session.get(
|
submit_page = session.get(
|
||||||
f"{BASE_URL}/contests/{contest_id}/submit",
|
f"{BASE_URL}/contests/{contest_id}/submit",
|
||||||
headers=HEADERS,
|
|
||||||
timeout=TIMEOUT_SECONDS,
|
timeout=TIMEOUT_SECONDS,
|
||||||
)
|
)
|
||||||
submit_page.raise_for_status()
|
submit_page.raise_for_status()
|
||||||
|
|
@ -418,7 +430,7 @@ class AtcoderScraper(BaseScraper):
|
||||||
csrf_token = csrf_input.get("value", "") or "" # type: ignore[union-attr]
|
csrf_token = csrf_input.get("value", "") or "" # type: ignore[union-attr]
|
||||||
|
|
||||||
task_screen_name = f"{contest_id}_{problem_id}"
|
task_screen_name = f"{contest_id}_{problem_id}"
|
||||||
submit_resp = _session.post(
|
submit_resp = session.post(
|
||||||
f"{BASE_URL}/contests/{contest_id}/submit",
|
f"{BASE_URL}/contests/{contest_id}/submit",
|
||||||
data={
|
data={
|
||||||
"data.TaskScreenName": task_screen_name,
|
"data.TaskScreenName": task_screen_name,
|
||||||
|
|
@ -426,13 +438,26 @@ class AtcoderScraper(BaseScraper):
|
||||||
"sourceCode": source_code,
|
"sourceCode": source_code,
|
||||||
"csrf_token": csrf_token,
|
"csrf_token": csrf_token,
|
||||||
},
|
},
|
||||||
headers=HEADERS,
|
|
||||||
timeout=TIMEOUT_SECONDS,
|
timeout=TIMEOUT_SECONDS,
|
||||||
|
allow_redirects=False,
|
||||||
|
)
|
||||||
|
if submit_resp.status_code in (301, 302):
|
||||||
|
location = submit_resp.headers.get("Location", "")
|
||||||
|
if "/submissions/me" in location:
|
||||||
|
return SubmitResult(
|
||||||
|
success=True,
|
||||||
|
error="",
|
||||||
|
submission_id="",
|
||||||
|
verdict="submitted",
|
||||||
|
)
|
||||||
|
return SubmitResult(
|
||||||
|
success=False,
|
||||||
|
error=f"Submit may have failed: redirected to {location}",
|
||||||
)
|
)
|
||||||
submit_resp.raise_for_status()
|
submit_resp.raise_for_status()
|
||||||
|
|
||||||
return SubmitResult(
|
return SubmitResult(
|
||||||
success=True, error="", submission_id="", verdict="submitted"
|
success=False,
|
||||||
|
error="Unexpected response from submit (expected redirect)",
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return SubmitResult(success=False, error=str(e))
|
return SubmitResult(success=False, error=str(e))
|
||||||
|
|
@ -495,9 +520,31 @@ async def main_async() -> int:
|
||||||
print(contest_result.model_dump_json())
|
print(contest_result.model_dump_json())
|
||||||
return 0 if contest_result.success else 1
|
return 0 if contest_result.success else 1
|
||||||
|
|
||||||
|
if mode == "submit":
|
||||||
|
if len(sys.argv) != 5:
|
||||||
|
print(
|
||||||
|
SubmitResult(
|
||||||
|
success=False,
|
||||||
|
error="Usage: atcoder.py submit <contest_id> <problem_id> <language>",
|
||||||
|
).model_dump_json()
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
source_code = sys.stdin.read()
|
||||||
|
creds_raw = os.environ.get("CP_CREDENTIALS", "{}")
|
||||||
|
try:
|
||||||
|
credentials = json.loads(creds_raw)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
credentials = {}
|
||||||
|
language_id = get_language_id("atcoder", sys.argv[4]) or sys.argv[4]
|
||||||
|
submit_result = await scraper.submit(
|
||||||
|
sys.argv[2], sys.argv[3], source_code, language_id, credentials
|
||||||
|
)
|
||||||
|
print(submit_result.model_dump_json())
|
||||||
|
return 0 if submit_result.success else 1
|
||||||
|
|
||||||
result = MetadataResult(
|
result = MetadataResult(
|
||||||
success=False,
|
success=False,
|
||||||
error="Unknown mode. Use 'metadata <contest_id>', 'tests <contest_id>', or 'contests'",
|
error="Unknown mode. Use 'metadata <contest_id>', 'tests <contest_id>', 'contests', or 'submit <contest_id> <problem_id> <language>'",
|
||||||
url="",
|
url="",
|
||||||
)
|
)
|
||||||
print(result.model_dump_json())
|
print(result.model_dump_json())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue