awacke1 commited on
Commit
2ecb5bf
ยท
verified ยท
1 Parent(s): 25818e0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +123 -171
app.py CHANGED
@@ -5,21 +5,13 @@ import uuid
5
  from datetime import datetime
6
  import os
7
  import random
8
- import time
9
  import hashlib
10
- from PIL import Image
11
  import glob
12
  import base64
13
- import io
14
- import streamlit.components.v1 as components
15
  import edge_tts
16
- from audio_recorder_streamlit import audio_recorder
17
  import nest_asyncio
18
- import re
19
- import pytz
20
  from gradio_client import Client
21
  from streamlit_marquee import streamlit_marquee
22
- from collections import defaultdict, Counter
23
 
24
  # Patch asyncio for nesting
25
  nest_asyncio.apply()
@@ -27,83 +19,59 @@ nest_asyncio.apply()
27
  # Page Config
28
  st.set_page_config(
29
  layout="wide",
30
- page_title="Colorado Sim: Mountains, Memes, & Mayhem ๐Ÿ˜‚๐Ÿ”๏ธ",
31
  page_icon="๐ŸฆŒ"
32
  )
33
 
34
- # Static Config with Character-Driven Sim
35
- Site_Name = "Colorado Sim Chat ๐Ÿ”๏ธโœจ"
36
- START_ROOM = "Rocky Mountain Hub ๐ŸŒฒ"
37
- FUN_USERNAMES = {
38
- "Trailblazer Tim ๐ŸŒ„": "en-US-GuyNeural", # Adventurous hiker
39
- "Meme Queen Mia ๐Ÿ˜‚": "en-US-JennyNeural", # Meme enthusiast
40
- "Elk Whisperer Eve ๐ŸฆŒ": "en-GB-SoniaNeural", # Wildlife lover
41
- "Tech Titan Tara ๐Ÿ’พ": "en-AU-NatashaNeural", # Tech-savvy coder
42
- "Ski Guru Sam โ›ท๏ธ": "en-CA-ClaraNeural", # Skiing pro
43
- "Cosmic Camper Cal ๐ŸŒ ": "en-US-AriaNeural", # Stargazing storyteller
44
- "Rasta Ranger Rick ๐Ÿƒ": "en-GB-RyanNeural", # Chill outdoorsman
45
- "Boulder Bro Ben ๐Ÿชจ": "en-AU-WilliamNeural" # Rock-climbing dude
46
  }
47
- EDGE_TTS_VOICES = list(set(FUN_USERNAMES.values()))
48
- FILE_EMOJIS = {"md": "๐Ÿ“", "mp3": "๐ŸŽต", "png": "๐Ÿ–ผ๏ธ", "mp4": "๐ŸŽฅ", "zip": "๐Ÿ“ฆ"}
49
 
50
  # Directories
51
- for d in ["chat_logs", "audio_logs", "audio_cache"]:
52
  os.makedirs(d, exist_ok=True)
53
 
54
  CHAT_DIR = "chat_logs"
55
- AUDIO_CACHE_DIR = "audio_cache"
56
  AUDIO_DIR = "audio_logs"
57
  STATE_FILE = "user_state.txt"
58
- CHAT_FILE = os.path.join(CHAT_DIR, "sim_chat.md")
59
-
60
- # Timestamp Helper
61
- def format_timestamp_prefix(username=""):
62
- central = pytz.timezone('US/Central')
63
- now = datetime.now(central)
64
- return f"{now.strftime('%Y%m%d_%H%M%S')}-by-{username}"
65
 
66
  # Session State Init
67
  def init_session_state():
68
  defaults = {
69
  'server_running': False, 'server_task': None, 'active_connections': {},
70
- 'last_chat_update': 0, 'message_text': "", 'audio_cache': {},
71
- 'transcript_history': [], 'last_transcript': "", 'tts_voice': "en-US-AriaNeural",
72
- 'chat_history': [], 'marquee_settings': {
73
- "background": "#1E1E1E", "color": "#FFFFFF", "font-size": "14px",
74
- "animationDuration": "20s", "width": "100%", "lineHeight": "35px"
75
- }, 'username': None, 'autosend': True, 'last_message': "",
76
- 'mp3_files': {}, 'timer_start': time.time(), 'auto_refresh': True, 'refresh_rate': 10
77
  }
78
  for k, v in defaults.items():
79
  if k not in st.session_state:
80
  st.session_state[k] = v
81
 
