FlameF0X commited on
Commit
9f2377e
·
verified ·
1 Parent(s): 60bbdd3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +34 -27
app.py CHANGED
@@ -4,23 +4,29 @@ from datetime import datetime
4
  from collections import deque
5
 
6
  # --- GLOBAL SERVER STATE (In-Memory Only) ---
7
- # We use a simple dictionary to store the last 50 messages for active channels.
8
- # When the Space restarts or sleeps, all data is wiped.
9
  class ChatServer:
10
  def __init__(self):
11
- # channels = { "channel_name": deque([messages], maxlen=50) }
12
  self.channels = {"Main": deque(maxlen=50)}
13
- # active_users = { "username": {"last_seen": timestamp, "profile_pic": url} }
14
  self.active_users = {}
15
- # dms = { "user1-user2": deque(maxlen=50) }
16
  self.dms = {}
 
 
17
 
18
- def prune_users(self):
19
- """Remove users who haven't polled in 30 seconds."""
20
  now = time.time()
21
  expired = [u for u, data in self.active_users.items() if now - data['last_seen'] > 30]
22
  for u in expired:
23
- del self.active_users[u]
 
 
 
 
 
 
 
 
 
24
 
25
  def broadcast(self, channel, user, message, profile_pic):
26
  if channel not in self.channels:
@@ -70,7 +76,7 @@ def get_messages_html(messages):
70
  return html
71
 
72
  def get_active_users_html(current_user):
73
- server.prune_users()
74
  html = "<div style='display: flex; flex-direction: column; gap: 5px;'>"
75
  for user, data in server.active_users.items():
76
  status = "🟢" if user != current_user else "👤 (You)"
@@ -84,15 +90,16 @@ def get_active_users_html(current_user):
84
  html += "</div>"
85
  return html
86
 
 
 
 
 
 
 
 
87
  # --- GRADIO INTERFACE ---
88
 
89
- with gr.Blocks(css="""
90
- #chat-container { height: 500px; overflow-y: auto; border: 1px solid #ddd; padding: 15px; border-radius: 8px; background: var(--body-background-fill); }
91
- .user-list { border-left: 1px solid #ddd; padding-left: 15px; }
92
- #msg-input input { border-radius: 20px !important; }
93
- """) as demo:
94
-
95
- # HF OAuth session data
96
  user_session = gr.State(None)
97
  current_room = gr.State("Main")
98
  dm_target = gr.State(None)
