awacke1 commited on
Commit
1116e85
Β·
verified Β·
1 Parent(s): ef89835

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +223 -215
app.py CHANGED
@@ -99,8 +99,13 @@ UNICODE_FONTS = [
99
  ("Regional Indicator", lambda x: "".join(chr(ord(c) - 0x41 + 0x1F1E6) if 'A' <= c <= 'Z' else c for c in x)),
100
  ]
101
 
102
- server_running = False
103
- server_task = None
 
 
 
 
 
104
 
105
  # Timestamp wizardry - clock ticks with flair! ⏰🎩
106
  def format_timestamp_prefix():
@@ -146,7 +151,7 @@ async def load_chat():
146
  username = st.session_state.get('username', 'System 🌟')
147
  await asyncio.to_thread(log_action, username, "πŸ“œπŸš€ - Chat loader - history unleashed!")
148
  if not os.path.exists(CHAT_FILE):
149
- await asyncio.to_thread(lambda: open(CHAT_FILE, 'w').write(f"# {START_ROOM} Chat\n\nWelcome to the cosmic hub - start chatting! 🎀\n"))
150
  with open(CHAT_FILE, 'r') as f:
151
  content = await asyncio.to_thread(f.read)
152
  return content
@@ -263,8 +268,6 @@ async def get_audio_html(audio_path, width="100%"):
263
  audio_url = f"data:audio/mpeg;base64,{base64.b64encode(await asyncio.to_thread(open, audio_path, 'rb').read()).decode()}"
264
  return f'<audio controls style="width: {width};"><source src="{audio_url}" type="audio/mpeg">Your browser does not support the audio element.</audio>'
265
 
266
- active_connections = {}
267
-
268
  # Websocket handler - chat links up! πŸŒπŸ”—
269
  async def websocket_handler(websocket, path):
270
  username = st.session_state.get('username', 'System 🌟')
@@ -272,7 +275,7 @@ async def websocket_handler(websocket, path):
272
  try:
273
  client_id = str(uuid.uuid4())
274
  room_id = "chat"
275
- active_connections.setdefault(room_id, {})[client_id] = websocket
276
  chat_content = await load_chat()
277
  username = st.session_state.get('username', random.choice(list(FUN_USERNAMES.keys())))
278
  if not await has_joined_before(client_id, chat_content):
@@ -286,31 +289,30 @@ async def websocket_handler(websocket, path):
286
  except websockets.ConnectionClosed:
287
  pass
288
  finally:
289
- if room_id in active_connections and client_id in active_connections[room_id]:
290
- del active_connections[room_id][client_id]
291
 
292
  # Message broadcaster - words fly far! πŸ“’βœˆοΈ
293
  async def broadcast_message(message, room_id):
294
  username = st.session_state.get('username', 'System 🌟')
295
  await asyncio.to_thread(log_action, username, "πŸ“’βœˆοΈ - Message broadcaster - words fly far!")
296
- if room_id in active_connections:
297
  disconnected = []
298
- for client_id, ws in active_connections[room_id].items():
299
  try:
300
  await ws.send(message)
301
  except websockets.ConnectionClosed:
302
  disconnected.append(client_id)
303
  for client_id in disconnected:
304
- del active_connections[room_id][client_id]
305
 
306
  # Server starter - web spins up! πŸ–₯οΈπŸŒ€
307
  async def run_websocket_server():
308
  username = st.session_state.get('username', 'System 🌟')
309
  await asyncio.to_thread(log_action, username, "πŸ–₯οΈπŸŒ€ - Server starter - web spins up!")
310
- global server_running, server_task
311
- if not server_running:
312
  server = await websockets.serve(websocket_handler, '0.0.0.0', 8765)
313
- server_running = True
314
  await server.wait_closed()
315
 
316
  # Voice processor - speech to text! πŸŽ€πŸ“
@@ -322,216 +324,222 @@ async def process_voice_input(audio_bytes):
322
  await save_chat_entry(username, text)
323
 
324
  # Interface builder - UI takes shape! πŸŽ¨πŸ–ŒοΈ
