"""Request a real WhatsApp mobile-linking QR in an isolated background browser. The official WhatsApp Web client creates the keys and requests the server ref. This script reads the displayed QR payload and renders it locally. It does not extract credentials from the existing Desktop account or implement fake pairing. Requires: pip install websockets 'qrcode[pil]' """ from __future__ import annotations import argparse, base64, datetime as dt, hashlib, json, os from pathlib import Path import socket, subprocess, sys, time, urllib.parse, urllib.request import http.server,threading from .common import BASE as ROOT READ_QR=r'''(() => { const element=Array.from(document.querySelectorAll('[data-ref]')).find(e=>{const v=e.getAttribute('data-ref');return v&&v.split(',').length>=4;}); const payload=element?.getAttribute('data-ref')??null; let linked=false; try {linked=!!require('WAWebUserPrefsMeUser').getMaybeMePnUser();}catch{} let refreshRequested=false; if(!linked&&!payload){const button=document.querySelector('[data-testid="link_device_qr_expired_refresh_button"]');if(button){button.click();refreshRequested=true;}} return {payload,linked,refreshRequested,title:document.title,origin:location.origin}; })()''' def chrome_path(explicit): if explicit:return Path(explicit).resolve() candidates=[Path(os.environ.get('PROGRAMFILES','C:/Program Files'))/'Google/Chrome/Application/chrome.exe',Path(os.environ.get('PROGRAMFILES(X86)','C:/Program Files (x86)'))/'Google/Chrome/Application/chrome.exe',Path(os.environ.get('LOCALAPPDATA',''))/'Google/Chrome/Application/chrome.exe'] return next((p for p in candidates if p.is_file()),None) def call(url,expression): from websockets.sync.client import connect with connect(url,open_timeout=10,max_size=1_000_000) as ws: ws.send(json.dumps({'id':1,'method':'Runtime.evaluate','params':{'expression':expression,'returnByValue':True}})) while True: result=json.loads(ws.recv(timeout=15)) if result.get('id')!=1:continue if result.get('error') or result.get('result',{}).get('exceptionDetails'):raise RuntimeError('Page probe failed') return result['result']['result'].get('value') def describe(payload): parts=payload.split(',') lengths=[] for part in parts[1:]: try:lengths.append(len(base64.b64decode(part,validate=True))) except Exception:lengths.append(None) return {'field_count':len(parts),'payload_length':len(payload),'sha256':hashlib.sha256(payload.encode()).hexdigest(),'base64_field_byte_lengths':lengths} def write_qr(payload,out): import qrcode qr=qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M,box_size=10,border=4) qr.add_data(payload);qr.make(fit=True) qr.make_image(fill_color='black',back_color='white').save(out/'pairing-qr.png') # No raw QR payload, identity secret or session key is written to the report. report={'timestamp_utc':dt.datetime.now(dt.timezone.utc).isoformat(),'status':'waiting_for_mobile_scan','source':'Official WhatsApp Web QR DOM in isolated headless Chrome','qr_metadata':describe(payload),'note':'QR rotates and requires the matching browser session to remain running.'} (out/'pairing-status.json').write_text(json.dumps(report,indent=2),encoding='utf-8') (out/'pairing-qr.html').write_text('
WhatsApp → Linked devices → Link a device.

This QR links the isolated Python test session. The image updates while the script runs.
',encoding='utf-8') def serve_viewer(out): class Handler(http.server.SimpleHTTPRequestHandler): def __init__(self,*a,**kw):super().__init__(*a,directory=str(out),**kw) def log_message(self,*a):pass def do_GET(self): if urllib.parse.urlsplit(self.path).path not in ('/pairing-qr.html','/pairing-qr.png','/pairing-status.json'): self.send_error(404);return super().do_GET() server=http.server.ThreadingHTTPServer(('127.0.0.1',0),Handler) threading.Thread(target=server.serve_forever,daemon=True).start() return server,f'http://127.0.0.1:{server.server_port}/pairing-qr.html' def main(argv=None): p=argparse.ArgumentParser(description=__doc__) p.add_argument('--browser',type=Path);p.add_argument('--profile',type=Path,default=ROOT/'whatsapp-pairing-profile');p.add_argument('--output-dir',type=Path,default=ROOT/'whatsapp-pairing');p.add_argument('--watch-seconds',type=int,default=300);p.add_argument('--startup-seconds',type=int,default=90) args=p.parse_args(argv);browser=chrome_path(args.browser) if not browser or not browser.is_file():p.error('Chrome executable missing; supply --browser') if not 10<=args.watch_seconds<=3600:p.error('--watch-seconds must be 10..3600') profile=args.profile.resolve();out=args.output_dir.resolve() if '5319275a.whatsappdesktop' in str(profile).lower():p.error('Refusing the existing WhatsApp Desktop profile') marker=profile/'python-pairing-profile.json' if profile.exists() and any(profile.iterdir()) and not marker.exists():p.error('Profile must be empty or created by this script') profile.mkdir(parents=True,exist_ok=True);out.mkdir(parents=True,exist_ok=True) marker.write_text(json.dumps({'purpose':'isolated official WhatsApp Web pairing test'}),encoding='utf-8') with socket.socket() as sock:sock.bind(('127.0.0.1',0));port=sock.getsockname()[1] command=[str(browser),'--headless=new','--no-first-run','--no-default-browser-check','--remote-debugging-address=127.0.0.1',f'--remote-debugging-port={port}',f'--user-data-dir={profile}','--window-size=1200,1000','https://web.whatsapp.com/'] flags=subprocess.CREATE_NO_WINDOW if sys.platform=='win32' else 0 proc=subprocess.Popen(command,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,creationflags=flags) viewer,viewer_url=serve_viewer(out) endpoint=f'http://127.0.0.1:{port}';(out/'pairing-runtime.json').write_text(json.dumps({'browser_pid':proc.pid,'cdp_endpoint':endpoint,'viewer_url':viewer_url,'profile':str(profile)},indent=2),encoding='utf-8') start=time.monotonic();last_payload=None;generated=0;linked=False print(f'Isolated background session started; QR output: {out}',flush=True) print(f'REA observation endpoint: {endpoint}',flush=True) print(f'QR viewer (auto-refresh): {viewer_url}',flush=True) try: while time.monotonic()-start