82
- # Marquee Helpers
83
- def update_marquee_settings_ui():
84
- st.sidebar.markdown("### ๐ŸŽฏ Marquee Settings ๐ŸŽจ")
85
- cols = st.sidebar.columns(2)
86
- with cols[0]:
87
- st.session_state['marquee_settings']['background'] = st.color_picker("๐ŸŒˆ Background", "#1E1E1E")
88
- st.session_state['marquee_settings']['color'] = st.color_picker("โœ๏ธ Text", "#FFFFFF")
89
- with cols[1]:
90
- st.session_state['marquee_settings']['font-size'] = f"{st.slider('๐Ÿ“ Size', 10, 24, 14)}px"
91
- st.session_state['marquee_settings']['animationDuration'] = f"{st.slider('โฑ๏ธ Speed', 1, 20, 20)}s"
92
-
93
- def display_marquee(text, settings, key_suffix=""):
94
- truncated = text[:280] + "..." if len(text) > 280 else text
95
- streamlit_marquee(content=truncated, **settings, key=f"marquee_{key_suffix}")
96
- st.write("")
97
-
98
- # Text & File Helpers
99
  def clean_text_for_tts(text):
100
  return re.sub(r'[#*!\[\]]+', '', ' '.join(text.split()))[:200] or "No text"
101
 
102
- def clean_text_for_filename(text):
103
- return '_'.join(re.sub(r'[^\w\s-]', '', text.lower()).split())[:50]
104
-
105
  def generate_filename(prompt, username, file_type="md"):
106
- timestamp = format_timestamp_prefix(username)
107
  hash_val = hashlib.md5(prompt.encode()).hexdigest()[:8]
108
  return f"{timestamp}-{hash_val}.{file_type}"
109
 
@@ -116,8 +84,8 @@ def create_file(prompt, username, file_type="md"):
116
  def get_download_link(file, file_type="mp3"):
117
  with open(file, "rb") as f:
118
  b64 = base64.b64encode(f.read()).decode()
119
- mime_types = {"mp3": "audio/mpeg", "png": "image/png", "md": "text/markdown"}
120
- return f'<a href="data:{mime_types.get(file_type, "application/octet-stream")};base64,{b64}" download="{os.path.basename(file)}">{FILE_EMOJIS.get(file_type, "๐Ÿ“ฅ")} Download {os.path.basename(file)}</a>'
121
 
122
  def save_username(username):
123
  with open(STATE_FILE, 'w') as f:
@@ -129,20 +97,13 @@ def load_username():
129
  return f.read().strip()
130
  return None
131
 
132
- def load_mp3_viewer():
133
- mp3_files = sorted(glob.glob("*.mp3"), key=os.path.getmtime)
134
- for i, mp3 in enumerate(mp3_files, 1):
135
- filename = os.path.basename(mp3)
136
- if filename not in st.session_state['mp3_files']:
137
- st.session_state['mp3_files'][filename] = (i, mp3)
138
-
139
  # Audio Processing
140
  async def async_edge_tts_generate(text, voice, username):
141
  cache_key = f"{text[:100]}_{voice}"
142
  if cache_key in st.session_state['audio_cache']:
143
  return st.session_state['audio_cache'][cache_key]
144
  text = clean_text_for_tts(text)
145
- filename = f"{format_timestamp_prefix(username)}-{hashlib.md5(text.encode()).hexdigest()[:8]}.mp3"
146
  communicate = edge_tts.Communicate(text, voice)
147
  await communicate.save(filename)
148
  if os.path.exists(filename) and os.path.getsize(filename) > 0:
@@ -155,60 +116,60 @@ def play_and_download_audio(file_path):
155
  st.audio(file_path)
156
  st.markdown(get_download_link(file_path), unsafe_allow_html=True)
157
 
 
158
  async def save_chat_entry(username, message, voice, is_markdown=False):
159
  if not message.strip() or message == st.session_state.last_transcript:
160
  return None, None
161
- central = pytz.timezone('US/Central')
162
- timestamp = datetime.now(central).strftime("%Y-%m-%d %H:%M:%S")
163
- entry = f"[{timestamp}] {username} ({voice}): {message}" if not is_markdown else f"[{timestamp}] {username} ({voice}):\n```markdown\n{message}\n```"
164
  md_file = create_file(entry, username, "md")
165
  with open(CHAT_FILE, 'a') as f:
166
  f.write(f"{entry}\n")
167
  audio_file = await async_edge_tts_generate(message, voice, username)
168
- await broadcast_message(f"{username}|{message}", "chat")
169
- st.session_state.last_chat_update = time.time()
170
  st.session_state.chat_history.append(entry)
171
  st.session_state.last_transcript = message
 
 
172
  return md_file, audio_file
173
 
174
  async def load_chat():
175
  if not os.path.exists(CHAT_FILE):
176
  with open(CHAT_FILE, 'a') as f:
177
- f.write(f"# {START_ROOM} Chat\n\nWelcome to the Colorado Sim! ๐Ÿ”๏ธ\n")
178
  with open(CHAT_FILE, 'r') as f:
179
  content = f.read().strip()
