61790706d0
---ci--- project: inboxos phase: 1 milestone: v1.4 status: execute wave: 1 task: T1 ---/ci---
116 lines
3.6 KiB
Python
Executable File
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())
|