403Webshell
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 :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /opt/smarterchild//user_store.py
"""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)

Youez - 2016 - github.com/yon3zu
LinuXploit