diff --git a/.gitignore b/.gitignore index e2f17926..60fcba5b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ options.mine.js .vagrant raqia.json + +scratch/ diff --git a/lib/email.js b/lib/email.js new file mode 100644 index 00000000..2f7c2144 --- /dev/null +++ b/lib/email.js @@ -0,0 +1,40 @@ +var SMTPConnection = require('smtp-connection') + +/* + +Nice job signing up! Let's finish getting you set up. +Simply change the settings in your email software to these: +SMTP Server: mail.smtp2go.com +SMTP Port: 2525 (recommended) +Username: josh@lamassu.is +Password: view / edit +*/ + +var options = { + port: 2525, + host: 'mail.smtp2go.com', + requireTLS: true +} + +function send (from, to, subject, body, cb) { + var connection = new SMTPConnection(options) + connection.connect(function () { + connection.login({user: 'josh@lamassu.is', pass: 'HPtXGp}9baafiqns%6YFH'}, function (err) { + if (err) return console.error(err) + var envelope = { + from: from, + to: to + } + var message = 'Subject: ' + subject + '\n\n' + body + connection.send(envelope, message, function (err, info) { + connection.quit() + cb(err, info) + }) + }) + }) +} + +send('josh@lamassu.is', 'joshmh@gmail.com', 'Another test', 'Screen is stale.\n\nTest3', function (err, info) { + console.log(err) + console.log(info) +}) diff --git a/lib/notifier.js b/lib/notifier.js new file mode 100644 index 00000000..333b9138 --- /dev/null +++ b/lib/notifier.js @@ -0,0 +1,90 @@ +'use strict' + +var R = require('ramda') + +var db = null + +function init (_db) { + db = _db +} + +function toInt10 (str) { return parseInt(str, 10) } + +function jsonParse (event) { + return R.assoc('note', JSON.parse(event.note), event) +} + +function sameState (a, b) { + return a.note.sessionId === b.note.sessionId && a.note.state === b.note.state +} + +function checkStuckScreen (deviceEvents) { + var sortedEvents = R.sortBy(R.compose(toInt10, R.prop('device_time')), R.map(jsonParse, deviceEvents)) + var noRepeatEvents = R.dropRepeatsWith(sameState, sortedEvents) + var lastEvent = R.last(noRepeatEvents) + var IDLE_STATES = ['idle', 'dualIdle'] + var STALE_STATE = 60 * 1000 + + if (!lastEvent) { + console.log('No data for device') + return + } + + var state = lastEvent.note.state + if (R.contains(state, IDLE_STATES)) { + console.log('Machine is idle [OK]') + return + } + + console.log(lastEvent.age) + if (lastEvent.age > STALE_STATE) { + console.log('Stale state: ' + state + ' [ALERT]') + return + } + + console.log('[OK]') +} + +function checkStatus () { +/* + - Fetch devices from devices table + - Fetch all machine_events into memory + - For each device, verify the following: + - stuck on screen + - last screen is >5m stale and is not idle screen + - report stuck on screen and name of screen + - not scanning qr codes? + - low bitcoins -- need a separate strategy, but server has this info + - var fiatBalance = plugins.fiatBalance(); + - machine isn't pinging server +*/ + db.devices(function (err, devices) { + if (err) return console.error(err) + + db.machineEvents(function (err, events) { + if (err) return console.error(err) + + devices.rows.forEach(function (deviceRow) { + var deviceFingerprint = deviceRow.fingerprint + var deviceEvents = events.rows.filter(function (eventRow) { + return eventRow.device_fingerprint === deviceFingerprint + }) + + console.log('DEVICE: ' + deviceRow.fingerprint) + checkStuckScreen(deviceEvents) + }) + }) + }) +} + +var _db = require('./postgresql_interface') +var connectionString = 'postgres://lamassu:lamassu@localhost/lamassu' +_db.init(connectionString) + +init(_db) + +checkStatus() + +// TODO: How to know which alerts have been sent? +// Send alert every 10m while alert state +// Remember last sent alert in memory diff --git a/lib/postgresql_interface.js b/lib/postgresql_interface.js index f0e1116b..189fa621 100644 --- a/lib/postgresql_interface.js +++ b/lib/postgresql_interface.js @@ -109,6 +109,7 @@ function query(client, queryStr, values, cb) { client.query(queryStr, values, function(err, results) { if (err) { if (!isLowSeverity(err)) { + console.error(err) console.log(queryStr); console.log(values); } @@ -127,6 +128,7 @@ function silentQuery(client, queryStr, values, cb) { client.query(queryStr, values, function(err) { if (err) { if (!isLowSeverity(err)) { + console.error(err) console.log(queryStr); console.log(values); } @@ -506,12 +508,12 @@ exports.machineEvent = function machineEvent(rec, cb) { var TTL = 2 * 60 * 60 * 1000 connect(function(cerr, client, done) { if (cerr) return cb(cerr); - var fields = ['id', 'device_fingerprint', 'event_type', 'note', 'device_time', 'created'] + var fields = ['id', 'device_fingerprint', 'event_type', 'note', 'device_time'] var sql = getInsertQuery('machine_events', fields, false) - var values = [rec.id, rec.fingerprint, rec.eventType, rec.note, rec.deviceTime, new Date().toISOString()] + var values = [rec.id, rec.fingerprint, rec.eventType, rec.note, rec.deviceTime] - var deleteSql = 'DELETE FROM machine_events WHERE created < $1'; - var deleteValues = [new Date(Date.now() - TTL).toISOString()]; + var deleteSql = 'DELETE FROM machine_events WHERE (EXTRACT(EPOCH FROM (now() - created))) * 1000 > $1'; + var deleteValues = [TTL]; query(client, deleteSql, deleteValues, function(err) { if (err) console.error(err) @@ -524,6 +526,31 @@ exports.machineEvent = function machineEvent(rec, cb) { }); }; +exports.devices = function devices(cb) { + connect(function(cerr, client, done) { + if (cerr) return cb(cerr); + var sql = 'SELECT fingerprint, name FROM devices ' + + 'WHERE authorized=$1'; + query(client, sql, [true], function(err, results) { + done(); + if (err) return cb(err); + cb(null, results) + }) + }) +} + +exports.machineEvents = function machineEvents(cb) { + connect(function(cerr, client, done) { + if (cerr) return cb(cerr); + var sql = 'SELECT *, (EXTRACT(EPOCH FROM (now() - created))) * 1000 AS age FROM machine_events' + query(client, sql, [], function(err, results) { + done(); + if (err) return cb(err); + cb(null, results) + }) + }) +} + /* exports.init('postgres://lamassu:lamassu@localhost/lamassu'); connect(function(err, client, done) { diff --git a/lib/sms.js b/lib/sms.js new file mode 100644 index 00000000..2b0dbce7 --- /dev/null +++ b/lib/sms.js @@ -0,0 +1,12 @@ +var accountSid = 'AC5b08587439d5e0adb5132d133941ab76' +var authToken = 'b4acf1212c0271d852706d17711e9670' +var client = require('twilio')(accountSid, authToken) + +client.messages.create({ + body: '[Lamassu] ALERT Stale screen: acceptingFirstBill', + to: '+359899948650', + from: '+16035383222' +}, function (err, message) { + console.log(err) + console.log(message) +}) diff --git a/package.json b/package.json index 17d10ec4..282f6850 100644 --- a/package.json +++ b/package.json @@ -16,21 +16,24 @@ "joi": "^5.1.0", "lamassu-bitcoinaverage": "~1.0.0", "lamassu-bitcoind": "^1.1.0", + "lamassu-bitgo": "^0.1.3", "lamassu-bitpay": "~1.0.0", "lamassu-bitstamp": "^1.0.2", "lamassu-blockchain": "^1.1.3", "lamassu-blockcypher": "~0.1.0", + "lamassu-coinapult": "^0.4.5", + "lamassu-coinbase": "^1.0.4", "lamassu-coindesk": "~1.0.0", + "lamassu-coinfloor": "^0.1.2", "lamassu-config": "~0.4.0", "lamassu-identitymind": "^1.0.1", - "lamassu-coinbase": "^1.0.4", - "lamassu-coinapult": "^0.4.5", - "lamassu-coinfloor": "^0.1.2", - "lamassu-bitgo": "^0.1.3", "lodash": "^2.4.1", "minimist": "0.0.8", "node-uuid": "^1.4.2", "pg": "~2.11.1", + "ramda": "^0.19.1", + "smtp-connection": "^2.2.1", + "twilio": "^3.3.0-edge", "wreck": "5.1.0" }, "repository": {