"""Mail utility helpers."""
import re
from datetime import datetime
from email.header import decode_header
from email.utils import mktime_tz, parsedate_tz
from typing import Any
def decode_header_value(value: Any) -> str:
"""Decode email header value safely."""
if value is None:
return ""
raw = value if isinstance(value, str) else str(value)
parts = decode_header(raw)
result: list[str] = []
for part, charset in parts:
if isinstance(part, bytes):
result.append(part.decode(charset or "utf-8", errors="replace"))
else:
result.append(part)
return "".join(result)
def format_date(date_str: str | None) -> str:
"""Format email date string to local-friendly form."""
if not date_str:
return ""
try:
tt = parsedate_tz(date_str)
if tt is None:
return date_str
ts = mktime_tz(tt)
dt = datetime.fromtimestamp(ts)
return dt.strftime("%Y-%m-%d %H:%M")
except Exception:
return date_str
def strip_html_tags(html: str) -> str:
"""Very basic HTML tag stripping for fallback text extraction."""
text = re.sub(r"", "", html, flags=re.S)
text = re.sub(r"", "", text, flags=re.S)
# Replace block-level tags with newlines before stripping remaining tags
text = re.sub(r"(p|div|h[1-6]|li|tr|blockquote)>", "\n", text, flags=re.I)
text = re.sub(r"
", "\n", text, flags=re.I)
text = re.sub(r"<[^>]+>", "", text)
text = re.sub(r" ", " ", text)
text = re.sub(r"<", "<", text)
text = re.sub(r">", ">", text)
text = re.sub(r"&", "&", text)
text = re.sub(r"\n\s*\n+", "\n\n", text)
return text.strip()
def truncate_text(text: str, max_len: int = 60) -> str:
"""Truncate text with ellipsis."""
if len(text) <= max_len:
return text
return text[: max_len - 1] + "…"