""" modules/birthday.py — FRIDAY Birthday & Date Tracker Remembers important dates: - Birthdays - Anniversaries - Special dates - Greets automatically """ import time import json import os from datetime import datetime from config import DATA_DIR BIRTHDAY_FILE = os.path.join(DATA_DIR, "birthdays.json") def _load() -> dict: if not os.path.exists(BIRTHDAY_FILE): return {"dates": []} try: with open(BIRTHDAY_FILE, "r") as f: return json.load(f) except Exception: return {"dates": []} def _save(data: dict): os.makedirs(DATA_DIR, exist_ok=True) with open(BIRTHDAY_FILE, "w") as f: json.dump(data, f, indent=2) # ── Add Date ───────────────────────────────────────────── def add_date(name: str, date: str, type: str = "birthday") -> str: """Add birthday/date.""" data = _load() data.setdefault("dates", []).append({ "name": name[:50], "date": date, # MM-DD or YYYY-MM-DD "type": type, "added": time.time(), }) _save(data) return f"Added {type} for {name}." def remove_date(name: str) -> str: """Remove date.""" data = _load() dates = [d for d in data.get("dates", []) if d.get("name", "").lower() != name.lower()] data["dates"] = dates _save(data) return f"Removed {name}." # ── Check Today ────────────────────────────────────────────── def check_today() -> list: """Check what's today.""" today = datetime.now() today_str = f"{today.month:02d}-{today.day:02d}" data = _load() celebrating = [] for d in data.get("dates", []): date_str = d.get("date", "") # Strip year if present if date_str and len(date_str) >= 5: date_check = date_str[-5:] # MM-DD if date_check == today_str: celebrating.append(d) return celebrating def get_upcoming(days: int = 7) -> list: """Get upcoming dates.""" data = _load() today = datetime.now() upcoming = [] for d in data.get("dates", []): date_str = d.get("date", "") if date_str and len(date_str) >= 5: month_day = date_str[-5:] month = int(month_day.split("-")[0]) day = int(month_day.split("-")[1]) # Simple comparison (ignoring year) if month >= today.month: if month == today.month and day >= today.day: upcoming.append(d) elif month > today.month: upcoming.append(d) return upcoming[:5] # ── Greeting ──────────────────────────────────────────── def get_greeting() -> str | None: """Get birthday greeting if today.""" today = check_today() if today: names = [d["name"] for d in today] if len(names) == 1: return f"HAPPY BIRTHDAY {names[0]}! 🎂" elif len(names) > 1: return f"HAPPY BIRTHDAY {', '.join(names[:-1])} and {names[-1]}! 🎂" return None # ── Voice Commands ──────────────────────────────��───────── def handle_command(command: str, speak) -> bool: """Handle birthday commands.""" c = command.lower() # Add birthday if "birthday" in c: if "on " in c: # Parse: birthday john on may 15 parts = c.split("on ", 1) name = parts[0].replace("birthday", "").strip() date = parts[1].strip() if len(parts) > 1 else "" # Simple parse to MM-DD if date: import re nums = re.findall(r"\d+", date) if len(nums) >= 2: date_str = f"{int(nums[0]):02d}-{int(nums[1]):02d}" msg = add_date(name, date_str) speak(msg) return True elif len(nums) == 1 and int(nums[0]) <= 12: date_str = f"{int(nums[0]):02d}-15" msg = add_date(name, date_str) speak(msg) return True # Check today if "today" in c and "birthday" in c: greeting = get_greeting() if greeting: speak(greeting) else: speak("No birthdays today.") return True # Upcoming birthdays if "upcoming" in c or "next" in c: upcoming = get_upcoming() if upcoming: dates = [f"{d['name']}: {d['date']}" for d in upcoming] speak(f"Upcoming: {', '.join(dates)}") else: speak("No upcoming birthdays.") return True return False