FlameF0X commited on
Commit
e5805a5
·
verified ·
1 Parent(s): 57d02a1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +27 -22
app.py CHANGED
@@ -2,6 +2,7 @@ import gradio as gr
2
  import time
3
  from datetime import datetime
4
  from collections import deque
 
5
 
6
  # --- GLOBAL SERVER STATE (In-Memory Only) ---
7
  class ChatServer:
@@ -21,16 +22,24 @@ class ChatServer:
21
  if u in self.user_rooms:
22
  del self.user_rooms[u]
23
 
 
 
 
 
 
 
 
24
  def _add_message_to_queue(self, queue, user, message, profile_pic):
25
- html_text = message.replace("\n", "<br>")
 
26
  # Stacking Logic: Check if last message was same user and same text
27
- if queue and queue[-1]['user'] == user and queue[-1]['text'] == html_text:
28
  queue[-1]['count'] = queue[-1].get('count', 1) + 1
29
- queue[-1]['time'] = datetime.now().strftime("%H:%M") # Update time to latest
30
  else:
31
  msg_data = {
32
  "user": user,
33
- "text": html_text,
34
  "pic": profile_pic,
35
  "time": datetime.now().strftime("%H:%M"),
36
  "count": 1
@@ -56,14 +65,14 @@ def get_messages_html(messages):
56
  if not messages:
57
  return "<div style='color: gray; text-align: center; margin-top: 20px;'>No messages yet.</div>"
58
 
59
- html = "<div id='chat-flow' style='display: flex; flex-direction: column; gap: 12px; font-family: sans-serif;'>"
60
  for m in messages:
61
  pic_url = m.get('pic') or "https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
62
  display_text = m['text']
63
  if m.get('count', 1) > 1:
64
  display_text += f" <span style='color: #888; font-size: 0.8em; font-weight: normal;'>(x{m['count']})</span>"
65
 
66
- html += f"""
67
  <div class="msg-row" style="display: flex; align-items: flex-start; gap: 10px; margin-bottom: 4px;">
68
  <img src="{pic_url}" style="width: 36px; height: 36px; border-radius: 50%; border: 1px solid rgba(255,255,255,0.1); flex-shrink: 0;">
69
  <div style="background: rgba(255, 255, 255, 0.07); padding: 10px 14px; border-radius: 0 16px 16px 16px; max-width: 85%;">
@@ -76,8 +85,9 @@ def get_messages_html(messages):
76
  </div>
77
  </div>
78
  """
79
- # Robust Auto-Scroll Script using MutationObserver
80
- html += """
 
81
  </div>
82
  <script>
83
  (function() {
@@ -89,33 +99,32 @@ def get_messages_html(messages):
89
  const observer = new MutationObserver(scrollDown);
90
  observer.observe(container, { childList: true, subtree: true });
91
  container.dataset.observed = "true";
92
- scrollDown(); // Initial scroll
93
  } else if (container) {
94
  container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' });
95
  }
96
  })();
97
  </script>
98
  """
99
- return html
100
 
101
  def get_active_users_html(current_user):
102
  server.prune_server()
103
- html = "<div style='display: flex; flex-direction: column; gap: 8px; font-family: sans-serif;'>"
104
  for user, data in server.active_users.items():
105
  pic = data.get('pic') or "https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
106
  label = f"{user}" + (" (You)" if user == current_user else "")
107
- html += f"""
108
  <div style="display: flex; align-items: center; gap: 10px; font-size: 0.85em; padding: 4px;">
109
  <img src="{pic}" style="width: 22px; height: 22px; border-radius: 50%;">
110
  <span style="color: #ccc;">{label}</span>
111
  <div style="width: 8px; height: 8px; background: #4ade80; border-radius: 50%; margin-left: auto;"></div>
112
  </div>
113
  """
114
- return html + "</div>"
115
 
116
  # --- CSS ---
117
  custom_css = """
118
- /* Modern Font Stack */
119
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
120
 
121
  body, .gradio-container, .main-title, #chat-container, #side-panel, textarea, button {
@@ -158,8 +167,7 @@ body, .gradio-container, .main-title, #chat-container, #side-panel, textarea, bu
158
  }
159
  """
160
 
161
- # Updated Blocks constructor for Gradio 6.0+ compatibility
162
- with gr.Blocks() as demo:
163
  user_session = gr.State(None)
164
  current_room = gr.State("Main")
165
  dm_target = gr.State(None)
@@ -190,7 +198,7 @@ with gr.Blocks() as demo:
190
 
191
  with gr.Row():
192
  msg_input = gr.Textbox(
193
- placeholder="Type a message...",
194
  show_label=False,
195
  scale=10,
196
  elem_id="msg-input"
@@ -226,6 +234,7 @@ with gr.Blocks() as demo:
226
 
227
  def send_msg(text, session, room, dm_user):
228
  if not text or not session: return ""
 
229
  if dm_user:
230
  server.send_dm(session['name'], dm_user, text, session['pic'])
231
  else:
@@ -246,8 +255,4 @@ with gr.Blocks() as demo:
246
  room_list.change(lambda name: (name, None), room_list, [current_room, dm_target])
247
  start_dm_btn.click(lambda target: ("Main", target), dm_user_input, [current_room, dm_target])
248
 
249
- # Pass theme and css to launch() for Gradio 6.0+
250
- demo.launch(
251
- theme=gr.themes.Soft(primary_hue="orange", neutral_hue="slate"),
252
- css=custom_css
253
- )
 
2
  import time
3
  from datetime import datetime
4
  from collections import deque
5
+ import html
6
 
7
  # --- GLOBAL SERVER STATE (In-Memory Only) ---
8
  class ChatServer:
 
22
  if u in self.user_rooms:
23
  del self.user_rooms[u]
24
 
25
+ def _sanitize(self, text):
26
+ """Prevents HTML injection by escaping user input."""
27
+ # Escape all HTML characters (&, <, >, ", ')
28
+ escaped = html.escape(text)
29
+ # Re-allow line breaks only
30
+ return escaped.replace("\n", "<br>")
31
+
32
  def _add_message_to_queue(self, queue, user, message, profile_pic):
33
+ safe_text = self._sanitize(message)
34
+
35
  # Stacking Logic: Check if last message was same user and same text
36
+ if queue and queue[-1]['user'] == user and queue[-1]['text'] == safe_text:
37
  queue[-1]['count'] = queue[-1].get('count', 1) + 1
38
+ queue[-1]['time'] = datetime.now().strftime("%H:%M")
39
  else:
40
  msg_data = {
41
  "user": user,
42
+ "text": safe_text,
43
  "pic": profile_pic,
44
  "time": datetime.now().strftime("%H:%M"),
45
  "count": 1
 
65
  if not messages:
66
  return "<div style='color: gray; text-align: center; margin-top: 20px;'>No messages yet.</div>"
67
 
68
+ html_output = "<div id='chat-flow' style='display: flex; flex-direction: column; gap: 12px; font-family: sans-serif;'>"
69
  for m in messages:
70
  pic_url = m.get('pic') or "https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
71
  display_text = m['text']
72
  if m.get('count', 1) > 1:
73
  display_text += f" <span style='color: #888; font-size: 0.8em; font-weight: normal;'>(x{m['count']})</span>"
74
 
75
+ html_output += f"""
76
  <div class="msg-row" style="display: flex; align-items: flex-start; gap: 10px; margin-bottom: 4px;">
77
  <img src="{pic_url}" style="width: 36px; height: 36px; border-radius: 50%; border: 1px solid rgba(255,255,255,0.1); flex-shrink: 0;">
78
  <div style="background: rgba(255, 255, 255, 0.07); padding: 10px 14px; border-radius: 0 16px 16px 16px; max-width: 85%;">
 
85
  </div>
86
  </div>
87
  """
88
+
89
+ # SYSTEM SCRIPT (Safe since it's hardcoded by us, not user input)
90
+ html_output += """
91
  </div>
92
  <script>
93
  (function() {
 
99
  const observer = new MutationObserver(scrollDown);
100
  observer.observe(container, { childList: true, subtree: true });
101
  container.dataset.observed = "true";
102
+ scrollDown();
103
  } else if (container) {
104
  container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' });
105
  }
106
  })();
107
  </script>
108
  """
109
+ return html_output
110
 
111
  def get_active_users_html(current_user):
112
  server.prune_server()
113
+ html_list = "<div style='display: flex; flex-direction: column; gap: 8px; font-family: sans-serif;'>"
114
  for user, data in server.active_users.items():
115
  pic = data.get('pic') or "https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
116
  label = f"{user}" + (" (You)" if user == current_user else "")
117
+ html_list += f"""
118
  <div style="display: flex; align-items: center; gap: 10px; font-size: 0.85em; padding: 4px;">
119
  <img src="{pic}" style="width: 22px; height: 22px; border-radius: 50%;">
120
  <span style="color: #ccc;">{label}</span>
121
  <div style="width: 8px; height: 8px; background: #4ade80; border-radius: 50%; margin-left: auto;"></div>
122
  </div>
123
  """
124
+ return html_list + "</div>"
125
 
126
  # --- CSS ---
127
  custom_css = """
 
128
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
129
 
130
  body, .gradio-container, .main-title, #chat-container, #side-panel, textarea, button {
 
167
  }
168
  """
169
 
170
+ with gr.Blocks(css=custom_css) as demo:
 
171
  user_session = gr.State(None)
172
  current_room = gr.State("Main")
173
  dm_target = gr.State(None)
 
198
 
199
  with gr.Row():
200
  msg_input = gr.Textbox(
201
+ placeholder="Type a message (HTML is disabled)...",
202
  show_label=False,
203
  scale=10,
204
  elem_id="msg-input"
 
234
 
235
  def send_msg(text, session, room, dm_user):
236
  if not text or not session: return ""
237
+ # The server class now handles sanitization
238
  if dm_user:
239
  server.send_dm(session['name'], dm_user, text, session['pic'])
240
  else:
 
255
  room_list.change(lambda name: (name, None), room_list, [current_room, dm_target])
256
  start_dm_btn.click(lambda target: ("Main", target), dm_user_input, [current_room, dm_target])
257
 
258
+ demo.launch()