Files
universal-autoreg/providers/vani_turnstile.py

232 lines
10 KiB
Python

# -*- coding: utf-8 -*-
"""Vani.ai autoreg — cloakbrowser native turnstile + voidash/t-online email
Flow: /login?next=/chat -> email + native TS token -> Check your email -> code/link -> session
"""
import sys, re, json, time, random, string, os
import requests
sys.stdout.reconfigure(encoding='utf-8')
BASE = 'https://c.vani.ai'
LOGIN = BASE + '/login?next=%2Fchat'
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'vani_accounts.jsonl')
VOID = 'https://api.voidash.com/api/v1'
IMAP_POOL = r'C:\Users\User\Desktop\avtoreg\working_mails.txt'
r = lambda n: ''.join(random.choices(string.ascii_lowercase + string.digits, k=n))
def make_voidash():
d = requests.post(VOID + '/inboxes', json={'domain': 'voidash.bond'}, timeout=30).json()
return ('voidash', d['address'], d['session_key'])
def load_tonline(n=1):
used = set()
if os.path.exists('used_emails.txt'):
used = set(l.strip() for l in open('used_emails.txt') if l.strip())
out = []
for line in open(IMAP_POOL, encoding='utf-8'):
line = line.strip()
if ':' not in line or line.startswith('#'):
continue
em, pw = line.split(':', 1)
if '-' in em.split('@')[0] or em in used: # dash-алиасы мертвы
continue
out.append(('t-online', em, pw))
if len(out) >= n:
break
return out
def poll_voidash(key, timeout=240):
t0 = time.time()
while time.time() - t0 < timeout:
try:
msgs = requests.get(VOID + '/messages', headers={'Authorization': 'Bea' + 'rer ' + key}, timeout=30).json()
items = msgs if isinstance(msgs, list) else msgs.get('messages', msgs.get('data', []))
for m in items:
body = json.dumps(m, ensure_ascii=False)
if 'vani' in body.lower():
return body
except Exception:
pass
time.sleep(5)
return None
def poll_imap_tonline(email, pw, timeout=240):
import imaplib, email as emlib
t0 = time.time()
while time.time() - t0 < timeout:
try:
M = imaplib.IMAP4_SSL('secureimap.t-online.de', 993)
M.login(email, pw)
M.select('INBOX')
typ, data = M.search(None, '(SUBJECT "Vani")')
ids = data[0].split()
if not ids:
typ, data = M.search(None, 'ALL')
ids = data[0].split()[-5:]
for i in ids[-5:]:
typ, md = M.fetch(i, '(RFC822)')
msg = emlib.message_from_bytes(md[0][1])
frm = str(msg.get('From', ''))
if 'vani' not in frm.lower():
continue
body = ''
if msg.is_multipart():
for part in msg.walk():
if part.get_content_type() == 'text/html':
body = part.get_payload(decode=True).decode('utf-8', 'replace')
break
else:
body = msg.get_payload(decode=True).decode('utf-8', 'replace')
M.logout()
return json.dumps({'from': frm, 'subject': str(msg.get('Subject')), 'body': body}, ensure_ascii=False)
M.logout()
except Exception as e:
print(' imap retry:', str(e)[:80])
time.sleep(8)
return None
def extract_code_link(mailjson):
code = None
link = None
lk = re.search(r'https://c\.vani\.ai/[^\s"\\<]+', mailjson)
if lk:
link = lk.group(0).replace('\\u0026', '&').replace('\\/', '/').replace('\\u003d', '=')
# 6-digit code — в тексте, часто крупным шрифтом; ищем изолированные 6 цифр
for m in re.finditer(r'(?:code[^0-9]{0,40}|>)\s*([0-9]{6})\s*(?:<|$|[^0-9])', mailjson, re.I):
code = m.group(1)
break
if not code:
m = re.search(r'\b(\d{6})\b', re.sub(r'https?://\S+', '', mailjson))
if m:
code = m.group(1)
return code, link
def run(n, provider='voidash'):
import cloakbrowser
okc = 0
for i in range(n):
res = {'ok': False, 'ts': int(time.time()), 'provider': provider}
b = None
try:
if provider == 'voidash':
kind, emailaddr, cred = make_voidash()
else:
kind, emailaddr, cred = load_tonline(1)[0]
res['email'] = emailaddr
print(f'[{i}] email={emailaddr}')
b = cloakbrowser.launch(headless=True)
pg = b.new_page()
pg.goto(LOGIN, wait_until='domcontentloaded', timeout=60000)
pg.wait_for_selector('input[type=email]', timeout=25000)
pg.wait_for_timeout(2000)
# cookie banner accept
try:
btn = pg.evaluate("""() => { const b=[...document.querySelectorAll('button')].find(e=>(e.innerText||'').trim()==='Accept'); if(b){b.click();return 1;} return 0; }""")
except Exception:
pass
pg.fill('input[type=email]', emailaddr)
# ждём нативный turnstile токен в hidden input
tok = None
for _ in range(50):
v = pg.evaluate("() => document.querySelector('input[name=turnstile_token]')?.value?.length || 0")
if v and v > 50:
tok = v
break
pg.wait_for_timeout(2500)
res['ts_token_len'] = tok or 0
if not tok:
res['err'] = 'turnstile token timeout'
print(' TS timeout')
raise StopIteration
pg.wait_for_timeout(1000)
pg.evaluate("() => { const b=[...document.querySelectorAll('button')].find(e=>(e.innerText||'').includes('Email me a sign-in')); b.click(); }")
pg.wait_for_timeout(9000)
body = re.sub(r'\s+', ' ', pg.evaluate('() => document.body.innerText'))[:400]
res['after_submit'] = body
if 'Check your email' not in body and 'code' not in body.lower():
res['err'] = 'no confirmation: ' + body[:200]
print(' FAIL submit:', body[:150])
raise StopIteration
print(' confirmation OK, polling mail...')
mail = poll_voidash(cred, 240) if provider == 'voidash' else poll_imap_tonline(emailaddr, cred, 240)
if not mail:
res['err'] = 'no email'
raise StopIteration
open(f'mail_{i}.json', 'w', encoding='utf-8').write(mail)
code, link = extract_code_link(mail)
res['code'] = code
res['link'] = (link or '')[:140]
print(f' code={code} link={(link or "")[:80]}')
verified = False
if code:
# ввод кода на странице
try:
inputs = pg.evaluate("() => [...document.querySelectorAll('input')].map(e=>({t:e.type,n:e.name,ml:e.maxLength}))")
res['code_inputs'] = inputs
filled = False
for idx, inp in enumerate(inputs):
if inp.get('ml') == 6 or (inp.get('t') == 'text' and inp.get('n', '').lower().find('code') > -1):
sel = f"input >> nth={idx}"
pg.fill(sel, code)
filled = True
break
if not filled:
# OTP-style раздельные боксы
boxes = pg.query_selector_all("input[maxlength='1']")
if len(boxes) >= 6:
for j, ch in enumerate(code):
boxes[j].fill(ch)
filled = True
if filled:
pg.wait_for_timeout(2000)
pg.evaluate("() => { const b=[...document.querySelectorAll('button')].find(e=>/verify|continue|sign in|submit/i.test(e.innerText||'')); if(b) b.click(); }")
pg.wait_for_timeout(8000)
except Exception as e:
res['code_err'] = str(e)[:120]
body2 = re.sub(r'\s+', ' ', pg.evaluate('() => document.body.innerText'))[:300]
res['after_code'] = body2
if pg.url.rstrip('/').endswith('chat') or 'chat' in pg.url or 'welcome' in body2.lower() or 'new chat' in body2.lower():
verified = True
if not verified and link:
pg.goto(link, wait_until='domcontentloaded', timeout=60000)
pg.wait_for_timeout(8000)
body3 = re.sub(r'\s+', ' ', pg.evaluate('() => document.body.innerText'))[:300]
res['after_link'] = body3
res['final_url'] = pg.url
if '/login' not in pg.url:
verified = True
if verified:
cks = pg.context.cookies() if hasattr(pg, 'context') else pg.evaluate('() => document.cookie')
res['cookies'] = cks if isinstance(cks, list) else str(cks)[:500]
res['final_url'] = pg.url
res['ok'] = True
okc += 1
print(f' [{i}] OK verified, url={pg.url}')
else:
res['err'] = res.get('err') or 'not verified'
pg.screenshot(path=f'fail_{i}.png')
print(f' [{i}] FAIL: {res.get("err")}')
except StopIteration:
pass
except Exception as e:
res['err'] = str(e)[:200]
print(f' [{i}] EXC: {str(e)[:150]}')
finally:
if b:
try:
b.close()
except Exception:
pass
with open(OUT, 'a', encoding='utf-8') as f:
f.write(json.dumps(res, ensure_ascii=False) + '\n')
if res.get('email'):
with open('used_emails.txt', 'a') as f:
f.write(res['email'] + '\n')
time.sleep(random.randint(5, 12))
print(f'DONE ok={okc}/{n}')
if __name__ == '__main__':
n = int(sys.argv[1]) if len(sys.argv) > 1 else 1
provider = sys.argv[2] if len(sys.argv) > 2 else 'voidash'
run(n, provider)