Files
cloudinit-bot 61790706d0 feat(scripts): find_email.py — search by MID/sender/subject (REQ-INB-43)
---ci---
project: inboxos
phase: 1
milestone: v1.4
status: execute
wave: 1
task: T1
---/ci---
2026-06-09 13:59:16 +00:00

116 lines
3.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""Search InboxOS NocoDB by Message-ID, sender, subject, or date range.
v1.4 REQ-INB-43 — `scripts/find_email.py` — search by MID/sender/subject.
"""
from __future__ import annotations
import argparse
import os
import sys
from typing import Iterable
import requests
NOCODB_BASE_URL = os.environ.get("NOCODB_BASE_URL", "http://localhost:8080")
NOCODB_API_TOKEN = os.environ.get("NOCODB_API_TOKEN", "")
SEAWEEDFS_S3_BUCKET = os.environ.get("SEAWEEDFS_S3_BUCKET", "inboxos")
MODULE_TABLES = ("finance", "crm", "intel", "promotions")
TABLE_IDS = {
"finance": os.environ.get("NOCODB_TABLE_FINANCE", ""),
"crm": os.environ.get("NOCODB_TABLE_CRM", ""),
"intel": os.environ.get("NOCODB_TABLE_INTEL", ""),
"promotions": os.environ.get("NOCODB_TABLE_PROMOTIONS", ""),
"papra_index": os.environ.get("NOCODB_TABLE_PAPRA_INDEX", ""),
}
def _headers() -> dict[str, str]:
if not NOCODB_API_TOKEN:
sys.stderr.write("NOCODB_API_TOKEN is not set\n")
sys.exit(2)
return {"xc-token": NOCODB_API_TOKEN, "Content-Type": "application/json"}
def _query_table(table_id: str, where: str) -> list[dict]:
if not table_id:
return []
url = f"{NOCODB_BASE_URL}/api/v2/tables/{table_id}/records"
try:
r = requests.get(url, headers=_headers(), params={"where": where, "limit": 50}, timeout=10)
r.raise_for_status()
return r.json().get("list", [])
except requests.RequestException as exc:
sys.stderr.write(f"NocoDB unreachable: {exc}\n")
raise SystemExit(2)
def _s3_key(mid: str) -> str:
return f"eml/raw/{mid}.eml"
def search_mid(mid: str) -> Iterable[dict]:
for module in MODULE_TABLES:
rows = _query_table(TABLE_IDS[module], f"(Message-ID,eq,{mid})")
for row in rows:
yield {**row, "_module": module, "_s3_key": _s3_key(mid)}
def search_sender(sender: str) -> Iterable[dict]:
for module in MODULE_TABLES:
rows = _query_table(TABLE_IDS[module], f"(sender,like,%25{sender}%25)")
for row in rows:
yield {**row, "_module": module}
def search_subject(subject: str) -> Iterable[dict]:
for module in MODULE_TABLES:
rows = _query_table(TABLE_IDS[module], f"(subject,like,%25{subject}%25)")
for row in rows:
yield {**row, "_module": module}
def main() -> int:
p = argparse.ArgumentParser(description="Search InboxOS emails")
g = p.add_mutually_exclusive_group()
g.add_argument("--mid", help="Exact Message-ID match")
g.add_argument("--sender", help="Substring sender match")
g.add_argument("--subject", help="Substring subject match")
p.add_argument("--since", help="Filter received_at >= YYYY-MM-DD")
p.add_argument("--until", help="Filter received_at <= YYYY-MM-DD")
args = p.parse_args()
if args.mid:
results = list(search_mid(args.mid))
elif args.sender:
results = list(search_sender(args.sender))
elif args.subject:
results = list(search_subject(args.subject))
else:
p.print_help()
return 1
if not results:
print("No matches.")
return 0
print("MID | module | sender | subject | received_at | papra_link")
for row in results:
print(
f"{row.get('Message-ID', '')} | "
f"{row.get('_module', '')} | "
f"{row.get('sender', '')} | "
f"{row.get('subject', '')} | "
f"{row.get('received_at', '')} | "
f"{row.get('Papra_Link', '')}"
)
if "_s3_key" in row:
print(f" s3: s3://{SEAWEEDFS_S3_BUCKET}/{row['_s3_key']}")
return 0
if __name__ == "__main__":
raise SystemExit(main())