Skip to content

fix(Delegation): 修复误触勾玉立即完成及返回按钮死循环 - #1940

Merged
runhey merged 1 commit into
runhey:devfrom
kunkun008:fix/delegation-dead-loop
Oct 15, 2026
Merged

runhey merged 1 commit into
runhey:devfrom
kunkun008:fix/delegation-dead-loop

Conversation

@kunkun008

@kunkun008 kunkun008 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

问题

Related #1162 (式神委派反复进出卡死)

委派任务中存在以下问题:

  1. 点击委派任务名时防抖间隔只有 1s,页面过渡期间可能连击,误触右上角「完成(30勾玉)」按钮,弹出消耗勾玉立即完成的确认框。
  2. 该确认框遮挡「返回」按钮时,ui_click_until_disappear(I_D_BACK) 一直点不掉,最终触发 GameTooManyClickError 导致反复重启的死循环。
  3. 进入/开始委派的循环没有超时,异常界面下会无限循环。

修复

仅修改 asks/Delegation/script_task.py:

  • 进入委派循环中,出现勾玉确认框时优先点击全局资源 I_UI_CANCEL(默认阈值),保护勾玉。
  • 已在委派中(出现 I_D_BACK)时,改为带 6s 超时的返回循环,期间若被取消弹窗遮挡则先点取消,再点返回。
  • 委派任务名 OCR 点击间隔 1s → 3s:页面切换动画期间旧画面仍可能被 OCR 命中,1s 间隔会导致二次点击落到下一页右上角的「完成(30勾玉)」位置;3s 足够覆盖过渡动画。
  • 进入委派(15s)和开始委派(10s)循环增加超时,避免卡死。

测试

  • 本地静态检查通过;需要实机验证(委派进行中再次进入、误触勾玉确认框两种场景)。

Checklist

  • 未使用 sleep
  • 单文件修改,无无关改动、未删除原有注释
  • 使用全局 I_UI_CANCEL,默认阈值

Sourcery 摘要

通过防止意外完成 gem 操作,并确保异常界面退出而不是无限挂起,让委托导航更安全。

Bug 修复:

  • 进入委托任务时,优先取消确认对话框,防止意外确认消耗 gem。
  • 防止确认对话框遮挡返回按钮时,委托界面陷入返回循环。
  • 增加防护措施,避免进入或启动委托任务时陷入无限循环。

改进:

  • 增加委托任务名称点击的防抖时间,避免页面切换期间发生重复点击。
  • 为退出已处于活动状态的委托增加有界处理。
Original summary in English

Summary by Sourcery

Make delegation navigation safer by preventing accidental gem completion and ensuring abnormal screens exit instead of hanging indefinitely.

Bug Fixes:

  • Prevent accidental gem-spending confirmations when entering delegation tasks by prioritizing cancellation of the confirmation dialog.
  • Prevent delegation screens from getting stuck in return loops when a confirmation dialog obscures the back button.
  • Add safeguards against indefinite loops while entering or starting delegation tasks.

Enhancements:

  • Increase delegation task-name click debouncing to avoid duplicate clicks during page transitions.
  • Add bounded handling for exiting an already active delegation.

- Raise OCR click debounce on task name from 1s to 3s so page-transition
  double-clicks do not hit the top-right finish-for-jade button.
- Prefer GlobalGame I_UI_CANCEL (default threshold) when the jade confirm
  modal appears, both while entering and while exiting an in-progress task.
- Replace ui_click_until_disappear(I_D_BACK) with a timed back loop that
  clears blocking cancel modals, avoiding GameTooManyClickError restarts.
- Add enter/start timeouts so stuck states fail closed instead of looping.

Refs: runhey#1162

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

嘿——我发现了 3 个问题

给 AI 代理的提示
请处理这次代码审查中的评论:

## 单条评论

### 评论 1
<location path="tasks/Delegation/script_task.py" line_range="106" />
<code_context>
         ui_click(self.C_D_4, self.I_D_SELECT_4)
         # 委派开始
         logger.info(f'Delegation: {name} start')
+        start_timer = Timer(10).start()
         while 1:
             self.screenshot()
</code_context>
<issue_to_address>
**委派可能无限期挂起**

如果始终没有出现队伍选择指示器,`ui_click` 就会一直等待该指示器;但 `start_timer` 只有在全部四次选择调用完成后才创建,因此任务会一直卡住。

请在选择调用开始前启动超时计时器,以限制等待时间。
</issue_to_address>

