awacke1 commited on
Commit
b198154
·
verified ·
1 Parent(s): 564fd41

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +486 -445
app.py CHANGED
@@ -10,156 +10,126 @@ import time
10
  import hashlib
11
  from PIL import Image
12
  import glob
 
13
  from urllib.parse import quote
14
  import base64
15
  import io
16
- import streamlit.components.v1 as components
17
- import edge_tts
18
- from audio_recorder_streamlit import audio_recorder
19
- import nest_asyncio
20
 
21
- # Patch for nested async - sneaky fix! 🐍✨
22
- nest_asyncio.apply()
23
-
24
- # Static config - constants rule! 📏👑
25
  icons = '🤖🧠🔬📝'
26
  START_ROOM = "Sector 🌌"
27
 
28
- # Page setup - dressing up the window! 🖼️🎀
29
  st.set_page_config(
30
- page_title="🤖🧠MMO Chat Brain📝🔬",
31
  page_icon=icons,
32
  layout="wide",
33
  initial_sidebar_state="auto"
34
  )
35
 
36
- # Funky usernames - who’s who in the zoo! 🎭🐾
37
  FUN_USERNAMES = {
38
- "CosmicJester 🌌": "en-US-AriaNeural",
39
- "PixelPanda 🐼": "en-US-JennyNeural",
40
- "QuantumQuack 🦆": "en-GB-SoniaNeural",
41
- "StellarSquirrel 🐿️": "en-AU-NatashaNeural",
42
- "GizmoGuru ⚙️": "en-CA-ClaraNeural",
43
- "NebulaNinja 🌠": "en-US-GuyNeural",
44
- "ByteBuster 💾": "en-GB-RyanNeural",
45
- "GalacticGopher 🌍": "en-AU-WilliamNeural",
46
- "RocketRaccoon 🚀": "en-CA-LiamNeural",
47
- "EchoElf 🧝": "en-US-AriaNeural",
48
- "PhantomFox 🦊": "en-US-JennyNeural",
49
- "WittyWizard 🧙": "en-GB-SoniaNeural",
50
- "LunarLlama 🌙": "en-AU-NatashaNeural",
51
- "SolarSloth ☀️": "en-CA-ClaraNeural",
52
- "AstroAlpaca 🦙": "en-US-GuyNeural",
53
- "CyberCoyote 🐺": "en-GB-RyanNeural",
54
- "MysticMoose 🦌": "en-AU-WilliamNeural",
55
- "GlitchGnome 🧚": "en-CA-LiamNeural",
56
- "VortexViper 🐍": "en-US-AriaNeural",
57
- "ChronoChimp 🐒": "en-US-JennyNeural"
58
  }
59
 
60
- # Folders galore - organizing chaos! 📂🌀
61
  CHAT_DIR = "chat_logs"
62
  VOTE_DIR = "vote_logs"
63
  STATE_FILE = "user_state.txt"
64
- AUDIO_DIR = "audio_logs"
65
- HISTORY_DIR = "history_logs"
66
  os.makedirs(CHAT_DIR, exist_ok=True)
67
  os.makedirs(VOTE_DIR, exist_ok=True)
68
- os.makedirs(AUDIO_DIR, exist_ok=True)
69
- os.makedirs(HISTORY_DIR, exist_ok=True)
70
 
 
71
  CHAT_FILE = os.path.join(CHAT_DIR, "global_chat.md")
72
  QUOTE_VOTES_FILE = os.path.join(VOTE_DIR, "quote_votes.md")
73
- MEDIA_VOTES_FILE = os.path.join(VOTE_DIR, "media_votes.md")
74
- HISTORY_FILE = os.path.join(HISTORY_DIR, "chat_history.md")
75
 
76
- # Fancy digits - numbers got style! 🔢💃
77
- UNICODE_DIGITS = {i: f"{i}\uFE0F⃣" for i in range(10)}
 
 
 
78
 
79
- # Massive font collection - typography bonanza! 🖋️🎨
80
  UNICODE_FONTS = [
81
  ("Normal", lambda x: x),
82
  ("Bold", lambda x: "".join(chr(ord(c) + 0x1D400 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D41A - 0x61) if 'a' <= c <= 'z' else c for c in x)),
83
  ("Italic", lambda x: "".join(chr(ord(c) + 0x1D434 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D44E - 0x61) if 'a' <= c <= 'z' else c for c in x)),
84
- ("Bold Italic", lambda x: "".join(chr(ord(c) + 0x1D468 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D482 - 0x61) if 'a' <= c <= 'z' else c for c in x)),
85
  ("Script", lambda x: "".join(chr(ord(c) + 0x1D49C - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D4B6 - 0x61) if 'a' <= c <= 'z' else c for c in x)),
86
- ("Bold Script", lambda x: "".join(chr(ord(c) + 0x1D4D0 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D4EA - 0x61) if 'a' <= c <= 'z' else c for c in x)),
87
  ("Fraktur", lambda x: "".join(chr(ord(c) + 0x1D504 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D51E - 0x61) if 'a' <= c <= 'z' else c for c in x)),
88
- ("Bold Fraktur", lambda x: "".join(chr(ord(c) + 0x1D56C - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D586 - 0x61) if 'a' <= c <= 'z' else c for c in x)),
89
  ("Double Struck", lambda x: "".join(chr(ord(c) + 0x1D538 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D552 - 0x61) if 'a' <= c <= 'z' else c for c in x)),
90
  ("Sans Serif", lambda x: "".join(chr(ord(c) + 0x1D5A0 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D5BA - 0x61) if 'a' <= c <= 'z' else c for c in x)),
91
- ("Sans Serif Bold", lambda x: "".join(chr(ord(c) + 0x1D5D4 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D5EE - 0x61) if 'a' <= c <= 'z' else c for c in x)),
92
- ("Sans Serif Italic", lambda x: "".join(chr(ord(c) + 0x1D608 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D622 - 0x61) if 'a' <= c <= 'z' else c for c in x)),
93
- ("Sans Serif Bold Italic", lambda x: "".join(chr(ord(c) + 0x1D63C - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D656 - 0x61) if 'a' <= c <= 'z' else c for c in x)),
94
- ("Monospace", lambda x: "".join(chr(ord(c) + 0x1D670 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D68A - 0x61) if 'a' <= c <= 'z' else c for c in x)),
95
- ("Circled", lambda x: "".join(chr(ord(c) - 0x41 + 0x24B6) if 'A' <= c <= 'Z' else chr(ord(c) - 0x61 + 0x24D0) if 'a' <= c <= 'z' else c for c in x)),
96
- ("Squared", lambda x: "".join(chr(ord(c) - 0x41 + 0x1F130) if 'A' <= c <= 'Z' else c for c in x)),
97
- ("Negative Circled", lambda x: "".join(chr(ord(c) - 0x41 + 0x1F150) if 'A' <= c <= 'Z' else c for c in x)),
98
- ("Negative Squared", lambda x: "".join(chr(ord(c) - 0x41 + 0x1F170) if 'A' <= c <= 'Z' else c for c in x)),
99
- ("Regional Indicator", lambda x: "".join(chr(ord(c) - 0x41 + 0x1F1E6) if 'A' <= c <= 'Z' else c for c in x)),
100
  ]
101
 
102
- # Global state - keeping tabs! 🌍📋
103
- if 'server_running' not in st.session_state:
104
- st.session_state.server_running = False
105
- if 'server_task' not in st.session_state:
106
- st.session_state.server_task = None
107
- if 'active_connections' not in st.session_state:
108
- st.session_state.active_connections = {}
109
-
110
- # Timestamp wizardry - clock ticks with flair! ⏰🎩
111
- def format_timestamp_prefix():
112
- return datetime.now().strftime("%Y%m%d_%H%M%S")
113
 
114
- # Node naming - christening the beast! 🌐🍼
115
  def get_node_name():
 
