fix: 优化步骤 9 错误处理,直接报错不再重试认证页

This commit is contained in:
QLHazyCoder
2026-04-22 20:56:49 +08:00
parent 69434d67ee
commit d50d8b5305
5 changed files with 48 additions and 54 deletions
+1 -1
View File
@@ -623,7 +623,7 @@ Step 8 默认要求当前认证页已经处于登录验证码页。
- 等待按钮可点击 - 等待按钮可点击
- 获取按钮坐标 - 获取按钮坐标
- 通过 Chrome `debugger` 的输入事件点击该按钮 - 通过 Chrome `debugger` 的输入事件点击该按钮
- 点击后会持续检查页面是否真正离开当前状态;如果出现认证页 `重试` 页面,会先通过共享恢复逻辑最多自动点击 5 次 `重试` 尝试恢复,再重新执行当前轮的“继续”点击 - 点击后会持续检查页面是否真正离开当前状态;如果点击后出现认证页 `重试` 页面,则直接报错,不会在 Step 9 内部点击 `重试`
- 同时监听 `chrome.webNavigation.onBeforeNavigate` - 同时监听 `chrome.webNavigation.onBeforeNavigate`
- 一旦捕获本地回调地址,就把结果保存到 `Callback` - 一旦捕获本地回调地址,就把结果保存到 `Callback`
+2 -27
View File
@@ -7194,7 +7194,6 @@ async function getStep8PageState(tabId, responseTimeoutMs = 1500) {
async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS) { async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS) {
const start = Date.now(); const start = Date.now();
let recovered = false; let recovered = false;
let retryRecovered = false;
while (Date.now() - start < timeoutMs) { while (Date.now() - start < timeoutMs) {
throwIfStopped(); throwIfStopped();
@@ -7206,20 +7205,9 @@ async function waitForStep8Ready(tabId, timeoutMs = STEP8_READY_WAIT_TIMEOUT_MS)
throw new Error('步骤 9:认证页进入了手机号页面,当前不是 OAuth 同意页,无法继续自动授权。'); throw new Error('步骤 9:认证页进入了手机号页面,当前不是 OAuth 同意页,无法继续自动授权。');
} }
if (pageState?.retryPage) { if (pageState?.retryPage) {
await recoverAuthRetryPageOnTab(tabId, { throw new Error(`步骤 9:当前认证页已进入重试页,当前流程将直接报错。URL: ${pageState.url || 'unknown'}`);
flow: 'auth',
logLabel: '步骤 9:检测到认证页重试页,正在点击“重试”恢复',
step: 8,
timeoutMs: Math.max(1000, Math.min(12000, timeoutMs)),
});
retryRecovered = true;
await sleepWithStop(250);
continue;
} }
if (pageState?.consentReady) { if (pageState?.consentReady) {
if (retryRecovered) {
await addLog('步骤 9:认证页重试页已恢复,准备重新定位“继续”按钮...', 'info');
}
return pageState; return pageState;
} }
if (pageState === null && !recovered) { if (pageState === null && !recovered) {
@@ -7384,18 +7372,7 @@ async function waitForStep8ClickEffect(tabId, baselineUrl, timeoutMs = STEP8_CLI
throw new Error('步骤 9:点击“继续”后页面跳到了手机号页面,当前流程无法继续自动授权。'); throw new Error('步骤 9:点击“继续”后页面跳到了手机号页面,当前流程无法继续自动授权。');
} }
if (pageState?.retryPage) { if (pageState?.retryPage) {
await recoverAuthRetryPageOnTab(tabId, { throw new Error(`步骤 9:点击“继续”后页面进入认证页重试页,当前流程将直接报错。URL: ${pageState.url || baselineUrl || 'unknown'}`);
flow: 'auth',
logLabel: '步骤 9:点击“继续”后进入重试页,正在点击“重试”恢复',
step: 8,
timeoutMs: Math.max(1000, Math.min(12000, timeoutMs)),
});
return {
progressed: false,
reason: 'retry_page_recovered',
restartCurrentStep: true,
url: pageState.url || baselineUrl || '',
};
} }
if (pageState === null) { if (pageState === null) {
if (!recovered) { if (!recovered) {
@@ -7429,8 +7406,6 @@ function getStep8EffectLabel(effect) {
switch (effect?.reason) { switch (effect?.reason) {
case 'url_changed': case 'url_changed':
return `URL 已变化:${effect.url}`; return `URL 已变化:${effect.url}`;
case 'retry_page_recovered':
return '页面进入重试页并已恢复,需要重新执行当前步骤';
case 'page_reloading': case 'page_reloading':
return '页面正在跳转或重载'; return '页面正在跳转或重载';
case 'left_consent_page': case 'left_consent_page':
-6
View File
@@ -200,12 +200,6 @@
break; break;
} }
if (effect.restartCurrentStep) {
await addLog(`步骤 9${getStep8EffectLabel(effect)},准备重新定位“继续”按钮并重试...`, 'warn');
await sleepWithStop(STEP8_CLICK_RETRY_DELAY_MS);
continue;
}
if (round >= STEP8_MAX_ROUNDS) { if (round >= STEP8_MAX_ROUNDS) {
throw new Error(`步骤 9:连续 ${STEP8_MAX_ROUNDS} 轮点击“继续”后页面仍无反应。`); throw new Error(`步骤 9:连续 ${STEP8_MAX_ROUNDS} 轮点击“继续”后页面仍无反应。`);
} }
+44 -19
View File
@@ -51,10 +51,8 @@ function extractFunction(name) {
return source.slice(start, end); return source.slice(start, end);
} }
test('step 8 click effect returns restart_current_step when retry page is recovered', async () => { test('step 8 click effect throws when retry page appears after clicking continue', async () => {
const api = new Function(` const api = new Function(`
let recoverCalls = 0;
const chrome = { const chrome = {
tabs: { tabs: {
async get() { async get() {
@@ -69,10 +67,6 @@ const chrome = {
function throwIfStopped() {} function throwIfStopped() {}
async function sleepWithStop() {} async function sleepWithStop() {}
async function ensureStep8SignupPageReady() {} async function ensureStep8SignupPageReady() {}
async function recoverAuthRetryPageOnTab() {
recoverCalls += 1;
return { recovered: true };
}
async function getStep8PageState() { async function getStep8PageState() {
return { return {
url: 'https://auth.openai.com/authorize', url: 'https://auth.openai.com/authorize',
@@ -89,20 +83,51 @@ return {
async run() { async run() {
return waitForStep8ClickEffect(88, 'https://auth.openai.com/authorize', 1000); return waitForStep8ClickEffect(88, 'https://auth.openai.com/authorize', 1000);
}, },
snapshot() { };
return { recoverCalls }; `)();
await assert.rejects(
() => api.run(),
/点击“继续”后页面进入认证页重试页/
);
});
test('step 8 ready check throws when consent page is already a retry page before clicking', async () => {
const api = new Function(`
const chrome = {
tabs: {
async get() {
return {
id: 88,
url: 'https://auth.openai.com/authorize',
};
},
},
};
function throwIfStopped() {}
async function sleepWithStop() {}
async function ensureStep8SignupPageReady() {}
async function getStep8PageState() {
return {
url: 'https://auth.openai.com/authorize',
retryPage: true,
addPhonePage: false,
consentReady: false,
};
}
${extractFunction('waitForStep8Ready')}
return {
async run() {
return waitForStep8Ready(88, 1000);
}, },
}; };
`)(); `)();
const result = await api.run(); await assert.rejects(
const snapshot = api.snapshot(); () => api.run(),
/当前认证页已进入重试页/
assert.deepStrictEqual(result, { );
progressed: false,
reason: 'retry_page_recovered',
restartCurrentStep: true,
url: 'https://auth.openai.com/authorize',
});
assert.equal(snapshot.recoverCalls, 1);
}); });
+1 -1
View File
@@ -709,7 +709,7 @@
- Step 8 如果发现认证页已经进入登录超时报错/重试页,会直接报错并回到 Step 7 重新开始,而不是在 Step 8 内部点击 `重试` - Step 8 如果发现认证页已经进入登录超时报错/重试页,会直接报错并回到 Step 7 重新开始,而不是在 Step 8 内部点击 `重试`
- Step 8 的登录重试页判定也覆盖 `/email-verification` 上的 `405 / Route Error`,避免这类页面被误当成普通未知页。 - Step 8 的登录重试页判定也覆盖 `/email-verification` 上的 `405 / Route Error`,避免这类页面被误当成普通未知页。
- 任意认证页重试页如果正文中出现 `max_check_attempts`,会被视为 Cloudflare 风控触发:后台立刻完全停止流程,侧边栏会复用现有确认弹窗提示等待 15~30 分钟后再试,避免继续刷新或反复重试加重风控,确认按钮显示为“我知道了”。 - 任意认证页重试页如果正文中出现 `max_check_attempts`,会被视为 Cloudflare 风控触发:后台立刻完全停止流程,侧边栏会复用现有确认弹窗提示等待 15~30 分钟后再试,避免继续刷新或反复重试加重风控,确认按钮显示为“我知道了”。
- Step 9 在点击 OAuth 同意页 `继续` 后,会额外检查是否进入认证页重试页;若命中则先通过共享恢复逻辑最多自动点击 5 次 `重试` 尝试恢复,再重新执行当前轮的 `继续` 点击 - Step 9 在点击 OAuth 同意页 `继续` 后,会持续等待页面跳转;若点击后命中认证页重试页,则直接报错,不会在 Step 9 内部点击 `重试`
## 2026-04-21 2925 邮件时间窗补充 ## 2026-04-21 2925 邮件时间窗补充
- `2925` 在 Step 4 / Step 8 现在会携带固定的步骤开始时间窗口,实际筛选下限为“步骤开始时间向前回看 10 分钟”。 - `2925` 在 Step 4 / Step 8 现在会携带固定的步骤开始时间窗口,实际筛选下限为“步骤开始时间向前回看 10 分钟”。