180
- lines = content.split('\n')
181
- return list(dict.fromkeys(line for line in lines if line.strip()))
182
 
183
- # ArXiv Search with Gradio Client
184
  async def perform_arxiv_search(query, username):
185
  gradio_client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
186
  refs = gradio_client.predict(
187
- query, 10, "Semantic Search", "mistralai/Mixtral-8x7B-Instruct-v0.1", api_name="/update_with_rag_md"
188
  )[0]
189
- result = f"๐Ÿ”Ž ArXiv Insights:\n\n{refs}"
190
- voice = FUN_USERNAMES.get(username, "en-US-AriaNeural")
191
  md_file, audio_file = await save_chat_entry(username, result, voice, True)
192
  return md_file, audio_file
193
 
194
- # WebSocket Handling
195
  async def websocket_handler(websocket, path):
196
  client_id = str(uuid.uuid4())
197
- room_id = "chat"
198
  if room_id not in st.session_state.active_connections:
199
  st.session_state.active_connections[room_id] = {}
200
  st.session_state.active_connections[room_id][client_id] = websocket
201
- username = st.session_state.get('username', random.choice(list(FUN_USERNAMES.keys())))
202
- await save_chat_entry("System ๐ŸŒŸ", f"{username} has joined {START_ROOM}!", "en-US-AriaNeural")
203
  try:
204
  async for message in websocket:
205
  if '|' in message:
206
  username, content = message.split('|', 1)
207
- voice = FUN_USERNAMES.get(username, "en-US-AriaNeural")
208
  await save_chat_entry(username, content, voice)
209
- await perform_arxiv_search(content, username) # ArXiv response for every chat input
210
  except websockets.ConnectionClosed:
211
- await save_chat_entry("System ๐ŸŒŸ", f"{username} has left {START_ROOM}!", "en-US-AriaNeural")
212
  finally:
213
  if room_id in st.session_state.active_connections and client_id in st.session_state.active_connections[room_id]:
214
  del st.session_state.active_connections[room_id][client_id]
@@ -236,116 +197,107 @@ def start_websocket_server():
236
  asyncio.set_event_loop(loop)
237
  loop.run_until_complete(run_websocket_server())
238
 
239
- # Sim-Oriented Mad Libs
240
- def generate_mad_libs_story(username, inputs):
 
 
241
  story = f"""
242
- ๐ŸŒ„ In the wilds of Colorado, {username} stumbled upon {inputs['plural_noun']}.
243
- ๐ŸŒŸ These {inputs['adjective1']} critters were {inputs['verb_ing']} all over the Rockies,
244
- ๐ŸŽค freaking out climbers like {inputs['celebrity']}.
245
- ๐Ÿ› ๏ธ Holding a {inputs['object']}, {username} felt {inputs['emotion']} as a {inputs['animal']}
246
- โณ {inputs['verb_past']} past, yelling {inputs['exclamation']} near {inputs['place']}.
247
- ๐Ÿฝ๏ธ Later, munching {inputs['food']} with a {inputs['adjective2']} {inputs['body_part']},
248
- ๐ŸŽต {username} looped โ€œ{inputs['song']}โ€ {inputs['number']} times,
249
- ๐Ÿ’ผ deciding to {inputs['verb_present']} as a {inputs['occupation']}
250
- ๐ŸŽจ in a {inputs['color']} {inputs['noun']}โ€”all ending with a {inputs['sound']}!
251
  """
252
  return story.strip()
253
 
254
- # Main Interface
255
  def main():
256
  init_session_state()
257
- load_mp3_viewer()
258
  saved_username = load_username()
259
- if saved_username and saved_username in FUN_USERNAMES:
260
  st.session_state.username = saved_username
261
  if not st.session_state.username:
262
- st.session_state.username = random.choice(list(FUN_USERNAMES.keys()))
263
- st.session_state.tts_voice = FUN_USERNAMES[st.session_state.username]
264
- asyncio.run(save_chat_entry("System ๐ŸŒŸ", f"{st.session_state.username} has joined {START_ROOM}!", "en-US-AriaNeural"))
265
  save_username(st.session_state.username)
266
 
267
- st.title(f"๐ŸŽ‰ {Site_Name} - {st.session_state.username} ๐Ÿ”๏ธ")
268
- st.subheader("๐ŸŒฒ Chat, Explore Audio, or Create a Sim Story! ๐ŸŽค")
269
- update_marquee_settings_ui()
270
- chat_text = " ".join([line.split(": ")[-1] for line in asyncio.run(load_chat()) if ": " in line])
271
- display_marquee(f"๐Ÿ”๏ธ {START_ROOM} | ๐ŸŽ™๏ธ {st.session_state.username} | ๐Ÿ’ฌ {chat_text}", st.session_state['marquee_settings'], "welcome")
272
 
