add live page
This commit is contained in:
parent
114bcca24e
commit
eb8c42d0c0
19 changed files with 2759 additions and 199 deletions
337
oedb/resources/demo/websocket.py
Normal file
337
oedb/resources/demo/websocket.py
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
import json
|
||||
import time
|
||||
import threading
|
||||
import asyncio
|
||||
import websockets
|
||||
from oedb.utils.logging import logger
|
||||
|
||||
class WebSocketManager:
|
||||
"""
|
||||
Gestionnaire de WebSockets pour les fonctionnalités sociales d'OEDB.
|
||||
Gère les connexions WebSocket des utilisateurs et distribue les messages.
|
||||
Peut être utilisé soit de façon autonome, soit intégré avec uWSGI.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.clients = {}
|
||||
self.positions = {}
|
||||
self.lock = threading.Lock()
|
||||
self.server = None
|
||||
|
||||
async def handle_connection(self, websocket, path):
|
||||
"""
|
||||
Gère une connexion WebSocket entrante.
|
||||
|
||||
Args:
|
||||
websocket: La connexion WebSocket.
|
||||
path: Le chemin de la demande.
|
||||
"""
|
||||
client_id = id(websocket)
|
||||
logger.debug(f"Tentative de connexion WebSocket reçue: {client_id} - {path}")
|
||||
|
||||
try:
|
||||
logger.info(f"Nouvelle connexion WebSocket: {client_id} - {path}")
|
||||
|
||||
# Ajouter le client à la liste
|
||||
with self.lock:
|
||||
self.clients[client_id] = {
|
||||
'websocket': websocket,
|
||||
'username': None,
|
||||
'position': None,
|
||||
'last_seen': time.time(),
|
||||
'show_only_to_friends': False,
|
||||
}
|
||||
|
||||
# Envoyer la liste des utilisateurs connectés
|
||||
await self.send_users_list(websocket)
|
||||
|
||||
async for message in websocket:
|
||||
await self.handle_message(client_id, message)
|
||||
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
logger.info(f"Connexion WebSocket fermée: {client_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur WebSocket: {e}")
|
||||
finally:
|
||||
# Supprimer le client de la liste
|
||||
with self.lock:
|
||||
if client_id in self.clients:
|
||||
username = self.clients[client_id].get('username')
|
||||
if username and username in self.positions:
|
||||
del self.positions[username]
|
||||
del self.clients[client_id]
|
||||
|
||||
# Informer les autres clients de la déconnexion
|
||||
await self.broadcast_users_list()
|
||||
|
||||
async def handle_message(self, client_id, message):
|
||||
"""
|
||||
Traite un message WebSocket reçu.
|
||||
|
||||
Args:
|
||||
client_id: L'ID du client qui a envoyé le message.
|
||||
message: Le message JSON reçu.
|
||||
"""
|
||||
try:
|
||||
data = json.loads(message)
|
||||
message_type = data.get('type')
|
||||
|
||||
if message_type == 'position':
|
||||
await self.handle_position_update(client_id, data)
|
||||
elif message_type == 'pouet':
|
||||
await self.handle_pouet(data)
|
||||
elif message_type == 'friendRequest':
|
||||
await self.handle_friend_request(data)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"Message JSON invalide: {message}")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur de traitement du message: {e}")
|
||||
|
||||
async def handle_position_update(self, client_id, data):
|
||||
"""
|
||||
Traite une mise à jour de position d'un utilisateur.
|
||||
|
||||
Args:
|
||||
client_id: L'ID du client qui a envoyé la mise à jour.
|
||||
data: Les données de position.
|
||||
"""
|
||||
username = data.get('username')
|
||||
position = data.get('position')
|
||||
show_only_to_friends = data.get('showOnlyToFriends', False)
|
||||
|
||||
if not username or not position:
|
||||
return
|
||||
|
||||
# Mettre à jour les informations du client
|
||||
with self.lock:
|
||||
if client_id in self.clients:
|
||||
self.clients[client_id]['username'] = username
|
||||
self.clients[client_id]['position'] = position
|
||||
self.clients[client_id]['last_seen'] = time.time()
|
||||
self.clients[client_id]['show_only_to_friends'] = show_only_to_friends
|
||||
|
||||
# Mettre à jour la position dans le dictionnaire des positions
|
||||
self.positions[username] = {
|
||||
'position': position,
|
||||
'timestamp': data.get('timestamp'),
|
||||
'show_only_to_friends': show_only_to_friends
|
||||
}
|
||||
|
||||
# Diffuser la position à tous les autres clients
|
||||
await self.broadcast_position(username, position, data.get('timestamp'), show_only_to_friends)
|
||||
|
||||
# Envoyer la liste mise à jour des utilisateurs
|
||||
await self.broadcast_users_list()
|
||||
|
||||
async def handle_pouet(self, data):
|
||||
"""
|
||||
Traite un 'pouet pouet' envoyé d'un utilisateur à un autre.
|
||||
|
||||
Args:
|
||||
data: Les données du pouet pouet.
|
||||
"""
|
||||
from_user = data.get('from')
|
||||
to_user = data.get('to')
|
||||
|
||||
if not from_user or not to_user:
|
||||
return
|
||||
|
||||
# Trouver le client destinataire
|
||||
recipient_client_id = None
|
||||
with self.lock:
|
||||
for client_id, client_info in self.clients.items():
|
||||
if client_info.get('username') == to_user:
|
||||
recipient_client_id = client_id
|
||||
break
|
||||
|
||||
if recipient_client_id and recipient_client_id in self.clients:
|
||||
# Envoyer le pouet au destinataire
|
||||
try:
|
||||
await self.clients[recipient_client_id]['websocket'].send(json.dumps({
|
||||
'type': 'pouet',
|
||||
'from': from_user,
|
||||
'timestamp': data.get('timestamp')
|
||||
}))
|
||||
logger.info(f"Pouet pouet envoyé de {from_user} à {to_user}")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur d'envoi de pouet pouet: {e}")
|
||||
|
||||
async def handle_friend_request(self, data):
|
||||
"""
|
||||
Traite une demande d'ami d'un utilisateur à un autre.
|
||||
|
||||
Args:
|
||||
data: Les données de la demande d'ami.
|
||||
"""
|
||||
from_user = data.get('from')
|
||||
to_user = data.get('to')
|
||||
|
||||
if not from_user or not to_user:
|
||||
return
|
||||
|
||||
# Trouver le client destinataire
|
||||
recipient_client_id = None
|
||||
with self.lock:
|
||||
for client_id, client_info in self.clients.items():
|
||||
if client_info.get('username') == to_user:
|
||||
recipient_client_id = client_id
|
||||
break
|
||||
|
||||
if recipient_client_id and recipient_client_id in self.clients:
|
||||
# Envoyer la demande d'ami au destinataire
|
||||
try:
|
||||
await self.clients[recipient_client_id]['websocket'].send(json.dumps({
|
||||
'type': 'friendRequest',
|
||||
'from': from_user,
|
||||
'timestamp': data.get('timestamp')
|
||||
}))
|
||||
logger.info(f"Demande d'ami envoyée de {from_user} à {to_user}")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur d'envoi de demande d'ami: {e}")
|
||||
|
||||
async def broadcast_position(self, username, position, timestamp, show_only_to_friends):
|
||||
"""
|
||||
Diffuse la position d'un utilisateur à tous les autres utilisateurs.
|
||||
|
||||
Args:
|
||||
username: Le nom d'utilisateur.
|
||||
position: La position de l'utilisateur.
|
||||
timestamp: L'horodatage de la mise à jour.
|
||||
show_only_to_friends: Indique si la position est visible uniquement par les amis.
|
||||
"""
|
||||
message = json.dumps({
|
||||
'type': 'position',
|
||||
'username': username,
|
||||
'position': position,
|
||||
'timestamp': timestamp,
|
||||
'showOnlyToFriends': show_only_to_friends
|
||||
})
|
||||
|
||||
with self.lock:
|
||||
for client_id, client_info in self.clients.items():
|
||||
# Ne pas envoyer à l'utilisateur lui-même
|
||||
if client_info.get('username') == username:
|
||||
continue
|
||||
|
||||
try:
|
||||
await client_info['websocket'].send(message)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur d'envoi de broadcast de position: {e}")
|
||||
|
||||
async def send_users_list(self, websocket):
|
||||
"""
|
||||
Envoie la liste des utilisateurs connectés à un client spécifique.
|
||||
|
||||
Args:
|
||||
websocket: La connexion WebSocket du client.
|
||||
"""
|
||||
users = []
|
||||
with self.lock:
|
||||
for client_info in self.clients.values():
|
||||
if client_info.get('username'):
|
||||
users.append({
|
||||
'username': client_info['username'],
|
||||
'timestamp': time.time()
|
||||
})
|
||||
|
||||
try:
|
||||
await websocket.send(json.dumps({
|
||||
'type': 'users',
|
||||
'users': users
|
||||
}))
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur d'envoi de liste d'utilisateurs: {e}")
|
||||
|
||||
async def broadcast_users_list(self):
|
||||
"""
|
||||
Diffuse la liste des utilisateurs connectés à tous les clients.
|
||||
"""
|
||||
users = []
|
||||
with self.lock:
|
||||
for client_info in self.clients.values():
|
||||
if client_info.get('username'):
|
||||
users.append({
|
||||
'username': client_info['username'],
|
||||
'timestamp': time.time()
|
||||
})
|
||||
|
||||
message = json.dumps({
|
||||
'type': 'users',
|
||||
'users': users
|
||||
})
|
||||
|
||||
with self.lock:
|
||||
for client_info in self.clients.values():
|
||||
try:
|
||||
await client_info['websocket'].send(message)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur de broadcast de liste d'utilisateurs: {e}")
|
||||
|
||||
async def cleanup_inactive_clients(self):
|
||||
"""
|
||||
Nettoie les clients inactifs (pas de mise à jour depuis plus de 5 minutes).
|
||||
"""
|
||||
inactive_clients = []
|
||||
|
||||
with self.lock:
|
||||
current_time = time.time()
|
||||
for client_id, client_info in self.clients.items():
|
||||
if current_time - client_info['last_seen'] > 300: # 5 minutes
|
||||
inactive_clients.append(client_id)
|
||||
|
||||
for client_id in inactive_clients:
|
||||
username = self.clients[client_id].get('username')
|
||||
if username and username in self.positions:
|
||||
del self.positions[username]
|
||||
del self.clients[client_id]
|
||||
|
||||
if inactive_clients:
|
||||
logger.info(f"Nettoyage de {len(inactive_clients)} clients inactifs")
|
||||
await self.broadcast_users_list()
|
||||
|
||||
async def cleanup_task(self):
|
||||
"""
|
||||
Tâche périodique pour nettoyer les clients inactifs.
|
||||
"""
|
||||
while True:
|
||||
await asyncio.sleep(60) # Exécuter toutes les minutes
|
||||
await self.cleanup_inactive_clients()
|
||||
|
||||
async def start_server(self, host='0.0.0.0', port=8765):
|
||||
"""
|
||||
Démarre le serveur WebSocket.
|
||||
|
||||
Args:
|
||||
host: L'hôte à écouter.
|
||||
port: Le port à écouter.
|
||||
"""
|
||||
self.server = await websockets.serve(self.handle_connection, host, port)
|
||||
logger.info(f"Serveur WebSocket démarré sur {host}:{port}")
|
||||
|
||||
# Démarrer la tâche de nettoyage
|
||||
asyncio.create_task(self.cleanup_task())
|
||||
|
||||
# Garder le serveur en cours d'exécution
|
||||
await asyncio.Future()
|
||||
|
||||
def start(self, host='0.0.0.0', port=8765):
|
||||
"""
|
||||
Démarre le serveur WebSocket dans un thread séparé.
|
||||
|
||||
Args:
|
||||
host: L'hôte à écouter.
|
||||
port: Le port à écouter.
|
||||
"""
|
||||
def run_server():
|
||||
asyncio.run(self.start_server(host, port))
|
||||
|
||||
server_thread = threading.Thread(target=run_server, daemon=True)
|
||||
server_thread.start()
|
||||
logger.info(f"Serveur WebSocket démarré dans un thread séparé sur {host}:{port}")
|
||||
|
||||
# Créer une instance du gestionnaire WebSocket
|
||||
ws_manager = WebSocketManager()
|
||||
|
||||
# Démarrer automatiquement le serveur WebSocket
|
||||
ws_manager.start(host='127.0.0.1', port=8765)
|
||||
Loading…
Add table
Add a link
Reference in a new issue