fix: add IMAP ID command for 126.com/163.com support

These providers require a client identification (ID command, RFC 2971)
before SELECT INBOX, otherwise they reject with 'SELECT Unsafe Login'.
Extract shared login+select logic into _login_and_prepare helper.
This commit is contained in:
2026-07-02 19:56:14 +08:00
parent 82403de6df
commit 89149b506c

View File

@@ -48,16 +48,36 @@ def _get_text_from_msg(msg) -> str:
return ""
_PROVIDERS_NEED_ID = ("163.com", "126.com")
def _check_provider(email_addr: str, providers: tuple[str, ...]) -> bool:
return any(email_addr.endswith(p) for p in providers)
def _send_id_command(conn):
imaplib.Commands["ID"] = "AUTH"
args = ("name", "AI-Mail-Bot", "version", "1.0.0", "vendor", "personal")
conn._simple_command("ID", '("' + '" "'.join(args) + '")')
def _select_mailbox(conn, mailbox: str = "INBOX"):
status, data = conn.select(mailbox)
if status != "OK":
raise RuntimeError(f"无法选择邮箱 {mailbox}: {data}")
def fetch_unseen_emails(account: EmailAccount) -> list[Email]:
def _login_and_prepare(account: EmailAccount):
conn = imaplib.IMAP4_SSL(account.imap_server, account.imap_port)
conn.login(account.username, account.password)
if _check_provider(account.username, _PROVIDERS_NEED_ID):
_send_id_command(conn)
_select_mailbox(conn)
return conn
def fetch_unseen_emails(account: EmailAccount) -> list[Email]:
conn = _login_and_prepare(account)
_, data = conn.uid("SEARCH", None, "UNSEEN")
uids = data[0].split() if data[0] else []
@@ -84,9 +104,7 @@ def fetch_unseen_emails(account: EmailAccount) -> list[Email]:
def mark_as_seen(account: EmailAccount, uids: list[bytes]):
if not uids:
return
conn = imaplib.IMAP4_SSL(account.imap_server, account.imap_port)
conn.login(account.username, account.password)
_select_mailbox(conn)
conn = _login_and_prepare(account)
for uid in uids:
conn.uid("STORE", uid, "+FLAGS", "\\Seen")
conn.logout()