273
- tab_main = st.radio("๐ŸŽฎ Action:", ["๐ŸŽค Chat & Voice", "๐ŸŽต Audio Gallery", "โœจ Mad Libs Sim"], horizontal=True)
274
- st.checkbox("๐Ÿš€ Autosend Chat", key="autosend")
275
 
276
- if tab_main == "๐ŸŽค Chat & Voice":
277
- st.subheader(f"๐Ÿ’ฌ {START_ROOM} Chat ๐ŸŒŸ")
278
  chat_content = asyncio.run(load_chat())
279
- with st.container():
280
- st.code("\n".join(f"{i+1}. {line}" for i, line in enumerate(chat_content)), language="python")
281
-
282
- message = st.text_input(f"๐Ÿ’ฌ Message as {st.session_state.username}", key="message_input", placeholder="Type your wild thoughts! ๐ŸŒˆ")
283
- if message and message != st.session_state.last_message:
284
- st.session_state.last_message = message
285
- col_send = st.columns(1)[0]
286
- with col_send:
287
- if st.session_state.autosend or st.button("๐Ÿš€ Send Message ๐ŸŽ™๏ธ"):
288
- voice = FUN_USERNAMES.get(st.session_state.username, "en-US-AriaNeural")
289
- md_file, audio_file = asyncio.run(save_chat_entry(st.session_state.username, message, voice, True))
290
- if audio_file:
291
- play_and_download_audio(audio_file)
292
- st.rerun()
293
-
294
- elif tab_main == "๐ŸŽต Audio Gallery":
295
- st.subheader("๐ŸŽต Audio Gallery ๐ŸŽง")
296
- mp3_files = sorted(glob.glob("*.mp3"), key=os.path.getmtime)
297
  if mp3_files:
298
- for i, mp3 in enumerate(mp3_files):
299
- with st.expander(f"๐ŸŽต {i+1}. {os.path.basename(mp3)}"):
 
300
  play_and_download_audio(mp3)
301
  else:
302
- st.write("๐ŸŽถ No audio files yetโ€”chat or create a story to generate some! ๐ŸŽค")
303
 
304
- elif tab_main == "โœจ Mad Libs Sim":
305
- st.subheader("โœจ Create Your Colorado Sim Story! ๐ŸŽ‰")
306
- col1, col2 = st.columns(2)
307
  inputs = {}
 
308
  with col1:
309
- inputs['plural_noun'] = st.text_input("๐ŸŒŸ Plural Noun", placeholder="e.g., 'tacos'", key="plural_noun")
310
- inputs['adjective1'] = st.text_input("๐ŸŽจ Adjective", placeholder="e.g., 'spicy'", key="adjective1")
311
- inputs['verb_ing'] = st.text_input("โณ Verb ending in -ing", placeholder="e.g., 'yeeting'", key="verb_ing")
312
- inputs['celebrity'] = st.text_input("๐ŸŽค Celebrity Name", placeholder="e.g., 'Elon Musk'", key="celebrity")
313
- inputs['object'] = st.text_input("๐Ÿ› ๏ธ Random Object", placeholder="e.g., 'toaster'", key="object")
314
- inputs['emotion'] = st.text_input("โค๏ธ Emotion", placeholder="e.g., 'salty'", key="emotion")
315
- inputs['animal'] = st.text_input("๐ŸฆŒ Animal", placeholder="e.g., 'elk'", key="animal")
316
- inputs['verb_past'] = st.text_input("โฎ๏ธ Verb (past tense)", placeholder="e.g., 'yeeted'", key="verb_past")
317
- inputs['place'] = st.text_input("๐ŸŒ Place", placeholder="e.g., 'Rockies'", key="place")
318
- inputs['exclamation'] = st.text_input("โ— Exclamation", placeholder="e.g., 'Bruh!'", key="exclamation")
319
  with col2:
320
- inputs['food'] = st.text_input("๐Ÿฝ๏ธ Food Item", placeholder="e.g., 'pizza'", key="food")
321
- inputs['adjective2'] = st.text_input("โœจ Adjective", placeholder="e.g., 'cringe'", key="adjective2")
322
- inputs['body_part'] = st.text_input("๐Ÿฆด Body Part", placeholder="e.g., 'elbow'", key="body_part")
323
- inputs['number'] = st.text_input("๐Ÿ”ข Number", placeholder="e.g., '69'", key="number")
324
- inputs['song'] = st.text_input("๐ŸŽต Song Title", placeholder="e.g., 'Sweet Caroline'", key="song")
325
- inputs['verb_present'] = st.text_input("โ–ถ๏ธ Verb (present tense)", placeholder="e.g., 'slay'", key="verb_present")
326
- inputs['occupation'] = st.text_input("๐Ÿ’ผ Occupation", placeholder="e.g., 'influencer'", key="occupation")
327
- inputs['color'] = st.text_input("๐ŸŒˆ Color", placeholder="e.g., 'teal'", key="color")
328
- inputs['noun'] = st.text_input("๐Ÿ“‹ Noun", placeholder="e.g., 'chaos'", key="noun")
329
- inputs['sound'] = st.text_input("๐Ÿ”Š Silly Sound Effect", placeholder="e.g., 'Boop!'", key="sound")
330
-
331
- if st.button("๐ŸŽ‰ Generate Sim Story! ๐ŸŽค"):
332
- story = generate_mad_libs_story(st.session_state.username, inputs)
333
- st.markdown("### ๐ŸŒ„ Your Colorado Sim Adventure ๐Ÿ”๏ธ")
334
  st.write(story)
