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') 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, timers, and refresh rate flair! 🌟""" # Custom CSS for a sleek chat box and timer st.markdown(""" """, unsafe_allow_html=True) # Title and intro st.title(f"Chat Node: {NODE_NAME}") st.markdown("Chat live, switch names, and tweak the refresh rate! πŸŽ‰") # Session state for username and refresh rate if 'username' not in st.session_state: st.session_state.username = initial_username if 'refresh_rate' not in st.session_state: st.session_state.refresh_rate = 5 # Default 5 seconds if 'last_refresh' not in st.session_state: st.session_state.last_refresh = time.time() # 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.session_state.last_refresh = time.time() # Reset timer on send st.rerun() # Refresh rate controls st.subheader("Set Refresh Rate ⏳") refresh_rate = st.slider( "Refresh Rate (seconds)", min_value=1, max_value=300, # 5 minutes = 300 seconds value=st.session_state.refresh_rate, step=1 ) st.session_state.refresh_rate = refresh_rate # Emoji buttons for quick settings col1, col2, col3 = st.columns(3) with col1: if st.button("πŸ‡ Small (1s)"): st.session_state.refresh_rate = 1 st.session_state.last_refresh = time.time() st.rerun() with col2: if st.button("🐒 Medium (5s)"): st.session_state.refresh_rate = 5 st.session_state.last_refresh = time.time() st.rerun() with col3: if st.button("🐘 Large (5m)"): st.session_state.refresh_rate = 300 st.session_state.last_refresh = time.time() st.rerun() # Countdown timer elapsed = time.time() - st.session_state.last_refresh remaining = max(0, st.session_state.refresh_rate - int(elapsed)) st.markdown(f"

Next refresh in: {remaining} seconds

", unsafe_allow_html=True) # Auto-refresh logic if elapsed >= st.session_state.refresh_rate: st.session_state.last_refresh = time.time() st.rerun() else: st.markdown(f"", 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 {} 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())