fetch-login-code.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. (function attachBackgroundStep8(root, factory) {
  2. root.MultiPageBackgroundStep8 = factory();
  3. })(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep8Module() {
  4. function createStep8Executor(deps = {}) {
  5. const {
  6. addLog,
  7. chrome,
  8. CLOUDFLARE_TEMP_EMAIL_PROVIDER,
  9. confirmCustomVerificationStepBypass,
  10. ensureStep8VerificationPageReady,
  11. executeStep7,
  12. getOAuthFlowRemainingMs,
  13. getOAuthFlowStepTimeoutMs,
  14. getPanelMode,
  15. getMailConfig,
  16. getState,
  17. getTabId,
  18. HOTMAIL_PROVIDER,
  19. isTabAlive,
  20. isVerificationMailPollingError,
  21. LUCKMAIL_PROVIDER,
  22. resolveVerificationStep,
  23. reuseOrCreateTab,
  24. setState,
  25. setStepStatus,
  26. shouldSkipLoginVerificationForCpaCallback,
  27. shouldUseCustomRegistrationEmail,
  28. sleepWithStop,
  29. STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
  30. STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS,
  31. throwIfStopped,
  32. } = deps;
  33. async function getStep8ReadyTimeoutMs(actionLabel) {
  34. if (typeof getOAuthFlowStepTimeoutMs !== 'function') {
  35. return 15000;
  36. }
  37. return getOAuthFlowStepTimeoutMs(15000, {
  38. step: 8,
  39. actionLabel,
  40. });
  41. }
  42. function getStep8RemainingTimeResolver() {
  43. if (typeof getOAuthFlowRemainingMs !== 'function') {
  44. return undefined;
  45. }
  46. return async (details = {}) => getOAuthFlowRemainingMs({
  47. step: 8,
  48. actionLabel: details.actionLabel || '登录验证码流程',
  49. });
  50. }
  51. async function runStep8Attempt(state) {
  52. const mail = getMailConfig(state);
  53. if (mail.error) throw new Error(mail.error);
  54. const stepStartedAt = Date.now();
  55. const authTabId = await getTabId('signup-page');
  56. if (authTabId) {
  57. await chrome.tabs.update(authTabId, { active: true });
  58. } else {
  59. if (!state.oauthUrl) {
  60. throw new Error('缺少登录用 OAuth 链接,请先完成步骤 7。');
  61. }
  62. await reuseOrCreateTab('signup-page', state.oauthUrl);
  63. }
  64. throwIfStopped();
  65. await ensureStep8VerificationPageReady({
  66. timeoutMs: await getStep8ReadyTimeoutMs('确认登录验证码页已就绪'),
  67. });
  68. await addLog('步骤 8:登录验证码页面已就绪,开始获取验证码。', 'info');
  69. if (shouldUseCustomRegistrationEmail(state)) {
  70. await confirmCustomVerificationStepBypass(8);
  71. return;
  72. }
  73. throwIfStopped();
  74. if (mail.provider === HOTMAIL_PROVIDER || mail.provider === LUCKMAIL_PROVIDER || mail.provider === CLOUDFLARE_TEMP_EMAIL_PROVIDER) {
  75. await addLog(`步骤 8:正在通过 ${mail.label} 轮询验证码...`);
  76. } else {
  77. await addLog(`步骤 8:正在打开${mail.label}...`);
  78. const alive = await isTabAlive(mail.source);
  79. if (alive) {
  80. if (mail.navigateOnReuse) {
  81. await reuseOrCreateTab(mail.source, mail.url, {
  82. inject: mail.inject,
  83. injectSource: mail.injectSource,
  84. });
  85. } else {
  86. const tabId = await getTabId(mail.source);
  87. await chrome.tabs.update(tabId, { active: true });
  88. }
  89. } else {
  90. await reuseOrCreateTab(mail.source, mail.url, {
  91. inject: mail.inject,
  92. injectSource: mail.injectSource,
  93. });
  94. }
  95. }
  96. const shouldRefreshOAuthBeforeSubmit = getPanelMode(state) === 'cpa';
  97. let step7ReplayCompleted = false;
  98. await resolveVerificationStep(8, state, mail, {
  99. filterAfterTimestamp: stepStartedAt,
  100. getRemainingTimeMs: getStep8RemainingTimeResolver(),
  101. requestFreshCodeFirst: false,
  102. resendIntervalMs: (mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925')
  103. ? 0
  104. : STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
  105. beforeSubmit: shouldRefreshOAuthBeforeSubmit ? async (result) => {
  106. if (step7ReplayCompleted) {
  107. return;
  108. }
  109. step7ReplayCompleted = true;
  110. await addLog(`步骤 8:已拿到登录验证码 ${result.code},先刷新 CPA OAuth 链接并重走步骤 7,再回填验证码。`, 'warn');
  111. await rerunStep7ForStep8Recovery({
  112. logMessage: '步骤 8:正在重新获取最新 CPA OAuth 链接,并快速重走步骤 7...',
  113. postStepDelayMs: 1200,
  114. });
  115. await ensureStep8VerificationPageReady({
  116. timeoutMs: await getStep8ReadyTimeoutMs('重放步骤 7 后重新确认登录验证码页'),
  117. });
  118. await addLog('步骤 8:登录验证码页面已重新就绪,开始回填刚才获取到的验证码。', 'info');
  119. } : undefined,
  120. });
  121. }
  122. async function rerunStep7ForStep8Recovery(options = {}) {
  123. const {
  124. logMessage = '步骤 8:正在回到步骤 7,重新发起登录验证码流程...',
  125. postStepDelayMs = 3000,
  126. } = options;
  127. const currentState = await getState();
  128. await addLog(logMessage, 'warn');
  129. await executeStep7(currentState);
  130. if (postStepDelayMs > 0) {
  131. await sleepWithStop(postStepDelayMs);
  132. }
  133. }
  134. async function executeStep8(state) {
  135. if (shouldSkipLoginVerificationForCpaCallback(state)) {
  136. await setState({
  137. lastLoginCode: null,
  138. loginVerificationRequestedAt: null,
  139. oauthFlowDeadlineAt: null,
  140. });
  141. await setStepStatus(8, 'skipped');
  142. await addLog('步骤 8:当前已选择“第七步回调”,本轮无需获取登录验证码。', 'warn');
  143. return;
  144. }
  145. let currentState = state;
  146. let mailPollingAttempt = 1;
  147. let lastMailPollingError = null;
  148. while (true) {
  149. try {
  150. await runStep8Attempt(currentState);
  151. return;
  152. } catch (err) {
  153. if (!isVerificationMailPollingError(err)) {
  154. throw err;
  155. }
  156. lastMailPollingError = err;
  157. if (mailPollingAttempt >= STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS) {
  158. break;
  159. }
  160. mailPollingAttempt += 1;
  161. await addLog(
  162. `步骤 8:检测到邮箱轮询类失败,准备从步骤 7 重新开始(${mailPollingAttempt}/${STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS})...`,
  163. 'warn'
  164. );
  165. await rerunStep7ForStep8Recovery();
  166. currentState = await getState();
  167. }
  168. }
  169. if (lastMailPollingError) {
  170. throw new Error(
  171. `步骤 8:登录验证码流程在 ${STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS} 轮邮箱轮询恢复后仍未成功。最后一次原因:${lastMailPollingError.message}`
  172. );
  173. }
  174. throw new Error('步骤 8:登录验证码流程未成功完成。');
  175. }
  176. return { executeStep8 };
  177. }
  178. return { createStep8Executor };
  179. });