@@ -104,7 +111,6 @@ with gr.Blocks(css="""
104
  login_btn = gr.LoginButton()
105
 
106
  with gr.Row(visible=False) as chat_ui:
107
- # Sidebar
108
  with gr.Column(scale=2, variant="panel"):
109
  gr.Markdown("### 🏠 Rooms")
110
  room_list = gr.Radio(["Main"], value="Main", label="Join a Channel")
@@ -118,7 +124,6 @@ with gr.Blocks(css="""
118
  gr.Markdown("### 👥 Online Now")
119
  active_users_box = gr.HTML()
120
 
121
- # Main Chat Area
122
  with gr.Column(scale=8):
123
  room_title = gr.Markdown("## # Main")
124
  chat_display = gr.HTML(elem_id="chat-container")
@@ -132,8 +137,6 @@ with gr.Blocks(css="""
132
  )
133
  send_btn = gr.Button("Send", scale=1, variant="primary")
134
 
135
- # --- LOGIC ---
136
-
137
  def on_load(profile: gr.OAuthProfile | None):
138
  if profile is None:
139
  return gr.update(visible=False), None
@@ -141,17 +144,20 @@ with gr.Blocks(css="""
141
  username = profile.username
142
  pic = profile.profile_image or "https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
143
 
144
- # Register user
145
  server.active_users[username] = {"last_seen": time.time(), "pic": pic}
 
146
  return gr.update(visible=True), {"name": username, "pic": pic}
147
 
148
  demo.load(on_load, None, [chat_ui, user_session])
149
 
150
  def refresh_chat(session, room, dm_user):
151
- if not session: return "", ""
152
 
153
- # Heartbeat
154
  server.active_users[session['name']]['last_seen'] = time.time()
 
 
 
 
155
 
156
  if dm_user:
157
  dm_key = "-".join(sorted([session['name'], dm_user]))
@@ -168,7 +174,6 @@ with gr.Blocks(css="""
168
 
169
  def send_msg(text, session, room, dm_user):
170
  if not text or not session: return ""
171
-
172
  if dm_user:
173
  server.send_dm(session['name'], dm_user, text, session['pic'])
174
  else:
@@ -179,9 +184,11 @@ with gr.Blocks(css="""
179
  msg_input.submit(send_msg, [msg_input, user_session, current_room, dm_target], [msg_input])
180
 
181
  def create_and_join(name, current_options):
182
- if not name: return gr.update(), "Main"
183
  clean_name = name.strip().replace(" ", "-")
184
- new_options = list(set(current_options + [clean_name]))
 
 
185
  return gr.update(choices=new_options, value=clean_name), clean_name, None
186
 
187
  create_room_btn.click(create_and_join, [new_room_name, room_list], [room_list, current_room, dm_target])
@@ -197,4 +204,4 @@ with gr.Blocks(css="""
197
 
198
  start_dm_btn.click(start_dm, dm_user_input, [current_room, dm_target])
199
 
200
- demo.launch()
 
4
  from collections import deque
5
 
6
  # --- GLOBAL SERVER STATE (In-Memory Only) ---
 
 
7
  class ChatServer:
8
  def __init__(self):
 
9
  self.channels = {"Main": deque(maxlen=50)}
 
10
  self.active_users = {}
 
11
  self.dms = {}
12
+ # Track which users are in which room to delete empty rooms
13
+ self.user_rooms = {}
14
 
15
+ def prune_server(self):
16
+ """Remove inactive users and delete empty custom rooms."""
17
  now = time.time()
18
  expired = [u for u, data in self.active_users.items() if now - data['last_seen'] > 30]
19
  for u in expired:
20
+ if u in self.active_users:
21
+ del self.active_users[u]
22
+ if u in self.user_rooms:
23
+ del self.user_rooms[u]
24
+
25
+ # Delete rooms with no active occupants (except Main)
26
+ active_rooms = set(self.user_rooms.values())
27
+ rooms_to_delete = [r for r in self.channels.keys() if r != "Main" and r not in active_rooms]
28
+ for r in rooms_to_delete:
29
+ del self.channels[r]
30
 
31
  def broadcast(self, channel, user, message, profile_pic):
32
  if channel not in self.channels:
 
76
  return html
77
 
78
  def get_active_users_html(current_user):
79
+ server.prune_server()
80
  html = "<div style='display: flex; flex-direction: column; gap: 5px;'>"
81
  for user, data in server.active_users.items():
82
  status = "🟢" if user != current_user else "👤 (You)"
 
90
  html += "</div>"
91
  return html
92
 
93
+ # --- CSS STYLES ---
94
+ custom_css = """
95
+ #chat-container { height: 500px; overflow-y: auto; border: 1px solid #ddd; padding: 15px; border-radius: 8px; background: var(--body-background-fill); }
96
+ .user-list { border-left: 1px solid #ddd; padding-left: 15px; }
97
+ #msg-input input { border-radius: 20px !important; }
98
+ """
99
+
100
  # --- GRADIO INTERFACE ---
101
 
102
+ with gr.Blocks() as demo:
 
 
 
 
 
 
103
  user_session = gr.State(None)
104
  current_room = gr.State("Main")
105
  dm_target = gr.State(None)
 
111
  login_btn = gr.LoginButton()
112
 
113
  with gr.Row(visible=False) as chat_ui:
 
114
  with gr.Column(scale=2, variant="panel"):
115
  gr.Markdown("### 🏠 Rooms")
116
  room_list = gr.Radio(["Main"], value="Main", label="Join a Channel")
 
124
  gr.Markdown("### 👥 Online Now")
125
  active_users_box = gr.HTML()
126
 
 
127
  with gr.Column(scale=8):
128
  room_title = gr.Markdown("## # Main")
129
  chat_display = gr.HTML(elem_id="chat-container")
 
137
  )
138
  send_btn = gr.Button("Send", scale=1, variant="primary")
139
 
 
 
140
  def on_load(profile: gr.OAuthProfile | None):
141
  if profile is None:
142
  return gr.update(visible=False), None
 
144
  username = profile.username
145
  pic = profile.profile_image or "https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
146
 
 
147
  server.active_users[username] = {"last_seen": time.time(), "pic": pic}
148
+ server.user_rooms[username] = "Main"
149
  return gr.update(visible=True), {"name": username, "pic": pic}
150
 
151
  demo.load(on_load, None, [chat_ui, user_session])
152
 
153
  def refresh_chat(session, room, dm_user):
154
+ if not session: return "", "", ""
155
 
 
156
  server.active_users[session['name']]['last_seen'] = time.time()
157
+ server.user_rooms[session['name']] = room if not dm_user else "DM"
158
+
159
+ # Update room list options dynamically
160
+ available_rooms = list(server.channels.keys())
161
 
162
  if dm_user:
163
  dm_key = "-".join(sorted([session['name'], dm_user]))
 
174
 
175
  def send_msg(text, session, room, dm_user):
176
  if not text or not session: return ""
 
177
  if dm_user:
178
  server.send_dm(session['name'], dm_user, text, session['pic'])
179
  else:
 
184
  msg_input.submit(send_msg, [msg_input, user_session, current_room, dm_target], [msg_input])
185
 
186
  def create_and_join(name, current_options):
187
+ if not name: return gr.update(), "Main", None
188
  clean_name = name.strip().replace(" ", "-")
189
+ if clean_name not in server.channels:
190
+ server.channels[clean_name] = deque(maxlen=50)
191
+ new_options = list(server.channels.keys())
192
  return gr.update(choices=new_options, value=clean_name), clean_name, None
193
 
194
  create_room_btn.click(create_and_join, [new_room_name, room_list], [room_list, current_room, dm_target])
 
204
 
205
  start_dm_btn.click(start_dm, dm_user_input, [current_room, dm_target])
206
 
207
+ demo.launch(css=custom_css)