/* global setTimeout, clearTimeout */ const EventEmitter = require('events'); const { isEmpty } = require('lodash'); const offlinePingTime = 2 * 60 * 1000; // 2 minutes class LokiP2pAPI extends EventEmitter { constructor(ourKey) { super(); this.contactP2pDetails = {}; this.ourKey = ourKey; } reset() { Object.keys(this.contactP2pDetails).forEach(key => { clearTimeout(this.contactP2pDetails[key].pingTimer); delete this.contactP2pDetails[key]; }); } updateContactP2pDetails(pubKey, address, port, isP2PMessage = false) { // Stagger the timers so the friends don't ping each other at the same time const timerDuration = pubKey < this.ourKey ? 60 * 1000 // 1 minute : 2 * 60 * 1000; // 2 minutes // Get the current contact details // This will be empty if we don't have them const baseDetails = { ...(this.contactP2pDetails[pubKey] || {}) }; // Always set the new contact details this.contactP2pDetails[pubKey] = { address, port, timerDuration, pingTimer: null, isOnline: false, }; const contactExists = !isEmpty(baseDetails); const { isOnline } = baseDetails; const detailsChanged = baseDetails.address !== address || baseDetails.port !== port; // If we had the contact details // And we got a P2P message // And the contact was online // And the new details that we got matched the old // Then we don't need to bother pinging if (contactExists && isP2PMessage && isOnline && !detailsChanged) { // We also need to set the current contact details to show online // because they get reset to `false` above this.setContactOnline(pubKey); return; } /* Ping the contact. This happens in the following scenarios: 1. We didn't have the contact, we need to ping them to let them know our details. 2. isP2PMessage = false, so we assume the contact doesn't have our details. 3. We had the contact marked as offline, we need to make sure that we can reach their server. 4. The other contact details have changed, we need to make sure that we can reach their new server. */ this.pingContact(pubKey); } getContactP2pDetails(pubKey) { if (!this.contactP2pDetails[pubKey]) { return null; } return { ...this.contactP2pDetails[pubKey] }; } setContactOffline(pubKey) { this.emit('offline', pubKey); if (!this.contactP2pDetails[pubKey]) { return; } clearTimeout(this.contactP2pDetails[pubKey].pingTimer); this.contactP2pDetails[pubKey].pingTimer = setTimeout( this.pingContact.bind(this), offlinePingTime, pubKey ); this.contactP2pDetails[pubKey].isOnline = false; } setContactOnline(pubKey) { if (!this.contactP2pDetails[pubKey]) { return; } this.emit('online', pubKey); clearTimeout(this.contactP2pDetails[pubKey].pingTimer); this.contactP2pDetails[pubKey].isOnline = true; this.contactP2pDetails[pubKey].pingTimer = setTimeout( this.pingContact.bind(this), this.contactP2pDetails[pubKey].timerDuration, pubKey ); } isOnline(pubKey) { return !!( this.contactP2pDetails[pubKey] && this.contactP2pDetails[pubKey].isOnline ); } pingContact(pubKey) { if (!this.contactP2pDetails[pubKey]) { // Don't ping if we don't have their details return; } this.emit('pingContact', pubKey); } } module.exports = LokiP2pAPI;