File size: 4,527 Bytes
a98fb2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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"),
    }