import streamlit as st import asyncio import websockets import uuid import argparse from datetime import datetime import os import random import time # Fun usernames with emojis - the VIP list of quirky characters! πŸŽ‰πŸ˜œ FUN_USERNAMES = [ "CosmicJester 🌌", "PixelPanda 🐼", "QuantumQuack πŸ¦†", "StellarSquirrel 🐿️", "GizmoGuru βš™οΈ", "NebulaNinja 🌠", "ByteBuster πŸ’Ύ", "GalacticGopher 🌍", "RocketRaccoon πŸš€", "EchoElf 🧝", "PhantomFox 🦊", "WittyWizard πŸ§™", "LunarLlama πŸŒ™", "SolarSloth β˜€οΈ", "AstroAlpaca πŸ¦™", "CyberCoyote 🐺", "MysticMoose 🦌", "GlitchGnome 🧚", "VortexViper 🐍", "ChronoChimp πŸ’" ] # Directory for chat logs - the secret vault where tales are stashed! πŸ—„οΈπŸ”’ CHAT_DIR = "chat_logs" os.makedirs(CHAT_DIR, exist_ok=True) # Persistent chat file - the grand tome of all chatter! πŸ“–βœ¨ CHAT_FILE = os.path.join(CHAT_DIR, "global_chat.md") # Node name - the app’s codename generator, sneaky and slick! πŸ•΅οΈβ€β™‚οΈπŸ’Ύ def get_node_name(): """🎲 Spins the wheel of fate to name our node - a random alias or user pick! 🏷️""" parser = argparse.ArgumentParser(description='Start a chat node with a specific name') parser.add_argument('--node-name', type=str, default=None, help='Name for this chat node') parser.add_argument('--port', type=int, default=8501, help='Port to run the Streamlit interface on') # Default Streamlit port args = parser.parse_args() return args.node_name or f"node-{uuid.uuid4().hex[:8]}", args.port # Chat saver - the scribe etching epic messages into the eternal scroll! πŸ–‹οΈπŸ“œ def save_chat_entry(username, message): """πŸ–ŒοΈ Carves a chat line into the grand Markdown tome - history in the making! πŸ›οΈ""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") entry = f"[{timestamp}] {username}: {message}" try: with open(CHAT_FILE, 'a') as f: f.write(f"{entry}\n") return True except Exception as e: print(f"Oops! Failed to save chat: {e}") return False # Chat loader - the archaeologist unearthing the chat saga! β›οΈπŸ“š def load_chat(): """πŸ” Digs up the chat treasure from the filesystem - tales of old and new! πŸ’°""" if not os.path.exists(CHAT_FILE): with open(CHAT_FILE, 'w') as f: f.write("# Global Chat\n\nNo messages yet - start chatting! 🎀\n") try: with open(CHAT_FILE, 'r') as f: content = f.read() lines = content.strip().split('\n') numbered_content = "\n".join(f"{i+1}. {line}" for i, line in enumerate(lines) if line.strip()) return numbered_content except Exception as e: print(f"Chat load hiccup: {e}") return "# Error loading chat\nSomething went wonky! 😡" # User list grabber - the social butterfly spotting all the chatters! πŸ¦‹πŸ‘₯ def get_user_list(chat_content): """πŸ‘€ Peeks at the chat to spot all the cool cats talking - who’s in the club? 🎸""" users = set() for line in chat_content.split('\n'): if line.strip() and ': ' in line: user = line.split(': ')[1].split(' ')[0] users.add(user) return sorted(list(users)) active_connections = {} # WebSocket handler - the bouncer at the chat rave, keeping it hopping! πŸŽ‰πŸšͺ async def websocket_handler(websocket, path): """🎧 Guards the chat gate, letting messages fly and booting crashers! 🚨""" try: client_id = str(uuid.uuid4()) room_id = "chat" active_connections.setdefault(room_id, {})[client_id] = websocket print(f"Client {client_id} joined the chat party!") async for message in websocket: try: parts = message.split('|', 1) if len(parts) == 2: username, content = parts save_chat_entry(username, content) await broadcast_message(f"{username}|{content}", room_id) except Exception as e: print(f"Message mishap: {e}") await websocket.send(f"ERROR|Oops, bad message format! 😬") except websockets.ConnectionClosed: print(f"Client {client_id} bailed from the chat!") finally: if room_id in active_connections and client_id in active_connections[room_id]: del active_connections[room_id][client_id] if not active_connections[room_id]: del active_connections[room_id] # Broadcaster - the megaphone blasting chat vibes to all! πŸ“£πŸŽΆ async def broadcast_message(message, room_id): """πŸ“’ Shouts the latest chat beat to every dancer in the room - hear it loud! 🎡""" if room_id in active_connections: disconnected = [] for client_id, ws in active_connections[room_id].items(): try: await ws.send(message) except websockets.ConnectionClosed: disconnected.append(client_id) for client_id in disconnected: del active_connections[room_id][client_id] # WebSocket starter - the DJ spinning up the chat tunes! 🎧πŸ”₯ async def start_websocket_server(host='0.0.0.0', port=8765): """🌐 Cranks up the WebSocket jukebox, ready to rock the chat scene! 🎸""" server = await websockets.serve(websocket_handler, host, port) print(f"WebSocket server jamming on ws://{host}:{port}") return server # Chat interface maker - the stage builder for our chat extravaganza! 🎭🏟️ def create_streamlit_interface(initial_username): """πŸ–ŒοΈ Sets up the chat stage with live updates and name-switching flair! 🌟""" # Custom CSS for a sleek chat box st.markdown(""" """, unsafe_allow_html=True) # Title and intro st.title(f"Chat Node: {NODE_NAME}") st.markdown("Chat live, switch names, and keep the party going! πŸŽ‰") # Session state to persist username if 'username' not in st.session_state: st.session_state.username = initial_username # Chat display chat_content = load_chat() st.markdown(f"
{chat_content}
", unsafe_allow_html=True) # User switcher user_list = get_user_list(chat_content) new_username = st.selectbox("Switch User", user_list + [st.session_state.username], index=len(user_list)) if new_username != st.session_state.username: st.session_state.username = new_username # Message input and send message = st.text_input("Message", placeholder="Type your epic line here! ✍️") if st.button("Send πŸš€") and message.strip(): save_chat_entry(st.session_state.username, message) st.rerun() # Trigger a rerun to refresh the chat # Auto-refresh every second for live updates st.markdown("", unsafe_allow_html=True) # Main event - the ringmaster kicking off the chat circus! πŸŽͺ🀑 async def main(): """🎀 Drops the mic and starts the chat party - it’s showtime, folks! πŸŽ‰""" global NODE_NAME NODE_NAME, port = get_node_name() await start_websocket_server() # Grab username from URL or roll the dice! 🎲 query_params = st.query_params if hasattr(st, 'query_params') else {} # Streamlit 1.20+ syntax initial_username = query_params.get("username", random.choice(FUN_USERNAMES)) if query_params else random.choice(FUN_USERNAMES) print(f"Welcoming {initial_username} to the chat bash!") create_streamlit_interface(initial_username) if __name__ == "__main__": asyncio.run(main())