Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
162d588
Merge pull request #772 from XMOJ-Script-dev/dev
boomzero Feb 8, 2025
d724438
Merge pull request #781 from XMOJ-Script-dev/dev
boomzero Feb 21, 2025
8b4a2c1
Merge pull request #786 from XMOJ-Script-dev/dev
boomzero Feb 23, 2025
e40b292
Merge pull request #800 from XMOJ-Script-dev/dev
boomzero May 4, 2025
c8ed5aa
Merge pull request #804 from XMOJ-Script-dev/dev
boomzero Jun 4, 2025
e4f659f
Merge pull request #813 from XMOJ-Script-dev/dev
boomzero Jun 22, 2025
d24fe86
Merge pull request #819 from XMOJ-Script-dev/dev
boomzero Jul 10, 2025
1a75572
Merge pull request #823 from XMOJ-Script-dev/dev
boomzero Jul 25, 2025
b789bf2
Update Update.json
boomzero Jul 25, 2025
13058b6
Merge pull request #827 from XMOJ-Script-dev/dev
boomzero Aug 9, 2025
c22fd31
Merge pull request #836 from XMOJ-Script-dev/dev
boomzero Aug 22, 2025
6adac8d
Parse release notes from comment block
boomzero Aug 24, 2025
f86a45e
Update bug.yml
boomzero Aug 24, 2025
84474cc
Merge pull request #854 from XMOJ-Script-dev/dev
boomzero Sep 14, 2025
7950ae8
Merge pull request #857 from XMOJ-Script-dev/dev
boomzero Sep 27, 2025
dfe8cf8
Merge pull request #874 from XMOJ-Script-dev/dev
boomzero Oct 4, 2025
54d39cf
Update GitHub Actions workflow to skip bot triggers
PythonSmall-Q Feb 7, 2026
462621a
Merge pull request #891 from XMOJ-Script-dev/dev
PythonSmall-Q Feb 7, 2026
27863a7
Prevent UpdateVersion from running if last commit was by github-actio…
boomzero Feb 11, 2026
280bc24
Merge branch 'master' of github.com:XMOJ-Script-dev/XMOJ-Script
boomzero Feb 11, 2026
4a671fc
Merge pull request #908 from XMOJ-Script-dev/dev
boomzero Feb 16, 2026
e3bbe9e
Merge pull request #912 from XMOJ-Script-dev/dev
boomzero Feb 19, 2026
fab6593
Allow metadata updates on edited PRs after bot version commit
boomzero Feb 23, 2026
2f7e9d6
Allow metadata updates on edited PRs after bot version commit
boomzero Feb 23, 2026
e14e33c
Merge pull request #921 from XMOJ-Script-dev/dev
boomzero Feb 27, 2026
7551f8c
Merge pull request #956 from XMOJ-Script-dev/dev
PythonSmall-Q Mar 28, 2026
d833b40
Add Google Ads script to index.html
PythonSmall-Q May 3, 2026
bdd5dc7
Remove Google Ads script from index.html
PythonSmall-Q May 3, 2026
2126971
Add Google ad publisher entry to ads.txt
PythonSmall-Q May 3, 2026
a6251c3
Merge pull request #983 from XMOJ-Script-dev/dev
PythonSmall-Q May 23, 2026
1aa1b02
Merge pull request #999 from XMOJ-Script-dev/dev
boomzero Jul 27, 2026
5fe3558
fix: 修复已结束比赛提交回退功能失效 #1017
INFP-coder Aug 23, 2026
5f70e73
Refix
INFP-coder Aug 23, 2026
d65ae84
Refix
INFP-coder Aug 23, 2026
6e587af
Merge branch 'extern-contrib' into master
INFP-coder Aug 23, 2026
238bc2c
Update?
INFP-coder Aug 23, 2026
825161e
Update?
INFP-coder Aug 23, 2026
0e0a5eb
fixed
INFP-coder Aug 23, 2026
9f07ee2
fixed?
INFP-coder Aug 23, 2026
a516f91
Merge branch 'extern-contrib' into master
INFP-coder Aug 31, 2026
b0a0020
Refactor contest submission error handling
INFP-coder Aug 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Update.json
Original file line number Diff line number Diff line change
Expand Up @@ -2937,7 +2937,7 @@
],
"Notes": "No release notes were provided for this release."
},
"1.10.0": {
"1.999990.0": {
Comment thread
zsTree0830 marked this conversation as resolved.
"UpdateDate": 1753443146018,
"Prerelease": false,
"UpdateContents": [
Expand Down Expand Up @@ -3729,4 +3729,4 @@
"Notes": "### 修复提交界面不显示验证码\n\n评测队列繁忙时 XMOJ 会启用图片验证码,但提交界面是脚本自行渲染的,服务端的验证码字段被丢弃,导致提交静默失败。现在提交界面会显示验证码图片与输入框(点击图片可更换),并在提交时携带验证码。\n\n另新增 `AutoCaptcha` 开关(默认开启):在浏览器本地用模板匹配识别 4 位数字验证码,不联网、不调用任何 AI 服务。把握不大时会留空由您填写,不会填错。"
}
}
}
}
116 changes: 98 additions & 18 deletions XMOJ.user.js
Original file line number Diff line number Diff line change
Expand Up @@ -4474,6 +4474,20 @@ async function main() {
}

