feat: xAI Grok auto-registration tool
- DrissionPage + Chrome browser automation - Temporary email via mail.tm API - Verification code auto-extraction - Turnstile CAPTCHA bypass via extension - SSO cookie extraction and persistence
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
import requests
|
||||
import re
|
||||
import time
|
||||
import secrets
|
||||
import string
|
||||
|
||||
BASE_URL = "https://api.mail.tm"
|
||||
|
||||
|
||||
def _random_string(length=12):
|
||||
return "".join(secrets.choice(string.ascii_lowercase + string.digits) for _ in range(length))
|
||||
|
||||
|
||||
def get_email_and_token():
|
||||
try:
|
||||
# 1. 获取可用域名
|
||||
r = requests.get(f"{BASE_URL}/domains", timeout=30)
|
||||
r.raise_for_status()
|
||||
domains = r.json().get("hydra:member", [])
|
||||
if not domains:
|
||||
print("[Error] Mail.tm 没有可用域名")
|
||||
return None, None
|
||||
domain = domains[0]["domain"]
|
||||
|
||||
# 2. 生成随机邮箱和密码
|
||||
local_part = _random_string(10)
|
||||
email = f"{local_part}@{domain}"
|
||||
password = _random_string(16)
|
||||
|
||||
# 3. 创建账户
|
||||
r = requests.post(
|
||||
f"{BASE_URL}/accounts",
|
||||
json={"address": email, "password": password},
|
||||
timeout=30,
|
||||
)
|
||||
r.raise_for_status()
|
||||
|
||||
# 4. 获取 token
|
||||
r = requests.post(
|
||||
f"{BASE_URL}/token",
|
||||
json={"address": email, "password": password},
|
||||
timeout=30,
|
||||
)
|
||||
r.raise_for_status()
|
||||
token = r.json().get("token")
|
||||
if not token:
|
||||
print("[Error] 无法获取 Mail.tm token")
|
||||
return None, None
|
||||
|
||||
print(f"[*] 已获取临时邮箱: {email}")
|
||||
return email, token
|
||||
except Exception as e:
|
||||
print(f"[Error] 获取邮箱失败: {e}")
|
||||
return None, None
|
||||
|
||||
|
||||
def _extract_code(subject, text, html_text=""):
|
||||
"""从邮件文本中提取验证码。优先匹配 xAI/常见服务的验证码格式。"""
|
||||
text = text or ""
|
||||
html_text = html_text or ""
|
||||
subject = subject or ""
|
||||
full_text = f"{subject}\n{text}\n{html_text}"
|
||||
|
||||
# 1. xAI 主题自带验证码格式: EB5-XJA xAI confirmation code
|
||||
m = re.search(r"\b([A-Z0-9]{3}-[A-Z0-9]{3})\b.*confirmation\s*code", subject, re.IGNORECASE)
|
||||
if m:
|
||||
return m.group(1).upper()
|
||||
|
||||
# 2. 通用 xAI/OpenAI 验证码格式(字母数字混合)
|
||||
patterns = [
|
||||
# xAI 正文: code below to validate ... EB5-XJA
|
||||
r"code\s+below\s+to\s+validate.*?\n\s*([A-Z0-9]{3}-[A-Z0-9]{3})\s*\n",
|
||||
# 通用字母数字验证码: ABC-123, 123-ABC, A1B-2C3
|
||||
r"\b([A-Z0-9]{3}-[A-Z0-9]{3})\b",
|
||||
# verification code is 123456
|
||||
r"(?:verification\s*code|code\s*is)[:\s]+(\d{6})",
|
||||
# 中文验证码
|
||||
r"(?:验证码|代码|确认码)[:\s为]+(\d{6})",
|
||||
# code: 123456
|
||||
r"(?:code|验证码)[:\s]+(\d{6})",
|
||||
# 6位数字带上下文
|
||||
r"\b(\d{6})\b(?:\s*(?:is your|作为您的)\s*(?:verification\s*code|code|验证码))?",
|
||||
]
|
||||
|
||||
for pat in patterns:
|
||||
m = re.search(pat, full_text, re.IGNORECASE | re.DOTALL)
|
||||
if m:
|
||||
code = m.group(1)
|
||||
if code:
|
||||
return code.upper() if "-" in code else code
|
||||
|
||||
# 3. 兜底:仅在纯文本中匹配 6 位数字,避开 HTML 颜色代码
|
||||
if text:
|
||||
for m in re.finditer(r"(?<!#)\b(\d{6})\b", text):
|
||||
code = m.group(1)
|
||||
if not code.startswith("20"):
|
||||
return code
|
||||
return None
|
||||
|
||||
|
||||
def _is_verification_email(subject, sender):
|
||||
"""判断是否为验证码邮件。"""
|
||||
keywords = ["verification", "code", "验证码", "确认码", "xai", "x.ai", "openai", "grok"]
|
||||
text = f"{subject} {sender}".lower()
|
||||
return any(k in text for k in keywords)
|
||||
|
||||
|
||||
def get_oai_code(token, email):
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
deadline = time.time() + 180 # 最多轮询 180 秒
|
||||
|
||||
while time.time() < deadline:
|
||||
r = requests.get(f"{BASE_URL}/messages", headers=headers, timeout=30)
|
||||
r.raise_for_status()
|
||||
messages = r.json().get("hydra:member", [])
|
||||
|
||||
# 按创建时间倒序,优先处理最新邮件
|
||||
messages.sort(key=lambda m: m.get("createdAt", ""), reverse=True)
|
||||
|
||||
# 优先检查看起来像验证码的邮件
|
||||
verification_msgs = [m for m in messages if _is_verification_email(m.get("subject", ""), "")]
|
||||
other_msgs = [m for m in messages if m not in verification_msgs]
|
||||
ordered_msgs = verification_msgs + other_msgs
|
||||
|
||||
for msg in ordered_msgs:
|
||||
msg_id = msg.get("id")
|
||||
if not msg_id:
|
||||
continue
|
||||
|
||||
# 获取邮件详情
|
||||
r = requests.get(f"{BASE_URL}/messages/{msg_id}", headers=headers, timeout=30)
|
||||
r.raise_for_status()
|
||||
detail = r.json()
|
||||
|
||||
subject = detail.get("subject", "")
|
||||
text = detail.get("text", "")
|
||||
html = detail.get("html", [])
|
||||
html_content = " ".join(html) if isinstance(html, list) else str(html)
|
||||
sender = detail.get("from", {}).get("address", "")
|
||||
|
||||
code = _extract_code(subject, text, html_content)
|
||||
if code:
|
||||
# xAI 验证码为 WVB-8OE 格式,OTP 输入框通常只需 6 位字母数字,去掉连字符
|
||||
if "-" in code:
|
||||
code = code.replace("-", "")
|
||||
print(f"[*] 获取到验证码: {code}")
|
||||
# 标记为已读,避免重复提取
|
||||
try:
|
||||
requests.patch(f"{BASE_URL}/messages/{msg_id}", headers={**headers, "Content-Type": "application/merge-patch+json"}, json={"seen": True}, timeout=10)
|
||||
except Exception:
|
||||
pass
|
||||
return code
|
||||
time.sleep(3)
|
||||
|
||||
print("[Error] 轮询超时,未获取到验证码")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"[Error] 获取验证码失败: {e}")
|
||||
return None
|
||||
Reference in New Issue
Block a user