#!/usr/bin/env python3 """Scrape OpenAI/ChatGPT verification codes from 2925 Webmail via Chrome CDP. Key 2925 quirks captured from live runs: - Do NOT navigate to #/mailList?type=收件箱 and trust the result; click the left nav item `收件箱`. - Real mail rows are `table.maillist-table tr`. - The generated alias local-part may be hidden in sender tooltip/bounce text such as `...-chickliu2031som6d8=2925.com@tm1.openai.com`, not visible row text. - Open the newest matching row, then extract the strict 6-digit code near code words. Inputs: --cdp http://127.0.0.1:9227 --account chickliu2031@2925.com --alias chickliu2031som6d8@2925.com --out /tmp/openai_2925_email_code.txt The script prints JSON with code redacted and writes the raw code only to --out. """ import argparse, json, pathlib, re, sys, time, urllib.request, websocket, itertools def tabs(cdp): return json.loads(urllib.request.urlopen(cdp.rstrip('/') + '/json', timeout=10).read().decode()) def connect(cdp): pages = [t for t in tabs(cdp) if t.get('type') == 'page'] sel = next((t for t in pages if '2925.com' in (t.get('url') or '')), None) or pages[0] ws = websocket.create_connection(sel['webSocketDebuggerUrl'], timeout=20) counter = itertools.count(1) def call(method, params=None, timeout=45): i = next(counter) ws.send(json.dumps({'id': i, 'method': method, 'params': params or {}})) end = time.time() + timeout while time.time() < end: msg = json.loads(ws.recv()) if msg.get('id') == i: if 'error' in msg: raise RuntimeError(msg['error']) return msg.get('result', {}) raise TimeoutError(method) call('Page.enable') call('Runtime.enable') return call def ev(call, js, awaitp=False, timeout=45): return call('Runtime.evaluate', { 'expression': js, 'awaitPromise': awaitp, 'returnByValue': True, }, timeout).get('result', {}).get('value') def main(): ap = argparse.ArgumentParser() ap.add_argument('--cdp', default='http://127.0.0.1:9227') ap.add_argument('--account', required=True) ap.add_argument('--alias', required=True) ap.add_argument('--out', required=True) args = ap.parse_args() local = args.alias.split('@', 1)[0].lower() call = connect(args.cdp) js = r'''(async(local,account)=>{ const sleep=ms=>new Promise(r=>setTimeout(r,ms)); const click=e=>{if(!e)return false; e.scrollIntoView({block:'center'}); e.dispatchEvent(new MouseEvent('mousedown',{bubbles:true})); e.dispatchEvent(new MouseEvent('mouseup',{bubbles:true})); e.click(); return true;}; const all=[...document.querySelectorAll('*')]; const inbox=all.find(e=>(e.innerText||e.textContent||'').trim()==='收件箱'); const clickedInbox=click(inbox); await sleep(1800); const refresh=[...document.querySelectorAll('*')].filter(e=>(e.innerText||e.textContent||'').trim()==='刷新').pop(); const clickedRefresh=click(refresh); await sleep(3000); const pageText=document.body?.innerText||''; const accountOk=pageText.includes(account); const rows=[...document.querySelectorAll('table.maillist-table tr')].filter(r=>/OpenAI|ChatGPT|验证码|临时验证码|verification|code/i.test(r.innerText||'')); const rowSummaries=[]; let chosen=null, chosenIndex=-1; for(let i=0;i