import json, pathlib, urllib.request, urllib.parse, re, subprocess, time, sys ENV=pathlib.Path('/Users/chick/.hermes/env/codex_oauth_onboarding.env') text=ENV.read_text() def get(k,default=''): m=re.search(r'^'+re.escape(k)+r'=(.*)$', text, re.M) return (m.group(1).strip().strip('"\'') if m else default) TOKEN=get('FIVE_SIM_API_KEY') or get('FIVESIM_API_KEY') ORDER=[x.strip() for x in get('FIVE_SIM_COUNTRY_ORDER','').split(',') if x.strip()] or ([get('FIVE_SIM_COUNTRY_ID')] if get('FIVE_SIM_COUNTRY_ID') else ['vietnam']) LIMIT=int(get('PHONE_VERIFICATION_REPLACEMENT_LIMIT','3') or '3') PRODUCT=get('FIVE_SIM_PRODUCT','openai') or 'openai' OP=get('FIVE_SIM_OPERATOR','any') or 'any' META={'argentina':('阿根廷','54'),'netherlands':('荷兰','31'),'indonesia':('印度尼西亚','62'),'vietnam':('越南','84')} HEAD={'Authorization':'Bearer '+TOKEN,'Accept':'application/json'} def emit(**kw): print(json.dumps(kw,ensure_ascii=False),flush=True) def req(method,url,data=None,timeout=30): body=None if data is not None: body=json.dumps(data).encode() r=urllib.request.Request(url,data=body,headers=HEAD,method=method) if body: r.add_header('Content-Type','application/json') with urllib.request.urlopen(r,timeout=timeout) as resp: return json.loads(resp.read().decode()) def cdp(js,match='auth.openai.com',timeout=40): p=subprocess.run(['bash','-lc',f"CDP_MATCH='{match}' python3 /tmp/cdp_async_eval.py"],input=js,text=True,capture_output=True,timeout=timeout) return (p.stdout or '')+(p.stderr or '') def page_state(): return cdp("""return {url:location.href,title:document.title,text:document.body.innerText.slice(0,1600),country:[...document.querySelectorAll('button')].map(b=>b.innerText).find(t=>/\\(\\+\\d+\\)/.test(t)),tel:document.querySelector('input[type=tel]')?.value};""",timeout=25) def select_country(country): label,prefix=META[country] iso={"argentina":"AR","netherlands":"NL","indonesia":"ID","vietnam":"VN"}[country] js=f""" const iso={json.dumps(iso)}; const sel=document.querySelector('select'); sel.focus(); sel.value=iso; sel.dispatchEvent(new Event('input',{{bubbles:true}})); sel.dispatchEvent(new Event('change',{{bubbles:true}})); await new Promise(r=>setTimeout(r,800)); return {{selectValue:sel.value,country:[...document.querySelectorAll('button')].map(b=>b.innerText).find(t=>/\\(\\+\\d+\\)/.test(t)),text:document.body.innerText.slice(0,700)}}; """ return cdp(js,timeout=25) def buy(country): url=f'https://5sim.net/v1/user/buy/activation/{urllib.parse.quote(country)}/{urllib.parse.quote(OP)}/{urllib.parse.quote(PRODUCT)}' return req('GET',url,timeout=40) def cancel(aid): try: return req('GET',f'https://5sim.net/v1/user/cancel/{aid}',timeout=20) except Exception as e: return {'error':str(e)} def check(aid): return req('GET',f'https://5sim.net/v1/user/check/{aid}',timeout=20) def submit_phone(country,phone): label,prefix=META[country] digits=re.sub(r'\D+','',phone) local=digits[len(prefix):] if digits.startswith(prefix) else digits js=f""" const local={json.dumps(local)}; const input=document.querySelector('input[type=tel]'); const setter=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set; input.focus(); setter.call(input,''); input.dispatchEvent(new InputEvent('input',{{bubbles:true,inputType:'deleteContentBackward'}})); setter.call(input,local); input.dispatchEvent(new InputEvent('input',{{bubbles:true,inputType:'insertText',data:local}})); input.dispatchEvent(new Event('change',{{bubbles:true}})); await new Promise(r=>setTimeout(r,500)); let btn=[...document.querySelectorAll('button')].find(b=>(b.innerText||'').trim()==='继续'); btn?.click(); await new Promise(r=>setTimeout(r,12000)); return {{url:location.href,title:document.title,text:document.body.innerText.slice(0,1200),country:[...document.querySelectorAll('button')].map(b=>b.innerText).find(t=>/\\(\\+\\d+\\)/.test(t)),tel:document.querySelector('input[type=tel]')?.value}}; """ return cdp(js,timeout=45) def resend_phone(): js="""const btn=[...document.querySelectorAll('button')].find(b=>/重新发送/.test(b.innerText||'')); btn?.click(); await new Promise(r=>setTimeout(r,3000)); return {clicked:!!btn,text:document.body.innerText.slice(0,800),url:location.href};""" return cdp(js,timeout=20) def poll_sms(aid,rounds=8): for i in range(rounds): time.sleep(12) st=check(aid) sms=st.get('sms') or [] emit(phase='sms_poll',activation_id=aid,round=i+1,status=st.get('status'),sms_count=len(sms)) if sms: txt=json.dumps(sms,ensure_ascii=False) m=re.search(r'(?i.type==='text'||i.type==='tel'||i.inputMode==='numeric'); let input=inputs.find(i=>i!==document.querySelector('input[type=tel]'))||inputs[0]; const setter=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set; input.focus(); setter.call(input,code); input.dispatchEvent(new InputEvent('input',{{bubbles:true,inputType:'insertText',data:code}})); input.dispatchEvent(new Event('change',{{bubbles:true}})); await new Promise(r=>setTimeout(r,500)); [...document.querySelectorAll('button')].find(b=>/继续|Continue|验证/.test(b.innerText||''))?.click(); await new Promise(r=>setTimeout(r,15000)); return {{url:location.href,title:document.title,text:document.body.innerText.slice(0,1600)}}; """ final=cdp(js,timeout=45) emit(phase='phone_code_submitted',activation_id=aid,state=final[-500:]) sys.exit(0) emit(phase='no_sms_after_resend',country=country,activation_id=aid) emit(phase='cancel',activation_id=aid,result=cancel(aid).get('status')) emit(phase='phone_failed_all') sys.exit(3)