瀏覽代碼

feat: 添加步骤7重试机制,优化回退重跑步骤时自动失效后续状态

QLHazyCoder 4 月之前
父節點
當前提交
2165a0537f
共有 1 個文件被更改,包括 131 次插入2 次删除
  1. 131 2
      background.js

+ 131 - 2
background.js

@@ -7,6 +7,7 @@ const DUCK_AUTOFILL_URL = 'https://duckduckgo.com/email/settings/autofill';
 const STOP_ERROR_MESSAGE = '流程已被用户停止。';
 const STOP_ERROR_MESSAGE = '流程已被用户停止。';
 const HUMAN_STEP_DELAY_MIN = 700;
 const HUMAN_STEP_DELAY_MIN = 700;
 const HUMAN_STEP_DELAY_MAX = 2200;
 const HUMAN_STEP_DELAY_MAX = 2200;
+const STEP7_RESTART_MAX_ROUNDS = 5;
 
 
 initializeSessionStorageAccess();
 initializeSessionStorageAccess();
 
 
@@ -643,6 +644,15 @@ function isRetryableContentScriptTransportError(error) {
   return /back\/forward cache|message channel is closed|Receiving end does not exist|port closed before a response was received|A listener indicated an asynchronous response/i.test(message);
   return /back\/forward cache|message channel is closed|Receiving end does not exist|port closed before a response was received|A listener indicated an asynchronous response/i.test(message);
 }
 }
 
 
+function getErrorMessage(error) {
+  return String(typeof error === 'string' ? error : error?.message || '');
+}
+
+function isVerificationMailPollingError(error) {
+  const message = getErrorMessage(error);
+  return /未在 .*邮箱中找到新的匹配邮件|邮箱轮询结束,但未获取到验证码|无法获取新的(?:注册|登录)验证码|页面未能重新就绪|页面通信异常|did not respond in \d+s/i.test(message);
+}
+
 function isRestartCurrentAttemptError(error) {
 function isRestartCurrentAttemptError(error) {
   const message = String(typeof error === 'string' ? error : error?.message || '');
   const message = String(typeof error === 'string' ? error : error?.message || '');
   return /当前邮箱已存在,需要重新开始新一轮/.test(message);
   return /当前邮箱已存在,需要重新开始新一轮/.test(message);
@@ -670,6 +680,80 @@ function hasSavedProgress(statuses = {}) {
   return Object.values({ ...DEFAULT_STATE.stepStatuses, ...statuses }).some((status) => status !== 'pending');
   return Object.values({ ...DEFAULT_STATE.stepStatuses, ...statuses }).some((status) => status !== 'pending');
 }
 }
 
 
+function getDownstreamStateResets(step) {
+  if (step <= 1) {
+    return {
+      oauthUrl: null,
+      flowStartTime: null,
+      password: null,
+      lastEmailTimestamp: null,
+      lastSignupCode: null,
+      lastLoginCode: null,
+      localhostUrl: null,
+    };
+  }
+  if (step === 2) {
+    return {
+      password: null,
+      lastEmailTimestamp: null,
+      lastSignupCode: null,
+      lastLoginCode: null,
+      localhostUrl: null,
+    };
+  }
+  if (step === 3 || step === 4) {
+    return {
+      lastEmailTimestamp: null,
+      lastSignupCode: null,
+      lastLoginCode: null,
+      localhostUrl: null,
+    };
+  }
+  if (step === 5 || step === 6 || step === 7) {
+    return {
+      lastLoginCode: null,
+      localhostUrl: null,
+    };
+  }
+  if (step === 8) {
+    return {
+      localhostUrl: null,
+    };
+  }
+  return {};
+}
+
+async function invalidateDownstreamAfterStepRestart(step, options = {}) {
+  const { logLabel = `步骤 ${step} 重新执行` } = options;
+  const state = await getState();
+  const statuses = { ...(state.stepStatuses || {}) };
+  const changedSteps = [];
+
+  for (let downstream = step + 1; downstream <= 9; downstream++) {
+    if (statuses[downstream] !== 'pending') {
+      statuses[downstream] = 'pending';
+      changedSteps.push(downstream);
+    }
+  }
+
+  if (changedSteps.length) {
+    await setState({ stepStatuses: statuses });
+    for (const downstream of changedSteps) {
+      chrome.runtime.sendMessage({
+        type: 'STEP_STATUS_CHANGED',
+        payload: { step: downstream, status: 'pending' },
+      }).catch(() => {});
+    }
+    await addLog(`${logLabel},已重置后续步骤状态:${changedSteps.join(', ')}`, 'warn');
+  }
+
+  const resets = getDownstreamStateResets(step);
+  if (Object.keys(resets).length) {
+    await setState(resets);
+    broadcastDataUpdate(resets);
+  }
+}
+
 function clearStopRequest() {
 function clearStopRequest() {
   stopRequested = false;
   stopRequested = false;
 }
 }
@@ -928,6 +1012,9 @@ async function handleMessage(message, sender) {
         await ensureManualInteractionAllowed('手动执行步骤');
         await ensureManualInteractionAllowed('手动执行步骤');
       }
       }
       const step = message.payload.step;
       const step = message.payload.step;
