82 lines
2.4 KiB
JavaScript
82 lines
2.4 KiB
JavaScript
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);
|
|
});
|
|
});
|