perf: IMAP 两步策略 — 先 INTERNALDATE 过滤再下载 RFC822
避免对游标之前的邮件下载完整正文,大幅减少 QQ 等大收件箱的获取时间 第一步:批量 FETCH INTERNALDATE 快速过滤 第二步:仅对候选邮件下载 RFC822 解析正文 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
9c838da1f3
commit
5ade471cef
1 file changed
+42
-2
@@ -101,7 +101,10 @@ def _send_telegram_message(bot_token: str, chat_id: str, text: str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def _fetch_new_emails_imap(account: dict, since: str) -> List[dict]:
|
def _fetch_new_emails_imap(account: dict, since: str) -> List[dict]:
|
||||||
"""通过 IMAP 获取 received_at > since 的邮件,最多返回 50 封。"""
|
"""通过 IMAP 获取 received_at > since 的邮件,最多返回 50 封。
|
||||||
|
|
||||||
|
两步策略:先用 INTERNALDATE 快速过滤,再对命中的邮件下载正文。
|
||||||
|
"""
|
||||||
import email as email_lib
|
import email as email_lib
|
||||||
import email.header
|
import email.header
|
||||||
import imaplib
|
import imaplib
|
||||||
@@ -128,7 +131,44 @@ def _fetch_new_emails_imap(account: dict, since: str) -> List[dict]:
|
|||||||
_, data = conn.search(None, f'(SINCE "{since_date_str}")')
|
_, data = conn.search(None, f'(SINCE "{since_date_str}")')
|
||||||
msg_ids = data[0].split() if data[0] else []
|
msg_ids = data[0].split() if data[0] else []
|
||||||
|
|
||||||
for mid in msg_ids[-MAX_EMAILS_PER_FETCH:]:
|
if not msg_ids:
|
||||||
|
return results
|
||||||
|
|
||||||
|
# 第一步:批量获取 INTERNALDATE 快速过滤
|
||||||
|
candidate_ids = msg_ids[-MAX_EMAILS_PER_FETCH:]
|
||||||
|
id_range = b",".join(candidate_ids)
|
||||||
|
_, date_data = conn.fetch(id_range, "(INTERNALDATE)")
|
||||||
|
|
||||||
|
import re
|
||||||
|
date_pattern = re.compile(rb'INTERNALDATE "([^"]+)"')
|
||||||
|
new_msg_ids = []
|
||||||
|
|
||||||
|
for item in date_data:
|
||||||
|
if not isinstance(item, tuple):
|
||||||
|
continue
|
||||||
|
header_line = item[0] if isinstance(item[0], bytes) else b""
|
||||||
|
mid_match = re.match(rb"(\d+)", header_line)
|
||||||
|
date_match = date_pattern.search(header_line)
|
||||||
|
if not mid_match or not date_match:
|
||||||
|
continue
|
||||||
|
|
||||||
|
mid = mid_match.group(1)
|
||||||
|
idate_str = date_match.group(1).decode("ascii", errors="replace")
|
||||||
|
try:
|
||||||
|
from imaplib import Internaldate2tuple
|
||||||
|
import calendar
|
||||||
|
tt = Internaldate2tuple(b'"' + date_match.group(1) + b'"')
|
||||||
|
if tt:
|
||||||
|
ts = calendar.timegm(tt)
|
||||||
|
idate_iso = dt.utcfromtimestamp(ts).strftime("%Y-%m-%dT%H:%M:%S")
|
||||||
|
if idate_iso <= since:
|
||||||
|
continue
|
||||||
|
except Exception:
|
||||||
|
pass # 解析失败时保留,由后续 RFC822 过滤
|
||||||
|
new_msg_ids.append(mid)
|
||||||
|
|
||||||
|
# 第二步:仅对候选邮件下载 RFC822
|
||||||
|
for mid in new_msg_ids[-MAX_EMAILS_PER_FETCH:]:
|
||||||
try:
|
try:
|
||||||
_, msg_data = conn.fetch(mid, "(RFC822)")
|
_, msg_data = conn.fetch(mid, "(RFC822)")
|
||||||
raw = msg_data[0][1]
|
raw = msg_data[0][1]
|
||||||
|
|||||||
Reference in new issue
Block a user