Delete verification emails after code extraction

IMAP-delete Gmail/Hotmail verification messages once the code is parsed, with config toggles and non-blocking failure handling.
This commit is contained in:
chaos committed 2026-07-11 18:32:17 +08:00
1 parent 06044551c7
commit 187792d6be
3 files changed
+37 -1

No files matched your search

+1
View File
@@ -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
+5 -1
View File
@@ -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
}
+31
View File
@@ -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)