import threading import time from datetime import datetime, timedelta, timezone from models.state import ( messages, room_users, room_passwords, authorized_room_users, room_owners, kicked_users, user_sessions, user_ips, video_state, ) def cleanup_loop() -> None: """Background loop that periodically cleans expired messages, inactive users, and empty rooms.""" while True: now = datetime.now(timezone.utc) # 1. Remove chat messages older than 1 hour message_cutoff = now - timedelta(hours=1) for room, room_messages in list(messages.items()): messages[room] = [ msg for msg in room_messages if datetime.fromisoformat(msg["timestamp"]) > message_cutoff ] # 2. Remove inactive users from rooms (after 1 min of silence) inactive_cutoff = now - timedelta(minutes=1) for room in list(room_users.keys()): room_users[room] = { user: ts for user, ts in room_users[room].items() if datetime.fromisoformat(ts) > inactive_cutoff } # 3. Clean up on-demand rooms that have become empty # Rooms exist strictly while active; once empty for over 1 minute, everything vanishes. for room in list(room_users.keys()): if not room_users[room]: messages.pop(room, None) room_users.pop(room, None) room_passwords.pop(room, None) authorized_room_users.pop(room, None) room_owners.pop(room, None) kicked_users.pop(room, None) video_state.pop(room, None) # Also purge any dangling rooms in room_passwords that have no users for room in list(room_passwords.keys()): if room not in room_users or not room_users[room]: messages.pop(room, None) room_users.pop(room, None) room_passwords.pop(room, None) authorized_room_users.pop(room, None) room_owners.pop(room, None) kicked_users.pop(room, None) video_state.pop(room, None) # 4. Remove stale user sessions (after 3 days) user_cutoff = now - timedelta(days=3) for user, session in list(user_sessions.items()): if datetime.fromisoformat(session["last_active"]) < user_cutoff: user_sessions.pop(user, None) user_ips.pop(user, None) time.sleep(60) _cleanup_thread_started = False def start_cleanup_thread() -> None: """Starts daemon thread for ephemeral resource cleanup.""" global _cleanup_thread_started if _cleanup_thread_started: return _cleanup_thread_started = True thread = threading.Thread(target=cleanup_loop, daemon=True) thread.start()