sync: dev to extern-contrib - #1022
github-actions[bot] wants to merge 79 commits into
Conversation
回退路径本身可以解析出真实题号,问题在 POST 之后:内层 fetch 只把响应
console.log 掉,从不判断成功与否,随后外层无条件覆盖成"提交失败"。日志里
那次回退实际收到的是 XMOJ 的提交冷却页(`请勿重复提交`,HUSTOJ 的
$OJ_SUBMIT_COOLDOWN_TIME 默认 5 秒,XMOJ 渲染成页面而非 302),响应被丢掉,
所以状态里没有任何提交记录。
- 把回退逻辑抽成 SubmitToEndedContestProblem,返回 {Success, Message}
- 只把 redirected 当成功信号,成功后 return,不再被外层覆盖
- 遇到 `请勿重复提交` 等冷却过去后重试(3 秒一次,最多 5 次)
- 其余失败从响应的 .jumbotron 取服务端原文,不再显示通用报错
- 遇到 `验证码错误` 立即刷新验证码并停止重试(答案已被消耗)
- 题号改用 /\d+/ 提取,原 substring(2, 6) 会把 5 位题号截成 4 位
- 补齐缺失的 GetCaptchaParameter(),并在解析失败时恢复提交按钮
Closes #1017
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
冷却等待期间验证码输入框和刷新按钮仍可交互。如果用户在这 3 秒里清空了 输入框或刷新了图片,下一次重试会用空的 GetCaptchaParameter() 发出请求, 而空答案会让服务端把本 session 的 4 位验证码换成 8 位。 在循环每次 POST 之前调用 CaptchaIsMissing()(它自己会提示并恢复按钮), 并用 Handled 标志让外层直接返回,不覆盖它设置的提示。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
contest.php 或 submit.php 的 fetch 被 reject(网络错误)时,异常会一路穿出 SubmitToEndedContestProblem、穿出 .then 回调、穿出 PassCheck 的 async 监听器 ——整条链上没有任何 catch。结果 ShowSubmitStatus 和恢复按钮的两行都不会执行, 提交按钮永远停在"正在提交...",而错误框在监听器开头已经被设成 display: none, 用户什么提示都看不到。这正是本 PR 声称要修掉的那个症状。 把两处网络请求都包进 try/catch,失败时返回错误信息交给外层显示。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ack-1017 fix: 比赛结束后回退提交不再被静默丢弃
Reviewer's GuideVersion 3.6.5 improves submission of problems from ended contests by extracting the fallback into a resilient, retrying workflow, centralizing status/error handling, and updating release metadata. Sequence diagram for ended-contest problem submissionsequenceDiagram
actor User
participant SubmitHandler
participant XMOJ as XMOJ
participant ContestPage
participant SubmitPage
User->>SubmitHandler: click PassCheck
SubmitHandler->>XMOJ: POST submit.php
XMOJ-->>SubmitHandler: 没有这个比赛!
SubmitHandler->>ContestPage: fetch contest.php?cid=ContestID
ContestPage-->>SubmitHandler: contest HTML
SubmitHandler->>SubmitHandler: parse RealPID
loop up to 5 attempts
SubmitHandler->>SubmitHandler: CaptchaIsMissing()
SubmitHandler->>SubmitPage: POST submit.php with RealPID
alt redirected
SubmitPage-->>SubmitHandler: redirect response
SubmitHandler->>XMOJ: navigate to result URL
else 请勿重复提交
SubmitPage-->>SubmitHandler: cooldown response
SubmitHandler->>SubmitHandler: wait 3 seconds
else submission result
SubmitPage-->>SubmitHandler: result HTML
end
end
SubmitHandler->>SubmitHandler: ShowSubmitStatus(Message)
Flow diagram for resilient ended-contest submission fallbackflowchart TD
A[Initial submission fails with 没有这个比赛] --> B[Fetch contest.php using cid]
B --> C{Contest page valid?}
C -- No --> D[Show failure status]
C -- Yes --> E[Parse contest problem list and resolve RealPID]
E --> F{RealPID found?}
F -- No --> D
F -- Yes --> G[Check captcha]
G --> H[POST submission to submit.php for RealPID]
H --> I{Response}
I -- Redirected --> J[Navigate to submission result]
I -- 验证码错误 --> K[RefreshCaptcha and show captcha error]
I -- 请勿重复提交 --> L{Attempts remain?}
L -- Yes --> M[Wait 3 seconds and retry]
M --> G
L -- No --> D
I -- Other failure --> D
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Deploying xmoj-script-dev-channel with
|
| Latest commit: |
97546b4
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://69416af5.xmoj-script-dev-channel.pages.dev |
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="XMOJ.user.js" line_range="4639" />
<code_context>
- 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.");
+ let FailMessage = "提交失败!请关闭脚本后重试!";
+ if (text.indexOf("没有这个比赛!") !== -1 && SearchParams.get("pid") !== null) {
+ const FallbackResult = await SubmitToEndedContestProblem(CodeMirrorElement.getValue(), o2Switch, ShowSubmitStatus);
+ if (FallbackResult.Success || FallbackResult.Handled) {
+ return;
</code_context>
<issue_to_address>
**issue (bug_risk):** The fetch that submits the original contest request is still outside any try/catch. When this submit.php request rejects because of a network failure, the PassCheck async listener rejects before it can restore the submit button or show an error, so the button remains stuck on “正在提交...” with no visible message.
**Triggers:** When the initial contest submission request fails at the network level before returning a Response.
**Suggested fix:** Wrap the PassCheck submit fetch and response-processing chain in try/catch, and restore `Submit.disabled`/`Submit.value` while displaying a network-error message in the catch path.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 1 finding to address first, and when the contest-to-problem mapping or retry logic is wrong, this code can POST the user's source to the wrong problem or create duplicate submissions, and those external submission records are not undone by reverting the script. The impact is bounded to the affected submissions and does not involve money or access control.
Blocking findings: XMOJ.user.js:4639
| 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."); | ||
| let FailMessage = "提交失败!请关闭脚本后重试!"; | ||
| if (text.indexOf("没有这个比赛!") !== -1 && SearchParams.get("pid") !== null) { | ||
| const FallbackResult = await SubmitToEndedContestProblem(CodeMirrorElement.getValue(), o2Switch, ShowSubmitStatus); |
There was a problem hiding this comment.
issue (bug_risk): The fetch that submits the original contest request is still outside any try/catch. When this submit.php request rejects because of a network failure, the PassCheck async listener rejects before it can restore the submit button or show an error, so the button remains stuck on “正在提交...” with no visible message.
Triggers: When the initial contest submission request fails at the network level before returning a Response.
Suggested fix: Wrap the PassCheck submit fetch and response-processing chain in try/catch, and restore Submit.disabled/Submit.value while displaying a network-error message in the catch path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat: 验证码识别不出时自动换一张
Update to release 3.7.0
在每个对 querySelector 结果取属性/调用方法的地方先判断是否为 null, 避免在 403/404 等页面结构不同时抛出 TypeError。 - 语句:前面加 if (xxx.querySelector(...) != null) - 条件:在条件中加 xxx.querySelector(...) != null && - 变量声明/赋值:为 null 时使用空值([] / "" / 0) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0135Ggqqp6xktQZytLRJQ9nc
- 各页面在所需容器缺失时(如403/404页面)直接跳过处理, 避免后续代码使用未生成的元素(排名表头、CodeMirror 文本框等) - RefreshCaptcha: #vcode 缺失时直接返回 - 修改个人信息: 表单元素缺失时不提交,避免用空值覆盖用户信息 - 自动提交/刷AC率/获取数据: 找不到提交记录时跳过,避免提交错误代码或无限轮询 - 恢复发帖板块 ID 的原写法(前面已有 null 检查) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0135Ggqqp6xktQZytLRJQ9nc
- GetUserInfo: #statics 表格缺失时返回默认值且不写入缓存,避免索引空数组 - ImproveACRate: 没有解析到已通过的题目时保持按钮禁用,避免用无效题号提交 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0135Ggqqp6xktQZytLRJQ9nc
…ript-dev/XMOJ-Script into claude/issue-1009-u2n0g5
- 页面隐藏期间改为隐藏 body 而不是把根元素设为透明:根元素透明时主题背景 也会被隐藏,深色模式下会露出浏览器默认的白色背景 - 画布背景和 color-scheme 使用当前皮肤的背景变量(ThemeCanvasCSS), 旧版页面和新版页面共用 - 页面在导航栏转换完成、顶部导航栏应用后才显示(RevealPage), 不再先显示未转换的导航栏;1.5 秒后无论如何都会显示 - 新版顶部导航栏(NavbarStyler)在导航栏转换后立即应用, 不再等到第一次定时刷新,避免约 1 秒后内容整体下移 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0135Ggqqp6xktQZytLRJQ9nc
…ript-dev/XMOJ-Script into claude/issue-1009-u2n0g5
保底:main() 开始后 1.5 秒,且最迟 DOMContentLoaded 后 4 秒。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0135Ggqqp6xktQZytLRJQ9nc
…ript-dev/XMOJ-Script into claude/issue-1009-u2n0g5
黑白皮肤的表头使用反色背景,但全局链接颜色与表头背景相同(对比度 1:1), 排名表等表头中的题号链接因此不可见。表头中的链接改用反色,深色和浅色主题下 对比度分别为 13.8:1 和 21:1。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0135Ggqqp6xktQZytLRJQ9nc
…ript-dev/XMOJ-Script into claude/issue-1009-u2n0g5
- reinfo.php:遍历所有结果分组,逐层检查结构,跳过分组标题等没有测试点结构的元素, 修复多 subtask 时 “Cannot read properties of undefined” 的错误 - package.json 的 test 脚本改为运行 tests/contest-web.test.cjs,新增 Test 工作流在 PR 和推送时运行 - 单元测试截取代码时要求标记唯一且顺序正确,否则直接失败;新增 NewBootstrap 开启、 MonochromeUI 关闭时的测试 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0135Ggqqp6xktQZytLRJQ9nc
…ript-dev/XMOJ-Script into claude/issue-1009-u2n0g5
与讨论区等其他渲染后端 Markdown 的地方保持一致(Sourcery 安全检查)。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0135Ggqqp6xktQZytLRJQ9nc
…ript-dev/XMOJ-Script into claude/issue-1009-u2n0g5
公告内容由维护者控制,按原设计直接渲染 Markdown。撤销 99af565。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0135Ggqqp6xktQZytLRJQ9nc
The load-time hide used visibility: hidden on the body, and innerText skips invisible text, so the score captured before the reveal was empty. Hide with opacity instead and read the score with textContent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0135Ggqqp6xktQZytLRJQ9nc
…ript-dev/XMOJ-Script into claude/issue-1009-u2n0g5
…king - Problem page buttons are full size and outlined, as on the legacy page. - With RemoveUseless, hide the repeated buttons below the statement, as the legacy page does. - Sample input/output uses the legacy layout: side by side cards with the copy button on the right; file names are no longer oversized headings. - The early block now also removes old stylesheet links that were parsed before it ran. They set the root font size to 10px/14px until DOMContentLoaded, so the navbar shrank and grew back on reload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0135Ggqqp6xktQZytLRJQ9nc
…ript-dev/XMOJ-Script into claude/issue-1009-u2n0g5
sync-branches: New code has just landed in dev, so let's bring extern-contrib up to speed!
Summary by Sourcery
Synchronize the external-contrib branch with dev by improving captcha recovery and making ended-contest fallback submissions reliable.
New Features:
Bug Fixes:
Enhancements:
Build:
Chores:
Summary by cubic
Syncs
extern-contribwithdev, bumpingXMOJ.user.jsandpackage.jsonto 3.7.1. Brings the captcha auto-refresh, reliable ended-contest fallback submissions, and compatibility with the new/web/contest/app.New Features
/web/contest/app now gets the same theme, top navbar, problem switcher, and other enhancements as the legacy pages; old contest URLs redirect to the matching/webpages.Bug Fixes
请勿重复提交cooldowns (3 seconds apart, up to 5 attempts) instead of being silently dropped; other failures surface the server's message, and captcha errors refresh the captcha and stop retrying.status.php.Written for commit 97546b4. Summary will update on new commits.