fix: 修复已结束比赛提交回退功能失效 #1017 - #1018
Conversation
Signed-off-by: Zhu Chenrui <boomzero_zcr@outlook.com>
(cherry picked from commit c7137ff)
Signed-off-by: Zhu Chenrui <boomzero_zcr@outlook.com> (cherry picked from commit 07d7590) Update feature.yml Signed-off-by: Zhu Chenrui <boomzero_zcr@outlook.com> (cherry picked from commit 1a99430) Update docs.yml Signed-off-by: Zhu Chenrui <boomzero_zcr@outlook.com> (cherry picked from commit 6017bcf)
Signed-off-by: Shan Wenxiao <seanoj_noreply@yeah.net>
…ns[bot] This prevents infinite loops where the bot commits version updates, which triggers the workflow again, causing another commit. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The last-commit-author guard now only exits for non-edited events, so PR title/body changes still update Update.json metadata even when the branch tip is a github-actions[bot] commit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Exclude all bot actors (not just github-actions[bot]) from triggering the UpdateVersion workflow, preventing loops from AI code review bots. Allow edited events through the script-level guard so PR title/body changes still update Update.json metadata. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update to release 3.2.2
Signed-off-by: Shan Wenxiao <seanoj_noreply@yeah.net>
Removed Google Ads script from the HTML file. Signed-off-by: Shan Wenxiao <seanoj_noreply@yeah.net>
Signed-off-by: Shan Wenxiao <seanoj_noreply@yeah.net>
There was a problem hiding this comment.
All reported issues were addressed across 1 file (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
感谢你的第一个 PR!诊断方向是对的——核心问题确实是回退提交后没有 return,导致外层无条件覆盖成"提交失败"。另外用正则提取题号也比原来的 substring(2, 6) 更稳:原写法遇到 5 位题号会截断(18650 → 1865),会静默提交到错题,这个改进很有价值。
有几点需要处理。
1. 目标分支
外部贡献者的 PR 需要基于并指向 extern-contrib,不能直接指向 master。这是流程要求,和代码质量无关。
2. 阻塞项:成功判定会误报
if (retryText.indexOf("提交成功") !== -1 ||
retryText.indexOf("Solution") !== -1 ||
retryText.indexOf("status.php") !== -1) {status.php 出现在每个页面的导航栏里:
<li ><a href="status.php">状态</a></li>而真正提交成功时 submit.php 会 302 到 status.php,在上面的 if (retryResp.redirected) 分支就已经 return 了。也就是说这段 else 只有在失败时才会走到,而它必定命中 status.php —— 结果是每次失败都会显示 "✅ 回退提交成功!请查看状态。" 并把按钮置成"已提交"。这比原来的通用报错更糟,用户会以为代码已经交上去了。
建议只把 redirected 当成功信号,其余一律按失败处理,并从响应的 .jumbotron 里取服务端的真实提示显示给用户。
3. #1017 里"没有提交记录"的真正原因
这部分从 issue 里看不出来,我补充一下。XMOJ 基于 HUSTOJ,trunk/web/submit.php 里有一段提交冷却:
if (!isset($OJ_SUBMIT_COOLDOWN_TIME)) $OJ_SUBMIT_COOLDOWN_TIME = 5;
$time_point = date("Y-m-d H:i:s", time() - $OJ_SUBMIT_COOLDOWN_TIME);
$sql = "SELECT `in_date`,solution_id FROM `solution` WHERE `user_id`=? AND in_date>? ORDER BY `in_date` DESC LIMIT 1";上游命中时是 302,但 XMOJ 改成了直接渲染页面,.jumbotron 里是 请勿重复提交。issue 附的日志里,回退提交拿到的正是这个页面——所以 Response.redirected 是 false,响应体被丢掉,最终没有产生任何提交记录。
顺序上比赛校验(没有这个比赛!)在冷却检查之前,所以第一次 cid+pid 的失败提交本身不会写 solution 表,冷却是被用户前一次提交触发的。
要真正修好 #1017,回退提交遇到 请勿重复提交 时需要等冷却过去再重试(比如 3 秒一次、最多 5 次),期间在 ErrorMessage 里提示进度。
4. 一些小问题
- 删掉了 quicksubmit 的
// Credit:注释,那是 GPL-3.0 的署名要求,需要保留 - 删掉了所有
_xmoj_disposeErrorMessageEditors()调用,它负责在覆盖innerText前销毁错误框里的 Monaco 编辑器和 model,去掉会泄漏 retryText.substring(0, 200)会把<!DOCTYPE html>...原样显示给用户,建议换成解析出来的服务端提示- 进行中的提示用了
yellow,浅色主题下白底黄字看不清(原来用的是 red) - 缩进被打乱成了 4/8/36 空格混排,麻烦按原文件风格调回来
第 1、2 点处理掉就可以合了。第 3 点的冷却重试如果你不想写,我也可以补一个 commit 上去。再次感谢!
这条 review 是 LLM 帮我整理的,我不想手写这么长的东西。有说得不对的地方直接指出来。
|
补充更正:上面第 1 点我最初写成了 |
|
HUSTOJ 对提交有 5s 的 rate limit |
|
cnm点错了 |
|
算了先不squash了 |
|
本次修改仅限于 PassCheck 监听器。 |
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="XMOJ.user.js">
<violation number="1" location="XMOJ.user.js:4237">
P2: When the server reports a finished contest with `比赛已结束` or equivalent wording, this exact-match guard skips rollback. Match the supported finished-contest phrases before falling through to generic failure handling.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| const text = await response.text(); | ||
|
|
||
| // 回退处理:比赛已结束,尝试直接提交到真实题目 | ||
| if (text.indexOf("没有这个比赛!") !== -1 && new URL(location.href).searchParams.get("pid") !== null) { |
There was a problem hiding this comment.
P2: When the server reports a finished contest with 比赛已结束 or equivalent wording, this exact-match guard skips rollback. Match the supported finished-contest phrases before falling through to generic failure handling.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At XMOJ.user.js, line 4237:
<comment>When the server reports a finished contest with `比赛已结束` or equivalent wording, this exact-match guard skips rollback. Match the supported finished-contest phrases before falling through to generic failure handling.</comment>
<file context>
@@ -4211,116 +4211,127 @@ async function main() {
+ const text = await response.text();
+
+ // 回退处理:比赛已结束,尝试直接提交到真实题目
+ if (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");
</file context>
| if (text.indexOf("没有这个比赛!") !== -1 && new URL(location.href).searchParams.get("pid") !== null) { | |
| if ((text.indexOf("没有这个比赛!") !== -1 || text.indexOf("比赛已结束") !== -1 || text.indexOf("比赛已经结束") !== -1) && new URL(location.href).searchParams.get("pid") !== null) { |
There was a problem hiding this comment.
@INFP-coder I have started the AI code review. It will take a few minutes to complete.
|
分支和那几个小的都改好了, if (retryText.indexOf("提交成功") !== -1 ||
retryText.indexOf("Solution") !== -1 ||
retryText.indexOf("status.php") !== -1) {留着这条,你新写的 jumbotron 提取分支永远走不到。每个页面导航栏都有 真提交成功是 302,上面 另外冷却重试还没加。#1017 里"状态里没有提交记录"就是它引起的,不加的话原来的现象还在,只是从"提交失败"变成误报"提交成功"。要我补一个 commit 上去吗? 同上,LLM 帮我整理的。 |
There was a problem hiding this comment.
All reported issues were addressed across 4 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
1 issue found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="XMOJ.user.js">
<violation number="1" location="XMOJ.user.js:4312">
P2: After a non-redirected fallback submission succeeds, `PassCheck` remains visible and clickable, so users can submit the same code repeatedly despite the success state. Hide or disable `PassCheck` when marking the fallback submission successful.</violation>
</file>
Requires human review: Auto-approval blocked because this review re-detected 3 unresolved issues already reported by Cubic.
Re-trigger cubic
| ErrorElement.style.display = "block"; | ||
| ErrorMessage.style.color = "green"; | ||
| ErrorMessage.innerText = "✅ 回退提交成功!请查看状态。"; | ||
| Submit.disabled = true; |
There was a problem hiding this comment.
P2: After a non-redirected fallback submission succeeds, PassCheck remains visible and clickable, so users can submit the same code repeatedly despite the success state. Hide or disable PassCheck when marking the fallback submission successful.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At XMOJ.user.js, line 4312:
<comment>After a non-redirected fallback submission succeeds, `PassCheck` remains visible and clickable, so users can submit the same code repeatedly despite the success state. Hide or disable `PassCheck` when marking the fallback submission successful.</comment>
<file context>
@@ -4211,98 +4211,143 @@ async function main() {
+ ErrorElement.style.display = "block";
+ ErrorMessage.style.color = "green";
+ ErrorMessage.innerText = "✅ 回退提交成功!请查看状态。";
+ Submit.disabled = true;
+ Submit.value = "已提交";
+ return;
</file context>
| Submit.disabled = true; | |
| Submit.disabled = true; | |
| PassCheck.style.display = "none"; |
|
成功判定改对了, 不过现在这版有个更要命的问题:文件语法都过不了,整个脚本会加载失败。 4327–4329 连着三个 顺手再提几个:
冷却重试还是没加,所以 #1017 的现象目前还在——只是从误报"提交成功"变成了如实显示"❌ 回退提交失败:请勿重复提交"。这一步比之前对,但功能还没通。要我补一个 commit 上去吗? 推之前跑一下 同上,LLM 帮我整理的。 |
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="XMOJ.user.js">
<violation number="1" location="XMOJ.user.js:4316">
P0: The extra closing brace makes `XMOJ.user.js` syntactically invalid, so the userscript cannot load at all. Remove the obsolete brace left behind by deleting the nested success check.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| } else { | ||
| const textOnly = retryText.replace(/<[^>]*>/g, "").trim(); | ||
| errorMsg = textOnly.substring(0, 100) || errorMsg; | ||
| } |
There was a problem hiding this comment.
P0: The extra closing brace makes XMOJ.user.js syntactically invalid, so the userscript cannot load at all. Remove the obsolete brace left behind by deleting the nested success check.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At XMOJ.user.js, line 4316:
<comment>The extra closing brace makes `XMOJ.user.js` syntactically invalid, so the userscript cannot load at all. Remove the obsolete brace left behind by deleting the nested success check.</comment>
<file context>
@@ -4295,45 +4295,35 @@ if ((text.indexOf("没有这个比赛!") !== -1 || text.indexOf("比赛已结
- return;
+ const textOnly = retryText.replace(/<[^>]*>/g, "").trim();
+ errorMsg = textOnly.substring(0, 100) || errorMsg;
+ }
+ } catch (_) {
+ // 解析失败则使用默认信息
</file context>
|
冲突正好落在你改的那个 1. 处理器开头新增的验证码检查 PassCheck.addEventListener("click", async () => {
if (CaptchaIsMissing()) return;
...2. 回退提交的 body 要接上 extern-contrib 上现在是这样的: "body": "id=" + rPID + "&language=1&" + "source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch + GetCaptchaParameter()你这版回退提交没带 vcode。评测队列忙、验证码出来的时候,回退提交会因为缺 vcode 直接失败——而且按 #1019 注释里的说法,空答案会让服务端把这个 session 的 4 位验证码换成 8 位,代价不小。 另外前面那个语法错误(4329 行多的 同上,LLM 帮我整理的。 |
|
LLM好玩吗?PS:别忘了resolve conflict |
|
LLM 在 /loop |
|
哎回来看到一大堆LLM回复 给我整不会了 |
Signed-off-by: Feng Jiajun <kp6phnt44s@privaterelay.appleid.com>
Signed-off-by: Feng Jiajun <kp6phnt44s@privaterelay.appleid.com>
There was a problem hiding this comment.
3 issues found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="XMOJ.user.js">
<violation number="1" location="XMOJ.user.js:4588">
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.</violation>
<violation number="2" location="XMOJ.user.js:4646">
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.</violation>
<violation number="3" location="XMOJ.user.js:4648">
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.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| "method": "POST", | ||
| "body": "id=" + rPID + "&language=1&source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch2 | ||
| }); | ||
| if (retryResp.redirected) { |
There was a problem hiding this comment.
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>
| }, | ||
| "referrer": location.href, | ||
| "method": "POST", | ||
| "body": "id=" + rPID + "&language=1&source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch2 |
There was a problem hiding this comment.
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>
| "body": "id=" + rPID + "&language=1&source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch2 | |
| "body": "id=" + rPID + "&language=1&source=" + encodeURIComponent(CodeMirrorElement.getValue()) + o2Switch2 + GetCaptchaParameter() |
| 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) { |
There was a problem hiding this comment.
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>

新增回退提交后判断结果并阻断外层错误,外层处理会继续执行并覆盖为“提交失败”状态。
What does this PR aim to accomplish?:
修复已结束比赛的提交回退功能失效问题。当用户从已结束比赛的提交页(/submitpage.php?cid=&pid=)提交代码时,脚本原本应自动解析该比赛中的真实题目号,并向题目页面直接提交(回退),但此功能现已失效,导致用户显示“提交失败”且无任何回退记录。
本次 PR 旨在恢复此回退功能确保在各种比赛题目编号格式下均能正确解析真实 PID,同时优化错误处理,防止成功回退后被外层逻辑错误覆盖为“提交失败”。
How does this PR accomplish the above?:
改进真实题目号解析
原先依赖 substring(2, 6) 截取题号列,过于脆弱(当题号列为纯数字或格式变化时失败)。现改用 正则表达式 /\d+/ 从题号列文本中提取第一个数字,无论格式是“1”、“Problem 1”还是“问题 1”均可正确提取。
增加回退提交后的状态判断
回退提交后,检查响应是否发生重定向(Response.redirected),或响应文本中是否包含“提交成功”、“Solution”、“status.php”等成功标识。若成功,显示绿色成功提示,并 立即 return,阻止外层错误覆盖;若失败,显示具体错误信息并保留重试机会。
完善错误提示与用户体验
区分“回退中”、“回退成功”、“回退失败”三种状态,使用不同颜色(黄/绿/红)反馈,让用户清晰知晓当前进展。若解析真实 PID 失败,给出明确错误提示,引导用户手动提交。
错误检测范围扩展
除 “没有这个比赛!” 外,增加对 “比赛已结束” 等常见提示的检测(通过 text.indexOf 涵盖),但主要仍以 text.indexOf("没有这个比赛!") 作为回退触发点。
By submitting this pull request, I confirm the following:
git rebase)Testing:
/submitpage.php?cid=123&pid=0).Fixed Screenshot:
Summary by Sourcery
恢复已结束比赛的提交回退流程并改进其结果处理与用户反馈。
Bug Fixes:
Enhancements:
Chores:
Summary by cubic
修复已结束比赛的提交回退被误判为失败。旧行为:题号解析脆弱且回退成功会被外层覆盖为失败;新行为:仅以服务端重定向判定成功,命中后立即短路并跳转。
pid时回退;初次提交若已重定向则直接跳转。.jumbotron或纯文本前 100 字作为错误信息,允许重试。ads.txt;清理index.html多余空行。Written for commit b0a0020. Summary will update on new commits.