#!/usr/bin/env python3 """ JAE v4 - Process HOA Ledger IQ leads Fetches from ROI Calculator and Interest Form APIs Detects temperature and elevates HOT/WARM leads """ import json, re, time, urllib.request, urllib.error from datetime import datetime, timedelta from pathlib import Path SCRIPT_DIR = Path(__file__).parent for d in [SCRIPT_DIR / "state", SCRIPT_DIR / "logs"]: d.mkdir(parents=True, exist_ok=True) STATE_FILE = SCRIPT_DIR / "state" / "jae-v4-state.json" LOG_FILE = SCRIPT_DIR / "logs" / f"jae-v4-{datetime.now().strftime('%Y%m%d')}.log" # HOA Ledger IQ API endpoints CALC_API = "https://www.hoaledgeriq.com/api/calc-submissions" LEADS_API = "https://hoaledgeriq.com/api/leads" ADMIN_KEY = "K9mP2vL8x4qR7nZ" def log(msg): ts = datetime.now().strftime('%H:%M:%S') print(f"[{ts}] {msg}") with open(LOG_FILE, 'a') as f: f.write(f"[{ts}] {msg}\n") def load_state(): if STATE_FILE.exists(): return json.loads(STATE_FILE.read_text()) return {"last_check": (datetime.now() - timedelta(days=7)).isoformat(), "processed": 0, "upgraded": 0, "processed_ids": []} def save_state(s): STATE_FILE.write_text(json.dumps(s, indent=2)) def fetch_notes(): """Fetch leads from HOA Ledger IQ API (both calc submissions and interest form)""" all_leads = [] # Fetch ROI Calculator submissions try: req = urllib.request.Request( CALC_API, headers={"x-admin-key": ADMIN_KEY, "Accept": "application/json"} ) with urllib.request.urlopen(req, timeout=15) as r: data = json.loads(r.read().decode()) submissions = data.get('submissions', []) if data else [] for sub in submissions: all_leads.append({ 'id': f"calc_{sub['id']}", 'title': f"ROI Calc: {sub.get('email', 'Unknown')}", 'body': f"Email: {sub.get('email', '')}\nHomesites: {sub.get('homesites', 0)}\nProperty Type: {sub.get('property_type', '')}\nAnnual Income: ${sub.get('annual_income', 0):,}\nReserve Funds: ${sub.get('reserve_funds', 0):,}\nAI Rec: {sub.get('ai_recommendation', 'None')[:200]}", 'created_at': sub.get('created_at', ''), 'source': 'roi_calculator' }) except Exception as e: log(f"Calc API error: {e}") # Fetch Interest Form leads try: req = urllib.request.Request( LEADS_API, headers={"x-admin-key": ADMIN_KEY, "Accept": "application/json"} ) with urllib.request.urlopen(req, timeout=15) as r: data = json.loads(r.read().decode()) leads = data.get('leads', []) for lead in leads: all_leads.append({ 'id': f"lead_{lead['id']}", 'title': f"Interest: {lead.get('first_name', '')} {lead.get('last_name', '')}", 'body': f"Email: {lead.get('email', '')}\nOrg: {lead.get('org_name', '')}\nState: {lead.get('state', '')}\nRole: {lead.get('role', '')}\nUnits: {lead.get('unit_count', '')}\nBeta Interest: {lead.get('beta_interest', 0)}", 'created_at': lead.get('created_at', ''), 'source': 'interest_form' }) except Exception as e: log(f"Leads API error: {e}") return all_leads def detect_temp(title, body=""): """Detect temperature from title or content""" text = f"{title} {body}".upper() # Check for explicit temperature if 'HOT' in text or 'HIGH' in text or 'URGENT' in text: return 'HOT' if 'WARM' in text or 'MEDIUM' in text or 'INTERESTED' in text: return 'WARM' if 'COLD' in text or 'LOW' in text or 'NOT INTERESTED' in text: return 'COLD' # Auto-detect from engagement signals hot_signals = ['READY', 'INTERESTED', 'WANTS', 'NEEDS', 'BUDGET', 'TIMELINE', 'SOON', 'QUICK', 'BETA', 'TEST'] warm_signals = ['CONSIDERING', 'THINKING', 'MAYBE', 'LATER', 'RESEARCH', 'COMPARE'] for signal in hot_signals: if signal in text: return 'HOT' for signal in warm_signals: if signal in text: return 'WARM' # Default to WARM for unclassified leads (better to over-qualify) return 'WARM' def main(): log("=== JAE v4 Starting - HOA Ledger IQ Integration ===") state = load_state() notes = fetch_notes() log(f"Fetched {len(notes)} leads from HOA Ledger IQ APIs") processed_count = 0 upgraded_count = 0 for note in notes: note_id = note['id'] # Skip if already processed if note_id in state.get('processed_ids', []): continue # Detect temperature temp = detect_temp(note.get('title', ''), note.get('body', '')) # Process based on temperature if temp in ['HOT', 'WARM']: log(f"🔥 {temp} lead: {note['title'][:60]}") upgraded_count += 1 else: log(f"❄️ COLD lead: {note['title'][:60]}") processed_count += 1 state['processed_ids'].append(note_id) # Keep only last 1000 processed IDs if len(state['processed_ids']) > 1000: state['processed_ids'] = state['processed_ids'][-1000:] state['last_check'] = datetime.now().isoformat() state['processed'] += processed_count state['upgraded'] += upgraded_count save_state(state) log(f"=== Done: {processed_count} processed, {upgraded_count} upgraded ===") log("Waiting 3 hours...") if __name__ == "__main__": while True: main() time.sleep(3 * 60 * 60) # 3 hours