From 5ade471cefdd286849e64170e8ce91af5d62c075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E7=BB=8D=E5=BA=B7?= <3052147989@qq.com> Date: Thu, 5 Mar 2026 19:18:06 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20IMAP=20=E4=B8=A4=E6=AD=A5=E7=AD=96?= =?UTF-8?q?=E7=95=A5=20=E2=80=94=20=E5=85=88=20INTERNALDATE=20=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E5=86=8D=E4=B8=8B=E8=BD=BD=20RFC822?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 避免对游标之前的邮件下载完整正文,大幅减少 QQ 等大收件箱的获取时间 第一步:批量 FETCH INTERNALDATE 快速过滤 第二步:仅对候选邮件下载 RFC822 解析正文 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- outlook_web/services/telegram_push.py | 44 +++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/outlook_web/services/telegram_push.py b/outlook_web/services/telegram_push.py index 1753cdc..3a4c101 100644 --- a/outlook_web/services/telegram_push.py +++ b/outlook_web/services/telegram_push.py @@ -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]: - """通过 IMAP 获取 received_at > since 的邮件,最多返回 50 封。""" + """通过 IMAP 获取 received_at > since 的邮件,最多返回 50 封。 + + 两步策略:先用 INTERNALDATE 快速过滤,再对命中的邮件下载正文。 + """ import email as email_lib import email.header 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}")') 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: _, msg_data = conn.fetch(mid, "(RFC822)") raw = msg_data[0][1]