### 评论 2
<location path="tasks/Delegation/script_task.py" line_range="66" />
<code_context>
         if not self.ocr_appear(self.O_D_NAME):
             logger.warning(f'Delegation: {name} not found')
             return False
+        enter_timer = Timer(15).start()
         while 1:
</code_context>
<issue_to_address>
**委派超时后仍被报告为成功**

进入、返回或开始操作发生超时时,`delegate_one` 在某些超时情况下会返回 `False`,而只有开始操作超时时才会跳出循环;与此同时,`run` 忽略其结果并调用 `set_next_run(..., success=True)`,导致被跳过的任务仍会被安排并报告为成功。

所有超时情况都应返回失败,并让 `run` 根据该结果进行处理,而不是安排成功。

另请查看 `tasks/Delegation/script_task.py:83`、`tasks/Delegation/script_task.py:111-113`。
</issue_to_address>

### 评论 3
<location path="tasks/Delegation/script_task.py" line_range="62-69" />
<code_context>
+        enter_timer = Timer(15).start()
         while 1:
             self.screenshot()
             if self.appear(self.I_D_START):
                 break
+            if enter_timer.reached():
+                logger.warning(f'Delegation: {name} enter timeout')
+                return False
+
+            # 检测到弹出的勾玉确认框,优先点击取消保护勾玉
+            if self.appear_then_click(self.I_UI_CANCEL, interval=1):
+                continue
+
</code_context>
<issue_to_address>
**确认弹窗会阻止委派**

当确认弹窗遮挡 `I_D_BACK`,或其后方仍能检测到 `I_D_START` 时,循环会先检查开始或返回指示器,再检查 `I_UI_CANCEL`,因此可能在未关闭弹窗的情况下退出,后续任务处理也会在被阻挡的屏幕上继续进行。

请在两个循环中,检查开始或返回指示器之前先检查并关闭 `I_UI_CANCEL`。

另请查看 `tasks/Delegation/script_task.py:79-85`。
</issue_to_address>

Sourcery 评估

等待批准。 请先处理 3 个问题。

阻塞性问题:tasks/Delegation/script_task.py:106、tasks/Delegation/script_task.py:66、tasks/Delegation/script_task.py:69


Sourcery 对开源项目免费使用——如果您喜欢我们的审查结果,欢迎分享 ✨
Original comment in English

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="tasks/Delegation/script_task.py" line_range="106" />
<code_context>
         ui_click(self.C_D_4, self.I_D_SELECT_4)
         # 委派开始
         logger.info(f'Delegation: {name} start')
+        start_timer = Timer(10).start()
         while 1:
             self.screenshot()
</code_context>
<issue_to_address>
**Delegation can hang indefinitely**

When a team-selection indicator never appears, `ui_click` keeps waiting for the selection indicator, but `start_timer` is created only after all four selection calls finish, so the task remains stuck.

Bound the selection waits with a timeout that starts before the selection calls.
</issue_to_address>

### Comment 2
<location path="tasks/Delegation/script_task.py" line_range="66" />
<code_context>
         if not self.ocr_appear(self.O_D_NAME):
             logger.warning(f'Delegation: {name} not found')
             return False
+        enter_timer = Timer(15).start()
         while 1:
</code_context>
<issue_to_address>
**Timed-out delegation is reported successful**

When an enter, back, or start timeout fires, `delegate_one` returns `False` for some timeouts and only breaks on a start timeout, while `run` ignores its result and calls `set_next_run(..., success=True)`, so skipped work is scheduled and reported as successful.

Return failure for every timeout and make `run` handle that result instead of scheduling success.

Also at `tasks/Delegation/script_task.py:83`, `tasks/Delegation/script_task.py:111-113`.
</issue_to_address>

### Comment 3
<location path="tasks/Delegation/script_task.py" line_range="62-69" />
<code_context>
+        enter_timer = Timer(15).start()
         while 1:
             self.screenshot()
             if self.appear(self.I_D_START):
                 break
+            if enter_timer.reached():
+                logger.warning(f'Delegation: {name} enter timeout')
+                return False
+
+            # 检测到弹出的勾玉确认框,优先点击取消保护勾玉
+            if self.appear_then_click(self.I_UI_CANCEL, interval=1):
+                continue
+
</code_context>
<issue_to_address>
**Confirmation modal blocks delegation**

