atlas-os/scripts/atlas_lead_enrich.py
Chaib Aarab 301cc75de9 init: Atlas OS — open-source patterns + tools for solo founder LLM cluster
3 browser-native chat surfaces (atlas-chat / atlas-hud / atlas-command, no build step)
+ 11 Python automation scripts (cluster, tray, finance, reminders, autonomy loops)
+ 2 patterns documented (cluster-then-polish, anti-impulse R1-R10)
MIT licensed, anonymized from production stack.
2026-05-26 16:21:16 +02:00

149 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""
atlas_lead_enrich.py — Per-prospect WhatsApp message generator.
Use:
atlas_lead_enrich.py "Lunchroom Carrousel" "Oude Markt" --sector lunchroom --ig "ze posten dagmenu's"
Generates a personalized WhatsApp message using cluster_then_polish pattern.
Output: text printed + saved to ~/.config/atlas/drafts/lead_<slug>.md
"""
import sys
import re
import argparse
import io
from pathlib import Path
from datetime import datetime
if sys.stdout.encoding != 'utf-8':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
sys.path.insert(0, r"C:\Users\Gebruiker\.config\atlas")
try:
from cluster_then_polish import draft, save_draft
except ImportError:
print("Need cluster_then_polish.py — see ~/.config/atlas/")
sys.exit(1)
SECTOR_TEMPLATES = {
"lunchroom": {
"package": "Werkplek STARTER €799",
"pain": "bonnetjes tellen 4-5u/wk",
"ref": "Couscousbar",
},
"koffiebar": {
"package": "Werkplek STARTER €799",
"pain": "voorraad bijhouden + dagomzet tellen",
"ref": "Coffee Almahaba",
},
"friteshouse": {
"package": "Werkplek PRO €1.499",
"pain": "afhalen-rush volgorde + tap-pay",
"ref": "Dushi Sazon",
},
"cafetaria": {
"package": "Werkplek PRO €1.499",
"pain": "voorraad-melding + dag-rapportage",
"ref": "Cafetaria Online",
},
"kapper": {
"package": "Werkplek STARTER €799",
"pain": "gemiste afspraken want naast knippen ook bellen/typen",
"ref": "Charif's Barbershop",
},
"barber": {
"package": "Werkplek STARTER €799",
"pain": "afsprakenbeheer + kassa zonder onderbreken",
"ref": "Charif's Barbershop",
},
"horeca": {
"package": "Werkplek KASSA €2.499",
"pain": "POS + voorraad + rapportage in één",
"ref": "Bisogno",
},
"retail": {
"package": "Werkplek PRO €1.499",
"pain": "voorraad + kassa + dag-overzicht",
"ref": "Le Miris",
},
}
def slugify(name):
s = re.sub(r"[^a-z0-9]+", "-", name.lower())
return s.strip("-")[:40]
def build_message(name, location, sector, ig_hint, voornaam_hint=""):
"""Build personalized WhatsApp via cluster + polish."""
t = SECTOR_TEMPLATES.get(sector.lower(), SECTOR_TEMPLATES["horeca"])
voornaam = voornaam_hint or "{voornaam}"
prompt = f"""Schrijf één persoonlijk cold-WhatsApp bericht voor een Tilburgse zaak. NL, casual maar professional.
ZAAK: {name}
LOCATIE: {location}
SECTOR: {sector}
IG/CONTEXT HINT: {ig_hint}
EIGENAAR VOORNAAM: {voornaam}
AANBOD: {t['package']}
PIJNPUNT: {t['pain']}
REFERENTIE: {t['ref']}
REGELS:
- Open met "Hoi {voornaam}," + 1 zin die TONE-PERSOONLIJK aansluit op IG-context hint
- Korte pijn-kwestie noemen (max 1 zin)
- Aanbod met prijs upfront
- Zachte CTA: 15-20 min koffie of bezoek, geen verkoop-druk
- Max 80 woorden totaal
- Eindigt met één concrete vraag
Output ALLEEN het bericht, geen toelichting."""
# task=general → qwen2.5:7b on atlas-01 (always-on, NL-decent)
return draft(prompt, task="general", node_pref="atlas-01",
max_tokens=400, temperature=0.6, purpose="lead_outreach")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("name", help="Zaak naam")
ap.add_argument("location", help="Adres / wijk")
ap.add_argument("--sector", required=True, choices=list(SECTOR_TEMPLATES.keys()))
ap.add_argument("--ig", default="", help="IG/context detail om aan te haken")
ap.add_argument("--voornaam", default="", help="Eigenaar voornaam")
ap.add_argument("--out", default=None, help="Save path (auto if omitted)")
args = ap.parse_args()
print(f"Generating message for: {args.name} ({args.sector}) at {args.location}")
print(f"Hook: {args.ig or '(none)'}")
print()
result = build_message(args.name, args.location, args.sector, args.ig, args.voornaam)
if not result.get("ok"):
print("ERR:", result.get("err"))
sys.exit(1)
print("=" * 60)
print(result["text"])
print("=" * 60)
print(f"\n[{result['node']}/{result['model']} {result['elapsed_s']}s]")
print(f"polish_status: {result['polish_status']}")
print(f"HINT: {result['hint']}")
print()
out_path = args.out or (
Path.home() / ".config" / "atlas" / "drafts" /
f"lead_{slugify(args.name)}_{datetime.now().strftime('%Y%m%d_%H%M')}.md"
)
saved = save_draft(result, out_path)
print(f"\nSaved → {saved}")
print("\nReview, edit if needed, copy to WhatsApp, send.")
print("After sending: mark_polished(draft_id) to track conversion.")
if __name__ == "__main__":
main()