// ==UserScript== // @name Postman 注册机 // @namespace https://github.com/huajiqaq // @version 1.0 // @description 拦截 TempTom 邮件提取验证码,自动填入 Postman,导出 Token,自动升级 Enterprise 并跳转 AI 设置 // @author huajiqaq // @match https://www.temptom.com/* // @match https://identity.getpostman.com/* // @match https://*.postman.co/* // @run-at document-start // @grant GM_setValue // @grant GM_getValue // @grant GM_addValueChangeListener // @grant GM_cookie // @grant GM_xmlhttpRequest // @connect getpostman.com // @connect postman.co // @connect localhost // @license MIT // ==/UserScript== (function () { 'use strict'; const win = (typeof unsafeWindow !== 'undefined' && unsafeWindow) || window || document.defaultView; const PREFIX = 'tempom_'; const FROM_EMAIL_DOMAIN = 'notifications.getpostman.com'; const API_BASE = 'http://localhost:1930/api'; const LOCATION = window.location; const HOSTNAME = LOCATION.hostname; const PATH = LOCATION.pathname; const waitTasks = {}; // ===== 存储 ===== function setStore(key, val) { GM_setValue(PREFIX + key, val); } function getStore(key) { return GM_getValue(PREFIX + key, ''); } // ===== 工具 ===== function extractCode(text) { const m = text.match(/\b(\d{4,8})\b/); return m ? m[1] : null; } function generatePassword() { const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*'; let p = ''; for (let i = 0; i < 12; i++) p += chars[Math.floor(Math.random() * chars.length)]; return p; } function randomString(len) { const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; let result = ''; for (let i = 0; i < len; i++) { result += chars[Math.floor(Math.random() * chars.length)]; } return result; } function fillInput(el, val) { try { if (el && !el.value) { const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; nativeInputValueSetter.call(el, val); el.dispatchEvent(new Event('input', { bubbles: true })); el.dispatchEvent(new Event('change', { bubbles: true })); el.dispatchEvent(new Event('blur', { bubbles: true })); } } catch (e) { console.error('[fillInput]', e); } } function waitFor(selector, callback) { try { if (waitTasks[selector]) { clearInterval(waitTasks[selector].timer); delete waitTasks[selector]; console.log('[waitFor] 清理旧任务:', selector); } const el = document.querySelector(selector); if (el) { callback(el); return; } const timer = setInterval(function () { try { const el = document.querySelector(selector); if (el) { clearInterval(timer); if (waitTasks[selector]) { delete waitTasks[selector]; } console.log('[waitFor] 找到元素:', selector); callback(el); } } catch (e) { console.error('[waitFor]', e); } }, 200); waitTasks[selector] = { timer, callback }; } catch (e) { console.error('[waitFor]', e); } } function clearAllWaitTasks() { for (const selector in waitTasks) { if (waitTasks[selector]) { clearInterval(waitTasks[selector].timer); console.log('[清理] 清理 waitFor 任务:', selector); } } Object.keys(waitTasks).forEach(key => delete waitTasks[key]); } function clickEl(el) { try { if (el) { el.click(); console.log('[Postman] 点击:', el.tagName + (el.id ? '#' + el.id : '')); return true; } } catch (e) { console.error('[clickEl]', e); } return false; } function gmFetch(method, url, data, callback) { GM_xmlhttpRequest({ method: method, url: url, headers: { 'Content-Type': 'application/json' }, data: data ? JSON.stringify(data) : undefined, onload: function (res) { try { const json = JSON.parse(res.responseText); callback(null, json); } catch (e) { callback(e, null); } }, onerror: function (err) { callback(err, null); } }); } function clearDataAndRedirect() { try { console.log('[清理] 开始清理 Cookie...'); const domains = ['postman.co', '.postman.co', 'identity.getpostman.com', '.identity.getpostman.com']; let pending = domains.length; domains.forEach(function (domain) { GM_cookie('list', { domain: domain }, function (cookies, error) { if (error) { console.error('[清理] 获取 cookie (' + domain + ') 失败:', error); pending--; if (pending === 0) finishCleanup(); return; } if (cookies && cookies.length > 0) { let deleted = 0; cookies.forEach(function (cookie) { GM_cookie('delete', { domain: domain, name: cookie.name, path: cookie.path || '/' }, function (result, err) { if (err) { console.error('[清理] 删除 cookie (' + domain + ':' + cookie.name + ') 失败:', err); } else { console.log('[清理] cookie 已删除:', domain + ':' + cookie.name); } deleted++; if (deleted === cookies.length) { pending--; if (pending === 0) finishCleanup(); } }); }); } else { pending--; if (pending === 0) finishCleanup(); } }); }); function finishCleanup() { console.log('[清理] Cookie 清理完成,跳转到注册页面...'); setTimeout(function () { win.location.href = 'https://identity.getpostman.com/signup'; }, 1500); } } catch (e) { console.error('[清理]', e); } } function waitTurnstile(cb) { try { const startTime = Date.now(); let hasAlerted = false; const timer = setInterval(function () { try { let passed = false; if (win.turnstile && typeof win.turnstile.getResponse === 'function') { const r = win.turnstile.getResponse(); if (r && r.length > 0) passed = true; } const token = document.querySelector('input[name="cf-turnstile-response"]'); if (token && token.value && token.value.length > 0) passed = true; if (passed) { clearInterval(timer); console.log('[Turnstile] 通过'); if (cb) cb(); return; } if (!hasAlerted && Date.now() - startTime > 10000) { hasAlerted = true; alert('Turnstile 验证超时,请手动点击验证选框,脚本不处理复杂验证,如多次失败请更换 IP'); } } catch (e) { console.error(e); } }, 500); } catch (e) { console.error(e); } } // ===== TempTom ===== if (HOSTNAME.includes('temptom.com')) { const origFetch = win.fetch; const startTime = Date.now(); let lastMailbox = ''; function doRandom() { try { const btn = document.querySelector('button[title="随机"], button[title="random"]'); if (btn) { btn.click(); console.log('[TempTom] 随机邮箱'); } } catch (e) { console.error(e); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function () { setTimeout(doRandom, 500); }); } else { setTimeout(doRandom, 500); } win.fetch = function (input, init) { const url = typeof input === 'string' ? input : (input && input.url); return origFetch.call(this, input, init).then(function (r) { if (url && url.includes('/api.php')) { r.clone().text().then(function (data) { try { const json = JSON.parse(data); if (!json.ok || !json.emails) return; const mailbox = json.mailbox && json.mailbox.email || ''; if (mailbox && mailbox !== lastMailbox) { lastMailbox = mailbox; setStore('mailbox', mailbox); console.log('[TempTom] 邮箱:', mailbox); } const emails = json.emails.filter(function (e) { return e.from_email && e.from_email.includes(FROM_EMAIL_DOMAIN); }); if (!emails.length) return; const email = emails[0]; const receivedAt = new Date(email.received_at).getTime(); if (receivedAt <= startTime) return; const code = extractCode(email.subject || ''); if (!code) return; setStore('code', code); console.log('[TempTom] 验证码:', code); setTimeout(doRandom, 1000); } catch (e) { console.error(e); } }).catch(function (e) { console.error(e); }); } return r; }); }; console.log('[TempTom] 已启动'); return; } // ===== identity.getpostman.com ===== if (HOSTNAME.includes('identity.getpostman.com')) { function clickVerify() { try { const btn = document.getElementById('verify-btn'); if (btn) { btn.click(); console.log('[Postman] 验证'); } } catch (e) { console.error(e); } } function clickSignup() { try { const btn = document.getElementById('sign-up-btn'); if (btn) { btn.click(); console.log('[Postman] 注册'); } } catch (e) { console.error(e); } } // ===== 注册页 ===== if (PATH.includes('/signup')) { const mailbox = getStore('mailbox'); if (mailbox) { console.log('[Postman] 加载邮箱:', mailbox); waitFor('#email', function (el) { fillInput(el, mailbox); waitFor('#password', function (pwd) { fillInput(pwd, generatePassword()); waitTurnstile(clickSignup); }); }); } else { console.log('[Postman] 等待邮箱...'); GM_addValueChangeListener(PREFIX + 'mailbox', function (name, old, val, remote) { if (remote && val) { console.log('[Postman] 收到邮箱:', val); waitFor('#email', function (el) { fillInput(el, val); waitFor('#password', function (pwd) { fillInput(pwd, generatePassword()); waitTurnstile(clickSignup); }); }); } }); } console.log('[Postman] signup 已启动'); return; } // ===== 验证页 ===== if (PATH.includes('/verify-account')) { console.log('[Postman] 验证页面已启动'); function checkNotification() { try { const notif = document.querySelector('#notification-message, .notification-message, [data-testid="notification-message"]'); if (notif) { const msg = notif.textContent || notif.innerText; if (msg && msg.trim().length > 0) { console.warn('[Postman] 验证失败:', msg); const shouldRetry = confirm( '[Postman] 验证失败: ' + msg + '\n\n' + '是否清理数据并重试?\n' + '(点击"确定"将清理 Cookie 并重新开始注册流程)' ); if (shouldRetry) { console.log('[Postman] 用户选择清理数据重试'); clearDataAndRedirect(); } else { console.log('[Postman] 用户取消重试'); alert( '请尝试更改邮箱查看之前邮件,' + '如果之前邮件已经发送请手动输入后手动在随机邮箱页面刷新邮箱' ); } return true; } } return false; } catch (e) { console.error(e); return false; } } const notifTimer = setInterval(function () { try { if (checkNotification()) { clearInterval(notifTimer); } } catch (e) { console.error(e); } }, 500); function checkEmailMatch() { const randomEmail = getStore('mailbox'); try { waitFor('#signup-email-text', function (emailEl) { const verifyEmail = emailEl.textContent.trim(); console.log('[Postman] 验证页面邮箱:', verifyEmail); if (randomEmail && randomEmail !== verifyEmail) { console.warn('[Postman] 邮箱不一致!'); console.warn(' 随机邮箱:', randomEmail); console.warn(' 验证邮箱:', verifyEmail); // 弹窗询问是否清理数据重试 const shouldRetry = confirm( '邮箱不一致!\n\n' + '随机邮箱: ' + randomEmail + '\n' + '验证邮箱: ' + verifyEmail + '\n\n' + '是否清理数据并重试?\n' + '(点击"确定"将清理 Cookie 并重新开始)' ); if (shouldRetry) { console.log('[Postman] 用户选择清理数据重试'); clearDataAndRedirect(); } else { console.log('[Postman] 用户取消重试,请手动处理'); alert( '请尝试重试,重试后还不相同更改邮箱查看邮件,' + '如果之前邮件已经发送请务必手动输入后手动在随机邮箱页面刷新邮箱' ); } } }); } catch (e) { console.error(e); } } checkEmailMatch(); GM_addValueChangeListener(PREFIX + 'code', function (name, old, val, remote) { try { if (remote && val) { console.log('[Postman] 收到新验证码:', val); if (checkNotification()) return; waitFor('#verification-code', function (el) { try { fillInput(el, val); waitTurnstile(clickVerify); } catch (e) { console.error(e); } }); } } catch (e) { console.error(e); } }); console.log('[Postman] verify-account 已启动'); return; } console.log('[Postman] identity.getpostman.com 已启动,路径:', PATH); return; } // ===== postman.co ===== if (HOSTNAME.includes('postman.co')) { // 记录上一次路由路径,只有路由真正变化时才清理和执行 let lastPath = ''; function importAccount(tokenData, callback) { gmFetch('POST', API_BASE + '/accounts/import', tokenData, function (err, result) { if (err) { console.error('[导入] 请求失败:', err); alert('[导入] 请求失败: ' + err.message); if (callback) callback(err); return; } console.log('[导入] 结果:', result); if (callback) callback(null, result); }); } function warmupAccount(accountId, callback) { gmFetch('POST', API_BASE + '/accounts/' + accountId + '/warmup', null, function (err, result) { if (err) { console.error('[预热] 请求失败:', err); alert('[预热] 请求失败: ' + err.message); if (callback) callback(err); return; } console.log('[预热] 结果:', result); if (result.success) { console.log('[预热] 成功,账号已激活'); clearDataAndRedirect(); if (callback) callback(null, result); } else { alert('[预热] 失败: ' + (result.error || JSON.stringify(result))); if (callback) callback(result.error); } }); } function importAndWarmup(tokenData) { importAccount(tokenData, function (err, result) { if (err) return; if (!result.success) { alert('[导入失败] ' + JSON.stringify(result)); return; } if (result.results && result.results.length > 0) { const account = result.results[0]; if (account.status === 'created' || account.status === 'updated') { const accountId = account.accountId; console.log('[导入] 成功,账号ID:', accountId); warmupAccount(accountId); } else { alert('[导入失败] ' + JSON.stringify(account)); } } }); } function fetchTokenAndAlert() { try { const email = getStore('mailbox'); if (!email) { console.error('[Token] 未找到邮箱'); alert('[Token] 未找到邮箱,请先在 TempTom 获取邮箱'); return; } GM_cookie('list', { domain: 'postman.co', name: 'postman.sid' }, function (cookies, error) { try { if (error) { console.error('[Token] 获取 cookie 失败:', error); alert('[Token] 获取 cookie 失败: ' + error); return; } if (!cookies || cookies.length === 0) { console.error('[Token] 未找到 postman.sid cookie'); alert('[Token] 未找到 postman.sid cookie'); return; } const sid = cookies[0].value; const subdomain = LOCATION.hostname.replace(/\.postman\.co$/, ''); const USER_ID = win.USER_ID; const TEAM_ID = win.TEAM_ID; if (!USER_ID) { console.error('[Token] win.USER_ID 不存在'); alert('[Token] win.USER_ID 不存在'); return; } if (!TEAM_ID) { console.error('[Token] win.TEAM_ID 不存在'); alert('[Token] win.TEAM_ID 不存在'); return; } const result = { email: email, tokens: { postman_sid: sid, user_id: String(USER_ID), workspace_id: String(TEAM_ID), workspace_subdomain: subdomain } }; console.log('[Token] ===== Token JSON ====='); console.log(JSON.stringify(result, null, 2)); console.log('[Token] ========================='); setStore('postman_sid', sid); setStore('user_id', String(USER_ID)); setStore('workspace_id', String(TEAM_ID)); setStore('workspace_subdomain', subdomain); importAndWarmup(result); } catch (e) { console.error(e); } }); } catch (e) { console.error(e); } } // ===== Onboarding ===== function handleOnboarding() { console.log('[Postman] Onboarding 页面,开始自动填写...'); function fillOnboardingForm() { try { waitFor('[data-testid="onboarding-name-input"]', function (nameInput) { setTimeout(function () { try { const randomName = randomString(10); fillInput(nameInput, randomName); console.log('[Onboarding] 姓名:', randomName); } catch (e) { console.error(e); } waitFor('.aether-dropdown__control', function (firstControl) { try { const controls = document.querySelectorAll('.aether-dropdown__control'); const totalControls = controls.length; let completedControls = 0; controls.forEach(function (control, index) { setTimeout(function () { try { const input = control.querySelector('input[role="combobox"]'); if (!input) { completedControls++; return; } input.dispatchEvent(new FocusEvent('focus', { bubbles: true, cancelable: true, composed: true })); input.dispatchEvent(new FocusEvent('focusin', { bubbles: true, cancelable: true, composed: true })); setTimeout(function () { try { control.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true, button: 0, })); setTimeout(function () { try { waitFor('.aether-dropdown__option, [role="option"]', function (option) { try { option.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true, button: 0 })); option.click(); console.log('[Onboarding] 下拉 ' + (index + 1) + ' 已选'); completedControls++; if (completedControls === totalControls) { console.log('[Onboarding] 所有下拉已完成,开始处理团队按钮...'); waitFor('[data-testid="onboarding-team-size-1_member"]', function (teamBtn) { try { clickEl(teamBtn); console.log('[Onboarding] 已选 1 member'); setTimeout(function () { try { waitFor('[data-testid="onboarding-get-started-button"]', function (submitBtn) { try { clickEl(submitBtn); console.log('[Onboarding] 已点击提交'); } catch (e) { console.error(e); } }); } catch (e) { console.error(e); } }, 500); } catch (e) { console.error(e); } }); } } catch (e) { console.error(e); } }); } catch (e) { console.error(e); } }, 100); } catch (e) { console.error(e); } }, 100); } catch (e) { console.error(e); } }, index * 500); }); } catch (e) { console.error(e); } }); }, 100); }); } catch (e) { console.error('[Onboarding]', e); } } fillOnboardingForm(); } // ===== Workspace ===== function handleWorkspace() { console.log('[Postman] workspace 页面,开始升级...'); function startEnterpriseTrial() { try { waitFor('[data-testid="monetize-button-upgrade-button"]', function (upgradeBtn) { try { clickEl(upgradeBtn); waitFor('input[value*="enterprise"]', function (enterpriseRadio) { try { const container = enterpriseRadio.closest('[class*="Radio__StyledRadioContainer"]'); if (container && !container.classList.contains('upgrade-modal-plan-card-selected')) { const label = enterpriseRadio.closest('label') || enterpriseRadio.parentElement; if (label) clickEl(label); } waitFor('[aria-label="Start Enterprise Trial"]', function (trialBtn) { try { clickEl(trialBtn); console.log('[Postman] 试用已启动,跳转 AI 设置...'); setTimeout(function () { try { win.location.href = 'https://' + LOCATION.hostname + '/settings/team/ai'; } catch (e) { console.error(e); } }, 1000); } catch (e) { console.error(e); } }); } catch (e) { console.error(e); } }); } catch (e) { console.error(e); } }); } catch (e) { console.error(e); } } startEnterpriseTrial(); } // ===== AI Settings ===== function handleAISettings() { console.log('[Postman] AI 设置页面处理...'); function handleAISettingsUI() { try { const disableBtn = document.querySelector('[data-testid="ai-access-disable-button"]'); if (disableBtn) { console.log('[Postman] AI 已启用,获取 Token'); fetchTokenAndAlert(); return; } const enableBtn = document.querySelector('[data-testid="ai-access-enable-button"]'); if (enableBtn) { console.log('[Postman] AI 未启用,点击 Enable...'); clickEl(enableBtn); waitFor('[data-testid="ai-access-disable-button"]', function () { try { console.log('[Postman] AI 已启用,获取 Token'); fetchTokenAndAlert(); } catch (e) { console.error(e); } }); return; } console.log('[Postman] AI 设置页面状态未知,等待按钮...'); waitFor('[data-testid="ai-access-enable-button"], [data-testid="ai-access-disable-button"]', function () { try { handleAISettingsUI(); } catch (e) { console.error(e); } }); } catch (e) { console.error(e); } } handleAISettingsUI(); } // ===== 路由处理 ===== function handleRoute() { try { const currentPath = LOCATION.pathname; // 只有路径真正变化时才清理和执行 if (currentPath !== lastPath) { console.log('[Postman] 路由变化:', currentPath, '(上次:', lastPath || '无', ')'); // 路径变化时才清理旧任务 clearAllWaitTasks(); lastPath = currentPath; if (currentPath.includes('/onboarding')) { handleOnboarding(); } else if (currentPath.includes('/workspace')) { handleWorkspace(); } else if (currentPath.includes('/settings/team/ai')) { handleAISettings(); } } else { console.log('[Postman] 路由未变化,跳过:', currentPath); } } catch (e) { console.error('[handleRoute]', e); } } // ===== 监听 SPA 路由变化 ===== function setupRouteListener() { try { const originalPushState = history.pushState; history.pushState = function () { try { originalPushState.apply(this, arguments); handleRoute(); } catch (e) { console.error(e); } }; const originalReplaceState = history.replaceState; history.replaceState = function () { try { originalReplaceState.apply(this, arguments); handleRoute(); } catch (e) { console.error(e); } }; window.addEventListener('popstate', function () { try { handleRoute(); } catch (e) { console.error(e); } }); window.addEventListener('hashchange', function () { try { handleRoute(); } catch (e) { console.error(e); } }); } catch (e) { console.error(e); } } setupRouteListener(); handleRoute(); console.log('[Postman] postman.co 已启动,路径:', PATH); return; } })();