|
@@ -110,9 +110,57 @@ class EMail:
|
|
|
return r.json().get("emails", [])
|
|
return r.json().get("emails", [])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+class PMailClient:
|
|
|
|
|
+ def __init__(self, proxies: Any = None):
|
|
|
|
|
+ self.session = requests.Session(proxies=proxies, impersonate="chrome")
|
|
|
|
|
+ self.session.headers.update({
|
|
|
|
|
+ "Accept": "application/json, text/plain, */*",
|
|
|
|
|
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
|
|
|
+ "Cache-Control": "no-cache",
|
|
|
|
|
+ "Content-Type": "application/json;charset=UTF-8;",
|
|
|
|
|
+ "Lang": "zhCn",
|
|
|
|
|
+ "Origin": "https://pmail.030208.xyz",
|
|
|
|
|
+ "Pragma": "no-cache",
|
|
|
|
|
+ "Priority": "u=1, i",
|
|
|
|
|
+ "Referer": "https://pmail.030208.xyz/",
|
|
|
|
|
+ "Sec-Ch-Ua": '"Chromium";v="146", "Not-A.Brand";v="24", "Microsoft"',
|
|
|
|
|
+ "Sec-Ch-Ua-Mobile": "?0",
|
|
|
|
|
+ "Sec-Ch-Ua-Platform": '"Windows"',
|
|
|
|
|
+ "Sec-Fetch-Dest": "empty",
|
|
|
|
|
+ "Sec-Fetch-Mode": "cors",
|
|
|
|
|
+ "Sec-Fetch-Site": "same-origin",
|
|
|
|
|
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0",
|
|
|
|
|
+ "Cookie": "session=beG8AdZzfjgHWPwR0mrgoWGvjMU30zwPgvfNgT31sSk"
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ def list_emails(self, target_email: str) -> List[Dict[str, Any]]:
|
|
|
|
|
+ url = "https://pmail.030208.xyz/api/email/list"
|
|
|
|
|
+ payload = {"tag": {"type": 0, "status": -1, "group_id": 0}, "page_size": 10}
|
|
|
|
|
+ try:
|
|
|
|
|
+ resp = self.session.post(url, json=payload, timeout=15)
|
|
|
|
|
+ if resp.status_code == 200:
|
|
|
|
|
+ data = resp.json()
|
|
|
|
|
+ emails_list = data.get("data", {}).get("list", [])
|
|
|
|
|
+ results = []
|
|
|
|
|
+ for item in emails_list[:3]:
|
|
|
|
|
+ to_arr = item.get("to", [])
|
|
|
|
|
+ if to_arr and len(to_arr) > 0:
|
|
|
|
|
+ addr = to_arr[0].get("EmailAddress", "")
|
|
|
|
|
+ if addr == target_email:
|
|
|
|
|
+ results.append({
|
|
|
|
|
+ "subject": item.get("title", ""),
|
|
|
|
|
+ "txt": item.get("title", ""),
|
|
|
|
|
+ "from": addr
|
|
|
|
|
+ })
|
|
|
|
|
+ return results
|
|
|
|
|
+ except Exception:
|
|
|
|
|
+ pass
|
|
|
|
|
+ return []
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def get_email_and_code_fetcher(proxies: Any = None, provider: str = "auto"):
|
|
def get_email_and_code_fetcher(proxies: Any = None, provider: str = "auto"):
|
|
|
provider = (provider or "auto").strip().lower()
|
|
provider = (provider or "auto").strip().lower()
|
|
|
- if provider not in {"auto", "gptmail", "tempmail"}:
|
|
|
|
|
|
|
+ if provider not in {"auto", "gptmail", "tempmail", "pmail"}:
|
|
|
raise ValueError(f"不支持的邮箱提供商: {provider}")
|
|
raise ValueError(f"不支持的邮箱提供商: {provider}")
|
|
|
|
|
|
|
|
def _build_tempmail_bundle():
|
|
def _build_tempmail_bundle():
|
|
@@ -202,16 +250,60 @@ def get_email_and_code_fetcher(proxies: Any = None, provider: str = "auto"):
|
|
|
|
|
|
|
|
return email, _gen_password(), fetch_code, _extract_all_codes, "gptmail"
|
|
return email, _gen_password(), fetch_code, _extract_all_codes, "gptmail"
|
|
|
|
|
|
|
|
|
|
+ def _build_pmail_bundle():
|
|
|
|
|
+ client = PMailClient(proxies)
|
|
|
|
|
+ email = _random_name().lower() + "@030208.xyz"
|
|
|
|
|
+ print(f"[+] 分配自建域邮箱别名: {email} (PMail)")
|
|
|
|
|
+ print("[*] 自动轮询已启动(关联至主信箱 husj@030208.xyz)")
|
|
|
|
|
+
|
|
|
|
|
+ def _extract_all_codes() -> List[str]:
|
|
|
|
|
+ results: List[str] = []
|
|
|
|
|
+ try:
|
|
|
|
|
+ summaries = client.list_emails(email)
|
|
|
|
|
+ for s in summaries:
|
|
|
|
|
+ body = f"{s.get('subject', '')} {s.get('txt', '')}"
|
|
|
|
|
+ results.extend(re.findall(r"(?<!\d)(\d{6})(?!\d)", body))
|
|
|
|
|
+ except Exception:
|
|
|
|
|
+ pass
|
|
|
|
|
+ return results
|
|
|
|
|
+
|
|
|
|
|
+ def fetch_code(timeout_sec: int = 180, poll: float = 6.0, exclude_codes: Optional[List[str]] = None) -> str | None:
|
|
|
|
|
+ exclude = set(exclude_codes or [])
|
|
|
|
|
+ start = time.monotonic()
|
|
|
|
|
+ attempt = 0
|
|
|
|
|
+ while time.monotonic() - start < timeout_sec:
|
|
|
|
|
+ attempt += 1
|
|
|
|
|
+ try:
|
|
|
|
|
+ summaries = client.list_emails(email)
|
|
|
|
|
+ print(f"[otp][pmail] 轮询 #{attempt}, 收到 {len(summaries)} 封目标邮件, 目标: {email}")
|
|
|
|
|
+ for s in summaries:
|
|
|
|
|
+ body = f"{s.get('subject', '')} {s.get('txt', '')}"
|
|
|
|
|
+ for code in re.findall(r"(?<!\d)(\d{6})(?!\d)", body):
|
|
|
|
|
+ if code not in exclude:
|
|
|
|
|
+ return code
|
|
|
|
|
+ except Exception:
|
|
|
|
|
+ pass
|
|
|
|
|
+ time.sleep(poll)
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ return email, _gen_password(), fetch_code, _extract_all_codes, "pmail"
|
|
|
|
|
+
|
|
|
|
|
+ if provider == "pmail":
|
|
|
|
|
+ return _build_pmail_bundle()
|
|
|
if provider == "tempmail":
|
|
if provider == "tempmail":
|
|
|
return _build_tempmail_bundle()
|
|
return _build_tempmail_bundle()
|
|
|
if provider == "gptmail":
|
|
if provider == "gptmail":
|
|
|
return _build_gptmail_bundle()
|
|
return _build_gptmail_bundle()
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
- return _build_tempmail_bundle()
|
|
|
|
|
|
|
+ return _build_pmail_bundle()
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
- print(f"[邮箱] TempMail.lol 初始化失败,回退 GPTMail: {e}")
|
|
|
|
|
- return _build_gptmail_bundle()
|
|
|
|
|
|
|
+ print(f"[邮箱] PMail 初始化失败,回退 TempMail: {e}")
|
|
|
|
|
+ try:
|
|
|
|
|
+ return _build_tempmail_bundle()
|
|
|
|
|
+ except Exception as e2:
|
|
|
|
|
+ print(f"[邮箱] TempMail 初始化失败,回退 GPTMail: {e2}")
|
|
|
|
|
+ return _build_gptmail_bundle()
|
|
|
|
|
|
|
|
# ========== OAuth 核心逻辑 (对齐原版的完美重定向流) ==========
|
|
# ========== OAuth 核心逻辑 (对齐原版的完美重定向流) ==========
|
|
|
|
|
|