335
- voice = FUN_USERNAMES.get(st.session_state.username, "en-US-AriaNeural")
336
  md_file, audio_file = asyncio.run(save_chat_entry(st.session_state.username, story, voice, True))
337
  if audio_file:
338
  play_and_download_audio(audio_file)
 
 
339
 
340
- # Sidebar
341
- st.sidebar.subheader("๐ŸŽญ Character Settings")
342
- new_username = st.sidebar.selectbox("๐Ÿ”„ Switch Character", list(FUN_USERNAMES.keys()), index=list(FUN_USERNAMES.keys()).index(st.session_state.username))
343
  if new_username != st.session_state.username:
344
- asyncio.run(save_chat_entry("System ๐ŸŒŸ", f"{st.session_state.username} switched to {new_username}", "en-US-AriaNeural"))
345
  st.session_state.username = new_username
346
- st.session_state.tts_voice = FUN_USERNAMES[new_username]
347
  save_username(st.session_state.username)
348
  st.rerun()
 
 
 
 
 
 
 
 
349
 
350
- st.sidebar.subheader("๐Ÿ’ฌ Chat History")
351
- chat_content = asyncio
 
5
  from datetime import datetime
6
  import os
7
  import random
 
8
  import hashlib
 
9
  import glob
10
  import base64
 
 
11
  import edge_tts
 
12
  import nest_asyncio
 
 
13
  from gradio_client import Client
14
  from streamlit_marquee import streamlit_marquee
 
15
 
16
  # Patch asyncio for nesting
17
  nest_asyncio.apply()
 
19
  # Page Config
20
  st.set_page_config(
21
  layout="wide",
22
+ page_title="Rocky Mountain Quest ๐Ÿ”๏ธ๐ŸŽฎ",
23
  page_icon="๐ŸฆŒ"
24
  )
25
 
26
+ # Game Config
27
+ GAME_NAME = "Rocky Mountain Quest ๐Ÿ”๏ธ๐ŸŽฎ"
28
+ START_LOCATION = "Trailhead Camp โ›บ"
29
+ CHARACTERS = {
30
+ "Trailblazer Tim ๐ŸŒ„": {"voice": "en-US-GuyNeural", "desc": "Fearless hiker seeking epic trails!"},
31
+ "Meme Queen Mia ๐Ÿ˜‚": {"voice": "en-US-JennyNeural", "desc": "Spreads laughs with wild memes!"},
32
+ "Elk Whisperer Eve ๐ŸฆŒ": {"voice": "en-GB-SoniaNeural", "desc": "Talks to wildlife, loves nature!"},
33
+ "Tech Titan Tara ๐Ÿ’พ": {"voice": "en-AU-NatashaNeural", "desc": "Codes her way through the Rockies!"},
34
+ "Ski Guru Sam โ›ท๏ธ": {"voice": "en-CA-ClaraNeural", "desc": "Shreds slopes, lives for snow!"},
35
+ "Cosmic Camper Cal ๐ŸŒ ": {"voice": "en-US-AriaNeural", "desc": "Stargazes and tells epic tales!"},
36
+ "Rasta Ranger Rick ๐Ÿƒ": {"voice": "en-GB-RyanNeural", "desc": "Chills with natureโ€™s vibes!"},
37
+ "Boulder Bro Ben ๐Ÿชจ": {"voice": "en-AU-WilliamNeural", "desc": "Climbs rocks, bro-style!"}
38
  }
39
+ FILE_EMOJIS = {"md": "๐Ÿ“œ", "mp3": "๐ŸŽต"}
 
40
 
41
  # Directories
42
+ for d in ["chat_logs", "audio_logs"]:
43
  os.makedirs(d, exist_ok=True)
44
 
45
  CHAT_DIR = "chat_logs"
 
46
  AUDIO_DIR = "audio_logs"
47
  STATE_FILE = "user_state.txt"
48
+ CHAT_FILE = os.path.join(CHAT_DIR, "quest_log.md")
 
 
 
 
 
 
49
 
50
  # Session State Init
51
  def init_session_state():
