awacke1 commited on
Commit
0112ded
Β·
verified Β·
1 Parent(s): 9b9db0f

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +342 -0
app.py ADDED
@@ -0,0 +1,342 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
17
+ # App Configuration
18
+ Site_Name = 'πŸ€–πŸ§ MMO Chat BrainπŸ“πŸ”¬'
19
+ title = "πŸ€–πŸ§ MMO Chat BrainπŸ“πŸ”¬"
20
+ icons = 'πŸ€–πŸ§ πŸ”¬πŸ“'
21
+ START_ROOM = "Sector 🌌"
22
+
23
+ st.set_page_config(
24
+ page_title=title,
25
+ page_icon=icons,
26
+ layout="wide",
27
+ initial_sidebar_state="auto"
28
+ )
29
+
30
+ # Fun usernames with emojis and rhyming labels
31
+ FUN_USERNAMES = [
32
+ "CosmicJester 🌌", "PixelPanda 🐼", "QuantumQuack πŸ¦†", "StellarSquirrel 🐿️",
33
+ "GizmoGuru βš™οΈ", "NebulaNinja 🌠", "ByteBuster πŸ’Ύ", "GalacticGopher 🌍",
34
+ "RocketRaccoon πŸš€", "EchoElf 🧝", "PhantomFox 🦊", "WittyWizard πŸ§™",
35
+ "LunarLlama πŸŒ™", "SolarSloth β˜€οΈ", "AstroAlpaca πŸ¦™", "CyberCoyote 🐺",
36
+ "MysticMoose 🦌", "GlitchGnome 🧚", "VortexViper 🐍", "ChronoChimp πŸ’"
37
+ ]
38
+
39
+ # Directories and files
40
+ CHAT_DIR = "chat_logs"
41
+ VOTE_DIR = "vote_logs"
42
+ STATE_FILE = "user_state.txt"
43
+ os.makedirs(CHAT_DIR, exist_ok=True)
44
+ os.makedirs(VOTE_DIR, exist_ok=True)
45
+
46
+ CHAT_FILE = os.path.join(CHAT_DIR, "global_chat.md")
47
+ QUOTE_VOTES_FILE = os.path.join(VOTE_DIR, "quote_votes.md")
48
+ MEDIA_VOTES_FILE = os.path.join(VOTE_DIR, "media_votes.md")
49
+ HISTORY_FILE = os.path.join(VOTE_DIR, "vote_history.md")
50
+
51
+ # Unicode digits and fonts
52
+ UNICODE_DIGITS = {i: f"{i}\uFE0F⃣" for i in range(10)}
53
+ UNICODE_FONTS = [
54
+ ("Normal", lambda x: x),
55
+ ("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)),
56
+ # ... (other font styles remain the same)
57
+ ]
58
+
59
+ server_running = False
60
+ server_task = None
61
+
62
+ def get_node_name():
63
+ parser = argparse.ArgumentParser(description='Start a chat node with a specific name')
64
+ parser.add_argument('--node-name', type=str, default=None)
65
+ parser.add_argument('--port', type=int, default=8501)
66
+ args = parser.parse_args()
67
+ return args.node_name or f"node-{uuid.uuid4().hex[:8]}", args.port
68
+
69
+ def save_chat_entry(username, message):
70
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
71
+ entry = f"[{timestamp}] {username}: {message}"
72
+ with open(CHAT_FILE, 'a') as f:
73
+ f.write(f"{entry}\n")
74
+
75
+ def load_chat():
76
+ if not os.path.exists(CHAT_FILE):
77
+ with open(CHAT_FILE, 'w') as f:
78
+ f.write(f"# {START_ROOM} Chat\n\nWelcome to the cosmic hub - start chatting! 🎀\n")
79
+ with open(CHAT_FILE, 'r') as f:
80
+ content = f.read()
81
+ lines = content.strip().split('\n')
82
+ return "\n".join(f"{i+1}. {line}" for i, line in enumerate(lines) if line.strip())
83
+
84
+ def get_user_list(chat_content):
85
+ users = set()
86
+ for line in chat_content.split('\n'):
87
+ if line.strip() and ': ' in line:
88
+ user = line.split(': ')[1].split(' ')[0]
89
+ users.add(user)
90
+ return sorted(list(users))
91
+
92
+ def has_joined_before(client_id, chat_content):
93
+ return any(f"Client-{client_id} has joined" in line for line in chat_content.split('\n'))
94
+
95
+ def get_message_suggestions(chat_content, prefix):
96
+ lines = chat_content.split('\n')
97
+ messages = [line.split(': ', 1)[1] for line in lines if ': ' in line and line.strip()]
98
+ return [msg for msg in messages if msg.lower().startswith(prefix.lower())][:5]
99
+
100
+ def load_quotes(source="famous"):
101
+ famous_quotes = [
102
+ "The true sign of intelligence is not knowledge but imagination. – Albert Einstein",
103
+ # ... (other quotes remain the same)
104
+ ]
105
+ custom_quotes = [
106
+ "Every age unfolds a new lesson. Life's chapters evolve, each teaching us anew.",
107
+ # ... (other custom quotes remain the same)
108
+ ]
109
+ return famous_quotes if source == "famous" else custom_quotes
110
+
111
+ def save_vote(file, item, user_hash):
112
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
113
+ entry = f"[{timestamp}] {user_hash} voted for {item}"
114
+ with open(file, 'a') as f:
115
+ f.write(f"{entry}\n")
116
+ with open(HISTORY_FILE, 'a') as f:
117
+ f.write(f"- {timestamp} - User {user_hash} voted for {item}\n")
118
+
119
+ def load_votes(file):
120
+ if not os.path.exists(file):
121
+ with open(file, 'w') as f:
122
+ f.write("# Vote Tally\n\nNo votes yet - get clicking! πŸ–±οΈ\n")
123
+ with open(file, 'r') as f:
124
+ lines = f.read().strip().split('\n')[2:] # Skip header
125
+ votes = {}
126
+ user_votes = set()
127
+ for line in lines:
128
+ if line.strip() and 'voted for' in line:
129
+ user_hash = line.split('] ')[1].split(' voted for ')[0]
130
+ item = line.split('voted for ')[1]
131
+ vote_key = f"{user_hash}-{item}"
132
+ if vote_key not in user_votes:
133
+ votes[item] = votes.get(item, 0) + 1
134
+ user_votes.add(vote_key)
135
+ return votes
136
+
137
+ def generate_user_hash():
138
+ if 'user_hash' not in st.session_state:
139
+ st.session_state.user_hash = hashlib.md5(str(random.getrandbits(128)).encode()).hexdigest()[:8]
140
+ return st.session_state.user_hash
141
+
142
+ def save_pasted_image(image_data):
143
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
144
+ filename = f"paste_{timestamp}.png"
145
+ filepath = os.path.join('./', filename)
146
+ if ',' in image_data:
147
+ image_data = image_data.split(',')[1]
148
+ img_bytes = base64.b64decode(image_data)
149
+ img = Image.open(io.BytesIO(img_bytes))
150
+ img.save(filepath, "PNG")
151
+ return filename
152
+
153
+ def get_video_html(video_path, width="100%"):
154
+ video_url = f"data:video/mp4;base64,{base64.b64encode(open(video_path, 'rb').read()).decode()}"
155
+ 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>'
156
+
157
+ def get_audio_html(audio_path, width="100%"):
158
+ audio_url = f"data:audio/mpeg;base64,{base64.b64encode(open(audio_path, 'rb').read()).decode()}"
159
+ return f'<audio controls style="width: {width};"><source src="{audio_url}" type="audio/mpeg">Your browser does not support the audio element.</audio>'
160
+
161
+ active_connections = {}
162
+
163
+ async def websocket_handler(websocket, path):
164
+ try:
165
+ client_id = str(uuid.uuid4())
166
+ room_id = "chat"
167
+ active_connections.setdefault(room_id, {})[client_id] = websocket
168
+ chat_content = load_chat()
169
+ username = st.session_state.get('username', random.choice(FUN_USERNAMES))
170
+ if not has_joined_before(client_id, chat_content):
171
+ save_chat_entry(f"Client-{client_id}", f"{username} has joined {START_ROOM}!")
172
+ async for message in websocket:
173
+ parts = message.split('|', 1)
174
+ if len(parts) == 2:
175
+ username, content = parts
176
+ save_chat_entry(username, content)
177
+ await broadcast_message(f"{username}|{content}", room_id)
178
+ except websockets.ConnectionClosed:
179
+ pass
180
+ finally:
181
+ if room_id in active_connections and client_id in active_connections[room_id]:
182
+ del active_connections[room_id][client_id]
183
+
184
+ async def broadcast_message(message, room_id):
185
+ if room_id in active_connections:
186
+ disconnected = []
187
+ for client_id, ws in active_connections[room_id].items():
188
+ try:
189
+ await ws.send(message)
190
+ except websockets.ConnectionClosed:
191
+ disconnected.append(client_id)
192
+ for client_id in disconnected:
193
+ del active_connections[room_id][client_id]
194
+
195
+ async def run_websocket_server():
196
+ global server_running, server_task
197
+ if not server_running:
198
+ server = await websockets.serve(websocket_handler, '0.0.0.0', 8765)
199
+ server_running = True
200
+ await server.wait_closed()
201
+
202
+ def create_streamlit_interface():
203
+ st.markdown("""
204
+ <style>
205
+ .chat-box {font-family: monospace; background: #1e1e1e; color: #d4d4d4; padding: 10px; border-radius: 5px; height: 300px; overflow-y: auto;}
206
+ .timer {font-size: 24px; color: #ffcc00; text-align: center; animation: pulse 1s infinite;}
207
+ @keyframes pulse {0% {transform: scale(1);} 50% {transform: scale(1.1);} 100% {transform: scale(1);}}
208
+ </style>
209
+ <script>
210
+ document.addEventListener('paste', function(e) {
211
+ const items = (e.clipboardData || window.clipboardData).items;
212
+ for (let i = 0; i < items.length; i++) {
213
+ if (items[i].type.indexOf('image') !== -1) {
214
+ const blob = items[i].getAsFile();
215
+ const reader = new FileReader();
216
+ reader.onload = function(event) {
217
+ const imageData = event.target.result;
218
+ sessionStorage.setItem('pastedImage', imageData);
219
+ document.getElementById('message_input').value = 'PastedImage:' + blob.name;
220
+ document.getElementById('send_button').click();
221
+ };
222
+ reader.readAsDataURL(blob);
223
+ }
224
+ }
225
+ });
226
+ </script>
227
+ """, unsafe_allow_html=True)
228
+
229
+ st.title(f"{Site_Name}")
230
+ st.markdown(f"Welcome to {START_ROOM} - chat, vote, upload, and enjoy! πŸŽ‰")
231
+
232
+ # Initialize client ID and username
233
+ if 'client_id' not in st.session_state:
234
+ st.session_state.client_id = str(uuid.uuid4())
235
+ if 'username' not in st.session_state:
236
+ available_names = [name for name in FUN_USERNAMES if not any(f"{name} has joined" in line for line in load_chat().split('\n'))]
237
+ st.session_state.username = random.choice(available_names) if available_names else random.choice(FUN_USERNAMES)
238
+
239
+ # Session state initialization
240
+ if 'refresh_rate' not in st.session_state:
241
+ st.session_state.refresh_rate = 5
242
+ if 'timer_start' not in st.session_state:
243
+ st.session_state.timer_start = time.time()
244
+ if 'quote_index' not in st.session_state:
245
+ quotes = load_quotes("famous")
246
+ st.session_state.quote_index = random.randint(0, max(0, len(quotes) - 1)) if quotes else 0
247
+ if 'quote_source' not in st.session_state:
248
+ st.session_state.quote_source = "famous"
249
+
250
+ # Chat section
251
+ st.subheader(f"{START_ROOM} Chat πŸ’¬")
252
+ chat_content = load_chat()
253
+ for i, line in enumerate(chat_content.split('\n')):
254
+ if line.strip() and ': ' in line:
255
+ col1, col2 = st.columns([5, 1])
256
+ with col1:
257
+ st.markdown(line)
258
+ with col2:
259
+ if st.button(f"πŸ‘", key=f"chat_vote_{i}"):
260
+ save_vote(QUOTE_VOTES_FILE, line, generate_user_hash())
261
+ st.rerun()
262
+
263
+ # Username change dropdown
264
+ new_username = st.selectbox("Change Name", [""] + FUN_USERNAMES, index=0)
265
+ if new_username and new_username != st.session_state.username:
266
+ save_chat_entry("System 🌟", f"{st.session_state.username} changed name to {new_username}")
267
+ st.session_state.username = new_username
268
+ st.rerun()
269
+
270
+ # Message input
271
+ message = st.text_input(f"Message as {st.session_state.username}", key="message_input")
272
+ if st.button("Send πŸš€", key="send_button") and message.strip():
273
+ if message.startswith("PastedImage:"):
274
+ image_data = sessionStorage.get('pastedImage')
275
+ if image_data:
276
+ filename = save_pasted_image(image_data)
277
+ if filename:
278
+ save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
279
+ else:
280
+ save_chat_entry(st.session_state.username, message)
281
+ st.rerun()
282
+
283
+ # Media section with upload and delete
284
+ st.subheader("Media Gallery 🎨🎢πŸŽ₯")
285
+ uploaded_file = st.file_uploader("Upload Media", type=['png', 'jpg', 'mp3', 'mp4'])
286
+ if uploaded_file:
287
+ file_path = os.path.join('./', uploaded_file.name)
288
+ with open(file_path, 'wb') as f:
289
+ f.write(uploaded_file.getbuffer())
290
+ st.success(f"Uploaded {uploaded_file.name}")
291
+
292
+ media_files = glob.glob("./*.png") + glob.glob("./*.jpg") + glob.glob("./*.mp3") + glob.glob("./*.mp4")
293
+ if media_files:
294
+ cols = st.columns(3)
295
+ media_votes = load_votes(MEDIA_VOTES_FILE)
296
+ for idx, media_file in enumerate(media_files):
297
+ with cols[idx % 3]:
298
+ if media_file.endswith(('.png', '.jpg')):
299
+ st.image(media_file, use_container_width=True)
300
+ elif media_file.endswith('.mp3'):
301
+ st.markdown(get_audio_html(media_file), unsafe_allow_html=True)
302
+ elif media_file.endswith('.mp4'):
303
+ st.markdown(get_video_html(media_file), unsafe_allow_html=True)
304
+ vote_count = media_votes.get(media_file, 0)
305
+ col1, col2 = st.columns(2)
306
+ with col1:
307
+ if st.button(f"πŸ‘ {vote_count}", key=f"media_vote_{idx}"):
308
+ save_vote(MEDIA_VOTES_FILE, media_file, generate_user_hash())
309
+ st.rerun()
310
+ with col2:
311
+ if st.button("πŸ—‘οΈ", key=f"media_delete_{idx}"):
312
+ os.remove(media_file)
313
+ st.rerun()
314
+
315
+ # Refresh timer
316
+ st.subheader("Refresh ⏳")
317
+ refresh_rate = st.slider("Refresh Rate", 1, 300, st.session_state.refresh_rate)
318
+ st.session_state.refresh_rate = refresh_rate
319
+ timer_placeholder = st.empty()
320
+ for i in range(st.session_state.refresh_rate, -1, -1):
321
+ font_name, font_func = random.choice(UNICODE_FONTS)
322
+ countdown_str = "".join(UNICODE_DIGITS[int(d)] for d in str(i)) if i < 10 else font_func(str(i))
323
+ timer_placeholder.markdown(f"<p class='timer'>⏳ {font_func('Refresh in:')} {countdown_str}</p>", unsafe_allow_html=True)
324
+ time.sleep(1)
325
+ st.rerun()
326
+
327
+ # Sidebar vote stats
328
+ st.sidebar.subheader("Vote Counts")
329
+ chat_votes = load_votes(QUOTE_VOTES_FILE)
330
+ media_votes = load_votes(MEDIA_VOTES_FILE)
331
+ for item, count in {**chat_votes, **media_votes}.items():
332
+ st.sidebar.write(f"{item}: {count} votes")
333
+
334
+ async def main():
335
+ global NODE_NAME, server_task
336
+ NODE_NAME, port = get_node_name()
337
+ if server_task is None:
338
+ server_task = asyncio.create_task(run_websocket_server())
339
+ create_streamlit_interface()
340
+
341
+ if __name__ == "__main__":
342
+ asyncio.run(main())