325
- async def create_streamlit_interface():
326
- username = st.session_state.get('username', 'System 🌟')
327
- await asyncio.to_thread(log_action, username, "πŸŽ¨πŸ–ŒοΈ - Interface builder - UI takes shape!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
 
329
- if 'username' not in st.session_state:
 
330
  chat_content = await load_chat()
331
- available_names = [name for name in FUN_USERNAMES if not any(f"{name} has joined" in line for line in chat_content.split('\n'))]
332
- st.session_state.username = random.choice(available_names) if available_names else random.choice(list(FUN_USERNAMES.keys()))
333
-
334
- if 'refresh_rate' not in st.session_state:
335
- st.session_state.refresh_rate = 5
336
- if 'timer_start' not in st.session_state:
337
- st.session_state.timer_start = time.time()
338
- if 'quote_line' not in st.session_state:
339
- st.session_state.quote_line = None
340
- if 'pasted_image_data' not in st.session_state:
341
- st.session_state.pasted_image_data = None
342
- if 'message_text' not in st.session_state:
343
- st.session_state.message_text = ""
344
- if 'audio_cache' not in st.session_state:
345
- st.session_state.audio_cache = {}
346
- if 'chat_history' not in st.session_state:
347
- st.session_state.chat_history = []
348
-
349
- st.markdown("""
350
- <style>
351
- .chat-box {font-family: monospace; background: #1e1e1e; color: #d4d4d4; padding: 10px; border-radius: 5px; height: 300px; overflow-y: auto;}
352
- .timer {font-size: 24px; color: #ffcc00; text-align: center; animation: pulse 1s infinite;}
353
- @keyframes pulse {0% {transform: scale(1);} 50% {transform: scale(1.1);} 100% {transform: scale(1);}}
354
- #paste-target {border: 2px dashed #ccc; padding: 20px; text-align: center; cursor: pointer;}
355
- </style>
356
- """, unsafe_allow_html=True)
357
-
358
- st.title(f"πŸ€–πŸ§ MMO {st.session_state.username}πŸ“πŸ”¬")
359
- st.markdown(f"Welcome to {START_ROOM} - chat, vote, upload, paste images, and enjoy quoting! πŸŽ‰")
360
-
361
- # Voice Input
362
- audio_bytes = audio_recorder()
363
- if audio_bytes:
364
- await process_voice_input(audio_bytes)
365
- st.rerun()
366
-
367
- # Chat Section
368
- st.subheader(f"{START_ROOM} Chat πŸ’¬")
369
- chat_content = await load_chat()
370
- chat_lines = chat_content.split('\n')
371
- chat_votes = await load_votes(QUOTE_VOTES_FILE)
372
- for i, line in enumerate(chat_lines):
373
- if line.strip() and ': ' in line:
374
- col1, col2, col3 = st.columns([4, 1, 1])
375
- with col1:
376
- st.markdown(line)
377
- username = line.split(': ')[1].split(' ')[0]
378
- audio_file = None
379
- cache_key = f"{line}_{FUN_USERNAMES.get(username, 'en-US-AriaNeural')}"
380
- if cache_key in st.session_state.audio_cache:
381
- audio_file = st.session_state.audio_cache[cache_key]
382
- else:
383
- audio_file = await async_edge_tts_generate(line.split(': ', 1)[1], FUN_USERNAMES.get(username, "en-US-AriaNeural"))
384
- st.session_state.audio_cache[cache_key] = audio_file
385
- if audio_file:
386
- play_and_download_audio(audio_file)
387
- with col2:
388
- vote_count = chat_votes.get(line.split('. ')[1] if '. ' in line else line, 0)
389
- if st.button(f"πŸ‘ {vote_count}", key=f"chat_vote_{i}"):
390
- comment = st.session_state.message_text
391
- await save_vote(QUOTE_VOTES_FILE, line.split('. ')[1] if '. ' in line else line, await generate_user_hash(), st.session_state.username, comment)
392
  if st.session_state.pasted_image_data:
393
  filename = await save_pasted_image(st.session_state.pasted_image_data)
394
  if filename:
395
- await save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
396
  st.session_state.pasted_image_data = None
397
- st.session_state.message_text = ''
398
- st.rerun()
399
- with col3:
400
- if st.button("πŸ“’ Quote", key=f"quote_{i}"):
401
- st.session_state.quote_line = line
402
- st.rerun()
403
-
404
- # Quoting Section
405
- if 'quote_line' in st.session_state:
406
- st.markdown(f"### Quoting: {st.session_state.quote_line}")
407
- quote_response = st.text_area("Add your response", key="quote_response")
408
- if st.button("Send Quote πŸš€", key="send_quote"):
409
- async def process_quote():
410
- await asyncio.to_thread(log_action, st.session_state.username, "πŸ“’πŸ’¬ - Quote processor - echoes resound!")
411
- markdown_response = f"### Quote Response\n- **Original**: {st.session_state.quote_line}\n- **{st.session_state.username} Replies**: {quote_response}"
412
- if st.session_state.pasted_image_data:
413
- filename = await save_pasted_image(st.session_state.pasted_image_data)
414
- if filename:
415
- markdown_response += f"\n- **Image**: ![Pasted Image]({filename})"
416
- st.session_state.pasted_image_data = None
417
- await save_chat_entry(st.session_state.username, markdown_response)
418
- await process_quote()
419
- del st.session_state.quote_line
420
  st.session_state.message_text = ''
421
  st.rerun()
422
 
423
- # Username changer
424
- new_username = st.selectbox("Change Name", [""] + list(FUN_USERNAMES.keys()), index=0)
425
- if new_username and new_username != st.session_state.username:
426
- await save_chat_entry("System 🌟", f"{st.session_state.username} changed name to {new_username}")
427
- st.session_state.username = new_username
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
  st.rerun()
429
 
430
- # Message Input
431
- 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))
432
- if st.button("Send πŸš€", key="send_button") and message.strip():
433
- await save_chat_entry(st.session_state.username, message)
434
- if st.session_state.pasted_image_data:
435
- filename = await save_pasted_image(st.session_state.pasted_image_data)
436
- if filename:
437
- await save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
438
- st.session_state.pasted_image_data = None
439
- st.session_state.message_text = ''
440
- st.rerun()
441
 