116
  parser = argparse.ArgumentParser(description='Start a chat node with a specific name')
117
- parser.add_argument('--node-name', type=str, default=None)
118
- parser.add_argument('--port', type=int, default=8501)
119
  args = parser.parse_args()
120
- username = st.session_state.get('username', 'System 🌟')
121
- log_action(username, "🌐🍼 - Node naming - christening the beast!")
122
  return args.node_name or f"node-{uuid.uuid4().hex[:8]}", args.port
123
 
124
- # Action logger - spying on deeds! 🕵️📜
125
- def log_action(username, action):
126
- if 'action_log' not in st.session_state:
127
- st.session_state.action_log = {}
128
- user_log = st.session_state.action_log.setdefault(username, {})
129
- current_time = time.time()
130
- user_log = {k: v for k, v in user_log.items() if current_time - v < 10}
131
- st.session_state.action_log[username] = user_log
132
- if action not in user_log:
133
- with open(HISTORY_FILE, 'a') as f:
134
- f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {username}: {action}\n")
135
- user_log[action] = current_time
136
-
137
- # Chat saver - words locked tight! 💬🔒
138
- async def save_chat_entry(username, message):
139
- await asyncio.to_thread(log_action, username, "💬🔒 - Chat saver - words locked tight!")
140
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
141
  entry = f"[{timestamp}] {username}: {message}"
142
- await asyncio.to_thread(lambda: open(CHAT_FILE, 'a').write(f"{entry}\n"))
143
- voice = FUN_USERNAMES.get(username, "en-US-AriaNeural")
144
- audio_file = await async_edge_tts_generate(message, voice)
145
- if audio_file:
146
- with open(HISTORY_FILE, 'a') as f:
147
- f.write(f"[{timestamp}] {username}: Audio generated - {audio_file}\n")
148
-
149
- # Chat loader - history unleashed! 📜🚀
150
- async def load_chat():
151
- username = st.session_state.get('username', 'System 🌟')
152
- await asyncio.to_thread(log_action, username, "📜🚀 - Chat loader - history unleashed!")
153
  if not os.path.exists(CHAT_FILE):
154
- await asyncio.to_thread(lambda: open(CHAT_FILE, 'a').write(f"# {START_ROOM} Chat\n\nWelcome to the cosmic hub - start chatting! 🎤\n"))
155
- with open(CHAT_FILE, 'r') as f:
156
- content = await asyncio.to_thread(f.read)
157
- return content
158
-
159
- # User lister - who’s in the gang! 👥🎉
160
- async def get_user_list(chat_content):
161
- username = st.session_state.get('username', 'System 🌟')
162
- await asyncio.to_thread(log_action, username, "👥🎉 - User lister - who’s in the gang!")
 
 
 
 
 
 
163
  users = set()
164
  for line in chat_content.split('\n'):
165
  if line.strip() and ': ' in line:
@@ -167,380 +137,451 @@ async def get_user_list(chat_content):
167
  users.add(user)
168
  return sorted(list(users))
169
 
170
- # Join checker - been here before? 🚪🔍
171
- async def has_joined_before(client_id, chat_content):
172
- username = st.session_state.get('username', 'System 🌟')
173
- await asyncio.to_thread(log_action, username, "🚪🔍 - Join checker - been here before?")
174
- return any(f"Client-{client_id} has joined" in line for line in chat_content.split('\n'))
175
-
176
- # Suggestion maker - old quips resurface! 💡📝
177
- async def get_message_suggestions(chat_content, prefix):
178
- username = st.session_state.get('username', 'System 🌟')
179
- await asyncio.to_thread(log_action, username, "💡📝 - Suggestion maker - old quips resurface!")
180
  lines = chat_content.split('\n')
181
  messages = [line.split(': ', 1)[1] for line in lines if ': ' in line and line.strip()]