PassCheck.addEventListener("click", async () => {
ErrorElement.style.display = "none";
document.querySelector("#Submit").disabled = true;
document.querySelector("#Submit").value = "正在提交...";
let o2Switch = "&enable_O2=on";
if (!document.querySelector("#enable_O2").checked) o2Switch = "";

const response = await fetch("https://www.xmoj.tech/submit.php", {
"headers": {
"content-type": "application/x-www-form-urlencoded"
},
"referrer": location.href,
"method": "POST",
"body": (SearchParams.get("id") != null ? "id=" + SearchParams.get("id") : "cid=" + SearchParams.get("cid") + "&pid=" + SearchParams.get("pid")) + "&language=1&" + "source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch
});
// This is the request that actually reaches submit.php, so the captcha is checked
// here as well as in the 提交 handler above.
if (CaptchaIsMissing()) return;
Expand Down Expand Up @@ -4571,25 +4585,91 @@ async function main() {
}
console.log(await Response.text());
});

}
if (UtilityEnabled("DebugMode")) {
console.log("Submission failed! Response:", text);
}
ErrorElement.style.display = "block";
ErrorMessage.style.color = "red";
try { _xmoj_disposeErrorMessageEditors(); } catch (e) {
console.error(e);
if (UtilityEnabled("DebugMode")) {
SmartAlert("XMOJ-Script internal error!\n\n" + e + "\n\n" + "If you see this message, please report it to the developer.\nDon't forget to include console logs and a way to reproduce the error!\n\nDon't want to see this message? Disable DebugMode.");
if (response.redirected) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: For a non-redirect response, the preceding .then consumes response.body, so this added await response.text() throws TypeError: Body is unusable before PID parsing and fallback submission. Read the initial response once and pass its text into the fallback logic instead of consuming it again.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At XMOJ.user.js, line 4588:

<comment>For a non-redirect response, the preceding `.then` consumes `response.body`, so this added `await response.text()` throws `TypeError: Body is unusable` before PID parsing and fallback submission. Read the initial response once and pass its text into the fallback logic instead of consuming it again.</comment>

<file context>
@@ -4585,119 +4585,91 @@ async function main() {
-    Submit.disabled = false;
-    Submit.value = "提交";
-});  // ← 这是唯一正确的结束
+                                    if (response.redirected) {
+                                        location.href = response.url;
+                                        return;
</file context>

location.href = response.url;
return;
}
}
ErrorMessage.innerText = "提交失败!请关闭脚本后重试!";
Submit.disabled = false;
Submit.value = "提交";
}
})
});
const text = await response.text();
if ((text.indexOf("没有这个比赛!") !== -1 || text.indexOf("比赛已结束") !== -1) && new URL(location.href).searchParams.get("pid") !== null) {
// Credit: https://github.com/boomzero/quicksubmit/blob/main/index.ts (GPL-3.0)
const cid = new URL(location.href).searchParams.get("cid");
const pidIndex = new URL(location.href).searchParams.get("pid");
const contestReq = await fetch("https://www.xmoj.tech/contest.php?cid=" + cid);
const res = await contestReq.text();
if (contestReq.status !== 200 || res.indexOf("比赛尚未开始或私有,不能查看题目。") !== -1) {
console.error("Failed to get contest page!");
_xmoj_disposeErrorMessageEditors();
ErrorElement.style.display = "block";
ErrorMessage.style.color = "red";
ErrorMessage.innerText = "无法获取比赛信息,请手动提交。";
Submit.disabled = false; Submit.value = "提交";
return;
}
const parser = new DOMParser();
const dom = parser.parseFromString(res, "text/html");
const rows = dom.querySelector("#problemset > tbody").rows;
const contestProblems = [];

for (let i = 0; i < rows.length; i++) {
const cell = rows[i].children[1];
const textContent = cell.textContent.trim();
const match = textContent.match(/\d+/);
contestProblems.push(match ? match[0] : null);
}

const rPID = contestProblems[parseInt(pidIndex)];
if (!rPID) {
_xmoj_disposeErrorMessageEditors();
ErrorElement.style.display = "block";
ErrorMessage.style.color = "red";
ErrorMessage.innerText = "无法解析真实题目 ID,请手动提交。";
Submit.disabled = false;
Submit.value = "提交";
return;
}

_xmoj_disposeErrorMessageEditors();
ErrorElement.style.display = "block";
ErrorMessage.style.color = "orange";
ErrorMessage.innerText = "比赛已结束,正在尝试向题目 " + rPID + " 提交…";
console.log("比赛已结束,正在尝试向题目 " + rPID + " 提交");

let o2Switch2 = "&enable_O2=on";
if (!document.querySelector("#enable_O2").checked) o2Switch2 = "";

const retryResp = await fetch("https://www.xmoj.tech/submit.php", {
"headers": {
"content-type": "application/x-www-form-urlencoded"
},
"referrer": location.href,
"method": "POST",
"body": "id=" + rPID + "&language=1&source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: When the server requires a captcha, the retry submission omits the entered vcode, so the fallback is rejected even though the user passed the captcha check. Append GetCaptchaParameter() to the retry request body.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At XMOJ.user.js, line 4646:

<comment>When the server requires a captcha, the retry submission omits the entered `vcode`, so the fallback is rejected even though the user passed the captcha check. Append `GetCaptchaParameter()` to the retry request body.</comment>

<file context>
@@ -4585,119 +4585,91 @@ async function main() {
+                                            },
+                                            "referrer": location.href,
+                                            "method": "POST",
+                                            "body": "id=" + rPID + "&language=1&source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch2
+                                    });
+                                if (retryResp.redirected) {
</file context>
Suggested change
"body": "id=" + rPID + "&language=1&source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch2
"body": "id=" + rPID + "&language=1&source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch2 + GetCaptchaParameter()

});
if (retryResp.redirected) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: This replacement leaves the retry conditional and fetch callback unterminated before the next listener registration, so the userscript cannot parse. Restore the missing closing }/}); before Submit.addEventListener and keep the failure handling inside a properly closed retry branch.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At XMOJ.user.js, line 4648:

