awacke1 commited on
Commit
4f5107e
Β·
verified Β·
1 Parent(s): 50acd70

Create backup6.app.py

Browse files
Files changed (1) hide show
  1. backup6.app.py +568 -0
backup6.app.py ADDED
@@ -0,0 +1,568 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import asyncio
3
+ import websockets
4
+ import uuid
5
+ import argparse
6
+ from datetime import datetime
7
+ import os
8
+ import random
9
+ 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
+ import re # Added back the re import for regular expressions
21
+ from streamlit_paste_button import paste_image_button
22
+
23
+ # Patch for nested async - sneaky fix! 🐍✨
24
+ nest_asyncio.apply()
25
+
26
+ # Static config - constants rule! πŸ“πŸ‘‘
27
+ icons = 'πŸ€–πŸ§ πŸ”¬πŸ“'
28
+ START_ROOM = "Sector 🌌"
29
+
30
+ # Page setup - dressing up the window! πŸ–ΌοΈπŸŽ€
31
+ st.set_page_config(
32
+ page_title="πŸ€–πŸ§ MMO Chat BrainπŸ“πŸ”¬",
33
+ page_icon=icons,
34
+ layout="wide",
35
+ initial_sidebar_state="auto"
36
+ )
37
+
38
+ # Funky usernames - who’s who in the zoo with unique voices! πŸŽ­πŸΎπŸŽ™οΈ
39
+ FUN_USERNAMES = {
40
+ "CosmicJester 🌌": "en-US-AriaNeural",
41
+ "PixelPanda 🐼": "en-US-JennyNeural",
42
+ "QuantumQuack πŸ¦†": "en-GB-SoniaNeural",
43
+ "StellarSquirrel 🐿️": "en-AU-NatashaNeural",
44
+ "GizmoGuru βš™οΈ": "en-CA-ClaraNeural",
45
+ "NebulaNinja 🌠": "en-US-GuyNeural",
46
+ "ByteBuster πŸ’Ύ": "en-GB-RyanNeural",
47
+ "GalacticGopher 🌍": "en-AU-WilliamNeural",
48
+ "RocketRaccoon πŸš€": "en-CA-LiamNeural",
49
+ "EchoElf 🧝": "en-US-AnaNeural",
50
+ "PhantomFox 🦊": "en-US-BrandonNeural",
51
+ "WittyWizard πŸ§™": "en-GB-ThomasNeural",
52
+ "LunarLlama πŸŒ™": "en-AU-FreyaNeural",
53
+ "SolarSloth β˜€οΈ": "en-CA-LindaNeural",
54
+ "AstroAlpaca πŸ¦™": "en-US-ChristopherNeural",
55
+ "CyberCoyote 🐺": "en-GB-ElliotNeural",
56
+ "MysticMoose 🦌": "en-AU-JamesNeural",
57
+ "GlitchGnome 🧚": "en-CA-EthanNeural",
58
+ "VortexViper 🐍": "en-US-AmberNeural",
59
+ "ChronoChimp πŸ’": "en-GB-LibbyNeural"
60
+ }
61
+
62
+ # Folders galore - organizing chaos! πŸ“‚πŸŒ€
63
+ CHAT_DIR = "chat_logs"
64
+ VOTE_DIR = "vote_logs"
65
+ STATE_FILE = "user_state.txt"
66
+ AUDIO_DIR = "audio_logs"
67
+ HISTORY_DIR = "history_logs"
68
+ MEDIA_DIR = "media_files"
69
+ os.makedirs(CHAT_DIR, exist_ok=True)
70
+ os.makedirs(VOTE_DIR, exist_ok=True)
71
+ os.makedirs(AUDIO_DIR, exist_ok=True)
72
+ os.makedirs(HISTORY_DIR, exist_ok=True)
73
+ os.makedirs(MEDIA_DIR, exist_ok=True)
74
+
75
+ CHAT_FILE = os.path.join(CHAT_DIR, "global_chat.md")
76
+ QUOTE_VOTES_FILE = os.path.join(VOTE_DIR, "quote_votes.md")
77
+ MEDIA_VOTES_FILE = os.path.join(VOTE_DIR, "media_votes.md")
78
+ HISTORY_FILE = os.path.join(HISTORY_DIR, "chat_history.md")
79
+
80
+ # Fancy digits - numbers got style! πŸ”’πŸ’ƒ
81
+ UNICODE_DIGITS = {i: f"{i}\uFE0F⃣" for i in range(10)}
82
+
83
+ # Massive font collection - typography bonanza! πŸ–‹οΈπŸŽ¨
84
+ UNICODE_FONTS = [
85
+ ("Normal", lambda x: x),
86
+ ("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)),
87
+ ("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)),
88
+ ("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)),
89
+ ("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)),
90
+ ("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)),
91
+ ("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)),
92
+ ("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)),
93
+ ("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)),
94
+ ("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)),
95
+ ("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)),
96
+ ("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)),
97
+ ("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)),
98
+ ("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)),
99
+ ("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)),
100
+ ("Squared", lambda x: "".join(chr(ord(c) - 0x41 + 0x1F130) if 'A' <= c <= 'Z' else c for c in x)),
101
+ ("Negative Circled", lambda x: "".join(chr(ord(c) - 0x41 + 0x1F150) if 'A' <= c <= 'Z' else c for c in x)),
102
+ ("Negative Squared", lambda x: "".join(chr(ord(c) - 0x41 + 0x1F170) if 'A' <= c <= 'Z' else c for c in x)),
103
+ ("Regional Indicator", lambda x: "".join(chr(ord(c) - 0x41 + 0x1F1E6) if 'A' <= c <= 'Z' else c for c in x)),
104
+ ]
105
+
106
+ # Global state - keeping tabs! πŸŒπŸ“‹
107
+ if 'server_running' not in st.session_state:
108
+ st.session_state.server_running = False
109
+ if 'server_task' not in st.session_state:
110
+ st.session_state.server_task = None
111
+ if 'active_connections' not in st.session_state:
112
+ st.session_state.active_connections = {}
113
+ if 'media_notifications' not in st.session_state:
114
+ st.session_state.media_notifications = []
115
+ if 'last_chat_update' not in st.session_state:
116
+ st.session_state.last_chat_update = 0
117
+ if 'displayed_chat_lines' not in st.session_state:
118
+ st.session_state.displayed_chat_lines = []
119
+ if 'old_val' not in st.session_state:
120
+ st.session_state.old_val = ""
121
+ if 'last_query' not in st.session_state:
122
+ st.session_state.last_query = ""
123
+ if 'message_text' not in st.session_state:
124
+ st.session_state.message_text = ""
125
+ if 'audio_cache' not in st.session_state:
126
+ st.session_state.audio_cache = {}
127
+ if 'pasted_image_data' not in st.session_state:
128
+ st.session_state.pasted_image_data = None
129
+ if 'quote_line' not in st.session_state:
130
+ st.session_state.quote_line = None
131
+ if 'refresh_rate' not in st.session_state:
132
+ st.session_state.refresh_rate = 5 # Default refresh rate
133
+ if 'base64_cache' not in st.session_state:
134
+ st.session_state.base64_cache = {} # Cache for base64 strings
135
+
136
+ # Timestamp wizardry - clock ticks with flair! ⏰🎩
137
+ def format_timestamp_prefix(username):
138
+ now = datetime.now()
139
+ return f"{now.strftime('%I-%M-%p-ct-%m-%d-%Y')}-by-{username}"
140
+
141
+ # Node naming - christening the beast! 🌐🍼
142
+ def get_node_name():
143
+ parser = argparse.ArgumentParser(description='Start a chat node with a specific name')
144
+ parser.add_argument('--node-name', type=str, default=None)
145
+ parser.add_argument('--port', type=int, default=8501)
146
+ args = parser.parse_args()
147
+ username = st.session_state.get('username', 'System 🌟')
148
+ log_action(username, "🌐🍼 - Node naming - christening the beast!")
149
+ return args.node_name or f"node-{uuid.uuid4().hex[:8]}", args.port
150
+
151
+ # Action logger - spying on deeds! πŸ•΅οΈπŸ“œ
152
+ def log_action(username, action):
153
+ if 'action_log' not in st.session_state:
154
+ st.session_state.action_log = {}
155
+ user_log = st.session_state.action_log.setdefault(username, {})
156
+ current_time = time.time()
157
+ user_log = {k: v for k, v in user_log.items() if current_time - v < 10}
158
+ st.session_state.action_log[username] = user_log
159
+ if action not in user_log:
160
+ with open(HISTORY_FILE, 'a') as f:
161
+ f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {username}: {action}\n")
162
+ user_log[action] = current_time
163
+
164
+ # Clean text - strip the fancy stuff! πŸ§ΉπŸ“
165
+ def clean_text_for_tts(text):
166
+ cleaned = re.sub(r'[#*!\[\]]+', '', text)
167
+ cleaned = ' '.join(cleaned.split())
168
+ return cleaned[:200] if cleaned else "No text to speak"
169
+
170
+ # Chat saver - words locked tight! πŸ’¬πŸ”’
171
+ async def save_chat_entry(username, message):
172
+ await asyncio.to_thread(log_action, username, "πŸ’¬πŸ”’ - Chat saver - words locked tight!")
173
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # Keep this for log consistency
174
+ entry = f"[{timestamp}] {username}: {message}"
175
+ await asyncio.to_thread(lambda: open(CHAT_FILE, 'a').write(f"{entry}\n"))
176
+ voice = FUN_USERNAMES.get(username, "en-US-AriaNeural")
177
+ cleaned_message = clean_text_for_tts(message)
178
+ audio_file = await async_edge_tts_generate(cleaned_message, voice)
179
+ if audio_file:
180
+ with open(HISTORY_FILE, 'a') as f:
181
+ f.write(f"[{timestamp}] {username}: Audio generated - {audio_file}\n")
182
+ await broadcast_message(f"{username}|{message}", "chat")
183
+ st.session_state.last_chat_update = time.time()
184
+
185
+ # Chat loader - history unleashed! πŸ“œπŸš€
186
+ async def load_chat():
187
+ username = st.session_state.get('username', 'System 🌟')
188
+ await asyncio.to_thread(log_action, username, "πŸ“œπŸš€ - Chat loader - history unleashed!")
189
+ if not os.path.exists(CHAT_FILE):
190
+ await asyncio.to_thread(lambda: open(CHAT_FILE, 'a').write(f"# {START_ROOM} Chat\n\nWelcome to the cosmic hub - start chatting! 🎀\n"))
191
+ with open(CHAT_FILE, 'r') as f:
192
+ content = await asyncio.to_thread(f.read)
193
+ return content
194
+
195
+ # User lister - who’s in the gang! πŸ‘₯πŸŽ‰
196
+ async def get_user_list(chat_content):
197
+ username = st.session_state.get('username', 'System 🌟')
198
+ await asyncio.to_thread(log_action, username, "πŸ‘₯πŸŽ‰ - User lister - who’s in the gang!")
199
+ users = set()
200
+ for line in chat_content.split('\n'):
201
+ if line.strip() and ': ' in line:
202
+ user = line.split(': ')[1].split(' ')[0]
203
+ users.add(user)
204
+ return sorted(list(users))
205
+
206
+ # Join checker - been here before? πŸšͺπŸ”
207
+ async def has_joined_before(client_id, chat_content):
208
+ username = st.session_state.get('username', 'System 🌟')
209
+ await asyncio.to_thread(log_action, username, "πŸšͺπŸ” - Join checker - been here before?")
210
+ return any(f"Client-{client_id}" in line for line in chat_content.split('\n'))
211
+
212
+ # Suggestion maker - old quips resurface! πŸ’‘πŸ“
213
+ async def get_message_suggestions(chat_content, prefix):
214
+ username = st.session_state.get('username', 'System 🌟')
215
+ await asyncio.to_thread(log_action, username, "πŸ’‘πŸ“ - Suggestion maker - old quips resurface!")
216
+ lines = chat_content.split('\n')
217
+ messages = [line.split(': ', 1)[1] for line in lines if ': ' in line and line.strip()]
218
+ return [msg for msg in messages if msg.lower().startswith(prefix.lower())][:5]
219
+
220
+ # Vote saver - cheers recorded! πŸ‘πŸ“Š
221
+ async def save_vote(file, item, user_hash, username, comment=""):
222
+ await asyncio.to_thread(log_action, username, "πŸ‘πŸ“Š - Vote saver - cheers recorded!")
223
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
224
+ entry = f"[{timestamp}] {user_hash} voted for {item}"
225
+ await asyncio.to_thread(lambda: open(file, 'a').write(f"{entry}\n"))
226
+ await asyncio.to_thread(lambda: open(HISTORY_FILE, "a").write(f"- {timestamp} - User {user_hash} voted for {item}\n"))
227
+ chat_message = f"{username} upvoted: \"{item}\""
228
+ if comment:
229
+ chat_message += f" - {comment}"
230
+ await save_chat_entry(username, chat_message)
231
+
232
+ # Vote counter - tallying the love! πŸ†πŸ“ˆ
233
+ async def load_votes(file):
234
+ username = st.session_state.get('username', 'System 🌟')
235
+ await asyncio.to_thread(log_action, username, "πŸ†πŸ“ˆ - Vote counter - tallying the love!")
236
+ if not os.path.exists(file):
237
+ await asyncio.to_thread(lambda: open(file, 'w').write("# Vote Tally\n\nNo votes yet - get clicking! πŸ–±οΈ\n"))
238
+ with open(file, 'r') as f:
239
+ content = await asyncio.to_thread(f.read)
240
+ lines = content.strip().split('\n')[2:]
241
+ votes = {}
242
+ user_votes = set()
243
+ for line in lines:
244
+ if line.strip() and 'voted for' in line:
245
+ user_hash = line.split('] ')[1].split(' voted for ')[0]
246
+ item = line.split('voted for ')[1]
247
+ vote_key = f"{user_hash}-{item}"
248
+ if vote_key not in user_votes:
249
+ votes[item] = votes.get(item, 0) + 1
250
+ user_votes.add(vote_key)
251
+ return votes
252
+
253
+ # Hash generator - secret codes ahoy! πŸ”‘πŸ•΅οΈ
254
+ async def generate_user_hash():
255
+ username = st.session_state.get('username', 'System 🌟')
256
+ await asyncio.to_thread(log_action, username, "πŸ”‘πŸ•΅οΈ - Hash generator - secret codes ahoy!")
257
+ if 'user_hash' not in st.session_state:
258
+ st.session_state.user_hash = hashlib.md5(str(random.getrandbits(128)).encode()).hexdigest()[:8]
259
+ return st.session_state.user_hash
260
+
261
+ # Audio maker - voices come alive! 🎢🌟
262
+ async def async_edge_tts_generate(text, voice, rate=0, pitch=0, file_format="mp3"):
263
+ username = st.session_state.get('username', 'System 🌟')
264
+ await asyncio.to_thread(log_action, username, "🎢🌟 - Audio maker - voices come alive!")
265
+ timestamp = format_timestamp_prefix(username)
266
+ filename = f"{timestamp}.{file_format}"
267
+ filepath = os.path.join(AUDIO_DIR, filename)
268
+ communicate = edge_tts.Communicate(text, voice, rate=f"{rate:+d}%", pitch=f"{pitch:+d}Hz")
269
+ try:
270
+ await communicate.save(filepath)
271
+ return filepath if os.path.exists(filepath) else None
272
+ except edge_tts.exceptions.NoAudioReceived:
273
+ with open(HISTORY_FILE, 'a') as f:
274
+ f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {username}: Audio failed - No audio received for '{text}'\n")
275
+ return None
276
+
277
+ # Audio player - tunes blast off! πŸ”ŠπŸš€
278
+ def play_and_download_audio(file_path):
279
+ if file_path and os.path.exists(file_path):
280
+ st.audio(file_path)
281
+ if file_path not in st.session_state.base64_cache:
282
+ with open(file_path, "rb") as f:
283
+ b64 = base64.b64encode(f.read()).decode()
284
+ st.session_state.base64_cache[file_path] = b64
285
+ b64 = st.session_state.base64_cache[file_path]
286
+ dl_link = f'<a href="data:audio/mpeg;base64,{b64}" download="{os.path.basename(file_path)}">🎡 Download {os.path.basename(file_path)}</a>'
287
+ st.markdown(dl_link, unsafe_allow_html=True)
288
+
289
+ # Image saver - pics preserved with naming! πŸ“ΈπŸ’Ύ
290
+ async def save_pasted_image(image, username):
291
+ await asyncio.to_thread(log_action, username, "πŸ“ΈπŸ’Ύ - Image saver - pics preserved!")
292
+ timestamp = format_timestamp_prefix(username)
293
+ filename = f"{timestamp}.png"
294
+ filepath = os.path.join(MEDIA_DIR, filename)
295
+ await asyncio.to_thread(image.save, filepath, "PNG")
296
+ return filepath
297
+
298
+ # Video renderer - movies roll with autoplay! πŸŽ₯🎬
299
+ async def get_video_html(video_path, width="100%"):
300
+ username = st.session_state.get('username', 'System 🌟')
301
+ await asyncio.to_thread(log_action, username, "πŸŽ₯🎬 - Video renderer - movies roll!")
302
+ with open(video_path, 'rb') as f:
303
+ video_data = await asyncio.to_thread(f.read)
304
+ video_url = f"data:video/mp4;base64,{base64.b64encode(video_data).decode()}"
305
+ return f'<video width="{width}" controls autoplay><source src="{video_url}" type="video/mp4">Your browser does not support the video tag.</video>'
306
+
307
+ # Audio renderer - sounds soar! 🎢✈️
308
+ async def get_audio_html(audio_path, width="100%"):
309
+ username = st.session_state.get('username', 'System 🌟')
310
+ await asyncio.to_thread(log_action, username, "🎢✈️ - Audio renderer - sounds soar!")
311
+ audio_url = f"data:audio/mpeg;base64,{base64.b64encode(await asyncio.to_thread(open, audio_path, 'rb').read()).decode()}"
312
+ return f'<audio controls style="width: {width};"><source src="{audio_url}" type="audio/mpeg">Your browser does not support the audio element.</audio>'
313
+
314
+ # Websocket handler - chat links up! πŸŒπŸ”—
315
+ async def websocket_handler(websocket, path):
316
+ username = st.session_state.get('username', 'System 🌟')
317
+ await asyncio.to_thread(log_action, username, "πŸŒπŸ”— - Websocket handler - chat links up!")
318
+ try:
319
+ client_id = str(uuid.uuid4())
320
+ room_id = "chat"
321
+ st.session_state.active_connections.setdefault(room_id, {})[client_id] = websocket
322
+ chat_content = await load_chat()
323
+ username = st.session_state.get('username', random.choice(list(FUN_USERNAMES.keys())))
324
+ if not await has_joined_before(client_id, chat_content):
325
+ await save_chat_entry(f"Client-{client_id}", f"{username} has joined {START_ROOM}!")
326
+ async for message in websocket:
327
+ parts = message.split('|', 1)
328
+ if len(parts) == 2:
329
+ username, content = parts
330
+ await save_chat_entry(username, content)
331
+ except websockets.ConnectionClosed:
332
+ pass
333
+ finally:
334
+ if room_id in st.session_state.active_connections and client_id in st.session_state.active_connections[room_id]:
335
+ del st.session_state.active_connections[room_id][client_id]
336
+
337
+ # Message broadcaster - words fly far! πŸ“’βœˆοΈ
338
+ async def broadcast_message(message, room_id):
339
+ username = st.session_state.get('username', 'System 🌟')
340
+ await asyncio.to_thread(log_action, username, "πŸ“’βœˆοΈ - Message broadcaster - words fly far!")
341
+ if room_id in st.session_state.active_connections:
342
+ disconnected = []
343
+ for client_id, ws in st.session_state.active_connections[room_id].items():
344
+ try:
345
+ await ws.send(message)
346
+ except websockets.ConnectionClosed:
347
+ disconnected.append(client_id)
348
+ for client_id in disconnected:
349
+ del st.session_state.active_connections[room_id][client_id]
350
+
351
+ # Server starter - web spins up! πŸ–₯οΈπŸŒ€
352
+ async def run_websocket_server():
353
+ username = st.session_state.get('username', 'System 🌟')
354
+ await asyncio.to_thread(log_action, username, "πŸ–₯οΈπŸŒ€ - Server starter - web spins up!")
355
+ if not st.session_state.server_running:
356
+ server = await websockets.serve(websocket_handler, '0.0.0.0', 8765)
357
+ st.session_state.server_running = True
358
+ await server.wait_closed()
359
+
360
+ # Voice processor - speech to text! πŸŽ€πŸ“
361
+ async def process_voice_input(audio_bytes):
362
+ username = st.session_state.get('username', 'System 🌟')
363
+ await asyncio.to_thread(log_action, username, "πŸŽ€πŸ“ - Voice processor - speech to text!")
364
+ if audio_bytes:
365
+ text = "Voice input simulation"
366
+ await save_chat_entry(username, text)
367
+
368
+ # Dummy AI lookup function (replace with actual implementation)
369
+ async def perform_ai_lookup(query, vocal_summary=True, extended_refs=False, titles_summary=True, full_audio=False, useArxiv=True, useArxivAudio=False):
370
+ username = st.session_state.get('username', 'System 🌟')
371
+ result = f"AI Lookup Result for '{query}' (Arxiv: {useArxiv}, Audio: {useArxivAudio})"
372
+ await save_chat_entry(username, result)
373
+ if useArxivAudio:
374
+ audio_file = await async_edge_tts_generate(result, FUN_USERNAMES.get(username, "en-US-AriaNeural"))
375
+ if audio_file:
376
+ st.audio(audio_file)
377
+
378
+ # Main execution - let’s roll! πŸŽ²πŸš€
379
+ def main():
380
+ NODE_NAME, port = get_node_name()
381
+
382
+ loop = asyncio.new_event_loop()
383
+ asyncio.set_event_loop(loop)
384
+
385
+ async def async_interface():
386
+ if 'username' not in st.session_state:
387
+ chat_content = await load_chat()
388
+ available_names = [name for name in FUN_USERNAMES if not any(f"{name} has joined" in line for line in chat_content.split('\n'))]
389
+ st.session_state.username = random.choice(available_names) if available_names else random.choice(list(FUN_USERNAMES.keys()))
390
+
391
+ st.title(f"πŸ€–πŸ§ MMO {st.session_state.username}πŸ“πŸ”¬")
392
+ st.markdown(f"Welcome to {START_ROOM} - chat, vote, upload, paste images, and enjoy quoting! πŸŽ‰")
393
+
394
+ if not st.session_state.server_task:
395
+ st.session_state.server_task = loop.create_task(run_websocket_server())
396
+
397
+ audio_bytes = audio_recorder()
398
+ if audio_bytes:
399
+ await process_voice_input(audio_bytes)
400
+ st.rerun()
401
+
402
+ # Load and display chat
403
+ st.subheader(f"{START_ROOM} Chat πŸ’¬")
404
+ chat_content = await load_chat()
405
+ chat_lines = chat_content.split('\n')
406
+ chat_votes = await load_votes(QUOTE_VOTES_FILE)
407
+
408
+ current_time = time.time()
409
+ if current_time - st.session_state.last_chat_update > 1 or not st.session_state.displayed_chat_lines:
410
+ new_lines = [line for line in chat_lines if line.strip() and ': ' in line and line not in st.session_state.displayed_chat_lines and not line.startswith('#')]
411
+ st.session_state.displayed_chat_lines.extend(new_lines)
412
+ st.session_state.last_chat_update = current_time
413
+
414
+ for i, line in enumerate(st.session_state.displayed_chat_lines):
415
+ col1, col2, col3, col4 = st.columns([3, 1, 1, 2])
416
+ with col1:
417
+ st.markdown(line)
418
+ if "Pasted image:" in line or "Uploaded media:" in line:
419
+ file_path = line.split(': ')[-1].strip()
420
+ if os.path.exists(file_path):
421
+ if file_path not in st.session_state.base64_cache:
422
+ with open(file_path, "rb") as f:
423
+ b64 = base64.b64encode(f.read()).decode()
424
+ st.session_state.base64_cache[file_path] = b64
425
+ b64 = st.session_state.base64_cache[file_path]
426
+ mime_type = "image/png" if file_path.endswith(('.png', '.jpg')) else "video/mp4" if file_path.endswith('.mp4') else "audio/mpeg"
427
+ if file_path.endswith(('.png', '.jpg')):
428
+ st.image(file_path, use_container_width=True)
429
+ dl_link = f'<a href="data:{mime_type};base64,{b64}" download="{os.path.basename(file_path)}">πŸ“₯ Download {os.path.basename(file_path)}</a>'
430
+ st.markdown(dl_link, unsafe_allow_html=True)
431
+ elif file_path.endswith('.mp4'):
432
+ st.markdown(await get_video_html(file_path), unsafe_allow_html=True)
433
+ dl_link = f'<a href="data:{mime_type};base64,{b64}" download="{os.path.basename(file_path)}">πŸ“₯ Download {os.path.basename(file_path)}</a>'
434
+ st.markdown(dl_link, unsafe_allow_html=True)
435
+ elif file_path.endswith('.mp3'):
436
+ st.markdown(await get_audio_html(file_path), unsafe_allow_html=True)
437
+ dl_link = f'<a href="data:{mime_type};base64,{b64}" download="{os.path.basename(file_path)}">πŸ“₯ Download {os.path.basename(file_path)}</a>'
438
+ st.markdown(dl_link, unsafe_allow_html=True)
439
+ with col2:
440
+ vote_count = chat_votes.get(line.split('. ')[1] if '. ' in line else line, 0)
441
+ if st.button(f"πŸ‘ {vote_count}", key=f"chat_vote_{i}"):
442
+ comment = st.session_state.message_text
443
+ await save_vote(QUOTE_VOTES_FILE, line.split('. ')[1] if '. ' in line else line, await generate_user_hash(), st.session_state.username, comment)
444
+ st.session_state.message_text = ''
445
+ st.rerun()
446
+ with col3:
447
+ if st.button("πŸ“’ Quote", key=f"quote_{i}"):
448
+ st.session_state.quote_line = line
449
+ st.rerun()
450
+ with col4:
451
+ username = line.split(': ')[1].split(' ')[0]
452
+ cache_key = f"{line}_{FUN_USERNAMES.get(username, 'en-US-AriaNeural')}"
453
+ if cache_key not in st.session_state.audio_cache:
454
+ cleaned_text = clean_text_for_tts(line.split(': ', 1)[1])
455
+ audio_file = await async_edge_tts_generate(cleaned_text, FUN_USERNAMES.get(username, "en-US-AriaNeural"))
456
+ st.session_state.audio_cache[cache_key] = audio_file
457
+ audio_file = st.session_state.audio_cache.get(cache_key)
458
+ if audio_file:
459
+ play_and_download_audio(audio_file)
460
+
461
+ if st.session_state.quote_line:
462
+ st.markdown(f"### Quoting: {st.session_state.quote_line}")
463
+ quote_response = st.text_area("Add your response", key="quote_response")
464
+ paste_result_quote = paste_image_button("πŸ“‹ Paste Image with Quote", key="paste_button_quote")
465
+ if paste_result_quote.image_data is not None:
466
+ st.image(paste_result_quote.image_data, caption="Received Image for Quote")
467
+ filename = await save_pasted_image(paste_result_quote.image_data, st.session_state.username)
468
+ if filename:
469
+ st.session_state.pasted_image_data = filename # Store for use in quote submission
470
+ if st.button("Send Quote πŸš€", key="send_quote"):
471
+ markdown_response = f"### Quote Response\n- **Original**: {st.session_state.quote_line}\n- **{st.session_state.username} Replies**: {quote_response}"
472
+ if st.session_state.pasted_image_data:
473
+ markdown_response += f"\n- **Image**: ![Pasted Image]({st.session_state.pasted_image_data})"
474
+ await save_chat_entry(st.session_state.username, f"Pasted image: {st.session_state.pasted_image_data}")
475
+ st.session_state.pasted_image_data = None
476
+ await save_chat_entry(st.session_state.username, markdown_response)
477
+ st.session_state.quote_line = None
478
+ st.session_state.message_text = ''
479
+ st.rerun()
480
+
481
+ new_username = st.selectbox("Change Name", [""] + list(FUN_USERNAMES.keys()), index=0)
482
+ if new_username and new_username != st.session_state.username:
483
+ await save_chat_entry("System 🌟", f"{st.session_state.username} changed name to {new_username}")
484
+ st.session_state.username = new_username
485
+ st.rerun()
486
+
487
+ 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))
488
+ paste_result_msg = paste_image_button("πŸ“‹ Paste Image with Message", key="paste_button_msg")
489
+ if paste_result_msg.image_data is not None:
490
+ st.image(paste_result_msg.image_data, caption="Received Image for Message")
491
+ filename = await save_pasted_image(paste_result_msg.image_data, st.session_state.username)
492
+ if filename:
493
+ st.session_state.pasted_image_data = filename # Store for use in message submission
494
+ if st.button("Send πŸš€", key="send_button") and (message.strip() or st.session_state.pasted_image_data):
495
+ if message.strip():
496
+ await save_chat_entry(st.session_state.username, message)
497
+ if st.session_state.pasted_image_data:
498
+ await save_chat_entry(st.session_state.username, f"Pasted image: {st.session_state.pasted_image_data}")
499
+ st.session_state.pasted_image_data = None
500
+ st.session_state.message_text = ''
501
+ st.rerun()
502
+
503
+ # Main action tabs and model use choices
504
+ tab_main = st.radio("Action:", ["🎀 Voice", "πŸ“Έ Media", "πŸ” ArXiv", "πŸ“ Editor"], horizontal=True)
505
+ useArxiv = st.checkbox("Search Arxiv for Research Paper Answers", value=True)
506
+ useArxivAudio = st.checkbox("Generate Audio File for Research Paper Answers", value=False)
507
+
508
+ # Enhanced Media Gallery with Image, Audio, Video
509
+ st.subheader("Upload Media 🎨🎢πŸŽ₯")
510
+ uploaded_file = st.file_uploader("Upload Media", type=['png', 'jpg', 'mp4', 'mp3'])
511
+ if uploaded_file:
512
+ timestamp = format_timestamp_prefix(st.session_state.username)
513
+ username = st.session_state.username
514
+ ext = uploaded_file.name.split('.')[-1]
515
+ filename = f"{timestamp}.{ext}"
516
+ file_path = os.path.join(MEDIA_DIR, filename)
517
+ await asyncio.to_thread(lambda: open(file_path, 'wb').write(uploaded_file.getbuffer()))
518
+ st.success(f"Uploaded {filename}")
519
+ await save_chat_entry(username, f"Uploaded media: {file_path}")
520
+ if file_path.endswith('.mp4'):
521
+ st.session_state.media_notifications.append(file_path)
522
+
523
+ st.subheader("Media Gallery 🎨🎢πŸŽ₯")
524
+ media_files = glob.glob(f"{MEDIA_DIR}/*.png") + glob.glob(f"{MEDIA_DIR}/*.jpg") + glob.glob(f"{MEDIA_DIR}/*.mp4") + glob.glob(f"{MEDIA_DIR}/*.mp3")
525
+ if media_files:
526
+ media_votes = await load_votes(MEDIA_VOTES_FILE)
527
+ st.write("### All Media Uploads")
528
+ seen_files = set() # Track unique file paths to prevent duplicates
529
+ for media_file in sorted(media_files, key=os.path.getmtime, reverse=True):
530
+ if media_file not in seen_files: # Only add if not already seen
531
+ seen_files.add(media_file)
532
+ filename = os.path.basename(media_file)
533
+ vote_count = media_votes.get(media_file, 0)
534
+
535
+ col1, col2 = st.columns([3, 1])
536
+ with col1:
537
+ st.markdown(f"**{filename}**")
538
+ if media_file.endswith(('.png', '.jpg')):
539
+ st.image(media_file, use_container_width=True)
540
+ elif media_file.endswith('.mp4'):
541
+ st.markdown(await get_video_html(media_file), unsafe_allow_html=True)
542
+ elif media_file.endswith('.mp3'):
543
+ st.markdown(await get_audio_html(media_file), unsafe_allow_html=True)
544
+ with col2:
545
+ if st.button(f"πŸ‘ {vote_count}", key=f"media_vote_{media_file}"):
546
+ await save_vote(MEDIA_VOTES_FILE, media_file, await generate_user_hash(), st.session_state.username)
547
+ st.rerun()
548
+
549
+ st.subheader("Refresh ⏳")
550
+ refresh_rate = st.slider("Refresh Rate", 1, 300, st.session_state.refresh_rate)
551
+ st.session_state.refresh_rate = refresh_rate
552
+ timer_placeholder = st.empty()
553
+ for i in range(st.session_state.refresh_rate, -1, -1):
554
+ font_name, font_func = random.choice(UNICODE_FONTS)
555
+ countdown_str = "".join(UNICODE_DIGITS[int(d)] for d in str(i)) if i < 10 else font_func(str(i))
556
+ timer_placeholder.markdown(f"<p class='timer'>⏳ {font_func('Refresh in:')} {countdown_str}</p>", unsafe_allow_html=True)
557
+ time.sleep(1) # Use synchronous sleep
558
+ st.rerun()
559
+
560
+ st.sidebar.subheader("Chat History πŸ“œ")
561
+ with open(HISTORY_FILE, 'r') as f:
562
+ history_content = f.read()
563
+ st.sidebar.markdown(history_content)
564
+
565
+ loop.run_until_complete(async_interface())
566
+
567
+ if __name__ == "__main__":
568
+ main()