# -*- coding: utf-8 -*-
"""
Outlook / Hotmail OAuth2 邮件读取示例 (zhanghaole.com)

2024 年 9 月微软协议更新后，很多 @outlook.com / @hotmail.com 账号已不能用
"邮箱+密码"登录邮件客户端，只能用 OAuth2 令牌登录（IMAP / POP3 / SMTP 同理）。
本脚本演示：用 卡密里的 refresh_token + client_id 换取 access_token，
再用 access_token 通过 IMAP 读取收件箱。更多功能请自行扩展。

依赖：  pip install requests
用法：  把下面 example() 里的三个值换成你卡密里的字段，然后运行  python3 hotmail_oauth2_demo.py

卡密格式（本站商品详情页会标明）：
    邮箱----密码----client_id----refresh_token
"""

import imaplib
import email
from email.header import decode_header

import requests

TOKEN_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
IMAP_HOST = "outlook.office365.com"


def get_access_token(refresh_token: str, client_id: str) -> str:
    """用 refresh_token 换取一个短期 access_token。"""
    resp = requests.post(
        TOKEN_URL,
        data={
            "client_id": client_id,
            "refresh_token": refresh_token,
            "grant_type": "refresh_token",
        },
        timeout=30,
    )
    body = resp.json()
    if body.get("error"):
        raise RuntimeError(f"获取 access_token 失败: {body.get('error_description', body['error'])}")
    return body["access_token"]


def open_mailbox(email_addr: str, access_token: str) -> imaplib.IMAP4_SSL:
    """用 XOAUTH2 登录 IMAP。"""
    auth_string = f"user={email_addr}\x01auth=Bearer {access_token}\x01\x01"
    mail = imaplib.IMAP4_SSL(IMAP_HOST)
    mail.authenticate("XOAUTH2", lambda _: auth_string.encode())
    return mail


def _subject(raw_header: bytes) -> str:
    text, enc = decode_header(raw_header)[0]
    return text.decode(enc or "utf-8", "ignore") if isinstance(text, bytes) else text


def list_recent(email_addr: str, access_token: str, limit: int = 10) -> None:
    """打印收件箱最近几封邮件的发件人和主题。"""
    mail = open_mailbox(email_addr, access_token)
    try:
        mail.select("INBOX")
        _, data = mail.search(None, "ALL")
        ids = data[0].split()
        for mail_id in reversed(ids[-limit:]):
            _, msg_data = mail.fetch(mail_id, "(RFC822)")
            msg = email.message_from_bytes(msg_data[0][1])
            print(f"- {msg.get('From', '')}  |  {_subject(msg.get('Subject', ''))}")
        print(f"done ({len(ids)} 封邮件，显示最近 {min(limit, len(ids))} 封)")
    finally:
        try:
            mail.logout()
        except Exception:
            pass


def example() -> None:
    # ↓↓↓ 换成你卡密里的字段（下面是占位示例，不是真实账号）↓↓↓
    email_addr = "your-account@outlook.com"
    client_id = "00000000-0000-0000-0000-000000000000"
    refresh_token = "PASTE_YOUR_REFRESH_TOKEN_HERE"

    access_token = get_access_token(refresh_token, client_id)
    list_recent(email_addr, access_token)


if __name__ == "__main__":
    example()
