chore: initial commit

This commit is contained in:
gameloader
2026-03-18 22:07:19 +08:00
commit e3a28050df
13 changed files with 51831 additions and 0 deletions

91
src/vmail_client.py Normal file
View File

@@ -0,0 +1,91 @@
import re
import secrets
import time
import httpx
MAILTM_BASE = "https://api.mail.tm"
class MailClient:
"""mail.tm temporary email client."""
def create_mailbox(self) -> dict:
"""Create a mailbox. Returns dict with address, password, token."""
domains_resp = httpx.get(f"{MAILTM_BASE}/domains", timeout=30)
domains_resp.raise_for_status()
domains = domains_resp.json().get("hydra:member", [])
active = next((d for d in domains if d.get("isActive")), None)
if not active:
raise RuntimeError("mail.tm returned no active domains")
for _ in range(10):
address = f"{secrets.token_hex(6)}@{active['domain']}"
password = secrets.token_urlsafe(18)
r = httpx.post(
f"{MAILTM_BASE}/accounts",
json={"address": address, "password": password},
timeout=30,
)
if r.status_code == 201:
token_resp = httpx.post(
f"{MAILTM_BASE}/token",
json={"address": address, "password": password},
timeout=30,
)
token_resp.raise_for_status()
return {
"id": r.json()["id"],
"address": address,
"password": password,
"token": token_resp.json()["token"],
}
if r.status_code == 422:
time.sleep(0.3)
continue
r.raise_for_status()
raise RuntimeError("Failed to create mail.tm mailbox after 10 attempts")
def wait_for_otp(self, mailbox: dict, timeout: int = 120, poll: float = 5.0) -> str:
"""Poll mailbox until a 6-digit OTP arrives."""
headers = {"Authorization": f"Bearer {mailbox['token']}"}
deadline = time.time() + timeout
while time.time() < deadline:
try:
r = httpx.get(
f"{MAILTM_BASE}/messages",
headers=headers,
timeout=30,
)
r.raise_for_status()
messages = r.json().get("hydra:member", [])
except httpx.HTTPError as exc:
print(f"mail poll error: {exc.__class__.__name__}")
time.sleep(poll)
continue
if messages:
try:
msg_r = httpx.get(
f"{MAILTM_BASE}/messages/{messages[0]['id']}",
headers=headers,
timeout=30,
)
msg_r.raise_for_status()
msg = msg_r.json()
except httpx.HTTPError as exc:
print(f"mail fetch error: {exc.__class__.__name__}")
time.sleep(poll)
continue
html = msg.get("html") or ""
if isinstance(html, list):
html = "\n".join(html)
body = msg.get("text") or html or ""
codes = re.findall(r"\b(\d{6})\b", body)
if codes:
return codes[0]
time.sleep(poll)
raise TimeoutError(f"No OTP received within {timeout}s")