<comment>This replacement leaves the retry conditional and fetch callback unterminated before the next listener registration, so the userscript cannot parse. Restore the missing closing `}`/`});` before `Submit.addEventListener` and keep the failure handling inside a properly closed retry branch.</comment>

<file context>
@@ -4585,119 +4585,91 @@ async function main() {
+                                            "method": "POST",
+                                            "body": "id=" + rPID + "&language=1&source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch2
+                                    });
+                                if (retryResp.redirected) {
+                                    location.href = retryResp.url;
+                                    return;
</file context>

location.href = retryResp.url;
return;
const retryText = await retryResp.text();
let errorMsg = "提交失败,请重试。";
try {
const dom2 = new DOMParser().parseFromString(retryText, "text/html");
const jumbotron = dom2.querySelector(".jumbotron");
if (jumbotron) {
errorMsg = jumbotron.textContent.trim();
} else {
const textOnly = retryText.replace(/<[^>]*>/g, "").trim();
errorMsg = textOnly.substring(0, 100) || errorMsg;
}
} catch (_) {
// 解析失败则使用默认信息
}

_xmoj_disposeErrorMessageEditors();
ErrorElement.style.display = "block";
ErrorMessage.style.color = "red";
ErrorMessage.innerText = "❌ 回退提交失败:" + errorMsg;
Submit.disabled = false;
Submit.value = "提交";
return;

Submit.addEventListener("click", async () => {
PassCheck.style.display = "none";
Expand Down
1 change: 1 addition & 0 deletions ads.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
google.com, pub-6214673028530012, DIRECT, f08c47fec0942fa0
1 change: 0 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<!DOCTYPE html>
<html>
<link rel="icon" href="favicon.ico">

<head>
<script type="text/javascript">
(function(c,l,a,r,i,t,y){
Expand Down
Loading