File size: 1,968 Bytes
0a367ef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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"
    )