fix: skip already-processing UIDs at FETCH level to avoid downloading email content
This commit is contained in:
2
main.py
2
main.py
@@ -48,7 +48,7 @@ def _email_poller(cfg):
|
||||
logger.info("邮件轮询线程已启动")
|
||||
while _running:
|
||||
try:
|
||||
for acct_idx, mail in poll_accounts(cfg):
|
||||
for acct_idx, mail in poll_accounts(cfg, skip_uids=_processing_uids):
|
||||
if not _running:
|
||||
return
|
||||
with _pending_lock:
|
||||
|
||||
@@ -109,8 +109,10 @@ def _login_and_prepare(account: EmailAccount, timeout: int = 30, select_inbox: b
|
||||
return conn
|
||||
|
||||
|
||||
def fetch_unseen_emails(account: EmailAccount) -> list[Email]:
|
||||
def fetch_unseen_emails(account: EmailAccount, skip_uids: set[bytes] | None = None) -> list[Email]:
|
||||
conn = _login_and_prepare(account, select_inbox=False)
|
||||
if skip_uids is None:
|
||||
skip_uids = set()
|
||||
|
||||
# 列出所有文件夹
|
||||
_, folder_data = conn.list()
|
||||
@@ -120,8 +122,6 @@ def fetch_unseen_emails(account: EmailAccount) -> list[Email]:
|
||||
if item is None:
|
||||
continue
|
||||
line = item.decode("utf-8", errors="replace")
|
||||
# IMAP LIST 格式: (flags) "delimiter" "folder_name"
|
||||
# 用正则提取最后一个引号内的内容
|
||||
m = re.search(r'"([^"]*)"\s*$', line)
|
||||
if m:
|
||||
folders.append(m.group(1))
|
||||
@@ -130,6 +130,7 @@ def fetch_unseen_emails(account: EmailAccount) -> list[Email]:
|
||||
logger.info("扫描文件夹: %s", ", ".join(folders))
|
||||
|
||||
emails = []
|
||||
skipped = 0
|
||||
for folder in folders:
|
||||
try:
|
||||
status, _ = conn.select(folder, readonly=True)
|
||||
@@ -140,9 +141,15 @@ def fetch_unseen_emails(account: EmailAccount) -> list[Email]:
|
||||
uids = data[0].split() if data[0] else []
|
||||
if not uids:
|
||||
continue
|
||||
logger.info(" %s: %d 封未读", folder, len(uids))
|
||||
|
||||
for uid in uids:
|
||||
# 先过滤掉正在处理的 UID,避免下载不需要的邮件
|
||||
new_uids = [uid for uid in uids if uid not in skip_uids]
|
||||
skipped += len(uids) - len(new_uids)
|
||||
if not new_uids:
|
||||
continue
|
||||
logger.info(" %s: %d 封未读 (%d 封跳过)", folder, len(new_uids), len(uids) - len(new_uids))
|
||||
|
||||
for uid in new_uids:
|
||||
try:
|
||||
_, msg_data = conn.uid("FETCH", uid, "RFC822")
|
||||
if msg_data[0] is None:
|
||||
|
||||
@@ -8,11 +8,11 @@ from src.tg_bot import send_summary, send_thinking, edit_message, format_summary
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def poll_accounts(cfg: Config) -> Generator[tuple[int, Email], None, None]:
|
||||
def poll_accounts(cfg: Config, skip_uids: set[bytes] | None = None) -> Generator[tuple[int, Email], None, None]:
|
||||
for idx, acct in enumerate(cfg.email_accounts):
|
||||
try:
|
||||
logger.info(f"检查邮箱: {acct.username}")
|
||||
emails = fetch_unseen_emails(acct)
|
||||
emails = fetch_unseen_emails(acct, skip_uids=skip_uids)
|
||||
if emails:
|
||||
logger.info(f" 发现 {len(emails)} 封新邮件")
|
||||
for mail in emails:
|
||||
|
||||
@@ -193,7 +193,8 @@ def _summary_keyboard(can_reply: bool = True, is_promotion: bool = False, links:
|
||||
for link in (links or []):
|
||||
url = link.get("url", "")
|
||||
text = link.get("text", "打开链接")[:30]
|
||||
if url.startswith("http"):
|
||||
# 校验 URL:合法 http/https,无多重协议头,长度合理
|
||||
if url.startswith("http") and url.count("://") == 1 and len(url) < 200:
|
||||
kb.append([{"text": f"🔗 {text}", "url": url}])
|
||||
return {"inline_keyboard": kb}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user