diff --git a/README.md b/README.md index 13f5dc8..45b3147 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,7 @@ cp config.example.json config.json - `defaultDomains`:Cloudflare 等 catch-all 域名,用于生成注册邮箱 - 验证码实际进入 Gmail;用 **应用专用密码**,不是登录密码 - 也可把账号密码只写在 `.env` 的 `GMAIL_USER` / `GMAIL_PASSWORD` +- 取到验证码后默认 **删除该邮件**(`gmail_delete_after_code: true`),避免 catch-all 邮箱堆积;失败不影响注册 #### B. Hotmail / Outlook diff --git a/config.example.json b/config.example.json index 974b13f..15efce4 100644 --- a/config.example.json +++ b/config.example.json @@ -186,5 +186,9 @@ "// gmail_imap_password": "Gmail 应用专用密码(非登录密码)", "gmail_imap_password": "", "gmail_imap_host": "imap.gmail.com", - "gmail_imap_port": 993 + "gmail_imap_port": 993, + "// gmail_delete_after_code": "Gmail 取到验证码后是否 IMAP 删除该邮件(失败不影响注册)", + "gmail_delete_after_code": true, + "// hotmail_delete_after_code": "Hotmail/Outlook 取到验证码后是否 IMAP 删除该邮件", + "hotmail_delete_after_code": true } diff --git a/grok_register_ttk.py b/grok_register_ttk.py index b48b808..7b6352d 100644 --- a/grok_register_ttk.py +++ b/grok_register_ttk.py @@ -1730,6 +1730,18 @@ def _hotmail_imap_get_code(mailbox_email, target_email, access_token, log_callba if code: if log_callback: log_callback(f"[*] Hotmail/Outlook 从邮件中提取到验证码: {code}") + if _config_bool(config.get("hotmail_delete_after_code", True), default=True): + try: + imap.store(mid, "+FLAGS", r"(\Deleted)") + try: + imap.expunge() + except Exception: + pass + if log_callback: + log_callback(f"[*] Hotmail/Outlook 已删除验证码邮件 mid={mid!r}") + except Exception as del_exc: + if log_callback: + log_callback(f"[Debug] Hotmail/Outlook 删除验证码邮件失败: {del_exc}") return code return None finally: @@ -1856,6 +1868,7 @@ def gmail_get_oai_code( raise Exception("Gmail IMAP 未配置:需要在 config.json 设置 gmail_imap_user/gmail_imap_password,或环境变量 GMAIL_USER/GMAIL_PASSWORD") imap_host = str(config.get("gmail_imap_host", "imap.gmail.com") or "imap.gmail.com").strip() imap_port = int(config.get("gmail_imap_port", 993) or 993) + delete_after = _config_bool(config.get("gmail_delete_after_code", True), default=True) deadline = time.time() + timeout # Resend earlier: first mail often arrives in 5-40s; resend at 45s as safety net. next_resend_at = time.time() + float(config.get("gmail_resend_after_sec", 45) or 45) @@ -1927,6 +1940,22 @@ def gmail_get_oai_code( ) return subject, sender, recipient_blob + def _delete_message(conn, mid, code: str) -> None: + """Best-effort delete of used verification mail; never blocks returning the code.""" + if not delete_after or conn is None or mid is None: + return + try: + conn.store(mid, "+FLAGS", r"(\Deleted)") + try: + conn.expunge() + except Exception: + pass + if log_callback: + log_callback(f"[*] Gmail IMAP 已删除验证码邮件 mid={mid!r} code={code}") + except Exception as exc: + if log_callback: + log_callback(f"[Debug] Gmail IMAP 删除验证码邮件失败: {exc}") + try: while time.time() < deadline: raise_if_cancelled(cancel_callback) @@ -1989,6 +2018,7 @@ def gmail_get_oai_code( f"[*] Gmail IMAP 从邮件中提取到验证码: {code} " f"(elapsed={elapsed:.1f}s polls={poll_n} via={qused or 'ALL'})" ) + _delete_message(conn, mid, code) return code # Rare: code only in body — fetch full message once try: @@ -2009,6 +2039,7 @@ def gmail_get_oai_code( f"[*] Gmail IMAP 从邮件正文提取到验证码: {code} " f"(elapsed={elapsed:.1f}s polls={poll_n})" ) + _delete_message(conn, mid, code) return code except Exception as exc: _close_imap(imap)