← Files LegalQuants LitigationARCHIVED FILE

skills/timenarratives/scripts/email_attachments.py

2.09 KB · Oct 5, 2026 · 18:33 UTC

↓ Download file

"""Classify EML attachments and construct terminal MIME-leaf records."""

from __future__ import annotations

import hashlib
from email.message import Message
from pathlib import PurePosixPath


def attachment_type(part: Message, payload: bytes, filename: str | None) -> str | None:
    suffix = PurePosixPath(filename or "").suffix.casefold()
    content_type = part.get_content_type().casefold()
    if content_type == "message/rfc822" or suffix == ".eml":
        return "email"
    if content_type in {"text/plain", "text/markdown"} or suffix in {
        ".txt",
        ".md",
        ".markdown",
    }:
        return "text"
    if (
        content_type
        == "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
        or suffix == ".docx"
    ) and payload.startswith(b"PK"):
        return "docx"
    return None


def is_inline_resource(part: Message) -> bool:
    content_type = part.get_content_type().casefold()
    return bool(part.get("Content-ID") or part.get("Content-Location")) or any(
        content_type.startswith(prefix)
        for prefix in ("image/", "audio/", "video/", "font/")
    )


def leaf_record(
    leaf_id: str,
    container_id: str,
    locator: str,
    parent: str | None,
    part: Message,
    role: str,
    disposition: str,
    *,
    reason: str | None = None,
    payload: bytes | None = None,
    child_id: str | None = None,
    author: str | None = None,
) -> dict:
    return {
        "mimeLeafId": leaf_id,
        "containerId": container_id,
        "locator": locator,
        "parentLocator": parent,
        "contentType": part.get_content_type().casefold(),
        "contentDisposition": part.get_content_disposition(),
        "filename": part.get_filename(),
        "contentId": part.get("Content-ID"),
        "payloadSha256": hashlib.sha256(payload).hexdigest()
        if payload is not None
        else None,
        "byteLength": len(payload) if payload is not None else None,
        "role": role,
        "childContainerId": child_id,
        "disposition": disposition,
        "reason": reason,
        "sourceAuthor": author,
    }

SHA-256: 2053eecc615d08b3278007fefd61166e1b8c6c3fd2007feb39b17f2085041319