| Server IP : 157.230.181.24 / Your IP : 216.73.217.11 Web Server : Apache/2.4.58 (Ubuntu) System : Linux conductive 6.8.0-117-generic #117-Ubuntu SMP PREEMPT_DYNAMIC Tue May 5 19:26:24 UTC 2026 x86_64 User : ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /opt/smarterchild/ |
Upload File : |
"""Per-buddy profile, survey answers, notes, and long-term facts."""
from __future__ import annotations
import json
import re
import threading
from datetime import datetime, timezone
from difflib import SequenceMatcher
from pathlib import Path
ROOT = Path("/opt/smarterchild/data")
ROOT.mkdir(parents=True, exist_ok=True)
def _now_iso() -> str:
return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
def _default_user(buddy: str) -> dict:
return {
"buddy": buddy,
"profile": {
"name": "",
"age": "",
"city": "",
"zip": "",
"band": "",
"movie": "",
"color": "",
"food": "",
},
"survey": {},
"notes": [],
"facts": [],
"quiz": {"score": 0, "asked": 0, "streak": 0},
"flow": None,
"updated_at": None,
}
class UserStore:
def __init__(self, root: Path = ROOT) -> None:
self.root = root
self.root.mkdir(parents=True, exist_ok=True)
self._lock = threading.Lock()
self._cache: dict[str, dict] = {}
def _path(self, buddy: str) -> Path:
safe = re.sub(r"[^\w.-]", "", buddy.lower()) or "buddy"
return self.root / f"{safe}.json"
def load(self, buddy: str) -> dict:
key = buddy.lower()
with self._lock:
if key in self._cache:
return self._cache[key]
path = self._path(buddy)
if path.exists():
data = json.loads(path.read_text())
else:
data = _default_user(buddy)
self._cache[key] = data
return data
def save(self, buddy: str, data: dict) -> None:
key = buddy.lower()
data["updated_at"] = _now_iso()
self._path(buddy).write_text(json.dumps(data, indent=2) + "\n")
self._cache[key] = data
@staticmethod
def _similar(a: str, b: str) -> bool:
return SequenceMatcher(None, a.lower().strip(), b.lower().strip()).ratio() >= 0.82
def add_fact(self, buddy: str, text: str, source: str = "") -> None:
text = text.strip()
if len(text) < 8:
return
with self._lock:
data = self.load(buddy)
facts: list[dict] = list(data.get("facts", []))
for f in facts:
if self._similar(text, f.get("text", "")):
f["text"] = text
f["learned_at"] = _now_iso()
f["source"] = source or f.get("source", "")
data["facts"] = facts[-80:]
self.save(buddy, data)
return
facts.append({"text": text, "learned_at": _now_iso(), "source": source})
data["facts"] = facts[-80:]
self.save(buddy, data)
def add_facts(self, buddy: str, new_facts: list[str], source: str = "") -> None:
for fact in new_facts:
self.add_fact(buddy, fact, source=source)
def add_note(self, buddy: str, text: str) -> None:
text = text.strip()
if not text:
return
with self._lock:
data = self.load(buddy)
notes = list(data.get("notes", []))
notes.append({"text": text, "at": _now_iso()})
data["notes"] = notes[-30:]
self.save(buddy, data)
def set_flow(self, buddy: str, flow: dict | None) -> None:
with self._lock:
data = self.load(buddy)
data["flow"] = flow
self.save(buddy, data)
def get_flow(self, buddy: str) -> dict | None:
return self.load(buddy).get("flow")
def set_profile_field(self, buddy: str, field: str, value: str) -> None:
with self._lock:
data = self.load(buddy)
prof = dict(data.get("profile") or {})
if field in prof:
prof[field] = value.strip()
data["profile"] = prof
survey = dict(data.get("survey") or {})
survey[field] = value.strip()
data["survey"] = survey
self.save(buddy, data)
def format_for_prompt(self, buddy: str) -> str | None:
data = self.load(buddy)
lines: list[str] = []
prof = data.get("profile") or {}
filled = {k: v for k, v in prof.items() if v}
if filled:
bits = ", ".join(f"{k}={v}" for k, v in filled.items())
lines.append(f"Profile you already know: {bits}")
survey = data.get("survey") or {}
if survey:
lines.append("Survey answers: " + ", ".join(f"{k}={v}" for k, v in survey.items()))
facts = data.get("facts") or []
for item in facts[-20:]:
t = item.get("text", "").strip()
if t:
lines.append(f"- {t}")
if not lines:
return None
return (
"WHAT YOU REMEMBER ABOUT THIS USER (SmarterChild-style — use naturally, don't say 'stored in database'):\n"
+ "\n".join(lines)
)
def format_profile_display(self, buddy: str) -> str:
data = self.load(buddy)
prof = data.get("profile") or {}
lines = ["Here's what I know about you:"]
labels = {
"name": "Name",
"age": "Age",
"city": "City",
"zip": "Zip",
"band": "Favorite band",
"movie": "Favorite movie",
"color": "Favorite color",
"food": "Favorite food",
}
any_set = False
for key, label in labels.items():
val = (prof.get(key) or "").strip()
if val:
any_set = True
lines.append(f"- {label}: {val}")
facts = data.get("facts") or []
if facts:
any_set = True
lines.append("- Other stuff you told me:")
for item in facts[-8:]:
t = item.get("text", "").strip()
if t:
lines.append(f" · {t}")
notes = data.get("notes") or []
if notes:
any_set = True
lines.append(f"- Saved notes: {len(notes)} (type notes to list)")
if not any_set:
return (
"I don't know much about you yet! Type survey to answer a few fun questions, "
"or just tell me stuff and I'll remember."
)
lines.append("Type survey anytime to update, or profile setup to walk through questions.")
return " ".join(lines)