Explorar el Código

feat: random password per account + accounts log

- generatePassword(): 14 chars with uppercase, lowercase, digits, symbols
- Each run generates a unique password, stored in state.password
- accounts array: { email, password, createdAt } persisted across resets
- No more hardcoded password
- seenCodes also preserved across resets
unknown hace 4 meses
padre
commit
69b5b780f8
Se han modificado 4 ficheros con 107 adiciones y 34 borrados
  1. 60 16
      background.js
  2. 44 16
      content/mail-163.js
  3. 1 1
      content/signup-page.js
  4. 2 1
      sidepanel/sidepanel.js

+ 60 - 16
background.js

@@ -16,7 +16,8 @@ const DEFAULT_STATE = {
   },
   oauthUrl: null,
   email: null,
-  password: 'mimashisha0.0',
+  password: null,
+  accounts: [], // { email, password, createdAt }
   lastEmailTimestamp: null,
   localhostUrl: null,
   flowStartTime: null,
@@ -38,8 +39,40 @@ async function setState(updates) {
 
 async function resetState() {
   console.log(LOG_PREFIX, 'Resetting all state');
+  // Preserve seenCodes and accounts across resets
+  const prev = await chrome.storage.session.get(['seenCodes', 'accounts']);
   await chrome.storage.session.clear();
-  await chrome.storage.session.set({ ...DEFAULT_STATE });
+  await chrome.storage.session.set({
+    ...DEFAULT_STATE,
+    seenCodes: prev.seenCodes || [],
+    accounts: prev.accounts || [],
+  });
+}
+
+/**
+ * Generate a random password: 14 chars, mix of uppercase, lowercase, digits, symbols.
+ */
+function generatePassword() {
+  const upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
+  const lower = 'abcdefghjkmnpqrstuvwxyz';
+  const digits = '23456789';
+  const symbols = '!@#$%&*?';
+  const all = upper + lower + digits + symbols;
+
+  // Ensure at least one of each type
+  let pw = '';
+  pw += upper[Math.floor(Math.random() * upper.length)];
+  pw += lower[Math.floor(Math.random() * lower.length)];
+  pw += digits[Math.floor(Math.random() * digits.length)];
+  pw += symbols[Math.floor(Math.random() * symbols.length)];
+
+  // Fill remaining 10 chars
+  for (let i = 0; i < 10; i++) {
+    pw += all[Math.floor(Math.random() * all.length)];
+  }
+
+  // Shuffle
+  return pw.split('').sort(() => Math.random() - 0.5).join('');
 }
 
 // ============================================================
@@ -406,17 +439,18 @@ async function autoRunLoop(totalRuns) {
   for (let run = 1; run <= totalRuns; run++) {
     autoRunCurrentRun = run;
 
-    if (run > 1) {
-      // Reset state for next run (keep vpsUrl, mailProvider settings)
-      await addLog(`=== Resetting for run ${run}/${totalRuns} ===`, 'info');
-      const state = await getState();
-      const keepSettings = { vpsUrl: state.vpsUrl, mailProvider: state.mailProvider };
-      await resetState();
-      await setState(keepSettings);
-      // Broadcast reset to side panel
-      chrome.runtime.sendMessage({ type: 'AUTO_RUN_RESET' }).catch(() => {});
-      await new Promise(r => setTimeout(r, 1000));
-    }
+    // Reset everything at the start of each run (keep VPS/mail settings)
+    const prevState = await getState();
+    const keepSettings = {
+      vpsUrl: prevState.vpsUrl,
+      mailProvider: prevState.mailProvider,
+      autoRunning: true,
+    };
+    await resetState();
+    await setState(keepSettings);
+    // Tell side panel to reset all UI
+    chrome.runtime.sendMessage({ type: 'AUTO_RUN_RESET' }).catch(() => {});
+    await new Promise(r => setTimeout(r, 500));
 
     await addLog(`=== Auto Run ${run}/${totalRuns} — Phase 1: Get OAuth link & open signup ===`, 'info');
     const status = (phase) => ({ type: 'AUTO_RUN_STATUS', payload: { phase, currentRun: run, totalRuns } });
@@ -558,12 +592,22 @@ async function executeStep3(state) {
   if (!state.email) {
     throw new Error('No email address. Paste email in Side Panel first.');
   }
-  await addLog(`Step 3: Filling email ${state.email} and password`);
+
+  // Generate a unique password for this account
+  const password = generatePassword();
+  await setState({ password });
+
+  // Save account record
+  const accounts = state.accounts || [];
+  accounts.push({ email: state.email, password, createdAt: new Date().toISOString() });
+  await setState({ accounts });
+
+  await addLog(`Step 3: Filling email ${state.email}, password generated (${password.length} chars)`);
   await sendToContentScript('signup-page', {
     type: 'EXECUTE_STEP',
     step: 3,
     source: 'background',
-    payload: { email: state.email },
+    payload: { email: state.email, password },
   });
 }
 
@@ -673,7 +717,7 @@ async function executeStep6(state) {
     type: 'EXECUTE_STEP',
     step: 6,
     source: 'background',
-    payload: { email: state.email, password: state.password || 'mimashisha0.0' },
+    payload: { email: state.email, password: state.password },
   });
 }
 

+ 44 - 16
content/mail-163.js

@@ -17,8 +17,23 @@ if (!isTopFrame) {
   console.log(MAIL163_PREFIX, 'Skipping child frame');
 } else {
 
-// Track codes we've already seen across polls to avoid duplicates
-const seenCodes = new Set();
+// Track codes we've already seen — persisted in chrome.storage.session to survive script re-injection
+let seenCodes = new Set();
+
+// Load previously seen codes on startup
+(async () => {
+  try {
+    const data = await chrome.storage.session.get('seenCodes');
+    if (data.seenCodes && Array.isArray(data.seenCodes)) {
+      seenCodes = new Set(data.seenCodes);
+      console.log(MAIL163_PREFIX, `Loaded ${seenCodes.size} previously seen codes`);
+    }
+  } catch {}
+})();
+
+async function persistSeenCodes() {
+  await chrome.storage.session.set({ seenCodes: [...seenCodes] });
+}
 
 // ============================================================
 // Message Handler (top frame only)
@@ -130,11 +145,14 @@ async function handlePollEmail(step, payload) {
         const code = extractVerificationCode(subject + ' ' + ariaLabel);
         if (code && !seenCodes.has(code)) {
           seenCodes.add(code);
+          persistSeenCodes();
           const source = useFallback && existingMailIds.has(id) ? 'fallback' : 'new';
           log(`Step ${step}: Code found: ${code} (${source}, subject: ${subject.slice(0, 40)})`, 'ok');
 
-          // Delete this email via right-click menu
+          // Delete this email via right-click menu, WAIT for it to finish before returning
           await deleteEmail(item, step);
+          // Extra wait to ensure deletion is processed
+          await sleep(1000);
 
           return { ok: true, code, emailTimestamp: Date.now(), mailId: id };
         } else if (code && seenCodes.has(code)) {
@@ -167,26 +185,36 @@ async function deleteEmail(item, step) {
     log(`Step ${step}: Deleting email...`);
 
     // Right-click on the mail item to trigger context menu
+    const rect = item.getBoundingClientRect();
     item.dispatchEvent(new MouseEvent('contextmenu', {
       bubbles: true, cancelable: true, button: 2,
-      clientX: item.getBoundingClientRect().x + 100,
-      clientY: item.getBoundingClientRect().y + 10,
+      clientX: rect.left + rect.width / 2,
+      clientY: rect.top + rect.height / 2,
     }));
 
-    await sleep(500);
-
-    // Find the context menu and click "删除邮件"
-    const menuItems = document.querySelectorAll('.nui-menu-item .nui-menu-item-text');
-    for (const menuItem of menuItems) {
-      if (menuItem.textContent.trim() === '删除邮件') {
-        menuItem.closest('.nui-menu-item').click();
-        log(`Step ${step}: Email deleted`, 'ok');
-        await sleep(500);
-        return;
+    // Wait for context menu to appear
+    let deleteMenuItem = null;
+    for (let i = 0; i < 10; i++) {
+      await sleep(300);
+      const menuItems = document.querySelectorAll('.nui-menu-item .nui-menu-item-text');
+      for (const mi of menuItems) {
+        if (mi.textContent.trim() === '删除邮件') {
+          deleteMenuItem = mi.closest('.nui-menu-item');
+          break;
+        }
       }
+      if (deleteMenuItem) break;
     }
 
-    log(`Step ${step}: Could not find "删除邮件" in context menu`, 'warn');
+    if (deleteMenuItem) {
+      deleteMenuItem.click();
+      log(`Step ${step}: Clicked "删除邮件"`, 'ok');
+      // Wait for the delete to process and item to disappear
+      await sleep(1000);
+      log(`Step ${step}: Email deleted successfully`);
+    } else {
+      log(`Step ${step}: Context menu "删除邮件" not found`, 'warn');
+    }
   } catch (err) {
     log(`Step ${step}: Failed to delete email: ${err.message}`, 'warn');
   }

+ 1 - 1
content/signup-page.js

@@ -111,7 +111,7 @@ async function step3_fillEmailPassword(payload) {
     }
   }
 
-  fillInput(passwordInput, 'mimashisha0.0');
+  fillInput(passwordInput, payload.password || 'mimashisha0.0');
   log('Step 3: Password filled');
 
   // Report complete BEFORE submit, because submit causes page navigation

+ 2 - 1
sidepanel/sidepanel.js

@@ -301,7 +301,7 @@ chrome.runtime.onMessage.addListener((message) => {
     }
 
     case 'AUTO_RUN_RESET': {
-      // Reset UI for next run (but keep buttons disabled since auto-run is still going)
+      // Full UI reset for next run
       displayOauthUrl.textContent = 'Waiting...';
       displayOauthUrl.classList.remove('has-value');
       displayLocalhostUrl.textContent = 'Waiting...';
@@ -309,6 +309,7 @@ chrome.runtime.onMessage.addListener((message) => {
       inputEmail.value = '';
       displayStatus.textContent = 'Ready';
       statusBar.className = 'status-bar';
+      logArea.innerHTML = '';
       document.querySelectorAll('.step-row').forEach(row => row.className = 'step-row');
       document.querySelectorAll('.step-status').forEach(el => el.textContent = '');
       updateProgressCounter();