52
  defaults = {
53
  'server_running': False, 'server_task': None, 'active_connections': {},
54
+ 'chat_history': [], 'audio_cache': {}, 'last_transcript': "",
55
+ 'username': None, 'score': 0, 'treasures': 0, 'location': START_LOCATION,
56
+ 'marquee_settings': {
57
+ "background": "#2E8B57", "color": "#FFFFFF", "font-size": "16px",
58
+ "animationDuration": "15s", "width": "100%", "lineHeight": "40px"
59
+ }
 
60
  }
61
  for k, v in defaults.items():
62
  if k not in st.session_state:
63
  st.session_state[k] = v
64
 
65
+ # Helpers
66
+ def format_timestamp(username=""):
67
+ now = datetime.now().strftime("%Y%m%d_%H%M%S")
68
+ return f"{now}-by-{username}"
69
+
 
 
 
 
 
 
 
 
 
 
 
 
70
  def clean_text_for_tts(text):
71
  return re.sub(r'[#*!\[\]]+', '', ' '.join(text.split()))[:200] or "No text"
72
 
 
 
 
73
  def generate_filename(prompt, username, file_type="md"):
74
+ timestamp = format_timestamp(username)
75
  hash_val = hashlib.md5(prompt.encode()).hexdigest()[:8]
76
  return f"{timestamp}-{hash_val}.{file_type}"
77
 
 
84
  def get_download_link(file, file_type="mp3"):
85
  with open(file, "rb") as f:
86
  b64 = base64.b64encode(f.read()).decode()
87
+ mime_types = {"mp3": "audio/mpeg", "md": "text/markdown"}
88
+ return f'<a href="data:{mime_types.get(file_type, "application/octet-stream")};base64,{b64}" download="{os.path.basename(file)}">{FILE_EMOJIS.get(file_type, "๐Ÿ“ฅ")} {os.path.basename(file)}</a>'
89
 
90
  def save_username(username):
91
  with open(STATE_FILE, 'w') as f:
 
97
  return f.read().strip()
98
  return None
99
 
 
 
 
 
 
 
 
100
  # Audio Processing
101
  async def async_edge_tts_generate(text, voice, username):
102
  cache_key = f"{text[:100]}_{voice}"
103
  if cache_key in st.session_state['audio_cache']:
104
  return st.session_state['audio_cache'][cache_key]
105
  text = clean_text_for_tts(text)
106
+ filename = f"{format_timestamp(username)}-{hashlib.md5(text.encode()).hexdigest()[:8]}.mp3"
107
  communicate = edge_tts.Communicate(text, voice)
108
  await communicate.save(filename)
109
  if os.path.exists(filename) and os.path.getsize(filename) > 0:
 
116
  st.audio(file_path)
117
  st.markdown(get_download_link(file_path), unsafe_allow_html=True)
118
 
119
+ # Chat and Quest Log
120
  async def save_chat_entry(username, message, voice, is_markdown=False):
121
  if not message.strip() or message == st.session_state.last_transcript:
122
  return None, None
123
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
124
+ entry = f"[{timestamp}] {username}: {message}" if not is_markdown else f"[{timestamp}] {username}:\n```markdown\n{message}\n```"
 
125
  md_file = create_file(entry, username, "md")
126
  with open(CHAT_FILE, 'a') as f:
127
  f.write(f"{entry}\n")
128
  audio_file = await async_edge_tts_generate(message, voice, username)
129
+ await broadcast_message(f"{username}|{message}", "quest")
 
130
  st.session_state.chat_history.append(entry)
131
  st.session_state.last_transcript = message
132
+ st.session_state.score += 10 # Points for participation
133
+ st.session_state.treasures += 1 # Audio treasure collected
134
  return md_file, audio_file
135
 
136
  async def load_chat():
137
  if not os.path.exists(CHAT_FILE):
138
  with open(CHAT_FILE, 'a') as f:
139
+ f.write(f"# {GAME_NAME} Log\n\nThe adventure begins at {START_LOCATION}! ๐Ÿ”๏ธ\n")
140
  with open(CHAT_FILE, 'r') as f:
141
  content = f.read().strip()
142
+ return content.split('\n')
 
143
 
144
+ # ArXiv Integration
145
  async def perform_arxiv_search(query, username):
146
  gradio_client = Client("awacke1/Arxiv-Paper-Search-And-QA-RAG-Pattern")
147
  refs = gradio_client.predict(
148
+ query, 5, "Semantic Search", "mistralai/Mixtral-8x7B-Instruct-v0.1", api_name="/update_with_rag_md"
149
  )[0]
150
+ result = f"๐Ÿ“š Ancient Rocky Knowledge:\n{refs}"
151
+ voice = CHARACTERS[username]["voice"]
152
  md_file, audio_file = await save_chat_entry(username, result, voice, True)
