jarvis-cloud / modules /companion.py
Jarvis2345's picture
Squash history β€” remove all prior commits (secret hygiene, S4)
a31f556
Raw
History Blame
5.37 kB
"""
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