UNPKG

rtcmulticonnection-v3

Version:

RTCMultiConnection is a WebRTC JavaScript wrapper library runs top over RTCPeerConnection API to support all possible peer-to-peer features.

325 lines (271 loc) 12.3 kB
// Muaz Khan - www.MuazKhan.com // MIT License - www.WebRTC-Experiment.com/licence // Documentation - github.com/muaz-khan/RTCMultiConnection module.exports = exports = function(app, socketCallback) { var listOfUsers = {}; var shiftedModerationControls = {}; var ScalableBroadcast; var io = require('socket.io'); try { io = io(app); io.on('connection', onConnection); } catch (e) { io = io.listen(app, { log: false, origins: '*:*' }); io.set('transports', [ 'websocket', // 'disconnect' EVENT will work only with 'websocket' 'xhr-polling', 'jsonp-polling' ]); io.sockets.on('connection', onConnection); } function onConnection(socket) { var params = socket.handshake.query; var socketMessageEvent = params.msgEvent || 'RTCMultiConnection-Message'; if (params.enableScalableBroadcast) { if (!ScalableBroadcast) { ScalableBroadcast = require('./Scalable-Broadcast.js'); } ScalableBroadcast(socket, params.maxRelayLimitPerUser); } if (!!listOfUsers[params.userid]) { params.dontUpdateUserId = true; var useridAlreadyTaken = params.userid; params.userid = (Math.random() * 1000).toString().replace('.', ''); socket.emit('userid-already-taken', useridAlreadyTaken, params.userid); } socket.userid = params.userid; listOfUsers[socket.userid] = { socket: socket, connectedWith: {}, isPublic: false, // means: isPublicModerator extra: {} }; socket.on('extra-data-updated', function(extra) { try { if (!listOfUsers[socket.userid]) return; listOfUsers[socket.userid].extra = extra; for (var user in listOfUsers[socket.userid].connectedWith) { listOfUsers[user].socket.emit('extra-data-updated', socket.userid, extra); } } catch (e) {} }); socket.on('become-a-public-moderator', function() { try { if (!listOfUsers[socket.userid]) return; listOfUsers[socket.userid].isPublic = true; } catch (e) {} }); socket.on('dont-make-me-moderator', function() { try { if (!listOfUsers[socket.userid]) return; listOfUsers[socket.userid].isPublic = false; } catch (e) {} }); socket.on('get-public-moderators', function(userIdStartsWith, callback) { try { userIdStartsWith = userIdStartsWith || ''; var allPublicModerators = []; for (var moderatorId in listOfUsers) { if (listOfUsers[moderatorId].isPublic && moderatorId.indexOf(userIdStartsWith) === 0 && moderatorId !== socket.userid) { var moderator = listOfUsers[moderatorId]; allPublicModerators.push({ userid: moderatorId, extra: moderator.extra }); } } callback(allPublicModerators); } catch (e) {} }); socket.on('changed-uuid', function(newUserId, callback) { if (params.dontUpdateUserId) { delete params.dontUpdateUserId; return; } try { if (listOfUsers[socket.userid] && listOfUsers[socket.userid].socket.id == socket.userid) { if (newUserId === socket.userid) return; var oldUserId = socket.userid; listOfUsers[newUserId] = listOfUsers[oldUserId]; listOfUsers[newUserId].socket.userid = socket.userid = newUserId; delete listOfUsers[oldUserId]; callback(); return; } socket.userid = newUserId; listOfUsers[socket.userid] = { socket: socket, connectedWith: {}, isPublic: false, extra: {} }; callback(); } catch (e) {} }); socket.on('set-password', function(password) { try { if (listOfUsers[socket.userid]) { listOfUsers[socket.userid].password = password; } } catch (e) {} }); socket.on('disconnect-with', function(remoteUserId, callback) { try { if (listOfUsers[socket.userid] && listOfUsers[socket.userid].connectedWith[remoteUserId]) { delete listOfUsers[socket.userid].connectedWith[remoteUserId]; socket.emit('user-disconnected', remoteUserId); } if (!listOfUsers[remoteUserId]) return callback(); if (listOfUsers[remoteUserId].connectedWith[socket.userid]) { delete listOfUsers[remoteUserId].connectedWith[socket.userid]; listOfUsers[remoteUserId].socket.emit('user-disconnected', socket.userid); } callback(); } catch (e) {} }); socket.on('close-entire-session', function(callback) { try { var connectedWith = listOfUsers[socket.userid].connectedWith; Object.keys(connectedWith).forEach(function(key) { if (connectedWith[key] && connectedWith[key].emit) { try { connectedWith[key].emit('closed-entire-session', socket.userid, listOfUsers[socket.userid].extra); } catch (e) {} } }); delete shiftedModerationControls[socket.userid]; callback(); } catch (e) { throw e; } }); function onMessageCallback(message) { try { if (!listOfUsers[message.sender]) { socket.emit('user-not-found', message.sender); return; } if (!message.message.userLeft && !listOfUsers[message.sender].connectedWith[message.remoteUserId] && !!listOfUsers[message.remoteUserId]) { listOfUsers[message.sender].connectedWith[message.remoteUserId] = listOfUsers[message.remoteUserId].socket; listOfUsers[message.sender].socket.emit('user-connected', message.remoteUserId); if (!listOfUsers[message.remoteUserId]) { listOfUsers[message.remoteUserId] = { socket: null, connectedWith: {}, isPublic: false, extra: {} }; } listOfUsers[message.remoteUserId].connectedWith[message.sender] = socket; if (listOfUsers[message.remoteUserId].socket) { listOfUsers[message.remoteUserId].socket.emit('user-connected', message.sender); } } if (listOfUsers[message.sender].connectedWith[message.remoteUserId] && listOfUsers[socket.userid]) { message.extra = listOfUsers[socket.userid].extra; listOfUsers[message.sender].connectedWith[message.remoteUserId].emit(socketMessageEvent, message); } } catch (e) {} } var numberOfPasswordTries = 0; socket.on(socketMessageEvent, function(message, callback) { if (message.remoteUserId && message.remoteUserId === socket.userid) { // remoteUserId MUST be unique return; } try { if (message.remoteUserId && message.remoteUserId != 'system' && message.message.newParticipationRequest) { if (listOfUsers[message.remoteUserId] && listOfUsers[message.remoteUserId].password) { if (numberOfPasswordTries > 3) { socket.emit('password-max-tries-over', message.remoteUserId); return; } if (!message.password) { numberOfPasswordTries++; socket.emit('join-with-password', message.remoteUserId); return; } if (message.password != listOfUsers[message.remoteUserId].password) { numberOfPasswordTries++; socket.emit('invalid-password', message.remoteUserId, message.password); return; } } } if (message.message.shiftedModerationControl) { if (!message.message.firedOnLeave) { onMessageCallback(message); return; } shiftedModerationControls[message.sender] = message; return; } if (message.remoteUserId == 'system') { if (message.message.detectPresence) { if (message.message.userid === socket.userid) { callback(false, socket.userid); return; } callback(!!listOfUsers[message.message.userid], message.message.userid); return; } } if (!listOfUsers[message.sender]) { listOfUsers[message.sender] = { socket: socket, connectedWith: {}, isPublic: false, extra: {} }; } // if someone tries to join a person who is absent if (message.message.newParticipationRequest) { var waitFor = 120; // 2 minutes var invokedTimes = 0; (function repeater() { invokedTimes++; if (invokedTimes > waitFor) { socket.emit('user-not-found', message.remoteUserId); return; } if (listOfUsers[message.remoteUserId] && listOfUsers[message.remoteUserId].socket) { onMessageCallback(message); return; } setTimeout(repeater, 1000); })(); return; } onMessageCallback(message); } catch (e) {} }); socket.on('disconnect', function() { try { var message = shiftedModerationControls[socket.userid]; if (message) { delete shiftedModerationControls[message.userid]; onMessageCallback(message); } } catch (e) {} try { // inform all connected users if (listOfUsers[socket.userid]) { for (var s in listOfUsers[socket.userid].connectedWith) { listOfUsers[socket.userid].connectedWith[s].emit('user-disconnected', socket.userid); if (listOfUsers[s] && listOfUsers[s].connectedWith[socket.userid]) { delete listOfUsers[s].connectedWith[socket.userid]; listOfUsers[s].socket.emit('user-disconnected', socket.userid); } } } } catch (e) {} delete listOfUsers[socket.userid]; }); if (socketCallback) { socketCallback(socket); } } };