153
  return md_file, audio_file
154
 
155
+ # WebSocket for Multiplayer
156
  async def websocket_handler(websocket, path):
157
  client_id = str(uuid.uuid4())
158
+ room_id = "quest"
159
  if room_id not in st.session_state.active_connections:
160
  st.session_state.active_connections[room_id] = {}
161
  st.session_state.active_connections[room_id][client_id] = websocket
162
+ username = st.session_state.get('username', random.choice(list(CHARACTERS.keys())))
163
+ await save_chat_entry(username, f"๐Ÿ—บ๏ธ Joins the quest at {START_LOCATION}!", CHARACTERS[username]["voice"])
164
  try:
165
  async for message in websocket:
166
  if '|' in message:
167
  username, content = message.split('|', 1)
168
+ voice = CHARACTERS.get(username, {"voice": "en-US-AriaNeural"})["voice"]
169
  await save_chat_entry(username, content, voice)
170
+ await perform_arxiv_search(content, username) # ArXiv response for every chat
171
  except websockets.ConnectionClosed:
172
+ await save_chat_entry(username, "๐Ÿƒ Leaves the quest!", CHARACTERS[username]["voice"])
173
  finally:
174
  if room_id in st.session_state.active_connections and client_id in st.session_state.active_connections[room_id]:
175
  del st.session_state.active_connections[room_id][client_id]
 
197
  asyncio.set_event_loop(loop)
198
  loop.run_until_complete(run_websocket_server())
199
 
200
+ # Game Quest (Mad Libs)
201
+ def generate_quest_story(username, inputs):
202
+ locations = ["Peak Summit ๐Ÿž๏ธ", "Elk Valley ๐ŸŒฒ", "Meme Cave ๐Ÿ•ณ๏ธ", "Tech Outpost ๐Ÿ’ป"]
203
+ st.session_state.location = random.choice(locations)
204
  story = f"""
205
+ ๐ŸŒ„ **{username}โ€™s Quest at {st.session_state.location}:**
206
+ ๐Ÿ—บ๏ธ {username} discovers {inputs['quantity']} {inputs['plural_noun']}!
207
+ ๐ŸŒŸ Theyโ€™re {inputs['adjective']} and {inputs['action']} everywhere.
208
+ ๐ŸŽฏ With a {inputs['tool']}, {username} faces a {inputs['creature']}
209
+ โšก that {inputs['event']}โ€”shouting โ€œ{inputs['shout']}!โ€
210
+ ๐ŸŽ‰ Victory earns {username} {st.session_state.treasures} audio treasures!
 
 
 
211
  """
212
  return story.strip()
213
 
214
+ # Main Game Loop
215
  def main():
216
  init_session_state()
 
217
  saved_username = load_username()
218
+ if saved_username and saved_username in CHARACTERS:
219
  st.session_state.username = saved_username
220
  if not st.session_state.username:
221
+ st.session_state.username = random.choice(list(CHARACTERS.keys()))
222
+ asyncio.run(save_chat_entry(st.session_state.username, "๐Ÿ—บ๏ธ Begins the Rocky Mountain Quest!", CHARACTERS[st.session_state.username]["voice"]))
 
223
  save_username(st.session_state.username)
224
 
225
+ st.title(f"๐ŸŽฎ {GAME_NAME}")
226
+ st.subheader(f"๐ŸŒ„ {st.session_state.username}โ€™s Adventure - Score: {st.session_state.score} ๐Ÿ†")
227
+ chat_text = " ".join([line.split(": ")[-1] for line in asyncio.run(load_chat()) if ": " in line][-5:])
228
+ streamlit_marquee(content=f"๐Ÿ”๏ธ {st.session_state.location} | ๐ŸŽ™๏ธ {st.session_state.username} | ๐Ÿ’ฌ {chat_text}",
229
+ **st.session_state['marquee_settings'], key="quest_marquee")
230
 
231
+ tab_main = st.radio("๐ŸŽฒ Quest Actions:", ["๐Ÿ—ฃ๏ธ Explore & Chat", "๐ŸŽต Treasure Vault", "๐Ÿ—บ๏ธ Quest Challenge"], horizontal=True)
 
232
 
233
+ if tab_main == "๐Ÿ—ฃ๏ธ Explore & Chat":
234
+ st.subheader(f"๐Ÿ—ฃ๏ธ Explore {st.session_state.location} ๐ŸŽ™๏ธ")
235
  chat_content = asyncio.run(load_chat())
