lamassu-server/lib/notifier/utils.js
Cesar c457faab40 Chore: make notification center UI
Chore: fiatBalancesNotify refactor

Chore: removed now-unused code in some files

Feat: change column "detail" in database to use jsonb

Chore: add notification center background and button

Chore: notifications screen scaffolding

Fix: change position of notification UI

Feat: join backend and frontend

Feat: notification icons and machine names

Feat: add clear all button, stripe overlay on invalid notification

Fix: rework notification styles

Feat: use popper to render notifications

Feat: make notification center UI

Fix: fix css on notification center

Fix: fix invalidateNotification

Chore: apply PR requested changes

Fix: PR fixes

Fix: make toggleable body/root styles be handled by react

Chore: delete old notifier file

Fix: undo variable name changes for cryptobalance notifs
2021-02-04 15:48:23 +00:00

200 lines
4.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const _ = require('lodash/fp')
const crypto = require('crypto')
const numeral = require('numeral')
const prettyMs = require('pretty-ms')
const coinUtils = require('../coin-utils')
const {
CODES_DISPLAY,
NETWORK_DOWN_TIME,
PING,
ALERT_SEND_INTERVAL
} = require('./codes')
const DETAIL_TEMPLATE = {
deviceId: '',
cryptoCode: '',
code: '',
cassette: '',
age: '',
customerId: '',
cryptoAddress: '',
direction: '',
fiat: '',
fiatCode: ''
}
function parseEventNote (event) {
return _.set('note', JSON.parse(event.note), event)
}
function checkPing (device) {
const age = Date.now() - (new Date(device.lastPing).getTime())
if (age > NETWORK_DOWN_TIME) return [{ code: PING, age, machineName: device.name }]
return []
}
const getDeviceTime = _.flow(_.get('device_time'), Date.parse)
const isActive = it => it.active && (it.balance || it.errors)
const codeDisplay = code => CODES_DISPLAY[code]
const alertFingerprint = {
fingerprint: null,
lastAlertTime: null
}
const getAlertFingerprint = () => alertFingerprint.fingerprint
const getLastAlertTime = () => alertFingerprint.lastAlertTime
const setAlertFingerprint = (fp, time = Date.now()) => {
alertFingerprint.fingerprint = fp
alertFingerprint.lastAlertTime = time
}
const shouldNotAlert = currentAlertFingerprint => {
return (
currentAlertFingerprint === getAlertFingerprint() &&
getLastAlertTime() - Date.now() < ALERT_SEND_INTERVAL
)
}
function buildAlertFingerprint (alertRec, notifications) {
const sms = getAlertTypes(alertRec, notifications.sms)
const email = getAlertTypes(alertRec, notifications.email)
if (sms.length === 0 && email.length === 0) return null
const smsTypes = _.map(codeDisplay, _.uniq(_.map('code', sms))).sort()
const emailTypes = _.map(codeDisplay, _.uniq(_.map('code', email))).sort()
const subject = _.concat(smsTypes, emailTypes).join(', ')
return crypto.createHash('sha256').update(subject).digest('hex')
}
function sendNoAlerts (plugins, smsEnabled, emailEnabled) {
const subject = '[Lamassu] All clear'
let rec = {}
if (smsEnabled) {
rec = _.set(['sms', 'body'])(subject)(rec)
}
if (emailEnabled) {
rec = _.set(['email', 'subject'])(subject)(rec)
rec = _.set(['email', 'body'])('No errors are reported for your machines.')(
rec
)
}
return plugins.sendMessage(rec)
}
const buildTransactionMessage = (tx, rec, highValueTx, machineName, customer) => {
const isCashOut = tx.direction === 'cashOut'
const direction = isCashOut ? 'Cash Out' : 'Cash In'
const crypto = `${coinUtils.toUnit(tx.cryptoAtoms, tx.cryptoCode)} ${
tx.cryptoCode
}`
const fiat = `${tx.fiat} ${tx.fiatCode}`
const customerName = customer.name || customer.id
const phone = customer.phone ? `- Phone: ${customer.phone}` : ''
let status = null
if (rec.error) {
status = `Error - ${rec.error}`
} else {
status = !isCashOut
? 'Successful'
: !rec.isRedemption
? 'Successful & awaiting redemption'
: 'Successful & dispensed'
}
const body = `
- Transaction ID: ${tx.id}
- Status: ${status}
- Machine name: ${machineName}
- ${direction}
- ${fiat}
- ${crypto}
- Customer: ${customerName}
${phone}
`
const smsSubject = `A ${highValueTx ? 'high value ' : ''}${direction.toLowerCase()} transaction just happened at ${machineName} for ${fiat}`
const emailSubject = `A ${highValueTx ? 'high value ' : ''}transaction just happened`
return [{
sms: {
body: `${smsSubject} ${status}`
},
email: {
emailSubject,
body
}
}, highValueTx]
}
function formatCurrency (num, code) {
return numeral(num).format('0,0.00') + ' ' + code
}
function formatAge (age, settings) {
return prettyMs(age, settings)
}
function buildDetail (obj) {
// obj validation
const objKeys = _.keys(obj)
const detailKeys = _.keys(DETAIL_TEMPLATE)
if ((_.difference(objKeys, detailKeys)).length > 0) {
return Promise.reject(new Error('Error when building detail object: invalid properties'))
}
return { ...DETAIL_TEMPLATE, ...obj }
}
function deviceAlerts (config, alertRec, device) {
let alerts = []
if (config.balance) {
alerts = _.concat(alerts, alertRec.devices[device].balanceAlerts)
}
if (config.errors) {
alerts = _.concat(alerts, alertRec.devices[device].deviceAlerts)
}
return alerts
}
function getAlertTypes (alertRec, config) {
let alerts = []
if (!isActive(config)) return alerts
if (config.balance) {
alerts = _.concat(alerts, alertRec.general)
}
_.forEach(device => {
alerts = _.concat(alerts, deviceAlerts(config, alertRec, device))
}, _.keys(alertRec.devices))
return alerts
}
module.exports = {
codeDisplay,
parseEventNote,
getDeviceTime,
checkPing,
isActive,
getAlertFingerprint,
getLastAlertTime,
setAlertFingerprint,
shouldNotAlert,
buildAlertFingerprint,
sendNoAlerts,
buildTransactionMessage,
formatCurrency,
formatAge,
buildDetail,
deviceAlerts
}