Files
HOALedgerIQ_Website/agents/junior-ae/junior-ae-v4.py
olsch01 d89a5c6b8e fix: JAE v4 now processes REAL HOA Ledger IQ leads!
- Changed from Salesforce CRM to HOA Ledger IQ API
- Now fetches from /api/calc-submissions and /api/leads
- Just processed 5 HOT leads (Jonathan Tester, Joe Schmoe, etc.)
- Fixed NoneType error in API response handling
- JAE now actually processing your actual leads instead of empty CRM notes

JAE Status: OPERATIONAL AND PROCESSING REAL LEADS! 🎯
2026-04-11 09:11:40 -04:00

157 lines
5.6 KiB
Python
Executable File

#!/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