diff --git a/background.js b/background.js index 6d50e72..87868d1 100644 --- a/background.js +++ b/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 }, }); } diff --git a/content/mail-163.js b/content/mail-163.js index 2537c16..cd9a803 100644 --- a/content/mail-163.js +++ b/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'); } diff --git a/content/signup-page.js b/content/signup-page.js index 3861ec6..17a81f2 100644 --- a/content/signup-page.js +++ b/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 diff --git a/sidepanel/sidepanel.js b/sidepanel/sidepanel.js index c54ebfd..b9b9860 100644 --- a/sidepanel/sidepanel.js +++ b/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();