'use strict'; var moment = require('moment'); var config; var momentFormat = 'YYYY-MM-DD HH:mm:ss'; var defaultMaxHealthyWaitMs = 2 * 60 * 1000; module.exports = MatrixUtil; function _cleanPartnerId(partnerId) { let cleaned = partnerId; try { cleaned = cleaned.toLocaleString(); cleaned = cleaned.trim().toLowerCase(); } catch(e) { return partnerId; } return cleaned; } function getMaxHealthyWaitMs() { if (!config || !config.globals || !Number(config.globals.max_healthy_wait_ms)) { return defaultMaxHealthyWaitMs; } return Number(config.globals.max_healthy_wait_ms); } function MatrixUtil(options) { if (!options) { console.trace('Undefined options'); process.exit(1); } this.matrix = options.matrix; if (!this.matrix) { console.trace("Matrix not set"); process.exit(1); } this.logger = options.logger; if (!this.logger) { console.trace("Logger not set"); process.exit(1); } config = options.config; if (!config) { console.trace("Config not set"); process.exit(1); }; } MatrixUtil.prototype.updateBuddyState = function(jid, state, statusText, resource) { if (!jid) {return; } if (jid == 'undefined') {return; } jid = _cleanPartnerId(jid); if (!resource) { resource = 'undefined'; } let logger = this.logger; let matrix = this.matrix; logger.verbose('Buddy state change', {jid: jid, state: state, statusText: statusText, resource: resource}); if (!matrix) { return; } if (!matrix.buddies) { matrix.buddies = {}; } if (!matrix.buddies[jid]) { matrix.buddies[jid] = {resources: {}}; } try { matrix.buddies[jid]['resources'][resource] = { state: state, statusText: statusText, last_update: moment().format(momentFormat) } } catch(e) { logger.warn('MatrixUtil: Exception on update resources on matrix', {jid: jid, state: state, statusText: statusText, resource: resource}); } if (resource != 'undefined') { try { delete matrix.buddies[jid].resources['undefined']; } catch(e) {}; } } MatrixUtil.prototype.isPartnerOffline = function(partner) { if (!partner) { return; } partner = _cleanPartnerId(partner); let matrix = this.matrix; let logger = this.logger; if (!matrix) { return false; } if (!matrix.buddies) { matrix.buddies = {}; } if (!matrix.buddies[partner]) { return true; } if (!matrix.buddies[partner].resources) { return true; }; let resources = matrix.buddies[partner].resources; for (let key in resources) { if (resources.hasOwnProperty(key)) { let resource = resources[key]; if (resources[key].state == 'online') { return false; } } } logger.verbose('Offline partner detected: ' + partner); return true; } MatrixUtil.prototype._isPartnerHealthy = function(partner) { if (!partner) { return; } partner = _cleanPartnerId(partner); if (this.isPartnerOffline(partner)) { return; } let matrix = this.matrix; let logger = this.logger; if (!matrix) { return false; } if (!matrix.buddies[partner]) { return false; } if (!matrix.buddies[partner]['waiting_for_response']) { return true; } if (!matrix.buddies[partner]['last_incoming']) { return false; } if (!matrix.buddies[partner]['last_incoming']['last_update_ts']) { return false; } let delta = Date.now() - Number(matrix.buddies[partner]['last_incoming']['last_update_ts']); let isHealthy = delta <= getMaxHealthyWaitMs(); logger.verbose('Partner healthy analized', {partner: partner, isHealthy: isHealthy, delta: delta, maxHealthyWaitMs: getMaxHealthyWaitMs()}); return isHealthy; } MatrixUtil.prototype.isPartnerHealthy = function(partner) { let matrix = this.matrix; let isHealthy = this._isPartnerHealthy(partner); if (!matrix.healthy_partners) { matrix.healthy_partners = []; } // update matrix let idx = matrix.healthy_partners.indexOf(partner); if (isHealthy) { if (idx < 0) { matrix.healthy_partners.push(partner); } } else { if (idx > -1) { matrix.healthy_partners.splice(idx, 1); } } return isHealthy; } MatrixUtil.prototype._updateLastResponseTime = function(partner) { let matrix = this.matrix; let logger = this.logger; if (!matrix.buddies[partner]['last_outgoing']) { logger.verbose('No outgoing yet, skip updateLastResponseTime'); return; } if (!matrix.buddies[partner]['last_outgoing']['last_update_ts']) { logger.verbose('No outgoing timestamp yet, skip updateLastResponseTime'); return; } if ( matrix.buddies[partner]['last_incoming'] && (Number(matrix.buddies[partner]['last_incoming']['last_update_ts']) > Number(matrix.buddies[partner]['last_outgoing']['last_update_ts'])) ) { return; } let delta = Date.now() - Number(matrix.buddies[partner]['last_outgoing']['last_update_ts']); delta = (delta / 1000).toFixed(2); logger.verbose('MatrixUtil: Response time in ' + delta + ' seconds', {partner: partner}); matrix.buddies[partner]['last_response_time_in_secs'] = delta; } MatrixUtil.prototype._updateLastMessage = function(partner, msg, direction) { if (!partner) { return; } partner = _cleanPartnerId(partner); let matrix = this.matrix; let logger = this.logger; if (!matrix) { return; } if (!matrix.buddies) { matrix.buddies = {}; } if (!matrix.buddies[partner]) { matrix.buddies[partner] = {}; } if (direction == 'incoming') { try { this._updateLastResponseTime(partner); } catch(e) { logger.warn('Exception when updateLastResponseTime', {err: e}); } } matrix.buddies[partner]['waiting_for_response'] = (direction == 'outgoing'); matrix.buddies[partner]['last_' + direction] = { msg: msg, last_update: moment().format(momentFormat), last_update_ts: Date.now() } if (direction == 'outgoing') { return; } } MatrixUtil.prototype.updateLastIncoming = function(partner, msg) { this._updateLastMessage(partner, msg, 'incoming'); } MatrixUtil.prototype.updateLastOutgoing = function(partner, msg) { this._updateLastMessage(partner, msg, 'outgoing'); }