128 lines
3.4 KiB
Python
128 lines
3.4 KiB
Python
|
|
"""
|
||
|
|
WebSocket server for moderation chat
|
||
|
|
"""
|
||
|
|
import socketio
|
||
|
|
import logging
|
||
|
|
from typing import Dict, Set
|
||
|
|
|
||
|
|
logger = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
# Create Socket.IO server
|
||
|
|
sio = socketio.AsyncServer(
|
||
|
|
async_mode='asgi',
|
||
|
|
cors_allowed_origins='*',
|
||
|
|
logger=False,
|
||
|
|
engineio_logger=False
|
||
|
|
)
|
||
|
|
|
||
|
|
# Track connected moderators
|
||
|
|
connected_moderators: Dict[str, Set[str]] = {} # {room: {sid1, sid2, ...}}
|
||
|
|
|
||
|
|
|
||
|
|
@sio.event
|
||
|
|
async def connect(sid, environ):
|
||
|
|
"""Handle client connection"""
|
||
|
|
logger.info(f"[WebSocket] Client connected: {sid}")
|
||
|
|
|
||
|
|
|
||
|
|
@sio.event
|
||
|
|
async def disconnect(sid):
|
||
|
|
"""Handle client disconnection"""
|
||
|
|
logger.info(f"[WebSocket] Client disconnected: {sid}")
|
||
|
|
|
||
|
|
# Remove from all rooms
|
||
|
|
for room, sids in connected_moderators.items():
|
||
|
|
if sid in sids:
|
||
|
|
sids.remove(sid)
|
||
|
|
await sio.emit('user_left', {'sid': sid}, room=room, skip_sid=sid)
|
||
|
|
|
||
|
|
|
||
|
|
@sio.event
|
||
|
|
async def join_moderation_chat(sid, data):
|
||
|
|
"""Join moderation chat room"""
|
||
|
|
try:
|
||
|
|
user_id = data.get('userId')
|
||
|
|
username = data.get('username')
|
||
|
|
|
||
|
|
if not user_id:
|
||
|
|
return
|
||
|
|
|
||
|
|
room = 'moderation_chat'
|
||
|
|
await sio.enter_room(sid, room)
|
||
|
|
|
||
|
|
if room not in connected_moderators:
|
||
|
|
connected_moderators[room] = set()
|
||
|
|
connected_moderators[room].add(sid)
|
||
|
|
|
||
|
|
logger.info(f"[WebSocket] {username} ({user_id}) joined moderation chat")
|
||
|
|
|
||
|
|
# Notify others
|
||
|
|
await sio.emit('user_joined', {
|
||
|
|
'userId': user_id,
|
||
|
|
'username': username,
|
||
|
|
'sid': sid
|
||
|
|
}, room=room, skip_sid=sid)
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"[WebSocket] Error joining chat: {e}")
|
||
|
|
|
||
|
|
|
||
|
|
@sio.event
|
||
|
|
async def leave_moderation_chat(sid, data):
|
||
|
|
"""Leave moderation chat room"""
|
||
|
|
try:
|
||
|
|
room = 'moderation_chat'
|
||
|
|
await sio.leave_room(sid, room)
|
||
|
|
|
||
|
|
if room in connected_moderators and sid in connected_moderators[room]:
|
||
|
|
connected_moderators[room].remove(sid)
|
||
|
|
|
||
|
|
# Notify others
|
||
|
|
await sio.emit('user_left', {'sid': sid}, room=room, skip_sid=sid)
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"[WebSocket] Error leaving chat: {e}")
|
||
|
|
|
||
|
|
|
||
|
|
@sio.event
|
||
|
|
async def moderation_message(sid, data):
|
||
|
|
"""Handle moderation chat message"""
|
||
|
|
try:
|
||
|
|
room = 'moderation_chat'
|
||
|
|
|
||
|
|
message_data = {
|
||
|
|
'userId': data.get('userId'),
|
||
|
|
'username': data.get('username'),
|
||
|
|
'message': data.get('message'),
|
||
|
|
'timestamp': data.get('timestamp')
|
||
|
|
}
|
||
|
|
|
||
|
|
# Broadcast to all in room
|
||
|
|
await sio.emit('moderation_message', message_data, room=room)
|
||
|
|
|
||
|
|
logger.info(f"[WebSocket] Message from {data.get('username')}: {data.get('message')[:50]}...")
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"[WebSocket] Error sending message: {e}")
|
||
|
|
|
||
|
|
|
||
|
|
@sio.event
|
||
|
|
async def typing(sid, data):
|
||
|
|
"""Handle typing indicator"""
|
||
|
|
try:
|
||
|
|
room = 'moderation_chat'
|
||
|
|
await sio.emit('typing', {
|
||
|
|
'userId': data.get('userId'),
|
||
|
|
'username': data.get('username'),
|
||
|
|
'isTyping': data.get('isTyping', True)
|
||
|
|
}, room=room, skip_sid=sid)
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"[WebSocket] Error handling typing: {e}")
|
||
|
|
|
||
|
|
|
||
|
|
def get_socketio_app():
|
||
|
|
"""Get Socket.IO ASGI app"""
|
||
|
|
return socketio.ASGIApp(sio)
|
||
|
|
|