lots of notifier improvements

This commit is contained in:
Josh Harvey 2016-04-23 19:27:53 +03:00
parent 8a87c7579d
commit 8a4b447db3
7 changed files with 172 additions and 26 deletions

30
dev/notify.js Normal file
View file

@ -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)
})

View file

@ -7,12 +7,10 @@ var rand = Math.floor(Math.random() * 1e6)
var rec = { var rec = {
email: { email: {
toEmail: 'joshmh@gmail.com',
subject: 'Test email ' + rand, subject: 'Test email ' + rand,
body: 'This is a test email from lamassu-server' body: 'This is a test email from lamassu-server'
}, },
sms: { sms: {
toNumber: process.argv[2],
body: '[Lamassu] This is a test sms ' + rand body: '[Lamassu] This is a test sms ' + rand
} }
} }

View file

@ -1,5 +1,7 @@
'use strict' 'use strict'
require('es6-promise').polyfill()
var http = require('http') var http = require('http')
var https = require('https') var https = require('https')
var express = require('express') var express = require('express')

View file

@ -10,6 +10,7 @@ function connect () {
} }
return pgp(psqlUrl) return pgp(psqlUrl)
} }
exports.connect = connect
function loadConfig () { function loadConfig () {
var db = connect() var db = connect()
@ -19,5 +20,4 @@ function loadConfig () {
return data.data return data.data
}) })
} }
exports.loadConfig = loadConfig exports.loadConfig = loadConfig

View file

@ -1,4 +1,7 @@
var crypto = require('crypto')
var R = require('ramda') var R = require('ramda')
var prettyMs = require('pretty-ms')
var numeral = require('numeral')
var db = null var db = null
var getBalances = null var getBalances = null
@ -21,7 +24,7 @@ function sameState (a, b) {
function checkBalance (rec) { function checkBalance (rec) {
var LOW_BALANCE_THRESHOLD = 10 var LOW_BALANCE_THRESHOLD = 10
return rec.fiatBalance < LOW_BALANCE_THRESHOLD 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 : null
} }
@ -110,5 +113,65 @@ function checkStatus () {
return alerts return alerts
}) })
.catch(function (err) {
console.log(err.stack)
})
} }
exports.checkStatus = checkStatus 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

View file

@ -2,7 +2,6 @@
var _ = require('lodash') var _ = require('lodash')
var async = require('async') var async = require('async')
var R = require('ramda')
var BigNumber = require('bignumber.js') var BigNumber = require('bignumber.js')
@ -15,6 +14,7 @@ var uuid = require('node-uuid')
var tradeIntervals = {} var tradeIntervals = {}
var CHECK_NOTIFICATION_INTERVAL = 60 * 1000 var CHECK_NOTIFICATION_INTERVAL = 60 * 1000
var ALERT_SEND_INTERVAL = 60 * 60 * 1000
var POLLING_RATE = 60 * 1000 // poll each minute var POLLING_RATE = 60 * 1000 // poll each minute
var REAP_RATE = 2 * 1000 var REAP_RATE = 2 * 1000
var PENDING_TIMEOUT = 70 * 1000 var PENDING_TIMEOUT = 70 * 1000
@ -51,6 +51,9 @@ var coins = {
ETH: {unitScale: 18} ETH: {unitScale: 18}
} }
var alertFingerprint = null
var lastAlertTime = null
// that's basically a constructor // that's basically a constructor
exports.init = function init (databaseHandle) { exports.init = function init (databaseHandle) {
if (!databaseHandle) { if (!databaseHandle) {
@ -428,7 +431,9 @@ exports.dispenseAck = function dispenseAck (session, rec) {
} }
exports.fiatBalance = function fiatBalance (cryptoCode) { 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 commission = cachedConfig.exchanges.settings.commission
var lastBalance = lastBalances[cryptoCode] var lastBalance = lastBalances[cryptoCode]
@ -634,49 +639,95 @@ exports.getcryptoCodes = function getcryptoCodes () {
return cryptoCodes return cryptoCodes
} }
function sendMessage (rec, cb) { function sendMessage (rec) {
console.log('DEBUG5') console.log('DEBUG50')
cb = cb || function () {} console.log('%j', rec)
console.log(cachedConfig.exchanges.plugins.current.notify) console.log(cachedConfig.exchanges.plugins.current.notify)
var pluginTypes = JSON.parse(cachedConfig.exchanges.plugins.current.notify) var pluginTypes = JSON.parse(cachedConfig.exchanges.plugins.current.notify)
console.log('DEBUG7')
console.log(pluginTypes) console.log(pluginTypes)
var pluginPromises = pluginTypes.map(function (pluginType) { var pluginPromises = pluginTypes.map(function (pluginType) {
if (pluginType === 'email') return emailPlugin.sendMessage(rec, cb) if (pluginType === 'email') return emailPlugin.sendMessage(rec)
if (pluginType === 'sms') return smsPlugin.sendMessage(rec, cb) if (pluginType === 'sms') return smsPlugin.sendMessage(rec)
throw new Error('No such plugin type: ' + pluginType) throw new Error('No such plugin type: ' + pluginType)
}) })
return Promise.all(pluginPromises) 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 () { function checkNotification () {
notifier.checkStatus() console.log('DEBUG39 ******************************')
.then(function (alerts) { return notifier.checkStatus()
if (alerts.length === 0) return .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) console.log('DEBUG42 ******************************')
var alertRecs = alerts.general
R.keys(alerts.devices).forEach(function (device) { var alertChanged = fingerprint === alertFingerprint &&
alertRecs = R.append(alerts.devices[device], alertRecs) lastAlertTime - Date.now() < ALERT_SEND_INTERVAL
}) if (alertChanged) return
var alertTypes = alertRecs.pluck('code', alerts)
var body = '' console.log('DEBUG43 ******************************')
var subject = notifier.alertSubject(alertRec)
var rec = { var rec = {
sms: { sms: {
body: '[Lamassu] Errors reported: ' + alertTypes.join(', ') body: subject
}, },
email: { email: {
subject: '[Lamassu] Errors reported: ' + alertTypes.join(', '), subject: subject,
body: body body: notifier.printEmailAlerts(alertRec)
} }
} }
sendMessage(rec) alertFingerprint = fingerprint
lastAlertTime = Date.now()
return sendMessage(rec)
})
.then(function () {
logger.debug('Successfully sent alerts')
}) })
.catch(function (err) { .catch(function (err) {
logger.error(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 () { exports.startCheckingNotification = function startCheckingNotification () {
notifier.init(db, checkBalances)
checkNotification()
setInterval(checkNotification, CHECK_NOTIFICATION_INTERVAL) setInterval(checkNotification, CHECK_NOTIFICATION_INTERVAL)
} }

View file

@ -36,8 +36,10 @@
"lodash": "^2.4.1", "lodash": "^2.4.1",
"minimist": "0.0.8", "minimist": "0.0.8",
"node-uuid": "^1.4.2", "node-uuid": "^1.4.2",
"numeral": "^1.5.3",
"pg": "^4.5.1", "pg": "^4.5.1",
"pg-promise": "^3.4.3", "pg-promise": "^3.4.3",
"pretty-ms": "^2.1.0",
"prompt": "^1.0.0", "prompt": "^1.0.0",
"promptly": "^1.1.0", "promptly": "^1.1.0",
"ramda": "^0.19.1", "ramda": "^0.19.1",