feat: add /bridge/stats endpoint, go/emote MUD commands

- /bridge/stats returns uptime, sessions, messages, rooms, WS connections
- 'go <room>' moves user between rooms via HTTP (was WS-only)
- 'emote <action>' broadcasts third-person action to room occupants
- Both commands notify other room occupants via room_events
This commit is contained in:
Alexander Whitestone
2026-04-13 15:08:38 -04:00
parent 601b7456a7
commit bb21d6c790

View File

@@ -275,6 +275,7 @@ class MultiUserBridge:
self._app.router.add_get("/bridge/sessions", self.handle_sessions)
self._app.router.add_get("/bridge/health", self.handle_health)
self._app.router.add_get("/bridge/rooms", self.handle_rooms)
self._app.router.add_get("/bridge/stats", self.handle_stats)
self._app.router.add_get("/bridge/room_events/{user_id}", self.handle_room_events)
self._app.router.add_get("/bridge/ws/{user_id}", self.handle_ws)
return self._app
@@ -319,6 +320,23 @@ class MultiUserBridge:
"total_users": self.sessions.active_count,
})
async def handle_stats(self, request: web.Request) -> web.Response:
"""GET /bridge/stats — Aggregate bridge statistics."""
uptime = time.time() - self._start_time
total_messages = sum(len(s.message_history) for s in self.sessions._sessions.values())
total_commands = sum(s.command_count for s in self.sessions._sessions.values())
rooms = {r: len(users) for r, users in self.sessions._room_occupants.items() if users}
ws_connections = sum(len(s.ws_connections) for s in self.sessions._sessions.values())
return web.json_response({
"uptime_seconds": round(uptime, 1),
"active_sessions": self.sessions.active_count,
"total_messages": total_messages,
"total_commands": total_commands,
"rooms": rooms,
"room_count": len(rooms),
"ws_connections": ws_connections,
})
async def handle_room_events(self, request: web.Request) -> web.Response:
"""GET /bridge/room_events/{user_id} — Drain pending room events for a user."""
user_id = request.match_info["user_id"]
@@ -512,7 +530,53 @@ class MultiUserBridge:
other_session = self.sessions.get(other_id)
if other_session:
other_session.room_events.append(broadcast)
return f'You say: "{speech}"'
return f'You say: \"{speech}\"'
if msg_lower.startswith("go ") or msg_lower.startswith("move "):
# Move to a new room (HTTP equivalent of WS move)
parts = message.split(None, 1)
if len(parts) < 2 or not parts[1].strip():
return "Go where? Usage: go <room>"
new_room = parts[1].strip()
old_room = session.room
if new_room == old_room:
return f"You're already in {new_room}."
# Update room tracking
self.sessions._room_occupants[old_room].discard(session.user_id)
session.room = new_room
self.sessions._room_occupants[new_room].add(session.user_id)
# Notify occupants in old room
old_occupants = self.sessions.get_room_occupants(old_room)
for other_id in old_occupants:
other_session = self.sessions.get(other_id)
if other_session:
other_session.room_events.append({
"type": "room_broadcast",
"from_user": session.user_id,
"from_username": session.username,
"room": old_room,
"message": f"{session.username} leaves for {new_room}.",
})
return f"You leave {old_room} and arrive in {new_room}."
if msg_lower.startswith("emote ") or msg_lower.startswith("/me "):
# Emote — broadcast action to room
action = message.split(None, 1)[1] if len(message.split(None, 1)) > 1 else ""
if not action:
return "Emote what? Usage: emote <action>"
occupants = self.sessions.get_room_occupants(session.room)
others = [o for o in occupants if o != session.user_id]
for other_id in others:
other_session = self.sessions.get(other_id)
if other_session:
other_session.room_events.append({
"type": "room_broadcast",
"from_user": session.user_id,
"from_username": session.username,
"room": session.room,
"message": f"{session.username} {action}",
})
return f"You {action}"
if msg_lower == "who":
all_sessions = self.sessions.list_sessions()