""" db_introductions.py — tracks which characters a user has already met. Used to give a one-line self-introduction the first time a user speaks with a character (e.g. Camus greeting before his first reply). After the first turn the character never re-introduces itself. Storage: `user_profiles.met_characters text[]` (run characters/shared/users_met_characters.sql). Keyed by id = user_id. It lives on user_profiles because that is the table that actually exists and is keyed by the user id (there is no public.users — auth users live in auth.users). It is deliberately kept OUT of TRACKED_FIELDS so the profile classifier never reads or overwrites it; we access it with targeted queries here. """ from db_user import supabase def _get_met(user_id: str) -> list: res = ( supabase.table("user_profiles") .select("met_characters") .eq("id", user_id) .limit(1) .execute() ) rows = res.data or [] return (rows[0].get("met_characters") if rows else None) or [] def has_met(user_id: str, character_id: str) -> bool: """True if the user has already been introduced to this character. Fails safe to True: on any error we assume they've met, so we never crash a turn and never risk re-greeting on every message if the column is missing. """ try: return character_id in _get_met(user_id) except Exception as e: print(f"[introductions] has_met failed for {user_id}/{character_id}: {e}") return True def mark_met(user_id: str, character_id: str) -> None: """Record that the user has now been introduced to this character. Upsert (not update) so it also persists for a brand-new user whose user_profiles row does not exist yet — other columns are left untouched. """ try: met = _get_met(user_id) if character_id in met: return supabase.table("user_profiles").upsert( {"id": user_id, "met_characters": met + [character_id]} ).execute() except Exception as e: print(f"[introductions] mark_met failed for {user_id}/{character_id}: {e}")