chatgpt.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // content/chatgpt.js — Content script for ChatGPT (steps 6, 7-receive, 8)
  2. // Injected on: chatgpt.com
  3. console.log('[MultiPage:chatgpt] Content script loaded on', location.href);
  4. // Listen for commands from Background
  5. chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  6. if (message.type === 'EXECUTE_STEP' || message.type === 'FILL_CODE') {
  7. handleCommand(message).then(() => {
  8. sendResponse({ ok: true });
  9. }).catch(err => {
  10. reportError(message.step, err.message);
  11. sendResponse({ error: err.message });
  12. });
  13. return true;
  14. }
  15. });
  16. async function handleCommand(message) {
  17. switch (message.type) {
  18. case 'EXECUTE_STEP':
  19. switch (message.step) {
  20. case 6: return await step6_loginChatGPT();
  21. case 8: return await step8_navigateOAuth();
  22. default: throw new Error(`chatgpt.js does not handle step ${message.step}`);
  23. }
  24. case 'FILL_CODE':
  25. return await step7_fillLoginCode(message.payload);
  26. }
  27. }
  28. // ============================================================
  29. // Step 6: Login ChatGPT
  30. // ============================================================
  31. async function step6_loginChatGPT() {
  32. log('Step 6: Looking for login button on ChatGPT...');
  33. // Get state for email and password
  34. const state = await chrome.runtime.sendMessage({ type: 'GET_STATE' });
  35. const email = state.email;
  36. const password = state.password;
  37. if (!email) throw new Error('No email found in state. Complete earlier steps first.');
  38. // Find login button on ChatGPT homepage
  39. let loginBtn = null;
  40. try {
  41. loginBtn = await waitForElementByText(
  42. 'a, button, [role="button"], [role="link"]',
  43. /log\s*in|sign\s*in|登录/i,
  44. 10000
  45. );
  46. } catch {
  47. try {
  48. loginBtn = await waitForElement(
  49. '[data-testid="login-button"], a[href*="auth"], a[href*="login"]',
  50. 5000
  51. );
  52. } catch {
  53. throw new Error('Could not find Login button on ChatGPT. URL: ' + location.href);
  54. }
  55. }
  56. simulateClick(loginBtn);
  57. log('Step 6: Clicked Login button, waiting for auth page...');
  58. await sleep(3000);
  59. // We may be redirected to an auth page (auth0.openai.com etc.)
  60. // Or the login form may appear on the same page
  61. // Try to find email input
  62. let emailInput = null;
  63. try {
  64. emailInput = await waitForElement(
  65. 'input[type="email"], input[name="email"], input[name="username"], input[id*="email"], input[placeholder*="email" i]',
  66. 15000
  67. );
  68. } catch {
  69. throw new Error('Could not find email input on login page. URL: ' + location.href);
  70. }
  71. fillInput(emailInput, email);
  72. log(`Step 6: Filled email: ${email}`);
  73. // Submit email
  74. await sleep(500);
  75. const submitBtn1 = document.querySelector('button[type="submit"]')
  76. || await waitForElementByText('button', /continue|next|submit|继续/i, 5000).catch(() => null);
  77. if (submitBtn1) {
  78. simulateClick(submitBtn1);
  79. log('Step 6: Submitted email');
  80. }
  81. await sleep(2000);
  82. // Check: password field or OTP?
  83. const passwordInput = document.querySelector('input[type="password"]');
  84. if (passwordInput) {
  85. log('Step 6: Password field found, filling password...');
  86. fillInput(passwordInput, password);
  87. await sleep(500);
  88. const submitBtn2 = document.querySelector('button[type="submit"]')
  89. || await waitForElementByText('button', /continue|log\s*in|submit|sign\s*in|登录/i, 5000).catch(() => null);
  90. if (submitBtn2) {
  91. simulateClick(submitBtn2);
  92. log('Step 6: Submitted password');
  93. }
  94. await sleep(3000);
  95. // Check if we need OTP after password
  96. const codeInput = document.querySelector(
  97. 'input[name="code"], input[maxlength="6"], input[inputmode="numeric"], input[type="text"][maxlength="6"]'
  98. );
  99. if (codeInput) {
  100. log('Step 6: OTP code input found after password. Waiting for step 7...');
  101. reportComplete(6, { needsOTP: true });
  102. } else {
  103. // Check if we're actually logged in (redirected to chatgpt.com main page)
  104. log('Step 6: Login appears successful (no OTP needed)');
  105. reportComplete(6, { needsOTP: false });
  106. }
  107. } else {
  108. // No password field — check for OTP input or "check your email" message
  109. const codeInput = document.querySelector(
  110. 'input[name="code"], input[maxlength="6"], input[inputmode="numeric"]'
  111. );
  112. if (codeInput) {
  113. log('Step 6: OTP flow detected (no password). Waiting for step 7...');
  114. reportComplete(6, { needsOTP: true });
  115. } else {
  116. // Maybe the page is still loading or transitioning
  117. log('Step 6: No password or OTP field found. May need email verification. Waiting for step 7...');
  118. reportComplete(6, { needsOTP: true });
  119. }
  120. }
  121. }
  122. // ============================================================
  123. // Step 7 (receiving end): Fill Login Verification Code
  124. // ============================================================
  125. async function step7_fillLoginCode(payload) {
  126. const { code } = payload;
  127. if (!code) throw new Error('No verification code provided.');
  128. log(`Step 7: Filling login verification code: ${code}`);
  129. // Find code input — single input or multiple single-digit inputs
  130. let codeInput = null;
  131. try {
  132. codeInput = await waitForElement(
  133. 'input[name="code"], input[name="otp"], input[maxlength="6"], input[type="text"][inputmode="numeric"], input[aria-label*="code" i], input[placeholder*="code" i]',
  134. 10000
  135. );
  136. } catch {
  137. // Check for multiple single-digit inputs
  138. const singleInputs = document.querySelectorAll('input[maxlength="1"]');
  139. if (singleInputs.length >= 6) {
  140. log('Step 7: Found single-digit code inputs, filling individually...');
  141. for (let i = 0; i < 6 && i < singleInputs.length; i++) {
  142. fillInput(singleInputs[i], code[i]);
  143. await sleep(100);
  144. }
  145. await sleep(1000);
  146. // Auto-submit may happen; if not, try clicking submit
  147. const submitBtn = document.querySelector('button[type="submit"]')
  148. || await waitForElementByText('button', /continue|verify|submit|confirm/i, 3000).catch(() => null);
  149. if (submitBtn) simulateClick(submitBtn);
  150. await sleep(3000);
  151. reportComplete(7);
  152. return;
  153. }
  154. throw new Error('Could not find verification code input on ChatGPT login. URL: ' + location.href);
  155. }
  156. fillInput(codeInput, code);
  157. await sleep(500);
  158. const submitBtn = document.querySelector('button[type="submit"]')
  159. || await waitForElementByText('button', /continue|verify|submit|confirm|确认/i, 5000).catch(() => null);
  160. if (submitBtn) {
  161. simulateClick(submitBtn);
  162. log('Step 7: Login verification code submitted');
  163. }
  164. await sleep(3000);
  165. reportComplete(7);
  166. }
  167. // ============================================================
  168. // Step 8: Navigate to OAuth URL
  169. // ============================================================
  170. async function step8_navigateOAuth() {
  171. const state = await chrome.runtime.sendMessage({ type: 'GET_STATE' });
  172. const oauthUrl = state.oauthUrl;
  173. if (!oauthUrl) throw new Error('No OAuth URL found. Complete step 1 first.');
  174. log(`Step 8: Navigating to OAuth URL: ${oauthUrl.slice(0, 80)}...`);
  175. log('Step 8: Waiting for localhost redirect (captured by background)...');
  176. // Navigate — the webNavigation listener in background will capture localhost redirect
  177. window.location.href = oauthUrl;
  178. // Don't reportComplete here — background handles it via webNavigation
  179. }