+      if (message.source === 'sidepanel') {
+        await invalidateDownstreamAfterStepRestart(step, { logLabel: `步骤 ${step} 重新执行` });
+      }
       // Save email if provided (from side panel step 3)
       // Save email if provided (from side panel step 3)
       if (message.payload.email) {
       if (message.payload.email) {
         await setEmailState(message.payload.email);
         await setEmailState(message.payload.email);
@@ -1340,6 +1427,9 @@ async function runAutoSequenceFromStep(startStep, context = {}) {
           `步骤 9:检测到 OAuth callback 超时,正在回到步骤 6 重新开始授权流程(${step9RestartAttempts}/${maxStep9RestartAttempts})...`,
           `步骤 9:检测到 OAuth callback 超时,正在回到步骤 6 重新开始授权流程(${step9RestartAttempts}/${maxStep9RestartAttempts})...`,
           'warn'
           'warn'
         );
         );
+        await invalidateDownstreamAfterStepRestart(6, {
+          logLabel: `步骤 9 超时后准备回到步骤 6 重试(${step9RestartAttempts}/${maxStep9RestartAttempts})`,
+        });
         step = 6;
         step = 6;
         continue;
         continue;
       }
       }
@@ -2022,7 +2112,7 @@ async function executeStep6(state) {
 // Step 7: Get Login Verification Code (qq-mail.js polls, then fills in chatgpt.js)
 // Step 7: Get Login Verification Code (qq-mail.js polls, then fills in chatgpt.js)
 // ============================================================
 // ============================================================
 
 
-async function executeStep7(state) {
+async function runStep7Attempt(state) {
   const mail = getMailConfig(state);
   const mail = getMailConfig(state);
   if (mail.error) throw new Error(mail.error);
   if (mail.error) throw new Error(mail.error);
   const stepStartedAt = Date.now();
   const stepStartedAt = Date.now();
@@ -2073,7 +2163,46 @@ async function executeStep7(state) {
     filterAfterTimestamp: stepStartedAt,
     filterAfterTimestamp: stepStartedAt,
     requestFreshCodeFirst: true,
     requestFreshCodeFirst: true,
   });
   });
-  return;
+}
+
+async function rerunStep6ForStep7Recovery() {
+  const currentState = await getState();
+  const waitForStep6 = waitForStepComplete(6, 120000);
+  await addLog('步骤 7:正在回到步骤 6,重新发起登录验证码流程...', 'warn');
+  await executeStep6(currentState);
+  await waitForStep6;
+  await sleepWithStop(3000);
+}
+
+async function executeStep7(state) {
+  let lastError = null;
+
+  for (let round = 1; round <= STEP7_RESTART_MAX_ROUNDS; round++) {
+    const currentState = round === 1 ? state : await getState();
+
+    try {
+      if (round > 1) {
+        await addLog(`步骤 7:正在进行第 ${round}/${STEP7_RESTART_MAX_ROUNDS} 轮登录验证码恢复尝试。`, 'warn');
+      }
+      await runStep7Attempt(currentState);
+      return;
+    } catch (err) {
+      lastError = err;
+
+      if (!isVerificationMailPollingError(err)) {
+        throw err;
+      }
+
+      if (round >= STEP7_RESTART_MAX_ROUNDS) {
+        break;
+      }
+
+      await addLog(`步骤 7:检测到邮箱轮询类失败,准备从步骤 6 重新开始(${round + 1}/${STEP7_RESTART_MAX_ROUNDS})...`, 'warn');
+      await rerunStep6ForStep7Recovery();
+    }
+  }
+
+  throw lastError || new Error(`步骤 7:登录验证码流程在 ${STEP7_RESTART_MAX_ROUNDS} 轮后仍未成功。`);
 }
 }
 
 
 // ============================================================
 // ============================================================