236
+ st.text_area("๐Ÿ“œ Quest Log", "\n".join(chat_content[-10:]), height=200, disabled=True)
237
+
238
+ message = st.text_input(f"๐Ÿ—จ๏ธ {st.session_state.username} says:", placeholder="Explore the wilds! ๐ŸŒฒ")
239
+ if st.button("๐ŸŒŸ Send & Explore ๐ŸŽค"):
240
+ if message:
241
+ voice = CHARACTERS[st.session_state.username]["voice"]
242
+ md_file, audio_file = asyncio.run(save_chat_entry(st.session_state.username, message, voice))
243
+ if audio_file:
244
+ play_and_download_audio(audio_file)
245
+ st.success(f"๐ŸŒ„ +10 points! New Score: {st.session_state.score}")
246
+
247
+ elif tab_main == "๐ŸŽต Treasure Vault":
248
+ st.subheader("๐ŸŽต Audio Treasure Vault ๐Ÿ†")
249
+ mp3_files = sorted(glob.glob("*.mp3"), key=os.path.getmtime, reverse=True)
 
 
 
 
250
  if mp3_files:
251
+ st.write(f"๐Ÿ… Treasures Collected: {st.session_state.treasures}")
252
+ for i, mp3 in enumerate(mp3_files[:10]):
253
+ with st.expander(f"๐ŸŽต Treasure #{i+1}: {os.path.basename(mp3)}"):
254
  play_and_download_audio(mp3)
255
  else:
256
+ st.write("๐Ÿ” No treasures yetโ€”explore or complete quests to collect audio loot! ๐ŸŽค")
257
 
258
+ elif tab_main == "๐Ÿ—บ๏ธ Quest Challenge":
259
+ st.subheader("๐Ÿ—บ๏ธ Rocky Mountain Quest Challenge ๐ŸŒŸ")
260
+ st.write("Fill in the blanks to embark on a wild adventure!")
261
  inputs = {}
262
+ col1, col2 = st.columns(2)
263
  with col1:
264
+ inputs['quantity'] = st.text_input("๐Ÿ”ข How Many?", "3", key="quantity")
265
+ inputs['plural_noun'] = st.text_input("๐ŸŒ„ Things?", "elk", key="plural_noun")
266
+ inputs['adjective'] = st.text_input("โœจ Describe Them?", "wild", key="adjective")
267
+ inputs['action'] = st.text_input("๐Ÿƒ What They Do?", "running", key="action")
 
 
 
 
 
 
268
  with col2:
269
+ inputs['tool'] = st.text_input("๐Ÿ› ๏ธ Your Tool?", "map", key="tool")
270
+ inputs['creature'] = st.text_input("๐Ÿฆ‡ Encounter?", "bear", key="creature")
271
+ inputs['event'] = st.text_input("โšก What Happens?", "roars", key="event")
272
+ inputs['shout'] = st.text_input("๐Ÿ—ฃ๏ธ Your Cry?", "Yeehaw!", key="shout")
273
+
274
+ if st.button("๐ŸŽ‰ Start Quest! ๐ŸŽค"):
275
+ story = generate_quest_story(st.session_state.username, inputs)
276
+ st.markdown(f"### ๐ŸŒ„ {st.session_state.username}โ€™s Quest Log")
 
 
 
 
 
 
277
  st.write(story)
278
+ voice = CHARACTERS[st.session_state.username]["voice"]
279
  md_file, audio_file = asyncio.run(save_chat_entry(st.session_state.username, story, voice, True))
280
  if audio_file:
281
  play_and_download_audio(audio_file)
282
+ st.session_state.score += 50 # Bonus for quest completion
283
+ st.success(f"๐Ÿ† Quest Complete! +50 points! New Score: {st.session_state.score}")
284
 
285
+ # Sidebar: Game HUD
286
+ st.sidebar.subheader("๐ŸŽฎ Adventurerโ€™s HUD")
287
+ new_username = st.sidebar.selectbox("๐Ÿง™โ€โ™‚๏ธ Choose Your Hero", list(CHARACTERS.keys()), index=list(CHARACTERS.keys()).index(st.session_state.username))
288
  if new_username != st.session_state.username:
289
+ asyncio.run(save_chat_entry(st.session_state.username, f"๐Ÿ”„ Transforms into {new_username}!", CHARACTERS[st.session_state.username]["voice"]))
290
  st.session_state.username = new_username
 
291
  save_username(st.session_state.username)
292
  st.rerun()
293
+ st.sidebar.write(f"๐Ÿ“œ {CHARACTERS[st.session_state.username]['desc']}")
294
+ st.sidebar.write(f"๐Ÿ“ Location: {st.session_state.location}")
295
+ st.sidebar.write(f"๐Ÿ… Score: {st.session_state.score}")
296
+ st.sidebar.write(f"๐ŸŽต Treasures: {st.session_state.treasures}")
297
+
298
+ if not st.session_state.get('server_running', False):
299
+ st.session_state.server_task = threading.Thread(target=start_websocket_server, daemon=True)
300
+ st.session_state.server_task.start()
301
 
302
+ if __name__ == "__main__":
303
+ main()