182
- return [msg for msg in messages if msg.lower().startswith(prefix.lower())][:5]
183
-
184
- # Vote saver - cheers recorded! 👍📊
185
- async def save_vote(file, item, user_hash, username, comment=""):
186
- await asyncio.to_thread(log_action, username, "👍📊 - Vote saver - cheers recorded!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
188
  entry = f"[{timestamp}] {user_hash} voted for {item}"
189
- await asyncio.to_thread(lambda: open(file, 'a').write(f"{entry}\n"))
190
- await asyncio.to_thread(lambda: open(HISTORY_FILE, "a").write(f"- {timestamp} - User {user_hash} voted for {item}\n"))
191
- chat_message = f"{username} upvoted: \"{item}\""
192
- if comment:
193
- chat_message += f" - {comment}"
194
- await save_chat_entry(username, chat_message)
195
-
196
- # Vote counter - tallying the love! 🏆📈
197
- async def load_votes(file):
198
- username = st.session_state.get('username', 'System 🌟')
199
- await asyncio.to_thread(log_action, username, "🏆📈 - Vote counter - tallying the love!")
 
 
200
  if not os.path.exists(file):
201
- await asyncio.to_thread(lambda: open(file, 'w').write("# Vote Tally\n\nNo votes yet - get clicking! 🖱️\n"))
202
- with open(file, 'r') as f:
203
- content = await asyncio.to_thread(f.read)
204
- lines = content.strip().split('\n')[2:] # Process content after reading
205
- votes = {}
206
- user_votes = set()
207
- for line in lines:
208
- if line.strip() and 'voted for' in line:
209
- user_hash = line.split('] ')[1].split(' voted for ')[0]
210
- item = line.split('voted for ')[1]
211
- vote_key = f"{user_hash}-{item}"
212
- if vote_key not in user_votes:
213
  votes[item] = votes.get(item, 0) + 1
214
- user_votes.add(vote_key)
215
- return votes
216
-
217
- # Hash generator - secret codes ahoy! 🔑🕵️
218
- async def generate_user_hash():
219
- username = st.session_state.get('username', 'System 🌟')
220
- await asyncio.to_thread(log_action, username, "🔑🕵️ - Hash generator - secret codes ahoy!")
 
221
  if 'user_hash' not in st.session_state:
222
- st.session_state.user_hash = hashlib.md5(str(random.getrandbits(128)).encode()).hexdigest()[:8]
223
- return st.session_state.user_hash
224
-
225
- # Audio maker - voices come alive! 🎶🌟
226
- async def async_edge_tts_generate(text, voice, rate=0, pitch=0, file_format="mp3"):
227
- username = st.session_state.get('username', 'System 🌟')
228
- await asyncio.to_thread(log_action, username, "🎶🌟 - Audio maker - voices come alive!")
229
- timestamp = format_timestamp_prefix()
230
- filename = os.path.join(AUDIO_DIR, f"audio_{timestamp}_{random.randint(1000, 9999)}.mp3")
231
- communicate = edge_tts.Communicate(text, voice, rate=f"{rate:+d}%", pitch=f"{pitch:+d}Hz")
232
- await communicate.save(filename)
233
- return filename if os.path.exists(filename) else None
234
-
235
- # Audio player - tunes blast off! 🔊🚀
236
- def play_and_download_audio(file_path):
237
- if file_path and os.path.exists(file_path):
238
- st.audio(file_path)
239
- with open(file_path, "rb") as f:
240
- b64 = base64.b64encode(f.read()).decode()
241
- dl_link = f'<a href="data:audio/mpeg;base64,{b64}" download="{os.path.basename(file_path)}">🎵 Download {os.path.basename(file_path)}</a>'
242
- st.markdown(dl_link, unsafe_allow_html=True)
243
-
244
- # Image saver - pics preserved! 📸💾
245
- async def save_pasted_image(image_data):
246
- username = st.session_state.get('username', 'System 🌟')
247
- await asyncio.to_thread(log_action, username, "📸💾 - Image saver - pics preserved!")
248
- timestamp = format_timestamp_prefix()
 
249
  filename = f"paste_{timestamp}.png"
250
- filepath = os.path.join('./', filename)
251
- if ',' in image_data:
252
- image_data = image_data.split(',')[1]
253
- img_bytes = base64.b64decode(image_data)
254
- img = Image.open(io.BytesIO(img_bytes))
255
- await asyncio.to_thread(img.save, filepath, "PNG")
256
- return filename
257
-
258
- # Video renderer - movies roll! 🎥🎬
259
- async def get_video_html(video_path, width="100%"):
260
- username = st.session_state.get('username', 'System 🌟')
261
- await asyncio.to_thread(log_action, username, "🎥🎬 - Video renderer - movies roll!")
262
- video_url = f"data:video/mp4;base64,{base64.b64encode(await asyncio.to_thread(open, video_path, 'rb').read()).decode()}"
263
- return f'<video width="{width}" controls autoplay muted loop><source src="{video_url}" type="video/mp4">Your browser does not support the video tag.</video>'
264
-
265
- # Audio renderer - sounds soar! 🎶✈️
266
- async def get_audio_html(audio_path, width="100%"):
267
- username = st.session_state.get('username', 'System 🌟')
268
- await asyncio.to_thread(log_action, username, "🎶✈️ - Audio renderer - sounds soar!")
269
- audio_url = f"data:audio/mpeg;base64,{base64.b64encode(await asyncio.to_thread(open, audio_path, 'rb').read()).decode()}"
270
- return f'<audio controls style="width: {width};"><source src="{audio_url}" type="audio/mpeg">Your browser does not support the audio element.</audio>'
271
-
272
- # Websocket handler - chat links up! 🌐🔗
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  async def websocket_handler(websocket, path):
274
- username = st.session_state.get('username', 'System 🌟')
275
- await asyncio.to_thread(log_action, username, "🌐🔗 - Websocket handler - chat links up!")
276
  try:
277
  client_id = str(uuid.uuid4())
278
- room_id = "chat"
279
- st.session_state.active_connections.setdefault(room_id, {})[client_id] = websocket
280
- chat_content = await load_chat()
281
  username = st.session_state.get('username', random.choice(list(FUN_USERNAMES.keys())))
282
- if not await has_joined_before(client_id, chat_content):
283
- await save_chat_entry(f"Client-{client_id}", f"{username} has joined {START_ROOM}!")
284
  async for message in websocket:
285
- parts = message.split('|', 1)
286
- if len(parts) == 2:
287
- username, content = parts
288
- await save_chat_entry(username, content)
289
- await broadcast_message(f"{username}|{content}", room_id)
 
 
 
 
290
  except websockets.ConnectionClosed:
291
- pass
 
292
  finally:
293
- if room_id in st.session_state.active_connections and client_id in st.session_state.active_connections[room_id]:
294
- del st.session_state.active_connections[room_id][client_id]
 
 
295
 
296
- # Message broadcaster - words fly far! 📢✈️
297
  async def broadcast_message(message, room_id):
298
- username = st.session_state.get('username', 'System 🌟')
299
- await asyncio.to_thread(log_action, username, "📢✈️ - Message broadcaster - words fly far!")
300
- if room_id in st.session_state.active_connections:
301
  disconnected = []
302
- for client_id, ws in st.session_state.active_connections[room_id].items():
303
  try:
304
  await ws.send(message)
305
  except websockets.ConnectionClosed:
306
  disconnected.append(client_id)
307
  for client_id in disconnected:
308
- del st.session_state.active_connections[room_id][client_id]
309
 
310
- # Server starter - web spins up! 🖥️🌀
311
  async def run_websocket_server():
312
- username = st.session_state.get('username', 'System 🌟')
313
- await asyncio.to_thread(log_action, username, "🖥️🌀 - Server starter - web spins up!")
314
- if not st.session_state.server_running:
315
  server = await websockets.serve(websocket_handler, '0.0.0.0', 8765)
316
- st.session_state.server_running = True
 
317
  await server.wait_closed()
318
 
319
- # Voice processor - speech to text! 🎤📝
320
- async def process_voice_input(audio_bytes):
321
- username = st.session_state.get('username', 'System 🌟')
322
- await asyncio.to_thread(log_action, username, "🎤📝 - Voice processor - speech to text!")
323
- if audio_bytes:
324
- text = "Voice input simulation" # Replace with actual speech-to-text logic
325
- await save_chat_entry(username, text)
326
-
327
- # Interface builder - UI takes shape! 🎨🖌️
328
- def create_streamlit_interface():
329
- # Run async tasks within an event loop
330
- loop = asyncio.new_event_loop()
331
- asyncio.set_event_loop(loop)
332
-
333
- async def async_interface():
334
- if 'username' not in st.session_state:
335
- chat_content = await load_chat()
336
- available_names = [name for name in FUN_USERNAMES if not any(f"{name} has joined" in line for line in chat_content.split('\n'))]
337
- st.session_state.username = random.choice(available_names) if available_names else random.choice(list(FUN_USERNAMES.keys()))
338
-
339
- if 'refresh_rate' not in st.session_state:
340
- st.session_state.refresh_rate = 5
341
- if 'timer_start' not in st.session_state:
342
- st.session_state.timer_start = time.time()
343
- if 'quote_line' not in st.session_state:
344
- st.session_state.quote_line = None
345
- if 'pasted_image_data' not in st.session_state:
346
- st.session_state.pasted_image_data = None
347
- if 'message_text' not in st.session_state:
348
- st.session_state.message_text = ""
349
- if 'audio_cache' not in st.session_state:
350
- st.session_state.audio_cache = {}
351
- if 'chat_history' not in st.session_state:
352
- st.session_state.chat_history = []
353
-
354
- st.markdown("""
355
- <style>
356
- .chat-box {font-family: monospace; background: #1e1e1e; color: #d4d4d4; padding: 10px; border-radius: 5px; height: 300px; overflow-y: auto;}
357
- .timer {font-size: 24px; color: #ffcc00; text-align: center; animation: pulse 1s infinite;}
358
- @keyframes pulse {0% {transform: scale(1);} 50% {transform: scale(1.1);} 100% {transform: scale(1);}}
359
- #paste-target {border: 2px dashed #ccc; padding: 20px; text-align: center; cursor: pointer;}
360
- </style>
361
- """, unsafe_allow_html=True)
362
-
363
- st.title(f"🤖🧠MMO {st.session_state.username}📝🔬")
364
- st.markdown(f"Welcome to {START_ROOM} - chat, vote, upload, paste images, and enjoy quoting! 🎉")
365
-
366
- # Start websocket server if not running
367
- if not st.session_state.server_task:
368
- st.session_state.server_task = loop.create_task(run_websocket_server())
369
-
370
- # Voice Input
371
- audio_bytes = audio_recorder()
372
- if audio_bytes:
373
- await process_voice_input(audio_bytes)
374
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
 
376
- # Chat Section
377
- st.subheader(f"{START_ROOM} Chat 💬")
378
- chat_content = await load_chat()
379
- chat_lines = chat_content.split('\n')
380
- chat_votes = await load_votes(QUOTE_VOTES_FILE)
381
- for i, line in enumerate(chat_lines):
382
- if line.strip() and ': ' in line:
383
- col1, col2, col3 = st.columns([4, 1, 1])
384
- with col1:
385
- st.markdown(line)
386
- username = line.split(': ')[1].split(' ')[0]
387
- audio_file = None
388
- cache_key = f"{line}_{FUN_USERNAMES.get(username, 'en-US-AriaNeural')}"
389
- if cache_key in st.session_state.audio_cache:
390
- audio_file = st.session_state.audio_cache[cache_key]
391
- else:
392
- audio_file = await async_edge_tts_generate(line.split(': ', 1)[1], FUN_USERNAMES.get(username, "en-US-AriaNeural"))
393
- st.session_state.audio_cache[cache_key] = audio_file
394
- if audio_file:
395
- play_and_download_audio(audio_file)
396
- with col2:
397
- vote_count = chat_votes.get(line.split('. ')[1] if '. ' in line else line, 0)
398
- if st.button(f"👍 {vote_count}", key=f"chat_vote_{i}"):
399
- comment = st.session_state.message_text
400
- await save_vote(QUOTE_VOTES_FILE, line.split('. ')[1] if '. ' in line else line, await generate_user_hash(), st.session_state.username, comment)
401
- if st.session_state.pasted_image_data:
402
- filename = await save_pasted_image(st.session_state.pasted_image_data)
403
- if filename:
404
- await save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
405
- st.session_state.pasted_image_data = None
406
- st.session_state.message_text = ''
407
- st.rerun()
408
- with col3:
409
- if st.button("📢 Quote", key=f"quote_{i}"):
410
- st.session_state.quote_line = line
411
- st.rerun()
412
-
413
- # Quoting Section
414
- if 'quote_line' in st.session_state:
415
- st.markdown(f"### Quoting: {st.session_state.quote_line}")
416
- quote_response = st.text_area("Add your response", key="quote_response")
417
- if st.button("Send Quote 🚀", key="send_quote"):
418
- async def process_quote():
419
- await asyncio.to_thread(log_action, st.session_state.username, "📢💬 - Quote processor - echoes resound!")
420
- markdown_response = f"### Quote Response\n- **Original**: {st.session_state.quote_line}\n- **{st.session_state.username} Replies**: {quote_response}"
421
- if st.session_state.pasted_image_data:
422
- filename = await save_pasted_image(st.session_state.pasted_image_data)
423
- if filename:
424
- markdown_response += f"\n- **Image**: ![Pasted Image]({filename})"
425
- st.session_state.pasted_image_data = None
426
- await save_chat_entry(st.session_state.username, markdown_response)
427
- loop.run_until_complete(process_quote())
428
- del st.session_state.quote_line
429
- st.session_state.message_text = ''
430
  st.rerun()
431
-
432
- # Username changer
433
- new_username = st.selectbox("Change Name", [""] + list(FUN_USERNAMES.keys()), index=0)
434
- if new_username and new_username != st.session_state.username:
435
- loop.run_until_complete(save_chat_entry("System 🌟", f"{st.session_state.username} changed name to {new_username}"))
436
- st.session_state.username = new_username
437
- st.rerun()
438
-
439
- # Message Input
440
- message = st.text_input(f"Message as {st.session_state.username}", key="message_input", value=st.session_state.message_text, on_change=lambda: st.session_state.update(message_text=st.session_state.message_input))
441
- if st.button("Send 🚀", key="send_button") and message.strip():
442
- loop.run_until_complete(save_chat_entry(st.session_state.username, message))
443
- if st.session_state.pasted_image_data:
444
- filename = loop.run_until_complete(save_pasted_image(st.session_state.pasted_image_data))
445
- if filename:
446
- loop.run_until_complete(save_chat_entry(st.session_state.username, f"Pasted image: {filename}"))
447
- st.session_state.pasted_image_data = None
448
- st.session_state.message_text = ''
449
  st.rerun()
450
-
451
- # Paste Target
452
- components.html(
453
- """
454
- <div id="paste-target">Paste an image here (Ctrl+V)</div>
455
- <script>
456
- const pasteTarget = document.getElementById('paste-target');
457
- pasteTarget.addEventListener('paste', (event) => {
458
- const items = (event.clipboardData || window.clipboardData).items;
459
- for (let i = 0; i < items.length; i++) {
460
- if (items[i].type.indexOf('image') !== -1) {
461
- const blob = items[i].getAsFile();
462
- const reader = new FileReader();
463
- reader.onload = (e) => {
464
- window.parent.postMessage({
465
- type: 'streamlit:setComponentValue',
466
- value: e.target.result
467
- }, '*');
468
- pasteTarget.innerHTML = '<p>Image pasted! Processing...</p>';
469
- };
470
- reader.readAsDataURL(blob);
471
- }
472
- }
473
- event.preventDefault();
474
- });
475
- </script>
476
- """,
477
- height=100
478
- )
479
-
480
- # Media Section
481
- st.subheader("Media Gallery 🎨🎶🎥")
482
- uploaded_file = st.file_uploader("Upload Media", type=['png', 'jpg', 'mp3', 'mp4'])
483
- if uploaded_file:
484
- file_path = os.path.join('./', uploaded_file.name)
485
- await asyncio.to_thread(lambda: open(file_path, 'wb').write(uploaded_file.getbuffer()))
486
- st.success(f"Uploaded {uploaded_file.name}")
487
-
488
- media_files = glob.glob("./*.png") + glob.glob("./*.jpg") + glob.glob("./*.mp3") + glob.glob("./*.mp4")
489
- if media_files:
490
- cols = st.columns(3)
491
- media_votes = loop.run_until_complete(load_votes(MEDIA_VOTES_FILE))
492
- for idx, media_file in enumerate(media_files):
493
- vote_count = media_votes.get(media_file, 0)
494
- if vote_count > 0:
495
- with cols[idx % 3]:
496
- if media_file.endswith(('.png', '.jpg')):
497
- st.image(media_file, use_container_width=True)
498
- elif media_file.endswith('.mp3'):
499
- st.markdown(loop.run_until_complete(get_audio_html(media_file)), unsafe_allow_html=True)
500
- elif media_file.endswith('.mp4'):
501
- st.markdown(loop.run_until_complete(get_video_html(media_file)), unsafe_allow_html=True)
502
- col1, col2 = st.columns(2)
503
- with col1:
504
- if st.button(f"👍 {vote_count}", key=f"media_vote_{idx}"):
505
- comment = st.session_state.message_text
506
- loop.run_until_complete(save_vote(MEDIA_VOTES_FILE, media_file, await generate_user_hash(), st.session_state.username, comment))
507
- if st.session_state.pasted_image_data:
508
- filename = loop.run_until_complete(save_pasted_image(st.session_state.pasted_image_data))
509
- if filename:
510
- loop.run_until_complete(save_chat_entry(st.session_state.username, f"Pasted image: {filename}"))
511
- st.session_state.pasted_image_data = None
512
- st.session_state.message_text = ''
513
- st.rerun()
514
- with col2:
515
- if st.button("🗑️", key=f"media_delete_{idx}"):
516
- await asyncio.to_thread(os.remove, media_file)
517
- st.rerun()
518
-
519
- # Refresh Timer
520
- st.subheader("Refresh ⏳")
521
- refresh_rate = st.slider("Refresh Rate", 1, 300, st.session_state.refresh_rate)
522
  st.session_state.refresh_rate = refresh_rate
523
- timer_placeholder = st.empty()
524
- for i in range(st.session_state.refresh_rate, -1, -1):
525
- font_name, font_func = random.choice(UNICODE_FONTS)
526
- countdown_str = "".join(UNICODE_DIGITS[int(d)] for d in str(i)) if i < 10 else font_func(str(i))
527
- timer_placeholder.markdown(f"<p class='timer'>⏳ {font_func('Refresh in:')} {countdown_str}</p>", unsafe_allow_html=True)
528
- loop.run_until_complete(asyncio.sleep(1))
529
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
 
531
- # Sidebar History
532
- st.sidebar.subheader("Chat History 📜")
533
- with open(HISTORY_FILE, 'r') as f:
534
- history_content = f.read()
535
- st.sidebar.markdown(history_content)
536
 
537
- # Run the async interface
538
- loop.run_until_complete(async_interface())
 
539
 
540
- # Main execution - let’s roll! 🎲🚀
541
- def main():
542
- NODE_NAME, port = get_node_name()
543
- create_streamlit_interface()
544
 
545
  if __name__ == "__main__":
546
- main()
 
10
  import hashlib
11
  from PIL import Image
12
  import glob
13
+ import re
14
  from urllib.parse import quote
15
  import base64
16
  import io
 
 
 
 
17
 
18
+ # App Configuration
19
+ Site_Name = '🤖🧠MMO Chat Brain📝🔬'
20
+ title = "🤖🧠MMO Chat Brain📝🔬"
 
21
  icons = '🤖🧠🔬📝'
22
  START_ROOM = "Sector 🌌"
23
 
 
24
  st.set_page_config(
25
+ page_title=title,
26
  page_icon=icons,
27
  layout="wide",
28
  initial_sidebar_state="auto"
29
  )
30
 
31
+ # Fun usernames with emojis and rhyming labels - the VIP list of quirky characters! 🎉😜
32
  FUN_USERNAMES = {
33
+ "CosmicJester 🌌": "Jest Best",
34
+ "PixelPanda 🐼": "Panda Dandy",
35
+ "QuantumQuack 🦆": "Quack Pack",
36
+ "StellarSquirrel 🐿️": "Squirrel Whirl",
37
+ "GizmoGuru ⚙️": "Guru Brew",
38
+ "NebulaNinja 🌠": "Ninja Zinger",
39
+ "ByteBuster 💾": "Buster Cluster",
40
+ "GalacticGopher 🌍": "Gopher Loafer",
41
+ "RocketRaccoon 🚀": "Raccoon Zoom",
42
+ "EchoElf 🧝": "Elf Shelf",
43
+ "PhantomFox 🦊": "Fox Rocks",
44
+ "WittyWizard 🧙": "Wizard Blizzard",
45
+ "LunarLlama 🌙": "Llama Drama",
46
+ "SolarSloth ☀️": "Sloth Growth",
47
+ "AstroAlpaca 🦙": "Alpaca Plaque",
48
+ "CyberCoyote 🐺": "Coyote Byte",
49
+ "MysticMoose 🦌": "Moose Juice",
50
+ "GlitchGnome 🧚": "Gnome Roam",
51
+ "VortexViper 🐍": "Viper Hyper",
52
+ "ChronoChimp 🐒": "Chimp Whimp"
53
  }
54
 
55
+ # Directories for chat and votes - the secret vaults where treasures are stashed! 🗄️🔒
56
  CHAT_DIR = "chat_logs"
57
  VOTE_DIR = "vote_logs"
58
  STATE_FILE = "user_state.txt"
 
 
59
  os.makedirs(CHAT_DIR, exist_ok=True)
60
  os.makedirs(VOTE_DIR, exist_ok=True)
 
 
61
 
62
+ # Persistent files - the grand tomes of chatter and votes! 📖✨
63
  CHAT_FILE = os.path.join(CHAT_DIR, "global_chat.md")
64
  QUOTE_VOTES_FILE = os.path.join(VOTE_DIR, "quote_votes.md")
65
+ IMAGE_VOTES_FILE = os.path.join(VOTE_DIR, "image_votes.md")
66
+ HISTORY_FILE = os.path.join(VOTE_DIR, "vote_history.md")
67
 
68
+ # Unicode digit conversion table - the magic map for emoji numbers! 🔢✨
69
+ UNICODE_DIGITS = {
70
+ 0: "0️⃣", 1: "1️⃣", 2: "2️⃣", 3: "3️⃣", 4: "4️⃣",
71
+ 5: "5️⃣", 6: "6️⃣", 7: "7️⃣", 8: "8️⃣", 9: "9️⃣"
72
+ }
73
 
74
+ # Unicode font examples - the stylish wardrobe of text flair! 👗📝
75
  UNICODE_FONTS = [
76
  ("Normal", lambda x: x),
77
  ("Bold", lambda x: "".join(chr(ord(c) + 0x1D400 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D41A - 0x61) if 'a' <= c <= 'z' else c for c in x)),
78
  ("Italic", lambda x: "".join(chr(ord(c) + 0x1D434 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D44E - 0x61) if 'a' <= c <= 'z' else c for c in x)),
 
79
  ("Script", lambda x: "".join(chr(ord(c) + 0x1D49C - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D4B6 - 0x61) if 'a' <= c <= 'z' else c for c in x)),
 
80
  ("Fraktur", lambda x: "".join(chr(ord(c) + 0x1D504 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D51E - 0x61) if 'a' <= c <= 'z' else c for c in x)),
 
81
  ("Double Struck", lambda x: "".join(chr(ord(c) + 0x1D538 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D552 - 0x61) if 'a' <= c <= 'z' else c for c in x)),
82
  ("Sans Serif", lambda x: "".join(chr(ord(c) + 0x1D5A0 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D5BA - 0x61) if 'a' <= c <= 'z' else c for c in x)),
83
+ ("Sans Bold", lambda x: "".join(chr(ord(c) + 0x1D5D4 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D5EE - 0x61) if 'a' <= c <= 'z' else c for c in x)),
84
+ ("Mono", lambda x: "".join(chr(ord(c) + 0x1D670 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x1D68A - 0x61) if 'a' <= c <= 'z' else c for c in x)),
85
+ ("Circle", lambda x: "".join(chr(ord(c) + 0x24B6 - 0x41) if 'A' <= c <= 'Z' else chr(ord(c) + 0x24D0 - 0x61) if 'a' <= c <= 'z' else c for c in x))
 
 
 
 
 
 
86
  ]
87
 
88
+ # Global WebSocket server flag
89
+ server_running = False
90
+ server_task = None
 
 
 
 
 
 
 
 
91
 
92
+ # Node name - the app’s codename generator, sneaky and slick! 🕵️‍♂️💾
93
  def get_node_name():
94
+ """🎲 Spins the wheel of fate to name our node - a random alias or user pick! 🏷️"""
95
  parser = argparse.ArgumentParser(description='Start a chat node with a specific name')
96
+ parser.add_argument('--node-name', type=str, default=None, help='Name for this chat node')
97
+ parser.add_argument('--port', type=int, default=8501, help='Port to run the Streamlit interface on')
98
  args = parser.parse_args()
 
 
99
  return args.node_name or f"node-{uuid.uuid4().hex[:8]}", args.port
100
 
101
+ # Chat saver - the scribe etching epic messages into the eternal scroll! 🖋️📜
102
+ def save_chat_entry(username, message):
103
+ """🖌️ Carves a chat line into the grand Markdown tome - history in the making! 🏛️"""
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
105
  entry = f"[{timestamp}] {username}: {message}"
106
+ try:
107
+ with open(CHAT_FILE, 'a') as f:
108
+ f.write(f"{entry}\n")
109
+ return True
110
+ except Exception as e:
111
+ print(f"Oops! Failed to save chat: {e}")
112
+ return False
113
+
114
+ # Chat loader - the archaeologist unearthing the chat saga! ⛏️📚
115
+ def load_chat():
116
+ """🔍 Digs up the chat treasure from the filesystem - tales of old and new! 💰"""
117
  if not os.path.exists(CHAT_FILE):
118
+ with open(CHAT_FILE, 'w') as f:
119
+ f.write(f"# {START_ROOM} Chat\n\nWelcome to the cosmic hub - start chatting! 🎤\n")
120
+ try:
121
+ with open(CHAT_FILE, 'r') as f:
122
+ content = f.read()
123
+ lines = content.strip().split('\n')
124
+ numbered_content = "\n".join(f"{i+1}. {line}" for i, line in enumerate(lines) if line.strip())
125
+ return numbered_content
126
+ except Exception as e:
127
+ print(f"Chat load hiccup: {e}")
128
+ return f"# Error loading {START_ROOM} chat\nSomething went wonky! 😵"
129
+
130
+ # User list grabber - the social butterfly spotting all the chatters! 🦋👥
131
+ def get_user_list(chat_content):
132
+ """👀 Peeks at the chat to spot all the cool cats talking - who’s in the club? 🎸"""
133
  users = set()
134
  for line in chat_content.split('\n'):
135
  if line.strip() and ': ' in line:
 
137
  users.add(user)
138
  return sorted(list(users))
139
 
140
+ # Message suggestion loader - the oracle of past chats! 🔮💬
141
+ def get_message_suggestions(chat_content, prefix):
142
+ """🔮 Pulls past messages from the cosmic archives for autocomplete magic! ✨"""
 
 
 
 
 
 
 
143
  lines = chat_content.split('\n')
144
  messages = [line.split(': ', 1)[1] for line in lines if ': ' in line and line.strip()]
145
+ return [msg for msg in messages if msg.lower().startswith(prefix.lower())][:5] # Top 5 matches
146
+
147
+ # Quote loader - the sage pulling wisdom from the ages! 📜🧙
148
+ def load_quotes(source="famous"):
149
+ """📚 Grabs a stack of wise words from famous folks or custom quips! 🗣️"""
150
+ famous_quotes = [
151
+ "The true sign of intelligence is not knowledge but imagination. – Albert Einstein",
152
+ "I have not failed. I've just found 10,000 ways that won't work. – Thomas Edison",
153
+ "Innovation distinguishes between a leader and a follower. – Steve Jobs",
154
+ "Research is what I'm doing when I don't know what I'm doing. – Wernher von Braun",
155
+ "The only way to discover the limits of the possible is to go beyond them into the impossible. – Arthur C. Clarke",
156
+ "Success is a science; if you have the conditions, you get the result. – Oscar Wilde",
157
+ "An expert is a person who has made all the mistakes that can be made in a very narrow field. – Niels Bohr",
158
+ "The important thing is to not stop questioning. Curiosity has its own reason for existing. – Albert Einstein",
159
+ "The best way to predict the future is to invent it. – Alan Kay",
160
+ "If I have seen further it is by standing on the shoulders of Giants. – Isaac Newton",
161
+ "Logic will get you from A to B. Imagination will take you everywhere. – Albert Einstein",
162
+ "Imagination is more important than knowledge. Knowledge is limited. Imagination encircles the world. – Albert Einstein",
163
+ "Science is a way of thinking much more than it is a body of knowledge. – Carl Sagan",
164
+ "We cannot solve our problems with the same thinking we used when we created them. – Albert Einstein",
165
+ "The true method of knowledge is experiment. – William Blake",
166
+ "The scientist is not a person who gives the right answers, he's one who asks the right questions. – Claude Levi-Strauss",
167
+ "It's kind of fun to do the impossible. – Walt Disney",
168
+ "Any sufficiently advanced technology is indistinguishable from magic. – Arthur C. Clarke",
169
+ "Creativity is intelligence having fun. – Albert Einstein",
170
+ "To invent, you need a good imagination and a pile of junk. – Thomas Edison"
171
+ ]
172
+ custom_quotes = [
173
+ "Every age unfolds a new lesson. Life's chapters evolve, each teaching us anew.",
174
+ "From infancy to twilight, our journey is painted in growth. Every stage shines with its own wisdom.",
175
+ "Love is the universal language, transcending boundaries and touching souls.",
176
+ "Through love, we find connection, unity, and the essence of existence."
177
+ ]
178
+ quotes = famous_quotes if source == "famous" else custom_quotes
179
+ return quotes if quotes else ["No quotes available - check back later! 📭"]
180
+
181
+ # Vote saver - the tally keeper counting thumbs up! 👍📊
182
+ def save_vote(file, item, user_hash):
183
+ """✍️ Tallies a vote in the grand ledger - your opinion matters! 🗳️"""
184
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
185
  entry = f"[{timestamp}] {user_hash} voted for {item}"
186
+ try:
187
+ with open(file, 'a') as f:
188
+ f.write(f"{entry}\n")
189
+ with open(HISTORY_FILE, 'a') as f:
190
+ f.write(f"- {timestamp} - User {user_hash} voted for {item}\n")
191
+ return True
192
+ except Exception as e:
193
+ print(f"Vote save flop: {e}")
194
+ return False
195
+
196
+ # Vote loader - the scorekeeper tallying the crowd’s cheers! 🎉🏅
197
+ def load_votes(file):
198
+ """📈 Counts the votes from the ledger - who’s winning the popularity contest? 🏆"""
199
  if not os.path.exists(file):
200
+ with open(file, 'w') as f:
201
+ f.write("# Vote Tally\n\nNo votes yet - get clicking! 🖱️\n")
202
+ try:
203
+ with open(file, 'r') as f:
204
+ lines = f.read().strip().split('\n')
205
+ votes = {}
206
+ for line in lines[2:]: # Skip header
207
+ if line.strip() and 'voted for' in line:
208
+ item = line.split('voted for ')[1]
 
 
 
209
  votes[item] = votes.get(item, 0) + 1
210
+ return votes
211
+ except Exception as e:
212
+ print(f"Vote load oopsie: {e}")
213
+ return {}
214
+
215
+ # User hash generator - the secret agent giving you a cool code! 🕵️‍♂️🔑
216
+ def generate_user_hash():
217
+ """🕵️ Crafts a snazzy 8-digit ID badge - you’re in the club now! 🎟️"""
218
  if 'user_hash' not in st.session_state:
219
+ session_id = str(random.getrandbits(128))
220
+ hash_object = hashlib.md5(session_id.encode())
221
+ st.session_state['user_hash'] = hash_object.hexdigest()[:8]
222
+ return st.session_state['user_hash']
223
+
224
+ # Username persistence - save and load across refreshes! 💾🔄
225
+ def save_username(username):
226
+ """💾 Stashes your cosmic alias before the refresh void claims it! 🌌"""
227
+ try:
228
+ with open(STATE_FILE, 'w') as f:
229
+ f.write(username)
230
+ except Exception as e:
231
+ print(f"Failed to save username: {e}")
232
+
233
+ def load_username():
234
+ """🔄 Resurrects your cosmic alias from the refresh abyss! 🌠"""
235
+ if os.path.exists(STATE_FILE):
236
+ try:
237
+ with open(STATE_FILE, 'r') as f:
238
+ return f.read().strip()
239
+ except Exception as e:
240
+ print(f"Failed to load username: {e}")
241
+ return None
242
+
243
+ # Image paste handler - the cosmic courier delivering pasted pics! 📸🚀
244
+ def save_pasted_image(image_data):
245
+ """📸 Saves a pasted image to the root vault with a cosmic name! 🌠"""
246
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
247
  filename = f"paste_{timestamp}.png"
248
+ filepath = os.path.join('./', filename) # Root directory
249
+ try:
250
+ if ',' in image_data:
251
+ image_data = image_data.split(',')[1]
252
+ img_bytes = base64.b64decode(image_data)
253
+ img = Image.open(io.BytesIO(img_bytes))
254
+ img.save(filepath, "PNG")
255
+ return filename
256
+ except Exception as e:
257
+ print(f"Failed to save pasted image: {e}")
258
+ return None
259
+
260
+ # Media HTML generators - IVA’s multimedia magic! 🎥🎶🖼️
261
+ def get_video_html(video_path, width="100%"):
262
+ """🎬 Rolls out the red carpet for videos - autoplay and loop like a star! 🌟"""
263
+ video_url = f"data:video/mp4;base64,{base64.b64encode(open(video_path, 'rb').read()).decode()}"
264
+ return f'''
265
+ <video width="{width}" controls autoplay muted loop>
266
+ <source src="{video_url}" type="video/mp4">
267
+ Your browser does not support the video tag.
268
+ </video>
269
+ '''
270
+
271
+ def get_audio_html(audio_path, width="100%"):
272
+ """🎶 Drops a beat with audio - your personal DJ in action! 🎧"""
273
+ audio_url = f"data:audio/mpeg;base64,{base64.b64encode(open(audio_path, 'rb').read()).decode()}"
274
+ return f'''
275
+ <audio controls style="width: {width};">
276
+ <source src="{audio_url}" type="audio/mpeg">
277
+ Your browser does not support the audio element.
278
+ </audio>
279
+ '''
280
+
281
+ active_connections = {}
282
+
283
+ # WebSocket handler - the bouncer at the Sector rave, keeping it hopping! 🎉🚪
284
  async def websocket_handler(websocket, path):
285
+ """🎧 Guards the cosmic gate, welcoming all to Sector and booting crashers! 🚨"""
 
286
  try:
287
  client_id = str(uuid.uuid4())
288
+ room_id = "chat" # Single room "Sector 🌌"
289
+ active_connections.setdefault(room_id, {})[client_id] = websocket
290
+ print(f"Client {client_id} joined the Sector party!")
291
  username = st.session_state.get('username', random.choice(list(FUN_USERNAMES.keys())))
292
+ save_chat_entry("System 🌟", f"{username} has joined {START_ROOM}!")
293
+
294
  async for message in websocket:
295
+ try:
296
+ parts = message.split('|', 1)
297
+ if len(parts) == 2:
298
+ username, content = parts
299
+ save_chat_entry(username, content)
300
+ await broadcast_message(f"{username}|{content}", room_id)
301
+ except Exception as e:
302
+ print(f"Message mishap: {e}")
303
+ await websocket.send(f"ERROR|Oops, bad message format! 😬")
304
  except websockets.ConnectionClosed:
305
+ print(f"Client {client_id} bailed from Sector!")
306
+ save_chat_entry("System 🌟", f"{username} has left {START_ROOM}!")
307
  finally:
308
+ if room_id in active_connections and client_id in active_connections[room_id]:
309
+ del active_connections[room_id][client_id]
310
+ if not active_connections[room_id]:
311
+ del active_connections[room_id]
312
 
313
+ # Broadcaster - the megaphone blasting Sector vibes to all! 📣🎶
314
  async def broadcast_message(message, room_id):
315
+ """📢 Shouts the latest Sector beat to every cosmic dancer - hear it loud! 🎵"""
316
+ if room_id in active_connections:
 
317
  disconnected = []
318
+ for client_id, ws in active_connections[room_id].items():
319
  try:
320
  await ws.send(message)
321
  except websockets.ConnectionClosed:
322
  disconnected.append(client_id)
323
  for client_id in disconnected:
324
+ del active_connections[room_id][client_id]
325
 
326
+ # WebSocket server runner - the DJ spinning up the Sector tunes once! 🎧🔥
327
  async def run_websocket_server():
328
+ """🌐 Cranks up the WebSocket jukebox once, keeping Sector rocking! 🎸"""
329
+ global server_running, server_task
330
+ if not server_running:
331
  server = await websockets.serve(websocket_handler, '0.0.0.0', 8765)
332
+ print(f"WebSocket server jamming on ws://0.0.0.0:8765")
333
+ server_running = True
334
  await server.wait_closed()
335
 
336
+ # Chat interface maker - the stage builder for our Sector extravaganza! 🎭🏟️
337
+ def create_streamlit_interface(initial_username):
338
+ """🖌️ Sets up the Sector stage with pulsing timers, voting, and IVA media flair! 🌟"""
339
+ # Custom CSS and JS for chat box, timer, and paste detection
340
+ st.markdown("""
341
+ <style>
342
+ .chat-box {
343
+ font-family: monospace;
344
+ background: #1e1e1e;
345
+ color: #d4d4d4;
346
+ padding: 10px;
347
+ border-radius: 5px;
348
+ height: 300px;
349
+ overflow-y: auto;
350
+ }
351
+ .timer {
352
+ font-size: 24px;
353
+ color: #ffcc00;
354
+ text-align: center;
355
+ animation: pulse 1s infinite;
356
+ }
357
+ @keyframes pulse {
358
+ 0% { transform: scale(1); }
359
+ 50% { transform: scale(1.1); }
360
+ 100% { transform: scale(1); }
361
+ }
362
+ </style>
363
+ <script>
364
+ document.addEventListener('paste', function(e) {
365
+ const items = (e.clipboardData || window.clipboardData).items;
366
+ for (let i = 0; i < items.length; i++) {
367
+ if (items[i].type.indexOf('image') !== -1) {
368
+ const blob = items[i].getAsFile();
369
+ const reader = new FileReader();
370
+ reader.onload = function(event) {
371
+ const imageData = event.target.result;
372
+ sessionStorage.setItem('pastedImage', imageData);
373
+ document.getElementById('message_input').value = 'PastedImage:' + blob.name;
374
+ document.getElementById('send_button').click();
375
+ };
376
+ reader.readAsDataURL(blob);
377
+ }
378
+ }
379
+ });
380
+ document.getElementById('message_input')?.addEventListener('keypress', function(e) {
381
+ if (e.key === 'Enter') {
382
+ document.getElementById('send_button').click();
383
+ }
384
+ });
385
+ </script>
386
+ """, unsafe_allow_html=True)
387
+
388
+ # Title and intro
389
+ st.title(f"{Site_Name}")
390
+ st.markdown(f"Welcome to {START_ROOM} - chat, vote, paste images, and enjoy IVA’s media magic! 🎉")
391
+
392
+ # Load or set username
393
+ saved_username = load_username()
394
+ if saved_username and saved_username in FUN_USERNAMES:
395
+ initial_username = saved_username
396
+ if 'username' not in st.session_state:
397
+ st.session_state.username = initial_username
398
+ save_chat_entry("System 🌟", f"{initial_username} has joined {START_ROOM}!")
399
+ save_username(st.session_state.username) # Persist before any refresh
400
+
401
+ # Session state for refresh rate and quote index
402
+ if 'refresh_rate' not in st.session_state:
403
+ st.session_state.refresh_rate = 5
404
+ if 'timer_start' not in st.session_state:
405
+ st.session_state.timer_start = time.time() # Autostart timer
406
+ if 'quote_index' not in st.session_state:
407
+ quotes = load_quotes("famous")
408
+ st.session_state.quote_index = random.randint(0, max(0, len(quotes) - 1)) if quotes else 0
409
+ if 'quote_source' not in st.session_state:
410
+ st.session_state.quote_source = "famous"
411
+ if 'pasted_image' not in st.session_state:
412
+ st.session_state.pasted_image = None
413
+
414
+ # Chat section
415
+ st.subheader(f"{START_ROOM} Chat 💬")
416
+ chat_content = load_chat()
417
+ chat_lines = chat_content.split('\n')
418
+ for i, line in enumerate(chat_lines):
419
+ if line.strip() and ': ' in line:
420
+ col1, col2 = st.columns([5, 1])
421
+ with col1:
422
+ st.markdown(line)
423
+ with col2:
424
+ if st.button(f"👍", key=f"chat_vote_{i}"):
425
+ user_hash = generate_user_hash()
426
+ save_vote(QUOTE_VOTES_FILE, line, user_hash)
427
+ st.session_state.timer_start = time.time()
428
+ save_username(st.session_state.username)
429
+ st.rerun()
430
+
431
+ user_list = get_user_list(chat_content)
432
+ new_username = st.selectbox("Switch Star", user_list + [st.session_state.username], index=len(user_list))
433
+ if new_username != st.session_state.username:
434
+ save_chat_entry("System 🌟", f"{st.session_state.username} switched to {new_username} in {START_ROOM}!")
435
+ st.session_state.username = new_username
436
+ st.session_state.timer_start = time.time()
437
+ save_username(st.session_state.username)
438
+
439
+ # Message input with search engine and paste handling
440
+ agent_icon = st.session_state.username.split()[-1] # Extract emoji
441
+ agent_label = FUN_USERNAMES.get(st.session_state.username, "Chatty Brat")
442
+ message = st.text_input(f"Message Mate {agent_icon} - {agent_label} 🔍📸", key="message_input", placeholder="Type or paste here...")
443
+ suggestions = get_message_suggestions(chat_content, message) if message else []
444
+ if suggestions:
445
+ suggestion = st.selectbox("Chat Snap", [""] + suggestions, index=0)
446
+ if suggestion:
447
+ message = suggestion
448
+ st.session_state.message_input = suggestion
449
+
450
+ col_send, _ = st.columns([1, 5])
451
+ with col_send:
452
+ send_button = st.button("Send Bend 🚀", key="send_button")
453
+ if send_button and message.strip():
454
+ if message.startswith("PastedImage:"):
455
+ # Handle pasted image
456
+ image_data = st.session_state.get('pasted_image')
457
+ if image_data:
458
+ filename = save_pasted_image(image_data)
459
+ if filename:
460
+ save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
461
+ st.session_state.pasted_image = None
462
+ else:
463
+ save_chat_entry(st.session_state.username, message)
464
+ st.session_state.timer_start = time.time()
465
+ save_username(st.session_state.username)
466
+ st.rerun()
467
 
468
+ # Quote section
469
+ st.subheader("Quote Note 📝")
470
+ quotes = load_quotes(st.session_state.quote_source)
471
+ if quotes:
472
+ st.session_state.quote_index = st.session_state.quote_index % len(quotes)
473
+ quote = quotes[st.session_state.quote_index]
474
+ col1, col2 = st.columns([5, 1])
475
+ with col1:
476
+ st.markdown(quote)
477
+ with col2:
478
+ if st.button("👍 Vote Float", key="quote_vote"):
479
+ user_hash = generate_user_hash()
480
+ save_vote(QUOTE_VOTES_FILE, quote, user_hash)
481
+ st.session_state.timer_start = time.time()
482
+ save_username(st.session_state.username)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483
  st.rerun()
484
+ if time.time() - st.session_state.timer_start > 10: # 10s quote refresh
485
+ st.session_state.quote_index = (st.session_state.quote_index + 1) % len(quotes)
486
+ st.session_state.quote_source = "custom" if st.session_state.quote_source == "famous" else "famous"
487
+ quotes = load_quotes(st.session_state.quote_source)
488
+ st.session_state.quote_index = st.session_state.quote_index % len(quotes) if quotes else 0
489
+ st.session_state.timer_start = time.time()
490
+ save_username(st.session_state.username)
 
 
 
 
 
 
 
 
 
 
 
491
  st.rerun()
492
+ else:
493
+ st.markdown("No quotes available - check back later! 📭")
494
+
495
+ # IVA Media Gallery - Root Directory
496
+ st.subheader("IVA Gallery 🎨🎶🎥")
497
+ media_files = (
498
+ glob.glob("./*.png") + glob.glob("./*.jpg") + glob.glob("./*.jpeg") +
499
+ glob.glob("./*.mp3") + glob.glob("./*.wav") +
500
+ glob.glob("./*.mp4")
501
+ )
502
+ if media_files:
503
+ media_cols = st.slider("Gallery Rally", min_value=1, max_value=5, value=1)
504
+ cols = st.columns(media_cols)
505
+ for idx, media_file in enumerate(media_files):
506
+ with cols[idx % media_cols]:
507
+ if media_file.endswith(('.png', '.jpg', '.jpeg')):
508
+ st.image(media_file, use_container_width=True)
509
+ elif media_file.endswith(('.mp3', '.wav')):
510
+ st.markdown(get_audio_html(media_file, width="100%"), unsafe_allow_html=True)
511
+ elif media_file.endswith('.mp4'):
512
+ st.markdown(get_video_html(media_file, width="100%"), unsafe_allow_html=True)
513
+ if st.button(f"👍 Vote Tote {os.path.basename(media_file)}", key=f"media_vote_{idx}"):
514
+ user_hash = generate_user_hash()
515
+ save_vote(IMAGE_VOTES_FILE, media_file, user_hash)
516
+ st.session_state.timer_start = time.time()
517
+ save_username(st.session_state.username)
518
+ st.rerun()
519
+ else:
520
+ st.error("No media files (images, audio, video) found in root directory!")
521
+
522
+ # Refresh rate controls with pulsing timer
523
+ st.subheader("Refresh Dash ")
524
+ refresh_rate = st.slider("Refresh Clash", min_value=1, max_value=300, value=st.session_state.refresh_rate, step=1)
525
+ if refresh_rate != st.session_state.refresh_rate:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  st.session_state.refresh_rate = refresh_rate
527
+ st.session_state.timer_start = time.time()
528
+ save_username(st.session_state.username)
529
+
530
+ col1, col2, col3 = st.columns(3)
531
+ with col1:
532
+ if st.button("🐇 Small Call (1s)"):
533
+ st.session_state.refresh_rate = 1
534
+ st.session_state.timer_start = time.time()
535
+ save_username(st.session_state.username)
536
+ with col2:
537
+ if st.button("🐢 Medium Fling (5s)"):
538
+ st.session_state.refresh_rate = 5
539
+ st.session_state.timer_start = time.time()
540
+ save_username(st.session_state.username)
541
+ with col3:
542
+ if st.button("🐘 Large Surge (5m)"):
543
+ st.session_state.refresh_rate = 300
544
+ st.session_state.timer_start = time.time()
545
+ save_username(st.session_state.username)
546
+
547
+ # Pulsing countdown timer with emoji digits and random Unicode font
548
+ timer_placeholder = st.empty()
549
+ start_time = st.session_state.timer_start
550
+ for i in range(st.session_state.refresh_rate, -1, -1):
551
+ font_name, font_func = random.choice(UNICODE_FONTS)
552
+ countdown_str = "".join(UNICODE_DIGITS[int(d)] for d in str(i)) if i < 10 else font_func(str(i))
553
+ timer_emoji = "⏳" if i % 2 == 0 else "💓" # Pulse effect
554
+ timer_placeholder.markdown(f"<p class='timer'>{timer_emoji} {font_func('Next refresh in:')} {countdown_str} {font_func('seconds')}</p>", unsafe_allow_html=True)
555
+ time.sleep(1) # Pulse every second
556
+ st.session_state.timer_start = time.time()
557
+ st.session_state.last_refresh = time.time()
558
+ save_username(st.session_state.username)
559
+ st.rerun()
560
+
561
+ # Sidebar vote stats
562
+ st.sidebar.subheader("Vote Loads")
563
+ chat_votes = load_votes(QUOTE_VOTES_FILE)
564
+ image_votes = load_votes(IMAGE_VOTES_FILE)
565
+ for item, count in chat_votes.items():
566
+ st.sidebar.write(f"{item}: {count} votes")
567
+ for image, count in image_votes.items():
568
+ st.sidebar.write(f"{image}: {count} votes")
569
+
570
+ # Main event - the ringmaster kicking off the Sector circus! 🎪🤡
571
+ async def main():
572
+ """🎤 Drops the mic and starts the Sector party - it’s showtime, folks! 🎉"""
573
+ global NODE_NAME, server_task
574
+ NODE_NAME, port = get_node_name()
575
 
576
+ # Start WebSocket server only once as a background task
577
+ if server_task is None:
578
+ server_task = asyncio.create_task(run_websocket_server())
 
 
579
 
580
+ query_params = st.query_params if hasattr(st, 'query_params') else {}
581
+ initial_username = query_params.get("username", random.choice(list(FUN_USERNAMES.keys()))) if query_params else random.choice(list(FUN_USERNAMES.keys()))
582
+ print(f"Welcoming {initial_username} to the Sector bash!")
583
 
584
+ create_streamlit_interface(initial_username)
 
 
 
585
 
586
  if __name__ == "__main__":
587
+ asyncio.run(main())