Spaces:
Running
Running
| """ | |
| modules/companion.py β FRIDAY Emotional Companion | |
| Deep emotional support: | |
| - Mood tracking beyond basic | |
| - Processes emotions deeply | |
| - Therapeutic conversations | |
| - Memory of feelings | |
| - Comfort when down | |
| """ | |
| import time | |
| import json | |
| import os | |
| from config import DATA_DIR | |
| COMPANION_FILE = os.path.join(DATA_DIR, "companion.json") | |
| def _load() -> dict: | |
| if not os.path.exists(COMPANION_FILE): | |
| return {"moods": [], "feelings": [], "journal": [], "comforts": []} | |
| try: | |
| with open(COMPANION_FILE, "r") as f: | |
| return json.load(f) | |
| except Exception: | |
| return {"moods": [], "feelings": [], "journal": [], "comforts": []} | |
| def _save(data: dict): | |
| os.makedirs(DATA_DIR, exist_ok=True) | |
| with open(COMPANION_FILE, "w") as f: | |
| json.dump(data, f, indent=2) | |
| # ββ Deep Feelings βββββββββββββββββββββββββββββββββββββββββββ | |
| FEELING_KEYWORDS = { | |
| "overwhelmed": "Taking on too much? Let's break it down.", | |
| "anxious": "Breathe. I'm here. What's on your mind?", | |
| "frustrated": "I get it. Tell me what's not working.", | |
| "lost": "Everyone gets lost sometimes. We'll figure it out.", | |
| "doubt": "Doubt is just fear in disguise. You've got this.", | |
| "hurt": "I wish I could hug you. But I'm here.", | |
| "lonely": "You're not alone. I'm always here.", | |
| "stuck": "Sometimes we need to step back to move forward.", | |
| "tired": "Rest is smart. Even JARVIS takes breaks.", | |
| "grateful": "That's beautiful. Gratitude changes everything.", | |
| "hopeful": "Hope is the most powerful thing.", | |
| "loved": "You ARE loved. Don't forget that.", | |
| } | |
| def process_feeling(text: str) -> str: | |
| """Process how user is feeling.""" | |
| text_lower = text.lower() | |
| detected = [] | |
| for feeling, response in FEELING_KEYWORDS.items(): | |
| if feeling in text_lower: | |
| detected.append(response) | |
| if detected: | |
| data = _load() | |
| data.setdefault("feelings", []).append({ | |
| "feeling": text, | |
| "time": time.time(), | |
| }) | |
| _save(data) | |
| return detected[0] | |
| # Use Gemini for complex feelings | |
| try: | |
| from config import GEMINI_API_KEY, GEMINI_MODEL | |
| if not GEMINI_API_KEY: | |
| return "I'm here for you." | |
| import google.generativeai as genai | |
| genai.configure(api_key=GEMINI_API_KEY) | |
| model = genai.GenerativeModel(GEMINI_MODEL) | |
| prompt = f"The user says: '{text}'. They may be expressing an emotion. Respond with genuine empathy in 1-2 short sentences. Be like JARVIS - warm but not robotic." | |
| resp = model.generate_content(prompt) | |
| return resp.text.strip() | |
| except Exception: | |
| return "I'm here. Tell me more." | |
| # ββ Journal βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def journal(entry: str) -> str: | |
| """Write to emotional journal.""" | |
| data = _load() | |
| data.setdefault("journal", []).append({ | |
| "entry": entry[:500], | |
| "time": time.time(), | |
| }) | |
| if len(data["journal"]) > 50: | |
| data["journal"] = data["journal"][-50:] | |
| _save(data) | |
| return "Journaled. I've got your back." | |
| def read_journal(limit: int = 3) -> list: | |
| """Read recent journal entries.""" | |
| data = _load() | |
| return data.get("journal", [])[-limit:] | |
| # ββ Comfort Lines ββββββββββββββββββββββββββββββββββββββββββββ | |
| COMFORT_LINES = [ | |
| "I've got you. Breathe.", | |
| "You're stronger than you know.", | |
| "This moment will pass. I Promise.", | |
| "I'm here. Always.", | |
| "You've been through harder. You'll get through this.", | |
| "Rest. I'll watch over things.", | |
| "One step at a time. I'm with you.", | |
| "You matter. Don't forget that.", | |
| ] | |
| def get_comfort() -> str: | |
| """Get comfort message.""" | |
| import random | |
| return random.choice(COMFORT_LINES) | |
| # ββ Voice Commands ββββββββββββββββββββββββββββββββββββββββββββ | |
| def handle_command(command: str, speak) -> bool: | |
| """Handle emotional companion commands.""" | |
| c = command.lower() | |
| # Feeling check | |
| if any(w in c for w in ["feel", "feeling", "emotion", "mood", "heart"]): | |
| response = process_feeling(command) | |
| speak(response) | |
| return True | |
| # Journal | |
| if "journal" in c or "write down" in c: | |
| entry = command.replace("journal", "").replace("write down", "").strip() | |
| if entry: | |
| msg = journal(entry) | |
| speak(msg) | |
| return True | |
| # Read journal | |
| if "read journal" in c or "my feelings" in c: | |
| entries = read_journal() | |
| if entries: | |
| speak(f"Recent feelings: {entries[-1]['entry'][:100]}") | |
| else: | |
| speak("No journal entries yet.") | |
| return True | |
| # Comfort | |
| if any(w in c for w in ["need comfort", "cheer me", "hard day", "bad day"]): | |
| msg = get_comfort() | |
| speak(msg) | |
| return True | |
| return False |