// ==UserScript== // @name cph-submit.frontend // @namespace http://tampermonkey.net/ // @version 0.1.0 // @description cph-submit 前台脚本 // @author Andy_hpy // @match https://codeforces.com/problemset/submit* // @match https://codeforces.com/contest/*/submit* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_addValueChangeListener // @run-at document-idle // @storageName cph-submit // ==/UserScript== // ---------- 工具函数 ---------- function isContestProblem(problemUrl) { return problemUrl && problemUrl.includes('contest'); } function getExpectedSubmitUrl(problemUrl) { if (!problemUrl) return null; if (!isContestProblem(problemUrl)) { return 'https://codeforces.com/problemset/submit'; } const match = problemUrl.match(/contest\/(\d+)/); if (match) { return `https://codeforces.com/contest/${match[1]}/submit`; } return null; } function isCorrectPage(task) { if (!task || !task.url) return false; const expected = getExpectedSubmitUrl(task.url); return expected && window.location.href.startsWith(expected); } function waitForElement(selector, timeout = 5000) { return new Promise((resolve, reject) => { const start = Date.now(); const check = () => { const el = document.querySelector(selector); if (el) resolve(el); else if (Date.now() - start > timeout) reject(new Error(`元素 ${selector} 未找到`)); else setTimeout(check, 200); }; check(); }); } // 等待cloudflare验证组件 async function waitForCaptchaIfNeeded(timeout = 60000) { const captchaInput = document.querySelector('input[id^="cf-chl-widget-"][id$="_response"]'); captchaInput.focus(); // 如果需要点击,可以直接按 Enter const startTime = Date.now(); while (Date.now() - startTime < timeout) { if (captchaInput.value.trim() !== '') { return true; } await new Promise(resolve => setTimeout(resolve, 500)); } return false; } async function fillAndSubmit(task) { try { const langEl = await waitForElement('select[name="programTypeId"]'); const codeEl = await waitForElement('#sourceCodeTextarea'); let problemEl; if (isContestProblem(task.url)) { problemEl = await waitForElement('select[name="submittedProblemIndex"]'); } else { problemEl = await waitForElement('input[name="submittedProblemCode"]'); } langEl.value = task.languageId; langEl.dispatchEvent(new Event('change', { bubbles: true })); langEl.dispatchEvent(new Event('blur', { bubbles: true })); codeEl.value = task.sourceCode; codeEl.dispatchEvent(new Event('input', { bubbles: true })); codeEl.dispatchEvent(new Event('blur', { bubbles: true })); if (isContestProblem(task.url)) { const problemLetter = task.url.split('/problem/')[1]; problemEl.value = problemLetter; } else { problemEl.value = task.problemName; } problemEl.dispatchEvent(new Event('change', { bubbles: true })); problemEl.dispatchEvent(new Event('blur', { bubbles: true })); const shouldSubmit = await waitForCaptchaIfNeeded(60000); if (!shouldSubmit) { alert('cloudflare验证组件超时,请手动提交'); GM_deleteValue('pendingSubmit'); return; } const submitBtnOrForm = document.querySelector("#singlePageSubmitButton"); if (submitBtnOrForm) { submitBtnOrForm.click(); } GM_deleteValue('pendingSubmit'); } catch (err) { console.error('自动填充失败', err); } } GM_addValueChangeListener("pendingSubmit", (name, oldValue, newValue, remote) => { if (newValue) { try { const task = JSON.parse(newValue); if (isCorrectPage(task)) { fillAndSubmit(task); } } catch (e) { console.error('解析任务失败', e); } } }); const taskJson = GM_getValue('pendingSubmit'); if (taskJson) { try { const task = JSON.parse(taskJson); if (Date.now() - task.timestamp > 60000) { GM_deleteValue('pendingSubmit'); } else if (isCorrectPage(task)) { fillAndSubmit(task); } } catch (e) { console.error('解析任务失败', e); } }