"""Email signup for Microfactory updates. Lightweight, privacy-first opt-in collection. Stores one JSONL row per signup: {ts, email, consent, source, app_version}. Local runs write to data/signups.jsonl. Space runs can additionally push to a private HF dataset via CommitScheduler if SIGNUPS_DATASET and HF_TOKEN are set. Never writes without explicit consent. """ from __future__ import annotations import json import os import re import threading from datetime import datetime, timezone from pathlib import Path from typing import Any SIGNUPS_FILE = Path(__file__).resolve().parent.parent / "data" / "signups.jsonl" SIGNUPS_DATASET = os.environ.get("SIGNUPS_DATASET", "kylebrodeur/microfactory-signups").strip() FLUSH_MINUTES = 5 _scheduler: Any = None _lock = threading.Lock() _EMAIL_RE = re.compile(r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$") def _get_scheduler(): """Lazy-init CommitScheduler for the private signup dataset.""" global _scheduler token = os.environ.get("HF_TOKEN", "").strip() if not token: return None if not SIGNUPS_DATASET or "/" not in SIGNUPS_DATASET: return None if _scheduler is None: with _lock: if _scheduler is None: try: from huggingface_hub import CommitScheduler except ImportError: return None SIGNUPS_FILE.parent.mkdir(parents=True, exist_ok=True) if not SIGNUPS_FILE.exists(): SIGNUPS_FILE.write_text("", encoding="utf-8") _scheduler = CommitScheduler( repo_id=SIGNUPS_DATASET, repo_type="dataset", folder_path=str(SIGNUPS_FILE.parent), every=FLUSH_MINUTES, token=token, allow_patterns=["signups.jsonl"], ) return _scheduler def is_active() -> bool: """True if signup syncing to HF is configured and active.""" return _get_scheduler() is not None def validate_email(email: str) -> str | None: """Return normalized email or None if invalid.""" if not email: return None email = email.strip().lower() if not _EMAIL_RE.match(email): return None return email def record_signup(email: str, consent: bool, source: str = "local") -> tuple[bool, str]: """Record one signup. Returns (ok, message). Requires explicit consent. Email is validated. Writes locally always; pushes to HF only when HF_TOKEN + SIGNUPS_DATASET are present. """ if not consent: return False, "Please check the box to opt in before submitting." normalized = validate_email(email) if normalized is None: return False, "Enter a valid email address." row = { "ts": datetime.now(timezone.utc).isoformat(), "email": normalized, "consent": True, "source": source, "app_version": os.environ.get("CHIEF_ENGINEER_VERSION", "0.1.0"), } try: SIGNUPS_FILE.parent.mkdir(parents=True, exist_ok=True) with _lock: with SIGNUPS_FILE.open("a", encoding="utf-8") as f: f.write(json.dumps(row, ensure_ascii=False) + "\n") sched = _get_scheduler() if sched is not None: try: sched.trigger() except Exception: pass return True, "You're on the list. Microfactory updates will hit that inbox." except Exception as e: return False, f"Could not save signup: {e}" def privacy_notice() -> str: return ( "