From 8a4b447db3a094a2ba4f5a3c8b2663303efb79e5 Mon Sep 17 00:00:00 2001 From: Josh Harvey Date: Sat, 23 Apr 2016 19:27:53 +0300 Subject: [PATCH] lots of notifier improvements --- dev/notify.js | 30 ++++++++++++++ dev/send-message.js | 2 - lib/app.js | 2 + lib/config.js | 2 +- lib/notifier.js | 65 ++++++++++++++++++++++++++++++- lib/plugins.js | 95 ++++++++++++++++++++++++++++++++++----------- package.json | 2 + 7 files changed, 172 insertions(+), 26 deletions(-) create mode 100644 dev/notify.js diff --git a/dev/notify.js b/dev/notify.js new file mode 100644 index 00000000..db09461a --- /dev/null +++ b/dev/notify.js @@ -0,0 +1,30 @@ +require('es6-promise').polyfill() + +var notifier = require('../lib/notifier') +var db = require('../lib/postgresql_interface') + +function getBalances () { + return [ + {fiatBalance: 23.2345, fiatCode: 'USD', cryptoCode: 'BTC'}, + {fiatBalance: 22, fiatCode: 'USD', cryptoCode: 'ETH'} + ] +} + +db.init('psql://lamassu:lamassu@localhost/lamassu') +notifier.init(db, getBalances) +console.log('DEBUG0') +notifier.checkStatus() +.then(function (alertRec) { + console.log('DEBUG1') + console.log('%j', alertRec) + var subject = notifier.alertSubject(alertRec) + console.log(subject) + var body = notifier.printEmailAlerts(alertRec) + console.log(body) + console.log(notifier.alertFingerprint(alertRec)) + process.exit(0) +}) +.catch(function (err) { + console.log(err.stack) + process.exit(1) +}) diff --git a/dev/send-message.js b/dev/send-message.js index bd627369..e4160951 100644 --- a/dev/send-message.js +++ b/dev/send-message.js @@ -7,12 +7,10 @@ var rand = Math.floor(Math.random() * 1e6) var rec = { email: { - toEmail: 'joshmh@gmail.com', subject: 'Test email ' + rand, body: 'This is a test email from lamassu-server' }, sms: { - toNumber: process.argv[2], body: '[Lamassu] This is a test sms ' + rand } } diff --git a/lib/app.js b/lib/app.js index 55fe94dc..03aae966 100644 --- a/lib/app.js +++ b/lib/app.js @@ -1,5 +1,7 @@ 'use strict' +require('es6-promise').polyfill() + var http = require('http') var https = require('https') var express = require('express') diff --git a/lib/config.js b/lib/config.js index 0b3be2c5..3794de35 100644 --- a/lib/config.js +++ b/lib/config.js @@ -10,6 +10,7 @@ function connect () { } return pgp(psqlUrl) } +exports.connect = connect function loadConfig () { var db = connect() @@ -19,5 +20,4 @@ function loadConfig () { return data.data }) } - exports.loadConfig = loadConfig diff --git a/lib/notifier.js b/lib/notifier.js index dc4b3523..a8cb0506 100644 --- a/lib/notifier.js +++ b/lib/notifier.js @@ -1,4 +1,7 @@ +var crypto = require('crypto') var R = require('ramda') +var prettyMs = require('pretty-ms') +var numeral = require('numeral') var db = null var getBalances = null @@ -21,7 +24,7 @@ function sameState (a, b) { function checkBalance (rec) { var LOW_BALANCE_THRESHOLD = 10 return rec.fiatBalance < LOW_BALANCE_THRESHOLD - ? {code: 'lowBalance', cryptoCode: rec.cryptoCode, fiatBalance: rec.fiatBalance} + ? {code: 'lowBalance', cryptoCode: rec.cryptoCode, fiatBalance: rec.fiatBalance, fiatCode: rec.fiatCode} : null } @@ -110,5 +113,65 @@ function checkStatus () { return alerts }) + .catch(function (err) { + console.log(err.stack) + }) } exports.checkStatus = checkStatus + +function formatCurrency (num, code) { + return numeral(num).format('0,0.00') + ' ' + code +} + +function emailAlert (alert) { + switch (alert.code) { + case 'ping': + var pingAge = prettyMs(alert.age, {compact: true, verbose: true}) + return 'Connection to machine down for ' + pingAge + case 'stale': + var stuckAge = prettyMs(alert.age, {compact: true, verbose: true}) + return 'Machine is stuck on ' + alert.state + 'screen for ' + stuckAge + case 'lowBalance': + var balance = formatCurrency(alert.fiatBalance, alert.fiatCode) + return 'Low balance of ' + balance + ' in ' + alert.cryptoCode + ' wallet' + } +} + +function emailAlerts (alerts) { + return alerts.map(emailAlert).join('\n') + '\n' +} + +function printEmailAlerts (alertRec) { + var body = 'Errors were reported by your Lamassu Machines.\n' + + if (alertRec.general.length !== 0) { + body = body + '\nGeneral errors:\n' + body = body + emailAlerts(alertRec.general) + } + + R.keys(alertRec.devices).forEach(function (device) { + body = body + '\nErrors for ' + device + ':\n' + body = body + emailAlerts(alertRec.devices[device]) + }) + + return body +} +exports.printEmailAlerts = printEmailAlerts + +function alertSubject (alertRec) { + var alerts = alertRec.general + R.keys(alertRec.devices).forEach(function (device) { + alerts = R.concat(alerts, alertRec.devices[device]) + }) + if (alerts.length === 0) return null + var alertTypes = R.uniq(R.pluck('code', alerts)).sort() + return '[Lamassu] Errors reported: ' + alertTypes.join(', ') +} +exports.alertSubject = alertSubject + +function alertFingerprint (alertRec) { + var subject = alertSubject(alertRec) + if (!subject) return null + return crypto.createHash('sha256').update(subject).digest('hex') +} +exports.alertFingerprint = alertFingerprint diff --git a/lib/plugins.js b/lib/plugins.js index 296a9a5b..26d8bfc9 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -2,7 +2,6 @@ var _ = require('lodash') var async = require('async') -var R = require('ramda') var BigNumber = require('bignumber.js') @@ -15,6 +14,7 @@ var uuid = require('node-uuid') var tradeIntervals = {} var CHECK_NOTIFICATION_INTERVAL = 60 * 1000 +var ALERT_SEND_INTERVAL = 60 * 60 * 1000 var POLLING_RATE = 60 * 1000 // poll each minute var REAP_RATE = 2 * 1000 var PENDING_TIMEOUT = 70 * 1000 @@ -51,6 +51,9 @@ var coins = { ETH: {unitScale: 18} } +var alertFingerprint = null +var lastAlertTime = null + // that's basically a constructor exports.init = function init (databaseHandle) { if (!databaseHandle) { @@ -428,7 +431,9 @@ exports.dispenseAck = function dispenseAck (session, rec) { } exports.fiatBalance = function fiatBalance (cryptoCode) { - var rawRate = exports.getDeviceRate(cryptoCode).rates.ask + var deviceRate = exports.getDeviceRate(cryptoCode) + if (!deviceRate) return null + var rawRate = deviceRate.rates.ask var commission = cachedConfig.exchanges.settings.commission var lastBalance = lastBalances[cryptoCode] @@ -634,49 +639,95 @@ exports.getcryptoCodes = function getcryptoCodes () { return cryptoCodes } -function sendMessage (rec, cb) { - console.log('DEBUG5') - cb = cb || function () {} +function sendMessage (rec) { + console.log('DEBUG50') + console.log('%j', rec) console.log(cachedConfig.exchanges.plugins.current.notify) var pluginTypes = JSON.parse(cachedConfig.exchanges.plugins.current.notify) - console.log('DEBUG7') console.log(pluginTypes) var pluginPromises = pluginTypes.map(function (pluginType) { - if (pluginType === 'email') return emailPlugin.sendMessage(rec, cb) - if (pluginType === 'sms') return smsPlugin.sendMessage(rec, cb) + if (pluginType === 'email') return emailPlugin.sendMessage(rec) + if (pluginType === 'sms') return smsPlugin.sendMessage(rec) throw new Error('No such plugin type: ' + pluginType) }) return Promise.all(pluginPromises) } +exports.sendMessage = sendMessage + +function sendNoAlerts () { + var subject = '[Lamassu] All clear' + var rec = { + sms: { + body: subject + }, + email: { + subject: subject, + body: 'No errors are reported for your machines.' + } + } + return sendMessage(rec) +} function checkNotification () { - notifier.checkStatus() - .then(function (alerts) { - if (alerts.length === 0) return + console.log('DEBUG39 ******************************') + return notifier.checkStatus() + .then(function (alertRec) { + console.log('DEBUG41 ******************************') + var fingerprint = notifier.alertFingerprint(alertRec) + if (!fingerprint) { + console.log('DEBUG40 ******************************') + var inAlert = !!alertFingerprint + alertFingerprint = null + lastAlertTime = null + if (inAlert) return sendNoAlerts() + } - // var devices = R.keys(alerts.devices) - var alertRecs = alerts.general - R.keys(alerts.devices).forEach(function (device) { - alertRecs = R.append(alerts.devices[device], alertRecs) - }) - var alertTypes = alertRecs.pluck('code', alerts) - var body = '' + console.log('DEBUG42 ******************************') + + var alertChanged = fingerprint === alertFingerprint && + lastAlertTime - Date.now() < ALERT_SEND_INTERVAL + if (alertChanged) return + + console.log('DEBUG43 ******************************') + + var subject = notifier.alertSubject(alertRec) var rec = { sms: { - body: '[Lamassu] Errors reported: ' + alertTypes.join(', ') + body: subject }, email: { - subject: '[Lamassu] Errors reported: ' + alertTypes.join(', '), - body: body + subject: subject, + body: notifier.printEmailAlerts(alertRec) } } - sendMessage(rec) + alertFingerprint = fingerprint + lastAlertTime = Date.now() + return sendMessage(rec) + }) + .then(function () { + logger.debug('Successfully sent alerts') }) .catch(function (err) { logger.error(err) }) } +function checkBalances () { + var cryptoCodes = exports.getcryptoCodes() + + var balances = [] + cryptoCodes.forEach(function (cryptoCode) { + var balance = exports.fiatBalance(cryptoCode) + if (!balance) return + var rec = {fiatBalance: balance, cryptoCode: cryptoCode, fiatCode: deviceCurrency} + balances.push(rec) + }) + + return balances +} + exports.startCheckingNotification = function startCheckingNotification () { + notifier.init(db, checkBalances) + checkNotification() setInterval(checkNotification, CHECK_NOTIFICATION_INTERVAL) } diff --git a/package.json b/package.json index 0d82a127..ae82b380 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,10 @@ "lodash": "^2.4.1", "minimist": "0.0.8", "node-uuid": "^1.4.2", + "numeral": "^1.5.3", "pg": "^4.5.1", "pg-promise": "^3.4.3", + "pretty-ms": "^2.1.0", "prompt": "^1.0.0", "promptly": "^1.1.0", "ramda": "^0.19.1",