""" 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