signup-flow-helpers.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. (function attachSignupFlowHelpers(root, factory) {
  2. root.MultiPageSignupFlowHelpers = factory();
  3. })(typeof self !== 'undefined' ? self : globalThis, function createSignupFlowHelpersModule() {
  4. function createSignupFlowHelpers(deps = {}) {
  5. const {
  6. buildGeneratedAliasEmail,
  7. chrome,
  8. ensureContentScriptReadyOnTab,
  9. ensureHotmailAccountForFlow,
  10. ensureLuckmailPurchaseForFlow,
  11. isGeneratedAliasProvider,
  12. isReusableGeneratedAliasEmail,
  13. isHotmailProvider,
  14. isLuckmailProvider,
  15. isSignupEmailVerificationPageUrl,
  16. isSignupPasswordPageUrl,
  17. reuseOrCreateTab,
  18. sendToContentScriptResilient,
  19. setEmailState,
  20. SIGNUP_ENTRY_URL,
  21. SIGNUP_PAGE_INJECT_FILES,
  22. waitForTabUrlMatch,
  23. } = deps;
  24. async function openSignupEntryTab(step = 1) {
  25. const tabId = await reuseOrCreateTab('signup-page', SIGNUP_ENTRY_URL, {
  26. inject: SIGNUP_PAGE_INJECT_FILES,
  27. injectSource: 'signup-page',
  28. });
  29. await ensureContentScriptReadyOnTab('signup-page', tabId, {
  30. inject: SIGNUP_PAGE_INJECT_FILES,
  31. injectSource: 'signup-page',
  32. timeoutMs: 45000,
  33. retryDelayMs: 900,
  34. logMessage: `步骤 ${step}:ChatGPT 官网仍在加载,正在重试连接内容脚本...`,
  35. });
  36. return tabId;
  37. }
  38. async function ensureSignupEntryPageReady(step = 1) {
  39. const tabId = await openSignupEntryTab(step);
  40. const result = await sendToContentScriptResilient('signup-page', {
  41. type: 'ENSURE_SIGNUP_ENTRY_READY',
  42. step,
  43. source: 'background',
  44. payload: {},
  45. }, {
  46. timeoutMs: 20000,
  47. retryDelayMs: 700,
  48. logMessage: `步骤 ${step}:官网注册入口正在切换,等待页面恢复...`,
  49. });
  50. if (result?.error) {
  51. throw new Error(result.error);
  52. }
  53. return { tabId, result: result || {} };
  54. }
  55. function resolveSignupPostEmailState(rawUrl) {
  56. if (isSignupPasswordPageUrl(rawUrl)) {
  57. return 'password_page';
  58. }
  59. if (isSignupEmailVerificationPageUrl(rawUrl)) {
  60. return 'verification_page';
  61. }
  62. return '';
  63. }
  64. async function ensureSignupPostEmailPageReadyInTab(tabId, step = 2, options = {}) {
  65. const { skipUrlWait = false } = options;
  66. let landingUrl = '';
  67. let landingState = '';
  68. if (!skipUrlWait) {
  69. const matchedTab = await waitForTabUrlMatch(tabId, (url) => Boolean(resolveSignupPostEmailState(url)), {
  70. timeoutMs: 45000,
  71. retryDelayMs: 300,
  72. });
  73. if (!matchedTab) {
  74. throw new Error('等待邮箱提交后的页面跳转超时,请检查页面是否仍停留在邮箱输入页。');
  75. }
  76. landingUrl = matchedTab.url || '';
  77. landingState = resolveSignupPostEmailState(landingUrl);
  78. }
  79. if (!landingState) {
  80. try {
  81. const currentTab = await chrome.tabs.get(tabId);
  82. landingUrl = landingUrl || currentTab?.url || '';
  83. landingState = resolveSignupPostEmailState(landingUrl);
  84. } catch {
  85. landingUrl = landingUrl || '';
  86. }
  87. }
  88. if (!landingState) {
  89. throw new Error(`邮箱提交后未能识别当前页面,既不是密码页也不是邮箱验证码页。URL: ${landingUrl || 'unknown'}`);
  90. }
  91. await ensureContentScriptReadyOnTab('signup-page', tabId, {
  92. inject: SIGNUP_PAGE_INJECT_FILES,
  93. injectSource: 'signup-page',
  94. timeoutMs: 45000,
  95. retryDelayMs: 900,
  96. logMessage: landingState === 'verification_page'
  97. ? `步骤 ${step}:邮箱验证码页仍在加载,正在等待页面恢复...`
  98. : `步骤 ${step}:密码页仍在加载,正在重试连接内容脚本...`,
  99. });
  100. if (landingState === 'verification_page') {
  101. return {
  102. ready: true,
  103. state: landingState,
  104. url: landingUrl,
  105. };
  106. }
  107. const result = await sendToContentScriptResilient('signup-page', {
  108. type: 'ENSURE_SIGNUP_PASSWORD_PAGE_READY',
  109. step,
  110. source: 'background',
  111. payload: {},
  112. }, {
  113. timeoutMs: 20000,
  114. retryDelayMs: 700,
  115. logMessage: `步骤 ${step}:认证页正在切换,等待密码页重新就绪...`,
  116. });
  117. if (result?.error) {
  118. throw new Error(result.error);
  119. }
  120. return {
  121. ...(result || {}),
  122. ready: true,
  123. state: landingState,
  124. url: landingUrl,
  125. };
  126. }
  127. async function ensureSignupPasswordPageReadyInTab(tabId, step = 2, options = {}) {
  128. const result = await ensureSignupPostEmailPageReadyInTab(tabId, step, options);
  129. if (result.state !== 'password_page') {
  130. throw new Error(`当前页面不是密码页,实际落地为 ${result.state || 'unknown'}。URL: ${result.url || 'unknown'}`);
  131. }
  132. return result;
  133. }
  134. async function resolveSignupEmailForFlow(state) {
  135. let resolvedEmail = state.email;
  136. if (isHotmailProvider(state)) {
  137. const account = await ensureHotmailAccountForFlow({
  138. allowAllocate: true,
  139. markUsed: true,
  140. preferredAccountId: state.currentHotmailAccountId || null,
  141. });
  142. resolvedEmail = account.email;
  143. } else if (isLuckmailProvider(state)) {
  144. const purchase = await ensureLuckmailPurchaseForFlow({ allowReuse: true });
  145. resolvedEmail = purchase.email_address;
  146. } else if (isGeneratedAliasProvider(state)) {
  147. if (!isReusableGeneratedAliasEmail?.(state, resolvedEmail)) {
  148. resolvedEmail = buildGeneratedAliasEmail(state);
  149. }
  150. }
  151. if (!resolvedEmail) {
  152. throw new Error('缺少邮箱地址,请先在侧边栏粘贴邮箱。');
  153. }
  154. if (resolvedEmail !== state.email) {
  155. await setEmailState(resolvedEmail);
  156. }
  157. return resolvedEmail;
  158. }
  159. return {
  160. ensureSignupEntryPageReady,
  161. ensureSignupPostEmailPageReadyInTab,
  162. ensureSignupPasswordPageReadyInTab,
  163. openSignupEntryTab,
  164. resolveSignupEmailForFlow,
  165. };
  166. }
  167. return {
  168. createSignupFlowHelpers,
  169. };
  170. });