442
- # Paste Target
443
- paste_component = components.html(
444
- """
445
- <div id="paste-target">Paste an image here (Ctrl+V)</div>
446
- <script>
447
- const pasteTarget = document.getElementById('paste-target');
448
- pasteTarget.addEventListener('paste', (event) => {
449
- const items = (event.clipboardData || window.clipboardData).items;
450
- for (let i = 0; i < items.length; i++) {
451
- if (items[i].type.indexOf('image') !== -1) {
452
- const blob = items[i].getAsFile();
453
- const reader = new FileReader();
454
- reader.onload = (e) => {
455
- window.parent.postMessage({
456
- type: 'streamlit:setComponentValue',
457
- value: e.target.result
458
- }, '*');
459
- pasteTarget.innerHTML = '<p>Image pasted! Processing...</p>';
460
- };
461
- reader.readAsDataURL(blob);
462
- }
463
- }
464
- event.preventDefault();
465
- });
466
- </script>
467
- """,
468
- height=100
469
- )
470
-
471
- # Media Section
472
- st.subheader("Media Gallery 🎨🎢πŸŽ₯")
473
- uploaded_file = st.file_uploader("Upload Media", type=['png', 'jpg', 'mp3', 'mp4'])
474
- if uploaded_file:
475
- file_path = os.path.join('./', uploaded_file.name)
476
- await asyncio.to_thread(lambda: open(file_path, 'wb').write(uploaded_file.getbuffer()))
477
- st.success(f"Uploaded {uploaded_file.name}")
478
-
479
- media_files = glob.glob("./*.png") + glob.glob("./*.jpg") + glob.glob("./*.mp3") + glob.glob("./*.mp4")
480
- if media_files:
481
- cols = st.columns(3)
482
- media_votes = await load_votes(MEDIA_VOTES_FILE)
483
- for idx, media_file in enumerate(media_files):
484
- vote_count = media_votes.get(media_file, 0)
485
- if vote_count > 0:
486
- with cols[idx % 3]:
487
- if media_file.endswith(('.png', '.jpg')):
488
- st.image(media_file, use_container_width=True)
489
- elif media_file.endswith('.mp3'):
490
- st.markdown(await get_audio_html(media_file), unsafe_allow_html=True)
491
- elif media_file.endswith('.mp4'):
492
- st.markdown(await get_video_html(media_file), unsafe_allow_html=True)
493
- col1, col2 = st.columns(2)
494
- with col1:
495
- if st.button(f"πŸ‘ {vote_count}", key=f"media_vote_{idx}"):
496
- comment = st.session_state.message_text
497
- await save_vote(MEDIA_VOTES_FILE, media_file, await generate_user_hash(), st.session_state.username, comment)
498
- if st.session_state.pasted_image_data:
499
- filename = await save_pasted_image(st.session_state.pasted_image_data)
500
- if filename:
501
- await save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
502
- st.session_state.pasted_image_data = None
503
- st.session_state.message_text = ''
504
- st.rerun()
505
- with col2:
506
- if st.button("πŸ—‘οΈ", key=f"media_delete_{idx}"):
507
- await asyncio.to_thread(os.remove, media_file)
508
- st.rerun()
509
-
510
- # Refresh Timer
511
- st.subheader("Refresh ⏳")
512
- refresh_rate = st.slider("Refresh Rate", 1, 300, st.session_state.refresh_rate)
513
- st.session_state.refresh_rate = refresh_rate
514
- timer_placeholder = st.empty()
515
- for i in range(st.session_state.refresh_rate, -1, -1):
516
- font_name, font_func = random.choice(UNICODE_FONTS)
517
- countdown_str = "".join(UNICODE_DIGITS[int(d)] for d in str(i)) if i < 10 else font_func(str(i))
518
- timer_placeholder.markdown(f"<p class='timer'>⏳ {font_func('Refresh in:')} {countdown_str}</p>", unsafe_allow_html=True)
519
- await asyncio.sleep(1)
520
- st.rerun()
521
-
522
- # Sidebar History
523
- st.sidebar.subheader("Chat History πŸ“œ")
524
- with open(HISTORY_FILE, 'r') as f:
525
- history_content = f.read()
526
- st.sidebar.markdown(history_content)
527
-
528
- # Main execution
529
- NODE_NAME, port = get_node_name()
530
- server_task = None
531
-
532
- # Start the websocket server
533
- if server_task is None:
534
- server_task = asyncio.create_task(run_websocket_server())
535
-
536
- # Run the interface
537
- asyncio.run(create_streamlit_interface())
 
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():
 
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
 
268
  audio_url = f"data:audio/mpeg;base64,{base64.b64encode(await asyncio.to_thread(open, audio_path, 'rb').read()).decode()}"
269
  return f'<audio controls style="width: {width};"><source src="{audio_url}" type="audio/mpeg">Your browser does not support the audio element.</audio>'
270
 
 
 
271
  # Websocket handler - chat links up! πŸŒπŸ”—
272
  async def websocket_handler(websocket, path):
273
  username = st.session_state.get('username', 'System 🌟')
 
275
  try:
276
  client_id = str(uuid.uuid4())
277
  room_id = "chat"
278
+ st.session_state.active_connections.setdefault(room_id, {})[client_id] = websocket
279
  chat_content = await load_chat()
280
  username = st.session_state.get('username', random.choice(list(FUN_USERNAMES.keys())))
281
  if not await has_joined_before(client_id, chat_content):
 
289
  except websockets.ConnectionClosed:
290
  pass
291
  finally:
292
+ if room_id in st.session_state.active_connections and client_id in st.session_state.active_connections[room_id]:
293
+ del st.session_state.active_connections[room_id][client_id]
294
 
295
  # Message broadcaster - words fly far! πŸ“’βœˆοΈ
296
  async def broadcast_message(message, room_id):
297
  username = st.session_state.get('username', 'System 🌟')
298
  await asyncio.to_thread(log_action, username, "πŸ“’βœˆοΈ - Message broadcaster - words fly far!")
299
+ if room_id in st.session_state.active_connections:
300
  disconnected = []
301
+ for client_id, ws in st.session_state.active_connections[room_id].items():
302
  try:
303
  await ws.send(message)
304
  except websockets.ConnectionClosed:
305
  disconnected.append(client_id)
306
  for client_id in disconnected:
307
+ del st.session_state.active_connections[room_id][client_id]
308
 
309
  # Server starter - web spins up! πŸ–₯οΈπŸŒ€
310
  async def run_websocket_server():
311
  username = st.session_state.get('username', 'System 🌟')
312
  await asyncio.to_thread(log_action, username, "πŸ–₯οΈπŸŒ€ - Server starter - web spins up!")
313
+ if not st.session_state.server_running:
 
314
  server = await websockets.serve(websocket_handler, '0.0.0.0', 8765)
315
+ st.session_state.server_running = True
316
  await server.wait_closed()
317
 
318
  # Voice processor - speech to text! πŸŽ€πŸ“
 
324
  await save_chat_entry(username, text)
325
 
326
  # Interface builder - UI takes shape! πŸŽ¨πŸ–ŒοΈ
327
+ def create_streamlit_interface():
328
+ # Run async tasks within an event loop
329
+ loop = asyncio.new_event_loop()
330
+ asyncio.set_event_loop(loop)
331
+
332
+ async def async_interface():
333
+ if 'username' not in st.session_state:
334
+ chat_content = await load_chat()
335
+ available_names = [name for name in FUN_USERNAMES if not any(f"{name} has joined" in line for line in chat_content.split('\n'))]
336
+ st.session_state.username = random.choice(available_names) if available_names else random.choice(list(FUN_USERNAMES.keys()))
337
+
338
+ if 'refresh_rate' not in st.session_state:
339
+ st.session_state.refresh_rate = 5
340
+ if 'timer_start' not in st.session_state:
341
+ st.session_state.timer_start = time.time()
342
+ if 'quote_line' not in st.session_state:
343
+ st.session_state.quote_line = None
344
+ if 'pasted_image_data' not in st.session_state:
345
+ st.session_state.pasted_image_data = None
346
+ if 'message_text' not in st.session_state:
347
+ st.session_state.message_text = ""
348
+ if 'audio_cache' not in st.session_state:
349
+ st.session_state.audio_cache = {}
350
+ if 'chat_history' not in st.session_state:
351
+ st.session_state.chat_history = []
352
+
353
+ st.markdown("""
354
+ <style>
355
+ .chat-box {font-family: monospace; background: #1e1e1e; color: #d4d4d4; padding: 10px; border-radius: 5px; height: 300px; overflow-y: auto;}
356
+ .timer {font-size: 24px; color: #ffcc00; text-align: center; animation: pulse 1s infinite;}
357
+ @keyframes pulse {0% {transform: scale(1);} 50% {transform: scale(1.1);} 100% {transform: scale(1);}}
358
+ #paste-target {border: 2px dashed #ccc; padding: 20px; text-align: center; cursor: pointer;}
359
+ </style>
360
+ """, unsafe_allow_html=True)
361
+
362
+ st.title(f"πŸ€–πŸ§ MMO {st.session_state.username}πŸ“πŸ”¬")
363
+ st.markdown(f"Welcome to {START_ROOM} - chat, vote, upload, paste images, and enjoy quoting! πŸŽ‰")
364
+
365
+ # Start websocket server if not running
366
+ if not st.session_state.server_task:
367
+ st.session_state.server_task = loop.create_task(run_websocket_server())
368
+
369
+ # Voice Input
370
+ audio_bytes = audio_recorder()
371
+ if audio_bytes:
372
+ await process_voice_input(audio_bytes)
373
+ st.rerun()
374
 
375
+ # Chat Section
376
+ st.subheader(f"{START_ROOM} Chat πŸ’¬")
377
  chat_content = await load_chat()
378
+ chat_lines = chat_content.split('\n')
379
+ chat_votes = await load_votes(QUOTE_VOTES_FILE)
380
+ for i, line in enumerate(chat_lines):
381
+ if line.strip() and ': ' in line:
382
+ col1, col2, col3 = st.columns([4, 1, 1])
383
+ with col1:
384
+ st.markdown(line)
385
+ username = line.split(': ')[1].split(' ')[0]
386
+ audio_file = None
387
+ cache_key = f"{line}_{FUN_USERNAMES.get(username, 'en-US-AriaNeural')}"
388
+ if cache_key in st.session_state.audio_cache:
389
+ audio_file = st.session_state.audio_cache[cache_key]
390
+ else:
391
+ audio_file = await async_edge_tts_generate(line.split(': ', 1)[1], FUN_USERNAMES.get(username, "en-US-AriaNeural"))
392
+ st.session_state.audio_cache[cache_key] = audio_file
393
+ if audio_file:
394
+ play_and_download_audio(audio_file)
395
+ with col2:
396
+ vote_count = chat_votes.get(line.split('. ')[1] if '. ' in line else line, 0)
397
+ if st.button(f"πŸ‘ {vote_count}", key=f"chat_vote_{i}"):
398
+ comment = st.session_state.message_text
399
+ await save_vote(QUOTE_VOTES_FILE, line.split('. ')[1] if '. ' in line else line, await generate_user_hash(), st.session_state.username, comment)
400
+ if st.session_state.pasted_image_data:
401
+ filename = await save_pasted_image(st.session_state.pasted_image_data)
402
+ if filename:
403
+ await save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
404
+ st.session_state.pasted_image_data = None
405
+ st.session_state.message_text = ''
406
+ st.rerun()
407
+ with col3:
408
+ if st.button("πŸ“’ Quote", key=f"quote_{i}"):
409
+ st.session_state.quote_line = line
410
+ st.rerun()
411
+
412
+ # Quoting Section
413
+ if 'quote_line' in st.session_state:
414
+ st.markdown(f"### Quoting: {st.session_state.quote_line}")
415
+ quote_response = st.text_area("Add your response", key="quote_response")
416
+ if st.button("Send Quote πŸš€", key="send_quote"):
417
+ async def process_quote():
418
+ await asyncio.to_thread(log_action, st.session_state.username, "πŸ“’πŸ’¬ - Quote processor - echoes resound!")
419
+ markdown_response = f"### Quote Response\n- **Original**: {st.session_state.quote_line}\n- **{st.session_state.username} Replies**: {quote_response}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  if st.session_state.pasted_image_data:
421
  filename = await save_pasted_image(st.session_state.pasted_image_data)
422
  if filename:
423
+ markdown_response += f"\n- **Image**: ![Pasted Image]({filename})"
424
  st.session_state.pasted_image_data = None
425
+ await save_chat_entry(st.session_state.username, markdown_response)
426
+ loop.run_until_complete(process_quote())
427
+ del st.session_state.quote_line
428
+ st.session_state.message_text = ''
429
+ st.rerun()
430
+
431
+ # Username changer
432
+ new_username = st.selectbox("Change Name", [""] + list(FUN_USERNAMES.keys()), index=0)
433
+ if new_username and new_username != st.session_state.username:
434
+ loop.run_until_complete(save_chat_entry("System 🌟", f"{st.session_state.username} changed name to {new_username}"))
435
+ st.session_state.username = new_username
436
+ st.rerun()
437
+
438
+ # Message Input
439
+ 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))
440
+ if st.button("Send πŸš€", key="send_button") and message.strip():
441
+ loop.run_until_complete(save_chat_entry(st.session_state.username, message))
442
+ if st.session_state.pasted_image_data:
443
+ filename = loop.run_until_complete(save_pasted_image(st.session_state.pasted_image_data))
444
+ if filename:
445
+ loop.run_until_complete(save_chat_entry(st.session_state.username, f"Pasted image: {filename}"))
446
+ st.session_state.pasted_image_data = None
 
447
  st.session_state.message_text = ''
448
  st.rerun()
449
 
450
+ # Paste Target
451
+ components.html(
452
+ """
453
+ <div id="paste-target">Paste an image here (Ctrl+V)</div>
454
+ <script>
455
+ const pasteTarget = document.getElementById('paste-target');
456
+ pasteTarget.addEventListener('paste', (event) => {
457
+ const items = (event.clipboardData || window.clipboardData).items;
458
+ for (let i = 0; i < items.length; i++) {
459
+ if (items[i].type.indexOf('image') !== -1) {
460
+ const blob = items[i].getAsFile();
461
+ const reader = new FileReader();
462
+ reader.onload = (e) => {
463
+ window.parent.postMessage({
464
+ type: 'streamlit:setComponentValue',
465
+ value: e.target.result
466
+ }, '*');
467
+ pasteTarget.innerHTML = '<p>Image pasted! Processing...</p>';
468
+ };
469
+ reader.readAsDataURL(blob);
470
+ }
471
+ }
472
+ event.preventDefault();
473
+ });
474
+ </script>
475
+ """,
476
+ height=100
477
+ )
478
+
479
+ # Media Section
480
+ st.subheader("Media Gallery 🎨🎢πŸŽ₯")
481
+ uploaded_file = st.file_uploader("Upload Media", type=['png', 'jpg', 'mp3', 'mp4'])
482
+ if uploaded_file:
483
+ file_path = os.path.join('./', uploaded_file.name)
484
+ await asyncio.to_thread(lambda: open(file_path, 'wb').write(uploaded_file.getbuffer()))
485
+ st.success(f"Uploaded {uploaded_file.name}")
486
+
487
+ media_files = glob.glob("./*.png") + glob.glob("./*.jpg") + glob.glob("./*.mp3") + glob.glob("./*.mp4")
488
+ if media_files:
489
+ cols = st.columns(3)
490
+ media_votes = loop.run_until_complete(load_votes(MEDIA_VOTES_FILE))
491
+ for idx, media_file in enumerate(media_files):
492
+ vote_count = media_votes.get(media_file, 0)
493
+ if vote_count > 0:
494
+ with cols[idx % 3]:
495
+ if media_file.endswith(('.png', '.jpg')):
496
+ st.image(media_file, use_container_width=True)
497
+ elif media_file.endswith('.mp3'):
498
+ st.markdown(loop.run_until_complete(get_audio_html(media_file)), unsafe_allow_html=True)
499
+ elif media_file.endswith('.mp4'):
500
+ st.markdown(loop.run_until_complete(get_video_html(media_file)), unsafe_allow_html=True)
501
+ col1, col2 = st.columns(2)
502
+ with col1:
503
+ if st.button(f"πŸ‘ {vote_count}", key=f"media_vote_{idx}"):
504
+ comment = st.session_state.message_text
505
+ loop.run_until_complete(save_vote(MEDIA_VOTES_FILE, media_file, await generate_user_hash(), st.session_state.username, comment))
506
+ if st.session_state.pasted_image_data:
507
+ filename = loop.run_until_complete(save_pasted_image(st.session_state.pasted_image_data))
508
+ if filename:
509
+ loop.run_until_complete(save_chat_entry(st.session_state.username, f"Pasted image: {filename}"))
510
+ st.session_state.pasted_image_data = None
511
+ st.session_state.message_text = ''
512
+ st.rerun()
513
+ with col2:
514
+ if st.button("πŸ—‘οΈ", key=f"media_delete_{idx}"):
515
+ await asyncio.to_thread(os.remove, media_file)
516
+ st.rerun()
517
+
518
+ # Refresh Timer
519
+ st.subheader("Refresh ⏳")
520
+ refresh_rate = st.slider("Refresh Rate", 1, 300, st.session_state.refresh_rate)
521
+ st.session_state.refresh_rate = refresh_rate
522
+ timer_placeholder = st.empty()
523
+ for i in range(st.session_state.refresh_rate, -1, -1):
524
+ font_name, font_func = random.choice(UNICODE_FONTS)
525
+ countdown_str = "".join(UNICODE_DIGITS[int(d)] for d in str(i)) if i < 10 else font_func(str(i))
526
+ timer_placeholder.markdown(f"<p class='timer'>⏳ {font_func('Refresh in:')} {countdown_str}</p>", unsafe_allow_html=True)
527
+ loop.run_until_complete(asyncio.sleep(1))
528
  st.rerun()
529
 
530
+ # Sidebar History
531
+ st.sidebar.subheader("Chat History πŸ“œ")
532
+ with open(HISTORY_FILE, 'r') as f:
533
+ history_content = f.read()
534
+ st.sidebar.markdown(history_content)
 
 
 
 
 
 
535
 
536
+ # Run the async interface
537
+ loop.run_until_complete(async_interface())
538
+
539
+ # Main execution - let’s roll! πŸŽ²πŸš€
540
+ def main():
541
+ NODE_NAME, port = get_node_name()
542
+ create_streamlit_interface()
543
+
544
+ if __name__ == "__main__":
545
+ main()