When a confirmation modal obscures `I_D_BACK` or leaves `I_D_START` detectable behind it, the loops test the start or back indicator before `I_UI_CANCEL`, so they exit without dismissing the modal and subsequent task handling continues on a blocked screen.

Check and dismiss `I_UI_CANCEL` before testing the start or back indicators in both loops.

Also at `tasks/Delegation/script_task.py:79-85`.
</issue_to_address>

Sourcery assessment

Approval pending. 3 findings to address first.

Blocking findings: tasks/Delegation/script_task.py:106, tasks/Delegation/script_task.py:66, tasks/Delegation/script_task.py:69


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

ui_click(self.C_D_4, self.I_D_SELECT_4)
# 委派开始
logger.info(f'Delegation: {name} start')
start_timer = Timer(10).start()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · 委派可能无限期挂起

如果始终没有出现队伍选择指示器,ui_click 就会一直等待该指示器;但 start_timer 只有在全部四次选择调用完成后才创建,因此任务会一直卡住。

请在选择调用开始前启动超时计时器,以限制等待时间。

给 AI 代理的提示
在 `tasks/Delegation/script_task.py` 第 106 行:

**委派可能无限期挂起**

如果始终没有出现队伍选择指示器,`ui_click` 就会一直等待该指示器;但 `start_timer` 只有在全部四次选择调用完成后才创建,因此任务会一直卡住。

请在选择调用开始前启动超时计时器,以限制等待时间。
Original comment in English

🟡 Medium · Delegation can hang indefinitely

When a team-selection indicator never appears, ui_click keeps waiting for the selection indicator, but start_timer is created only after all four selection calls finish, so the task remains stuck.

Bound the selection waits with a timeout that starts before the selection calls.

Prompt for AI agents
In `tasks/Delegation/script_task.py` at line 106:

**Delegation can hang indefinitely**

When a team-selection indicator never appears, `ui_click` keeps waiting for the selection indicator, but `start_timer` is created only after all four selection calls finish, so the task remains stuck.

Bound the selection waits with a timeout that starts before the selection calls.

break
if enter_timer.reached():
logger.warning(f'Delegation: {name} enter timeout')
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · 委派超时后仍被报告为成功

进入、返回或开始操作发生超时时,delegate_one 在某些超时情况下会返回 False,而只有开始操作超时时才会跳出循环;与此同时,run 忽略其结果并调用 set_next_run(..., success=True),导致被跳过的任务仍会被安排并报告为成功。

所有超时情况都应返回失败,并让 run 根据该结果进行处理,而不是安排成功。

另请查看 tasks/Delegation/script_task.py:83、tasks/Delegation/script_task.py:111-113。

给 AI 代理的提示
在 `tasks/Delegation/script_task.py` 第 66 行:

**委派超时后仍被报告为成功**

进入、返回或开始操作发生超时时,`delegate_one` 在某些超时情况下会返回 `False`,而只有开始操作超时时才会跳出循环;与此同时,`run` 忽略其结果并调用 `set_next_run(..., success=True)`,导致被跳过的任务仍会被安排并报告为成功。

所有超时情况都应返回失败,并让 `run` 根据该结果进行处理,而不是安排成功。

另请查看 `tasks/Delegation/script_task.py:83`、`tasks/Delegation/script_task.py:111-113`。
Original comment in English

🟡 Medium · Timed-out delegation is reported successful

When an enter, back, or start timeout fires, delegate_one returns False for some timeouts and only breaks on a start timeout, while run ignores its result and calls set_next_run(..., success=True), so skipped work is scheduled and reported as successful.

Return failure for every timeout and make run handle that result instead of scheduling success.

Also at tasks/Delegation/script_task.py:83, tasks/Delegation/script_task.py:111-113.

Prompt for AI agents
In `tasks/Delegation/script_task.py` at line 66:

**Timed-out delegation is reported successful**

When an enter, back, or start timeout fires, `delegate_one` returns `False` for some timeouts and only breaks on a start timeout, while `run` ignores its result and calls `set_next_run(..., success=True)`, so skipped work is scheduled and reported as successful.

Return failure for every timeout and make `run` handle that result instead of scheduling success.

Also at `tasks/Delegation/script_task.py:83`, `tasks/Delegation/script_task.py:111-113`.

Comment on lines 62 to +69
if self.appear(self.I_D_START):
break
if enter_timer.reached():
logger.warning(f'Delegation: {name} enter timeout')
return False

