universal-autoreg: initial public release (restored, redacted, hardened)
This commit is contained in:
231
providers/vani_turnstile.py
Normal file
231
providers/vani_turnstile.py
Normal file
@@ -0,0 +1,231 @@
|
||||
# -*- 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)
|
||||
Reference in New Issue
Block a user