const express = require('express'); const { WebSocketServer } = require('ws'); const { randomUUID } = require('crypto'); const path = require('path'); const app = express(); app.use(express.static(path.join(__dirname, 'public'))); const server = app.listen(3000, () => { console.log('Talkie running on http://localhost:3000'); }); const wss = new WebSocketServer({ server }); // roomId -> Set of { ws, id, name } const rooms = new Map(); function getRoomMembers(roomId) { return rooms.get(roomId) ?? new Set(); } function broadcast(roomId, data, excludeId = null) { const msg = JSON.stringify(data); getRoomMembers(roomId).forEach(client => { if (client.id !== excludeId && client.ws.readyState === 1) { client.ws.send(msg); } }); } wss.on('connection', (ws) => { const clientId = randomUUID(); let clientRoom = null; let clientName = 'Anonimo'; ws.send(JSON.stringify({ type: 'id', id: clientId })); ws.on('message', (raw) => { let msg; try { msg = JSON.parse(raw); } catch { return; } if (msg.type === 'join') { clientRoom = msg.room.trim().toUpperCase(); clientName = (msg.name || 'Anonimo').slice(0, 20); if (!rooms.has(clientRoom)) rooms.set(clientRoom, new Set()); const room = rooms.get(clientRoom); // Tell new user about existing peers const peers = [...room].map(c => ({ id: c.id, name: c.name })); ws.send(JSON.stringify({ type: 'peers', peers })); // Tell existing peers about the new user broadcast(clientRoom, { type: 'peer-joined', id: clientId, name: clientName }); room.add({ ws, id: clientId, name: clientName }); } // WebRTC signaling: forward to specific peer if (['offer', 'answer', 'ice'].includes(msg.type)) { const room = getRoomMembers(clientRoom); const target = [...room].find(c => c.id === msg.to); if (target?.ws.readyState === 1) { target.ws.send(JSON.stringify({ ...msg, from: clientId })); } } // Talking state: broadcast to room if (msg.type === 'talking') { broadcast(clientRoom, { type: 'talking', id: clientId, name: clientName, active: msg.active }, clientId); } }); ws.on('close', () => { if (!clientRoom) return; const room = getRoomMembers(clientRoom); room.forEach(c => { if (c.id === clientId) room.delete(c); }); broadcast(clientRoom, { type: 'peer-left', id: clientId, name: clientName }); if (room.size === 0) rooms.delete(clientRoom); }); });