import base64 import logging from pathlib import Path import aiosqlite from app.config import settings from app.database import update_notification_status from app.lib.graph.client import GraphClient from app.lib.utils.storage import write_email logger = logging.getLogger(__name__) _LARGE_ATTACHMENT_THRESHOLD = 3 * 1024 * 1024 # 3 MB async def fetch_and_store( client: GraphClient, message_id: str, notif_db_id: int, storage_path: Path, conn: aiosqlite.Connection, ) -> Path: email_data = await client.get( f"/users/{settings.mailbox_user}/messages/{message_id}", params={"$expand": "attachments", "$select": ( "id,subject,from,toRecipients,ccRecipients,receivedDateTime," "body,uniqueBody,hasAttachments,attachments,webLink" )}, ) attachment_bytes: list[tuple[str, str, bytes]] = [] for att in email_data.get("attachments", []): if att.get("@odata.type") != "#microsoft.graph.fileAttachment": continue filename = att.get("name", "attachment") content_type = att.get("contentType", "application/octet-stream") size = att.get("size", 0) if size > _LARGE_ATTACHMENT_THRESHOLD or not att.get("contentBytes"): raw = await _fetch_large_attachment(client, message_id, att["id"]) else: raw = base64.b64decode(att["contentBytes"]) attachment_bytes.append((filename, content_type, raw)) dest = await write_email(email_data, attachment_bytes, storage_path) await update_notification_status( conn, notif_db_id, "fetched", storage_path=str(dest) ) logger.info("Stored email %s at %s", message_id, dest) return dest async def _fetch_large_attachment( client: GraphClient, message_id: str, attachment_id: str ) -> bytes: return await client.get_raw( f"/users/{settings.mailbox_user}/messages/{message_id}/attachments/{attachment_id}/$value" )