signup-flow-helpers.js 5.9 KB

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