import json import time from pathlib import Path from typing import Dict, Any, Optional # Prosty storage w pliku JSON (działa w Space tak długo, jak instancja żyje) STORAGE_PATH = Path(__file__).resolve().parent.parent / "storage" / "habits.json" STORAGE_PATH.parent.mkdir(parents=True, exist_ok=True) # Opcjonalne: zaawansowany pipeline z reels-agent (transkrypcja, summary, Nebius) try: from . import transcriber, summarizer # type: ignore except Exception: transcriber = None summarizer = None def _load_db() -> Dict[str, Any]: if not STORAGE_PATH.exists(): return {} try: with STORAGE_PATH.open("r", encoding="utf-8") as f: return json.load(f) except Exception: return {} def _save_db(db: Dict[str, Any]) -> None: with STORAGE_PATH.open("w", encoding="utf-8") as f: json.dump(db, f, ensure_ascii=False, indent=2) def _build_initial_step(link: str, category: str) -> str: """ Tu próbujemy użyć starego pipeline'u (reels-agent): - transkrypcja - summary → konkretna akcja Jeśli te funkcje nie istnieją / wywalą się → fallback na prosty tekst. """ # Fallback – zawsze działa fallback = "Obejrzyj materiał i zapisz 1 konkretną rzecz, którą dziś wdrożysz." # Jeśli nie ma modułów – wracamy fallback if transcriber is None or summarizer is None: return fallback try: # PRZYKŁADOWA KONWENCJA – dopasujesz w razie czego u siebie: # transcriber.transcribe_link(link) -> str (pełna transkrypcja) # summarizer.summarize_to_action(text, category) -> str (krótka akcja) if hasattr(transcriber, "transcribe_link") and hasattr(summarizer, "summarize_to_action"): text = transcriber.transcribe_link(link) # type: ignore[attr-defined] action = summarizer.summarize_to_action(text, category) # type: ignore[attr-defined] if isinstance(action, str) and action.strip(): return action.strip() except Exception: # Nie zabijamy Space’a – po prostu wracamy prostą wersję return fallback return fallback def create_contract(uid: str, link: str, category: str) -> Dict[str, Any]: """ Tworzy / nadpisuje kontrakt nawyku: - uid: ID usera (np. szef_1_career) - link: IG/YT/TikTok - category: 'career' / 'health' / 'relationships' / ... Zwraca dict gotowy do wysłania jako JSON przez MCP. """ db = _load_db() initial_step = _build_initial_step(link, category) contract = { "uid": uid, "link": link, "category": category, "created_at": int(time.time()), "streak": 0, "last_done_at": None, "next_step": initial_step, "history": [], # lista wykonanych kroków } db[uid] = contract _save_db(db) return contract def get_contract(uid: str) -> Optional[Dict[str, Any]]: db = _load_db() return db.get(uid) def complete_step(uid: str, note: Optional[str] = None) -> Dict[str, Any]: """ User zrobił zadanie → zwiększamy streak, dopisujemy do historii i generujemy kolejny krok (na razie prosty). """ db = _load_db() if uid not in db: raise ValueError(f"Contract with uid={uid} not found") contract = db[uid] now = int(time.time()) # aktualizacja streak contract["streak"] = int(contract.get("streak") or 0) + 1 contract["last_done_at"] = now # dopisanie historii contract.setdefault("history", []) contract["history"].append( { "ts": now, "done_step": contract.get("next_step"), "note": note, } ) # nowy krok - tutaj możesz później wpiąć bardziej zaawansowany plan_generator link = contract.get("link", "") category = contract.get("category", "general") contract["next_step"] = _build_initial_step(link, category) db[uid] = contract _save_db(db) return contract def get_next_step(uid: str) -> Dict[str, Any]: """ Zwraca aktualny kontrakt i next_step dla danego uid. """ contract = get_contract(uid) if not contract: raise ValueError(f"Contract with uid={uid} not found") return { "uid": contract["uid"], "link": contract["link"], "category": contract["category"], "streak": contract.get("streak", 0), "next_step": contract.get("next_step"), "last_done_at": contract.get("last_done_at"), }