# 检测到弹出的勾玉确认框,优先点击取消保护勾玉
if self.appear_then_click(self.I_UI_CANCEL, interval=1):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High · 确认弹窗会阻止委派

当确认弹窗遮挡 I_D_BACK,或其后方仍能检测到 I_D_START 时,循环会先检查开始或返回指示器,再检查 I_UI_CANCEL,因此可能在未关闭弹窗的情况下退出,后续任务处理也会在被阻挡的屏幕上继续进行。

请在两个循环中,检查开始或返回指示器之前先检查并关闭 I_UI_CANCEL。

另请查看 tasks/Delegation/script_task.py:79-85。

给 AI 代理的提示
在 `tasks/Delegation/script_task.py` 第 62–69 行:

**确认弹窗会阻止委派**

当确认弹窗遮挡 `I_D_BACK`,或其后方仍能检测到 `I_D_START` 时,循环会先检查开始或返回指示器,再检查 `I_UI_CANCEL`,因此可能在未关闭弹窗的情况下退出,后续任务处理也会在被阻挡的屏幕上继续进行。

请在两个循环中,检查开始或返回指示器之前先检查并关闭 `I_UI_CANCEL`。

另请查看 `tasks/Delegation/script_task.py:79-85`。
Original comment in English

🟠 High · Confirmation modal blocks delegation

When a confirmation modal obscures I_D_BACK or leaves I_D_START detectable behind it, the loops test the start or back indicator before I_UI_CANCEL, so they exit without dismissing the modal and subsequent task handling continues on a blocked screen.

Check and dismiss I_UI_CANCEL before testing the start or back indicators in both loops.

Also at tasks/Delegation/script_task.py:79-85.

Prompt for AI agents
In `tasks/Delegation/script_task.py` at lines 62-69:

**Confirmation modal blocks delegation**

When a confirmation modal obscures `I_D_BACK` or leaves `I_D_START` detectable behind it, the loops test the start or back indicator before `I_UI_CANCEL`, so they exit without dismissing the modal and subsequent task handling continues on a blocked screen.

Check and dismiss `I_UI_CANCEL` before testing the start or back indicators in both loops.

Also at `tasks/Delegation/script_task.py:79-85`.

@github-actions

Copy link
Copy Markdown
Contributor

Findings

  • C4 [风险]
    位置:tasks/Delegation/script_task.py:39-47,delegate_one() 内的 ui_click()
    原因:新增的 15 秒和 10 秒超时没有覆盖四个 ui_click() 选择循环;任一目标控件未出现时,该 while 1 仍会无限循环,仍可能卡死。
    修改:为 ui_click() 增加 Timer 或最大重试次数,并在超时后返回失败,让 delegate_one() 能结束当前任务。

  • C6 [风险]
    位置:tasks/Delegation/script_task.py:103-116,delegate_one() 的开始委派循环
    原因:开始委派超时仅 break,随后函数隐式返回 None;run() 不检查结果,仍会继续后续委派并最终按成功完成任务处理,可能掩盖实际失败。
    修改:超时时显式返回 False,并在 run() 聚合 delegate_one() 结果或抛出可识别的任务失败异常。

  • C7 [风险]
    位置:tasks/Delegation/script_task.py:65-72,delegate_one() 的进入委派循环
    原因:代码先判断 I_D_START 再点击 I_UI_CANCEL;若勾玉确认框覆盖期间仍匹配到后台的开始控件,可能绕过取消保护直接进入后续点击流程。
    修改:将 I_UI_CANCEL 检测放到 I_D_START 判断之前,确认弹窗存在时始终先取消。

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • ab.chatgpt.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

Generated by PR Review Checklist for #1940 · codex · gpt56 · 13.1 AIC · ⌖ 2.97 AIC · ⊞ 12.1K · ◷

@runhey

runhey commented Oct 15, 2026

Copy link
Copy Markdown
Owner

#1870 会不会是这个引入的问题

@runhey
runhey merged commit 68122b3 into runhey:dev Oct 15, 2026
6 checks passed
@kunkun008

Copy link
Copy Markdown
Contributor Author

谢谢合并!我查了一下日志,应该不是 #1870 引起的:出问题时 #1870 改的领奖点击一次都没执行到。实际是点任务名进详情页时,页面还在过渡,脚本又点了一次同一位置,正好落在详情页右上角的「完成(30勾玉)」上;确认框是半透明的,后面的返回按钮还能匹配到但点不到,所以一直点返回。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants