awacke1 commited on
Commit
f9c0517
ยท
verified ยท
1 Parent(s): 304a90b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +105 -87
app.py CHANGED
@@ -58,15 +58,15 @@ UNICODE_FONTS = [
58
  server_running = False
59
  server_task = None
60
 
61
- def get_node_name():
62
  """๐ŸŒ - Naming Node with Code - Spins a name, oh so bold!"""
63
  action = "๐ŸŒ - Naming Node with Code - Spins a name, oh so bold!"
64
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
65
- log_action(username, action)
66
  parser = argparse.ArgumentParser(description='Start a chat node with a specific name')
67
  parser.add_argument('--node-name', type=str, default=None)
68
  parser.add_argument('--port', type=int, default=8501)
69
- args = parser.parse_args()
70
  return args.node_name or f"node-{uuid.uuid4().hex[:8]}", args.port
71
 
72
  def log_action(username, action):
@@ -75,42 +75,38 @@ def log_action(username, action):
75
  st.session_state.action_log = {}
76
  user_log = st.session_state.action_log.setdefault(username, {})
77
  current_time = time.time()
78
- # Clean up old entries (older than 10 seconds)
79
  user_log = {k: v for k, v in user_log.items() if current_time - v < 10}
80
  st.session_state.action_log[username] = user_log
81
- # Log if action hasn't been seen recently
82
  if action not in user_log:
83
  with open(CHAT_FILE, 'a') as f:
84
  f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {username}: {action}\n")
85
  user_log[action] = current_time
86
 
87
- def save_chat_entry(username, message):
88
  """๐Ÿ“ - Chat Snap Trap - Logs your yap, no cap! โœ๏ธ๐Ÿง """
89
- action = "๐Ÿ“ - Chat Snap Trap - Logs your yap, no cap! โœ๏ธ๐Ÿง " # Double rhyme bonus!
90
- log_action(username, action)
91
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
92
  entry = f"[{timestamp}] {username}: {message}"
93
- with open(CHAT_FILE, 'a') as f:
94
- f.write(f"{entry}\n")
95
 
96
- def load_chat():
97
  """๐Ÿ“– - Chat Fetch Quest - Grabs the log, no jest!"""
98
  action = "๐Ÿ“– - Chat Fetch Quest - Grabs the log, no jest!"
99
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
100
- log_action(username, action)
101
  if not os.path.exists(CHAT_FILE):
102
- with open(CHAT_FILE, 'w') as f:
103
- f.write(f"# {START_ROOM} Chat\n\nWelcome to the cosmic hub - start chatting! ๐ŸŽค\n")
104
  with open(CHAT_FILE, 'r') as f:
105
- content = f.read()
106
  lines = content.strip().split('\n')
107
  return "\n".join(f"{i+1}. {line}" for i, line in enumerate(lines) if line.strip())
108
 
109
- def get_user_list(chat_content):
110
  """๐Ÿ‘ฅ - Crew Clue Brew - Spots whoโ€™s who in the crew!"""
111
  action = "๐Ÿ‘ฅ - Crew Clue Brew - Spots whoโ€™s who in the crew!"
112
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
113
- log_action(username, action)
114
  users = set()
115
  for line in chat_content.split('\n'):
116
  if line.strip() and ': ' in line:
@@ -118,27 +114,27 @@ def get_user_list(chat_content):
118
  users.add(user)
119
  return sorted(list(users))
120
 
121
- def has_joined_before(client_id, chat_content):
122
  """๐Ÿšช - Join Check Trek - Sees whoโ€™s back, no wreck!"""
123
  action = "๐Ÿšช - Join Check Trek - Sees whoโ€™s back, no wreck!"
124
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
125
- log_action(username, action)
126
  return any(f"Client-{client_id} has joined" in line for line in chat_content.split('\n'))
127
 
128
- def get_message_suggestions(chat_content, prefix):
129
  """๐Ÿ” - Suggest Jest Chest - Finds old quips, the best!"""
130
  action = "๐Ÿ” - Suggest Jest Chest - Finds old quips, the best!"
131
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
132
- log_action(username, action)
133
  lines = chat_content.split('\n')
134
  messages = [line.split(': ', 1)[1] for line in lines if ': ' in line and line.strip()]
135
  return [msg for msg in messages if msg.lower().startswith(prefix.lower())][:5]
136
 
137
- def load_quotes(source="famous"):
138
  """๐Ÿ“œ - Quote Tote Note - Wise words float, we gloat!"""
139
  action = "๐Ÿ“œ - Quote Tote Note - Wise words float, we gloat!"
140
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
141
- log_action(username, action)
142
  famous_quotes = [
143
  "The true sign of intelligence is not knowledge but imagination. โ€“ Albert Einstein",
144
  ]
@@ -147,31 +143,28 @@ def load_quotes(source="famous"):
147
  ]
148
  return famous_quotes if source == "famous" else custom_quotes
149
 
150
- def save_vote(file, item, user_hash, username, comment=""):
151
  """๐Ÿ‘ - Vote Note Float - Cheers rise, we gloat!"""
152
  action = "๐Ÿ‘ - Vote Note Float - Cheers rise, we gloat!"
153
- log_action(username, action)
154
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
155
  entry = f"[{timestamp}] {user_hash} voted for {item}"
156
- with open(file, 'a') as f:
157
- f.write(f"{entry}\n")
158
- with open(HISTORY_FILE, "a") as f:
159
- f.write(f"- {timestamp} - User {user_hash} voted for {item}\n")
160
  chat_message = f"{username} upvoted: \"{item}\""
161
  if comment:
162
  chat_message += f" - {comment}"
163
- save_chat_entry(username, chat_message)
164
 
165
- def load_votes(file):
166
  """๐Ÿ† - Tally Rally Call - Counts the cheers, no stall!"""
167
  action = "๐Ÿ† - Tally Rally Call - Counts the cheers, no stall!"
168
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
169
- log_action(username, action)
170
  if not os.path.exists(file):
171
- with open(file, 'w') as f:
172
- f.write("# Vote Tally\n\nNo votes yet - get clicking! ๐Ÿ–ฑ๏ธ\n")
173
  with open(file, 'r') as f:
174
- lines = f.read().strip().split('\n')[2:] # Skip header
175
  votes = {}
176
  user_votes = set()
177
  for line in lines:
@@ -184,20 +177,20 @@ def load_votes(file):
184
  user_votes.add(vote_key)
185
  return votes
186
 
187
- def generate_user_hash():
188
  """๐Ÿ”‘ - Hash Dash Bash - Crafts a code, so brash!"""
189
  action = "๐Ÿ”‘ - Hash Dash Bash - Crafts a code, so brash!"
190
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
191
- log_action(username, action)
192
  if 'user_hash' not in st.session_state:
193
  st.session_state.user_hash = hashlib.md5(str(random.getrandbits(128)).encode()).hexdigest()[:8]
194
  return st.session_state.user_hash
195
 
196
- def save_pasted_image(image_data):
197
  """๐Ÿ“ธ - Snap Cap Trap - Saves your pic, no flap!"""
198
  action = "๐Ÿ“ธ - Snap Cap Trap - Saves your pic, no flap!"
199
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
200
- log_action(username, action)
201
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
202
  filename = f"paste_{timestamp}.png"
203
  filepath = os.path.join('./', filename)
@@ -205,23 +198,23 @@ def save_pasted_image(image_data):
205
  image_data = image_data.split(',')[1]
206
  img_bytes = base64.b64decode(image_data)
207
  img = Image.open(io.BytesIO(img_bytes))
208
- img.save(filepath, "PNG")
209
  return filename
210
 
211
- def get_video_html(video_path, width="100%"):
212
  """๐ŸŽฅ - Reel Deal Steal - Plays your flick, so real!"""
213
  action = "๐ŸŽฅ - Reel Deal Steal - Plays your flick, so real!"
214
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
215
- log_action(username, action)
216
- video_url = f"data:video/mp4;base64,{base64.b64encode(open(video_path, 'rb').read()).decode()}"
217
  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>'
218
 
219
- def get_audio_html(audio_path, width="100%"):
220
  """๐ŸŽถ - Tune Moon Boom - Drops a beat, so groom!"""
221
  action = "๐ŸŽถ - Tune Moon Boom - Drops a beat, so groom!"
222
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
223
- log_action(username, action)
224
- audio_url = f"data:audio/mpeg;base64,{base64.b64encode(open(audio_path, 'rb').read()).decode()}"
225
  return f'<audio controls style="width: {width};"><source src="{audio_url}" type="audio/mpeg">Your browser does not support the audio element.</audio>'
226
 
227
  active_connections = {}
@@ -230,20 +223,20 @@ async def websocket_handler(websocket, path):
230
  """๐ŸŒ - Web Sock Jock - Links the chat, no block!"""
231
  action = "๐ŸŒ - Web Sock Jock - Links the chat, no block!"
232
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
233
- log_action(username, action)
234
  try:
235
  client_id = str(uuid.uuid4())
236
  room_id = "chat"
237
  active_connections.setdefault(room_id, {})[client_id] = websocket
238
- chat_content = load_chat()
239
  username = st.session_state.get('username', random.choice(FUN_USERNAMES))
240
- if not has_joined_before(client_id, chat_content):
241
- save_chat_entry(f"Client-{client_id}", f"{username} has joined {START_ROOM}!")
242
  async for message in websocket:
243
  parts = message.split('|', 1)
244
  if len(parts) == 2:
245
  username, content = parts
246
- save_chat_entry(username, content)
247
  await broadcast_message(f"{username}|{content}", room_id)
248
  except websockets.ConnectionClosed:
249
  pass
@@ -255,7 +248,7 @@ async def broadcast_message(message, room_id):
255
  """๐Ÿ“ข - Shout Out Bout - Blasts the word, no doubt!"""
256
  action = "๐Ÿ“ข - Shout Out Bout - Blasts the word, no doubt!"
257
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
258
- log_action(username, action)
259
  if room_id in active_connections:
260
  disconnected = []
261
  for client_id, ws in active_connections[room_id].items():
@@ -270,18 +263,18 @@ async def run_websocket_server():
270
  """๐Ÿ–ฅ๏ธ - Server Ferver Verve - Spins the web, with nerve!"""
271
  action = "๐Ÿ–ฅ๏ธ - Server Ferver Verve - Spins the web, with nerve!"
272
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
273
- log_action(username, action)
274
  global server_running, server_task
275
  if not server_running:
276
  server = await websockets.serve(websocket_handler, '0.0.0.0', 8765)
277
  server_running = True
278
  await server.wait_closed()
279
 
280
- def create_streamlit_interface():
281
  """๐ŸŽจ - UI Brew Crew - Builds the view, so new!"""
282
  action = "๐ŸŽจ - UI Brew Crew - Builds the view, so new!"
283
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
284
- log_action(username, action)
285
 
286
  # Dynamic title based on username
287
  if 'username' in st.session_state:
@@ -300,13 +293,13 @@ def create_streamlit_interface():
300
  """, unsafe_allow_html=True)
301
 
302
  st.title(dynamic_title)
303
- st.markdown(f"Welcome to {START_ROOM} - chat, vote, upload, paste images, and enjoy! ๐ŸŽ‰")
304
 
305
  # Initialize client ID and username
306
  if 'client_id' not in st.session_state:
307
  st.session_state.client_id = str(uuid.uuid4())
308
  if 'username' not in st.session_state:
309
- available_names = [name for name in FUN_USERNAMES if not any(f"{name} has joined" in line for line in load_chat().split('\n'))]
310
  st.session_state.username = random.choice(available_names) if available_names else random.choice(FUN_USERNAMES)
311
 
312
  # Session state initialization
@@ -315,7 +308,7 @@ def create_streamlit_interface():
315
  if 'timer_start' not in st.session_state:
316
  st.session_state.timer_start = time.time()
317
  if 'quote_index' not in st.session_state:
318
- quotes = load_quotes("famous")
319
  st.session_state.quote_index = random.randint(0, max(0, len(quotes) - 1)) if quotes else 0
320
  if 'quote_source' not in st.session_state:
321
  st.session_state.quote_source = "famous"
@@ -356,49 +349,75 @@ def create_streamlit_interface():
356
  # Handle pasted image data
357
  pasted_image = st.session_state.get('pasted_image_data')
358
  if pasted_image:
359
- filename = save_pasted_image(pasted_image)
360
  if filename:
361
- save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
362
  st.session_state.pasted_image_data = None
363
  st.rerun()
364
 
365
- # Chat section
366
  st.subheader(f"{START_ROOM} Chat ๐Ÿ’ฌ")
367
- chat_content = load_chat()
368
- chat_votes = load_votes(QUOTE_VOTES_FILE)
369
- for i, line in enumerate(chat_content.split('\n')):
 
370
  if line.strip() and ': ' in line:
371
- col1, col2 = st.columns([5, 1])
372
  with col1:
373
  st.markdown(line)
374
  with col2:
375
  vote_count = chat_votes.get(line.split('. ')[1] if '. ' in line else line, 0)
376
  if st.button(f"๐Ÿ‘ {vote_count}", key=f"chat_vote_{i}"):
377
  comment = st.session_state.message_text
378
- save_vote(QUOTE_VOTES_FILE, line.split('. ')[1] if '. ' in line else line, generate_user_hash(), st.session_state.username, comment)
379
  if st.session_state.pasted_image_data:
380
- filename = save_pasted_image(st.session_state.pasted_image_data)
381
  if filename:
382
- save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
383
  st.session_state.pasted_image_data = None
384
  st.session_state.message_text = ''
385
  st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
 
387
  # Username change dropdown
388
  new_username = st.selectbox("Change Name", [""] + FUN_USERNAMES, index=0)
389
  if new_username and new_username != st.session_state.username:
390
- save_chat_entry("System ๐ŸŒŸ", f"{st.session_state.username} changed name to {new_username}")
391
  st.session_state.username = new_username
392
  st.rerun()
393
 
394
  # Message input
395
  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))
396
  if st.button("Send ๐Ÿš€", key="send_button") and message.strip():
397
- save_chat_entry(st.session_state.username, message)
398
  if st.session_state.pasted_image_data:
399
- filename = save_pasted_image(st.session_state.pasted_image_data)
400
  if filename:
401
- save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
402
  st.session_state.pasted_image_data = None
403
  st.session_state.message_text = ''
404
  st.rerun()
@@ -408,14 +427,13 @@ def create_streamlit_interface():
408
  uploaded_file = st.file_uploader("Upload Media", type=['png', 'jpg', 'mp3', 'mp4'])
409
  if uploaded_file:
410
  file_path = os.path.join('./', uploaded_file.name)
411
- with open(file_path, 'wb') as f:
412
- f.write(uploaded_file.getbuffer())
413
  st.success(f"Uploaded {uploaded_file.name}")
414
 
415
  media_files = glob.glob("./*.png") + glob.glob("./*.jpg") + glob.glob("./*.mp3") + glob.glob("./*.mp4")
416
  if media_files:
417
  cols = st.columns(3)
418
- media_votes = load_votes(MEDIA_VOTES_FILE)
419
  for idx, media_file in enumerate(media_files):
420
  vote_count = media_votes.get(media_file, 0)
421
  if vote_count > 0:
@@ -423,24 +441,24 @@ def create_streamlit_interface():
423
  if media_file.endswith(('.png', '.jpg')):
424
  st.image(media_file, use_container_width=True)
425
  elif media_file.endswith('.mp3'):
426
- st.markdown(get_audio_html(media_file), unsafe_allow_html=True)
427
  elif media_file.endswith('.mp4'):
428
- st.markdown(get_video_html(media_file), unsafe_allow_html=True)
429
  col1, col2 = st.columns(2)
430
  with col1:
431
  if st.button(f"๐Ÿ‘ {vote_count}", key=f"media_vote_{idx}"):
432
  comment = st.session_state.message_text
433
- save_vote(MEDIA_VOTES_FILE, media_file, generate_user_hash(), st.session_state.username, comment)
434
  if st.session_state.pasted_image_data:
435
- filename = save_pasted_image(st.session_state.pasted_image_data)
436
  if filename:
437
- 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
  with col2:
442
  if st.button("๐Ÿ—‘๏ธ", key=f"media_delete_{idx}"):
443
- os.remove(media_file)
444
  st.rerun()
445
 
446
  # Refresh timer
@@ -452,13 +470,13 @@ def create_streamlit_interface():
452
  font_name, font_func = random.choice(UNICODE_FONTS)
453
  countdown_str = "".join(UNICODE_DIGITS[int(d)] for d in str(i)) if i < 10 else font_func(str(i))
454
  timer_placeholder.markdown(f"<p class='timer'>โณ {font_func('Refresh in:')} {countdown_str}</p>", unsafe_allow_html=True)
455
- time.sleep(1)
456
  st.rerun()
457
 
458
  # Sidebar vote stats
459
  st.sidebar.subheader("Vote Counts")
460
- chat_votes = load_votes(QUOTE_VOTES_FILE)
461
- media_votes = load_votes(MEDIA_VOTES_FILE)
462
  for item, count in {**chat_votes, **media_votes}.items():
463
  if count > 0:
464
  st.sidebar.write(f"{item}: {count} votes")
@@ -467,12 +485,12 @@ async def main():
467
  """๐ŸŽฎ - Game Fame Claim - Starts the fun, no shame!"""
468
  action = "๐ŸŽฎ - Game Fame Claim - Starts the fun, no shame!"
469
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
470
- log_action(username, action)
471
  global NODE_NAME, server_task
472
- NODE_NAME, port = get_node_name()
473
  if server_task is None:
474
  server_task = asyncio.create_task(run_websocket_server())
475
- create_streamlit_interface()
476
 
477
  if __name__ == "__main__":
478
  asyncio.run(main())
 
58
  server_running = False
59
  server_task = None
60
 
61
+ async def get_node_name():
62
  """๐ŸŒ - Naming Node with Code - Spins a name, oh so bold!"""
63
  action = "๐ŸŒ - Naming Node with Code - Spins a name, oh so bold!"
64
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
65
+ await asyncio.to_thread(log_action, username, action)
66
  parser = argparse.ArgumentParser(description='Start a chat node with a specific name')
67
  parser.add_argument('--node-name', type=str, default=None)
68
  parser.add_argument('--port', type=int, default=8501)
69
+ args = await asyncio.to_thread(parser.parse_args)
70
  return args.node_name or f"node-{uuid.uuid4().hex[:8]}", args.port
71
 
72
  def log_action(username, action):
 
75
  st.session_state.action_log = {}
76
  user_log = st.session_state.action_log.setdefault(username, {})
77
  current_time = time.time()
 
78
  user_log = {k: v for k, v in user_log.items() if current_time - v < 10}
79
  st.session_state.action_log[username] = user_log
 
80
  if action not in user_log:
81
  with open(CHAT_FILE, 'a') as f:
82
  f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {username}: {action}\n")
83
  user_log[action] = current_time
84
 
85
+ async def save_chat_entry(username, message):
86
  """๐Ÿ“ - Chat Snap Trap - Logs your yap, no cap! โœ๏ธ๐Ÿง """
87
+ action = "๐Ÿ“ - Chat Snap Trap - Logs your yap, no cap! โœ๏ธ๐Ÿง "
88
+ await asyncio.to_thread(log_action, username, action)
89
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
90
  entry = f"[{timestamp}] {username}: {message}"
91
+ await asyncio.to_thread(lambda: open(CHAT_FILE, 'a').write(f"{entry}\n"))
 
92
 
93
+ async def load_chat():
94
  """๐Ÿ“– - Chat Fetch Quest - Grabs the log, no jest!"""
95
  action = "๐Ÿ“– - Chat Fetch Quest - Grabs the log, no jest!"
96
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
97
+ await asyncio.to_thread(log_action, username, action)
98
  if not os.path.exists(CHAT_FILE):
99
+ await asyncio.to_thread(lambda: open(CHAT_FILE, 'w').write(f"# {START_ROOM} Chat\n\nWelcome to the cosmic hub - start chatting! ๐ŸŽค\n"))
 
100
  with open(CHAT_FILE, 'r') as f:
101
+ content = await asyncio.to_thread(f.read)
102
  lines = content.strip().split('\n')
103
  return "\n".join(f"{i+1}. {line}" for i, line in enumerate(lines) if line.strip())
104
 
105
+ async def get_user_list(chat_content):
106
  """๐Ÿ‘ฅ - Crew Clue Brew - Spots whoโ€™s who in the crew!"""
107
  action = "๐Ÿ‘ฅ - Crew Clue Brew - Spots whoโ€™s who in the crew!"
108
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
109
+ await asyncio.to_thread(log_action, username, action)
110
  users = set()
111
  for line in chat_content.split('\n'):
112
  if line.strip() and ': ' in line:
 
114
  users.add(user)
115
  return sorted(list(users))
116
 
117
+ async def has_joined_before(client_id, chat_content):
118
  """๐Ÿšช - Join Check Trek - Sees whoโ€™s back, no wreck!"""
119
  action = "๐Ÿšช - Join Check Trek - Sees whoโ€™s back, no wreck!"
120
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
121
+ await asyncio.to_thread(log_action, username, action)
122
  return any(f"Client-{client_id} has joined" in line for line in chat_content.split('\n'))
123
 
124
+ async def get_message_suggestions(chat_content, prefix):
125
  """๐Ÿ” - Suggest Jest Chest - Finds old quips, the best!"""
126
  action = "๐Ÿ” - Suggest Jest Chest - Finds old quips, the best!"
127
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
128
+ await asyncio.to_thread(log_action, username, action)
129
  lines = chat_content.split('\n')
130
  messages = [line.split(': ', 1)[1] for line in lines if ': ' in line and line.strip()]
131
  return [msg for msg in messages if msg.lower().startswith(prefix.lower())][:5]
132
 
133
+ async def load_quotes(source="famous"):
134
  """๐Ÿ“œ - Quote Tote Note - Wise words float, we gloat!"""
135
  action = "๐Ÿ“œ - Quote Tote Note - Wise words float, we gloat!"
136
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
137
+ await asyncio.to_thread(log_action, username, action)
138
  famous_quotes = [
139
  "The true sign of intelligence is not knowledge but imagination. โ€“ Albert Einstein",
140
  ]
 
143
  ]
144
  return famous_quotes if source == "famous" else custom_quotes
145
 
146
+ async def save_vote(file, item, user_hash, username, comment=""):
147
  """๐Ÿ‘ - Vote Note Float - Cheers rise, we gloat!"""
148
  action = "๐Ÿ‘ - Vote Note Float - Cheers rise, we gloat!"
149
+ await asyncio.to_thread(log_action, username, action)
150
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
151
  entry = f"[{timestamp}] {user_hash} voted for {item}"
152
+ await asyncio.to_thread(lambda: open(file, 'a').write(f"{entry}\n"))
153
+ await asyncio.to_thread(lambda: open(HISTORY_FILE, "a").write(f"- {timestamp} - User {user_hash} voted for {item}\n"))
 
 
154
  chat_message = f"{username} upvoted: \"{item}\""
155
  if comment:
156
  chat_message += f" - {comment}"
157
+ await save_chat_entry(username, chat_message)
158
 
159
+ async def load_votes(file):
160
  """๐Ÿ† - Tally Rally Call - Counts the cheers, no stall!"""
161
  action = "๐Ÿ† - Tally Rally Call - Counts the cheers, no stall!"
162
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
163
+ await asyncio.to_thread(log_action, username, action)
164
  if not os.path.exists(file):
165
+ await asyncio.to_thread(lambda: open(file, 'w').write("# Vote Tally\n\nNo votes yet - get clicking! ๐Ÿ–ฑ๏ธ\n"))
 
166
  with open(file, 'r') as f:
167
+ lines = await asyncio.to_thread(f.read.strip().split, '\n')[2:]
168
  votes = {}
169
  user_votes = set()
170
  for line in lines:
 
177
  user_votes.add(vote_key)
178
  return votes
179
 
180
+ async def generate_user_hash():
181
  """๐Ÿ”‘ - Hash Dash Bash - Crafts a code, so brash!"""
182
  action = "๐Ÿ”‘ - Hash Dash Bash - Crafts a code, so brash!"
183
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
184
+ await asyncio.to_thread(log_action, username, action)
185
  if 'user_hash' not in st.session_state:
186
  st.session_state.user_hash = hashlib.md5(str(random.getrandbits(128)).encode()).hexdigest()[:8]
187
  return st.session_state.user_hash
188
 
189
+ async def save_pasted_image(image_data):
190
  """๐Ÿ“ธ - Snap Cap Trap - Saves your pic, no flap!"""
191
  action = "๐Ÿ“ธ - Snap Cap Trap - Saves your pic, no flap!"
192
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
193
+ await asyncio.to_thread(log_action, username, action)
194
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
195
  filename = f"paste_{timestamp}.png"
196
  filepath = os.path.join('./', filename)
 
198
  image_data = image_data.split(',')[1]
199
  img_bytes = base64.b64decode(image_data)
200
  img = Image.open(io.BytesIO(img_bytes))
201
+ await asyncio.to_thread(img.save, filepath, "PNG")
202
  return filename
203
 
204
+ async def get_video_html(video_path, width="100%"):
205
  """๐ŸŽฅ - Reel Deal Steal - Plays your flick, so real!"""
206
  action = "๐ŸŽฅ - Reel Deal Steal - Plays your flick, so real!"
207
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
208
+ await asyncio.to_thread(log_action, username, action)
209
+ video_url = f"data:video/mp4;base64,{base64.b64encode(await asyncio.to_thread(open(video_path, 'rb').read)).decode()}"
210
  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>'
211
 
212
+ async def get_audio_html(audio_path, width="100%"):
213
  """๐ŸŽถ - Tune Moon Boom - Drops a beat, so groom!"""
214
  action = "๐ŸŽถ - Tune Moon Boom - Drops a beat, so groom!"
215
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
216
+ await asyncio.to_thread(log_action, username, action)
217
+ audio_url = f"data:audio/mpeg;base64,{base64.b64encode(await asyncio.to_thread(open(audio_path, 'rb').read)).decode()}"
218
  return f'<audio controls style="width: {width};"><source src="{audio_url}" type="audio/mpeg">Your browser does not support the audio element.</audio>'
219
 
220
  active_connections = {}
 
223
  """๐ŸŒ - Web Sock Jock - Links the chat, no block!"""
224
  action = "๐ŸŒ - Web Sock Jock - Links the chat, no block!"
225
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
226
+ await asyncio.to_thread(log_action, username, action)
227
  try:
228
  client_id = str(uuid.uuid4())
229
  room_id = "chat"
230
  active_connections.setdefault(room_id, {})[client_id] = websocket
231
+ chat_content = await load_chat()
232
  username = st.session_state.get('username', random.choice(FUN_USERNAMES))
233
+ if not await has_joined_before(client_id, chat_content):
234
+ await save_chat_entry(f"Client-{client_id}", f"{username} has joined {START_ROOM}!")
235
  async for message in websocket:
236
  parts = message.split('|', 1)
237
  if len(parts) == 2:
238
  username, content = parts
239
+ await save_chat_entry(username, content)
240
  await broadcast_message(f"{username}|{content}", room_id)
241
  except websockets.ConnectionClosed:
242
  pass
 
248
  """๐Ÿ“ข - Shout Out Bout - Blasts the word, no doubt!"""
249
  action = "๐Ÿ“ข - Shout Out Bout - Blasts the word, no doubt!"
250
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
251
+ await asyncio.to_thread(log_action, username, action)
252
  if room_id in active_connections:
253
  disconnected = []
254
  for client_id, ws in active_connections[room_id].items():
 
263
  """๐Ÿ–ฅ๏ธ - Server Ferver Verve - Spins the web, with nerve!"""
264
  action = "๐Ÿ–ฅ๏ธ - Server Ferver Verve - Spins the web, with nerve!"
265
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
266
+ await asyncio.to_thread(log_action, username, action)
267
  global server_running, server_task
268
  if not server_running:
269
  server = await websockets.serve(websocket_handler, '0.0.0.0', 8765)
270
  server_running = True
271
  await server.wait_closed()
272
 
273
+ async def create_streamlit_interface():
274
  """๐ŸŽจ - UI Brew Crew - Builds the view, so new!"""
275
  action = "๐ŸŽจ - UI Brew Crew - Builds the view, so new!"
276
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
277
+ await asyncio.to_thread(log_action, username, action)
278
 
279
  # Dynamic title based on username
280
  if 'username' in st.session_state:
 
293
  """, unsafe_allow_html=True)
294
 
295
  st.title(dynamic_title)
296
+ st.markdown(f"Welcome to {START_ROOM} - chat, vote, upload, paste images, and enjoy quoting! ๐ŸŽ‰")
297
 
298
  # Initialize client ID and username
299
  if 'client_id' not in st.session_state:
300
  st.session_state.client_id = str(uuid.uuid4())
301
  if 'username' not in st.session_state:
302
+ available_names = [name for name in FUN_USERNAMES if not any(f"{name} has joined" in line for line in await load_chat().split('\n'))]
303
  st.session_state.username = random.choice(available_names) if available_names else random.choice(FUN_USERNAMES)
304
 
305
  # Session state initialization
 
308
  if 'timer_start' not in st.session_state:
309
  st.session_state.timer_start = time.time()
310
  if 'quote_index' not in st.session_state:
311
+ quotes = await load_quotes("famous")
312
  st.session_state.quote_index = random.randint(0, max(0, len(quotes) - 1)) if quotes else 0
313
  if 'quote_source' not in st.session_state:
314
  st.session_state.quote_source = "famous"
 
349
  # Handle pasted image data
350
  pasted_image = st.session_state.get('pasted_image_data')
351
  if pasted_image:
352
+ filename = await save_pasted_image(pasted_image)
353
  if filename:
354
+ await save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
355
  st.session_state.pasted_image_data = None
356
  st.rerun()
357
 
358
+ # Chat section with quoting
359
  st.subheader(f"{START_ROOM} Chat ๐Ÿ’ฌ")
360
+ chat_content = await load_chat()
361
+ chat_lines = chat_content.split('\n')
362
+ chat_votes = await load_votes(QUOTE_VOTES_FILE)
363
+ for i, line in enumerate(chat_lines):
364
  if line.strip() and ': ' in line:
365
+ col1, col2, col3 = st.columns([4, 1, 1])
366
  with col1:
367
  st.markdown(line)
368
  with col2:
369
  vote_count = chat_votes.get(line.split('. ')[1] if '. ' in line else line, 0)
370
  if st.button(f"๐Ÿ‘ {vote_count}", key=f"chat_vote_{i}"):
371
  comment = st.session_state.message_text
372
+ await save_vote(QUOTE_VOTES_FILE, line.split('. ')[1] if '. ' in line else line, await generate_user_hash(), st.session_state.username, comment)
373
  if st.session_state.pasted_image_data:
374
+ filename = await save_pasted_image(st.session_state.pasted_image_data)
375
  if filename:
376
+ await save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
377
  st.session_state.pasted_image_data = None
378
  st.session_state.message_text = ''
379
  st.rerun()
380
+ with col3:
381
+ if st.button("๐Ÿ“ข Quote", key=f"quote_{i}"):
382
+ st.session_state.quote_line = line
383
+ st.rerun()
384
+
385
+ # Quoting section
386
+ if 'quote_line' in st.session_state:
387
+ st.markdown(f"### Quoting: {st.session_state.quote_line}")
388
+ quote_response = st.text_area("Add your response", key="quote_response")
389
+ if st.button("Send Quote ๐Ÿš€", key="send_quote"):
390
+ async def process_quote():
391
+ """๐Ÿ“ข - Quote Float Boat - Echoes chat, we gloat!"""
392
+ action = "๐Ÿ“ข - Quote Float Boat - Echoes chat, we gloat!"
393
+ await asyncio.to_thread(log_action, st.session_state.username, action)
394
+ markdown_response = f"### Quote Response\n- **Original**: {st.session_state.quote_line}\n- **{st.session_state.username} Replies**: {quote_response}"
395
+ if st.session_state.pasted_image_data:
396
+ filename = await save_pasted_image(st.session_state.pasted_image_data)
397
+ if filename:
398
+ markdown_response += f"\n- **Image**: ![Pasted Image]({filename})"
399
+ st.session_state.pasted_image_data = None
400
+ await save_chat_entry(st.session_state.username, markdown_response)
401
+ await process_quote()
402
+ del st.session_state.quote_line
403
+ st.session_state.message_text = ''
404
+ st.rerun()
405
 
406
  # Username change dropdown
407
  new_username = st.selectbox("Change Name", [""] + FUN_USERNAMES, index=0)
408
  if new_username and new_username != st.session_state.username:
409
+ await save_chat_entry("System ๐ŸŒŸ", f"{st.session_state.username} changed name to {new_username}")
410
  st.session_state.username = new_username
411
  st.rerun()
412
 
413
  # Message input
414
  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))
415
  if st.button("Send ๐Ÿš€", key="send_button") and message.strip():
416
+ await save_chat_entry(st.session_state.username, message)
417
  if st.session_state.pasted_image_data:
418
+ filename = await save_pasted_image(st.session_state.pasted_image_data)
419
  if filename:
420
+ await save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
421
  st.session_state.pasted_image_data = None
422
  st.session_state.message_text = ''
423
  st.rerun()
 
427
  uploaded_file = st.file_uploader("Upload Media", type=['png', 'jpg', 'mp3', 'mp4'])
428
  if uploaded_file:
429
  file_path = os.path.join('./', uploaded_file.name)
430
+ await asyncio.to_thread(lambda: open(file_path, 'wb').write(uploaded_file.getbuffer()))
 
431
  st.success(f"Uploaded {uploaded_file.name}")
432
 
433
  media_files = glob.glob("./*.png") + glob.glob("./*.jpg") + glob.glob("./*.mp3") + glob.glob("./*.mp4")
434
  if media_files:
435
  cols = st.columns(3)
436
+ media_votes = await load_votes(MEDIA_VOTES_FILE)
437
  for idx, media_file in enumerate(media_files):
438
  vote_count = media_votes.get(media_file, 0)
439
  if vote_count > 0:
 
441
  if media_file.endswith(('.png', '.jpg')):
442
  st.image(media_file, use_container_width=True)
443
  elif media_file.endswith('.mp3'):
444
+ st.markdown(await get_audio_html(media_file), unsafe_allow_html=True)
445
  elif media_file.endswith('.mp4'):
446
+ st.markdown(await get_video_html(media_file), unsafe_allow_html=True)
447
  col1, col2 = st.columns(2)
448
  with col1:
449
  if st.button(f"๐Ÿ‘ {vote_count}", key=f"media_vote_{idx}"):
450
  comment = st.session_state.message_text
451
+ await save_vote(MEDIA_VOTES_FILE, media_file, await generate_user_hash(), st.session_state.username, comment)
452
  if st.session_state.pasted_image_data:
453
+ filename = await save_pasted_image(st.session_state.pasted_image_data)
454
  if filename:
455
+ await save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
456
  st.session_state.pasted_image_data = None
457
  st.session_state.message_text = ''
458
  st.rerun()
459
  with col2:
460
  if st.button("๐Ÿ—‘๏ธ", key=f"media_delete_{idx}"):
461
+ await asyncio.to_thread(os.remove, media_file)
462
  st.rerun()
463
 
464
  # Refresh timer
 
470
  font_name, font_func = random.choice(UNICODE_FONTS)
471
  countdown_str = "".join(UNICODE_DIGITS[int(d)] for d in str(i)) if i < 10 else font_func(str(i))
472
  timer_placeholder.markdown(f"<p class='timer'>โณ {font_func('Refresh in:')} {countdown_str}</p>", unsafe_allow_html=True)
473
+ await asyncio.sleep(1)
474
  st.rerun()
475
 
476
  # Sidebar vote stats
477
  st.sidebar.subheader("Vote Counts")
478
+ chat_votes = await load_votes(QUOTE_VOTES_FILE)
479
+ media_votes = await load_votes(MEDIA_VOTES_FILE)
480
  for item, count in {**chat_votes, **media_votes}.items():
481
  if count > 0:
482
  st.sidebar.write(f"{item}: {count} votes")
 
485
  """๐ŸŽฎ - Game Fame Claim - Starts the fun, no shame!"""
486
  action = "๐ŸŽฎ - Game Fame Claim - Starts the fun, no shame!"
487
  username = st.session_state.get('username', 'System ๐ŸŒŸ')
488
+ await asyncio.to_thread(log_action, username, action)
489
  global NODE_NAME, server_task
490
+ NODE_NAME, port = await get_node_name()
491
  if server_task is None:
492
  server_task = asyncio.create_task(run_websocket_server())
493
+ await create_streamlit_interface()
494
 
495
  if __name__ == "__main__":
496
  asyncio.run(main())