From 3d059287aee0cc6758659766980f49ae1fd72033 Mon Sep 17 00:00:00 2001 From: vjrj Date: Tue, 11 Sep 2018 14:42:31 +0200 Subject: [PATCH] Notif process also in cron --- .../modules/server/notificationsProcess.js | 137 +++++++++++++++++ imports/startup/server/cron.js | 22 ++- .../startup/server/notificationsObserver.js | 144 ++---------------- 3 files changed, 167 insertions(+), 136 deletions(-) create mode 100644 imports/modules/server/notificationsProcess.js diff --git a/imports/modules/server/notificationsProcess.js b/imports/modules/server/notificationsProcess.js new file mode 100644 index 0000000..300f925 --- /dev/null +++ b/imports/modules/server/notificationsProcess.js @@ -0,0 +1,137 @@ +/* eslint-disable import/no-absolute-path */ + +import { Meteor } from 'meteor/meteor'; +import Notifications from '/imports/api/Notifications/Notifications'; +import i18n from 'i18next'; +import moment from 'moment'; +import { dateLongFormat } from '/imports/api/Common/dates'; +// import sendMail from '/imports/startup/server/email'; +import sendEmail, { subjectTruncate } from '/imports/modules/server/send-email'; +import { isMailServerMaster } from '/imports/startup/server/email'; +// import { hr } from '/imports/startup/server/email'; +import getEmailOf from '/imports/modules/get-email-of-user'; +import image from 'google-maps-image-api-url'; +import gcm from 'node-gcm'; +import { trim } from '/imports/ui/components/NotificationsObserver/util.js'; + +let validFcmSender = true; + +if (!(Meteor.settings.private.fcmApiToken && Meteor.settings.private.fcmApiToken.length > 0)) { + console.warn('Missing settings.private.fcmApiToken key, mobile notifications will not work'); + validFcmSender = false; +} + +const fcmSender = new gcm.Sender(Meteor.settings.private.fcmApiToken); + +// https://www.npmjs.com/package/google-maps-image-api-url +// https://stackoverflow.com/questions/24355007/is-there-no-way-to-embed-a-google-map-into-an-html-email +// https://developers.google.com/maps/documentation/static-maps/intro +function imgUrl(lat, lng) { + return image({ + key: Meteor.settings.gmaps.key, + type: 'staticmap', + center: `${lat},${lng}`, + size: '640x480', + zoom: 16, + maptype: 'hybrid', + language: 'es', + markers: `icon: ${Meteor.settings.private.fireIconUrl}|${lat},${lng}` + }); +} + +/* + function imgEl(lat, lng) { + return ``; + } */ + +const processNotif = (notif) => { + if (isMailServerMaster && notif.type === 'mobile' && !notif.notified) { + const user = Meteor.users.findOne({ _id: notif.userId }); + moment.locale(user.lang); + // duplicate code below + const body = `${trim(notif.content)}`; + + // https://firebase.google.com/docs/cloud-messaging/concept-options + const msg = new gcm.Message(); + + msg.addNotification({ + title: i18n.t('Alerta de fuego'), + body, + click_action: 'FLUTTER_NOTIFICATION_CLICK', + tag: notif._id, // prevent duplication of fire notifications + sound: 'default', // Indicates sound to be played. Supports only default currently. + icon: 'launch_image' // 'ic_launcher' + }); + + msg.addData('id', notif._id._str); + msg.addData('description', body); + msg.addData('lat', notif.geo.coordinates[1]); + msg.addData('lon', notif.geo.coordinates[0]); + msg.addData('when', notif.when); + msg.addData('subsId', notif.subsId._str); + msg.addData('sealed', notif.sealed); + + const registrationTokens = []; + if (!user.fireBaseToken) { + console.warn('This mobile user doesn\'t have a firebase registration token'); + } else { + registrationTokens.push(user.fireBaseToken); + // FIXME: better join users + if (validFcmSender) { + fcmSender.send(msg, { registrationTokens }, Meteor.bindEnvironment(function processResult(err, response) { + if (err) { + console.error(`FCM error: ${err}`); + // FIXME send to sentry + } else { + // console.log(`FCM response: ${response}`); + Notifications.update(notif._id, { $set: { notified: true, notifiedAt: new Date() } }); + } + })); + } + } + } else if (notif.type === 'web' && !notif.emailNotified) { + const user = Meteor.users.findOne({ _id: notif.userId }); + const { firstName, emailAddress } = getEmailOf(user); + + if (emailAddress) { + const img = imgUrl(notif.geo.coordinates[1], notif.geo.coordinates[0]); + // const url = imgUrl(notif.geo.coordinates[1], notif.geo.coordinates[0]); + const fireUrl = `${Meteor.absoluteUrl('fire/')}${notif.sealed}`; + // const fireHtmlUrl = `${i18n.t('Más información sobre este fuego')}`; + // TODO get _id of fire + // const fireTextUrl = `${i18n.t('Más información sobre este fuego')}:\n${fireUrl}`; + // FIXME use our map as url and static map as img + moment.locale(user.lang); + // moment user tz ? + const message = `${trim(notif.content)} (${i18n.t('fireDetectedAt', { when: dateLongFormat(notif.when) })}).`; + + // TODO Comunes Address + + const emailOpts = { + to: emailAddress, + // userName: firstName, + // sendAt: new Date(), + subject: subjectTruncate.apply(message), + // text: `${message}\n\n${fireTextUrl}\n\n`, + // template: '

{{appName}}

{{{html}}}', + lang: user.lang, + template: 'new-fire', + templateVars: { + applicationName: i18n.t('AppName'), + firstName, + message, + fireUrl, + img, + subsUrl: Meteor.absoluteUrl('subscriptions') + } + }; + sendEmail(emailOpts).catch((error) => { + throw new Meteor.Error('500', `${error}`); + }); + // sendMail(emailOpts, true); + Notifications.update(notif._id, { $set: { emailNotified: true, emailNotifiedAt: new Date() } }); + } + } +}; + +export default processNotif; diff --git a/imports/startup/server/cron.js b/imports/startup/server/cron.js index 9b0fa77..cb9d755 100644 --- a/imports/startup/server/cron.js +++ b/imports/startup/server/cron.js @@ -4,6 +4,8 @@ import { Meteor } from 'meteor/meteor'; import { tweetIberiaFires, tweetEuropeFires } from '/imports/api/ActiveFires/server/tweetFiresInZone'; import { isMailServerMaster, sendEmailsFromQueue } from '/imports/startup/server/email'; +import Notifications from '/imports/api/Notifications/Notifications'; +import processNotif from '/imports/modules/server/notificationsProcess.js'; // https://github.com/thesaucecode/meteor-synced-cron/ @@ -44,7 +46,7 @@ Meteor.startup(() => { timezone: 'Europe/Madrid', schedule: (parser) => { // http://bunkat.github.io/later/ - const sched = parser.text('every 1 min'); + const sched = parser.text('every 10 min'); if (sched.error !== -1) { console.error(`Mail cron 'when' field parsed with errors: ${sched.error}`); } @@ -52,6 +54,24 @@ Meteor.startup(() => { }, job: () => sendEmailsFromQueue() }); + + SyncedCron.add({ + name: 'Process pending notif', + timezone: 'Europe/Madrid', + schedule: (parser) => { + // http://bunkat.github.io/later/ + const sched = parser.text('every 15 min'); + if (sched.error !== -1) { + console.error(`Mail cron 'when' field parsed with errors: ${sched.error}`); + } + return sched; + }, + job: () => { + Notifications.find({ nofitied: null }).forEach((notif) => { + processNotif(notif); + }); + } + }); } const esEn = Meteor.settings.private.twitter.es.enabled; diff --git a/imports/startup/server/notificationsObserver.js b/imports/startup/server/notificationsObserver.js index 0065e15..70be8e0 100644 --- a/imports/startup/server/notificationsObserver.js +++ b/imports/startup/server/notificationsObserver.js @@ -1,145 +1,19 @@ /* eslint-disable import/no-absolute-path */ -import i18n from 'i18next'; import { Meteor } from 'meteor/meteor'; -import moment from 'moment'; -import { dateLongFormat } from '/imports/api/Common/dates'; import Notifications from '/imports/api/Notifications/Notifications'; -// import sendMail from '/imports/startup/server/email'; -import sendEmail, { subjectTruncate } from '/imports/modules/server/send-email'; +import processNotif from '/imports/modules/server/notificationsProcess.js'; import { isMailServerMaster } from '/imports/startup/server/email'; -// import { hr } from '/imports/startup/server/email'; -import getEmailOf from '/imports/modules/get-email-of-user'; -import image from 'google-maps-image-api-url'; -import gcm from 'node-gcm'; -import { trim } from '/imports/ui/components/NotificationsObserver/util.js'; Meteor.startup(() => { - let validFcmSender = true; - - if (!(Meteor.settings.private.fcmApiToken && Meteor.settings.private.fcmApiToken.length > 0)) { - console.warn('Missing settings.private.fcmApiToken key, mobile notifications will not work'); - validFcmSender = false; - } - - const fcmSender = new gcm.Sender(Meteor.settings.private.fcmApiToken); - - // https://www.npmjs.com/package/google-maps-image-api-url - // https://stackoverflow.com/questions/24355007/is-there-no-way-to-embed-a-google-map-into-an-html-email - // https://developers.google.com/maps/documentation/static-maps/intro - function imgUrl(lat, lng) { - return image({ - key: Meteor.settings.gmaps.key, - type: 'staticmap', - center: `${lat},${lng}`, - size: '640x480', - zoom: 16, - maptype: 'hybrid', - language: 'es', - markers: `icon: ${Meteor.settings.private.fireIconUrl}|${lat},${lng}` + if (isMailServerMaster) { + Notifications.find().observe({ + added: function notifAdded(notif) { + processNotif(notif); + }, + changed: function notifChanged(updatedNotif) { // , oldNotif) { + processNotif(updatedNotif); + } }); } - - /* - function imgEl(lat, lng) { - return ``; - } */ - - function process(notif) { - if (isMailServerMaster && notif.type === 'mobile' && !notif.notified) { - const user = Meteor.users.findOne({ _id: notif.userId }); - moment.locale(user.lang); - // duplicate code below - const body = `${trim(notif.content)}`; - - // https://firebase.google.com/docs/cloud-messaging/concept-options - const msg = new gcm.Message(); - - msg.addNotification({ - title: i18n.t('Alerta de fuego'), - body, - click_action: 'FLUTTER_NOTIFICATION_CLICK', - tag: notif._id, // prevent duplication of fire notifications - sound: 'default', // Indicates sound to be played. Supports only default currently. - icon: 'launch_image' // 'ic_launcher' - }); - - msg.addData('id', notif._id._str); - msg.addData('description', body); - msg.addData('lat', notif.geo.coordinates[1]); - msg.addData('lon', notif.geo.coordinates[0]); - msg.addData('when', notif.when); - msg.addData('subsId', notif.subsId._str); - msg.addData('sealed', notif.sealed); - - const registrationTokens = []; - if (!user.fireBaseToken) { - console.warn('This mobile user doesn\'t have a firebase registration token'); - } else { - registrationTokens.push(user.fireBaseToken); - // FIXME: better join users - if (validFcmSender) { - fcmSender.send(msg, { registrationTokens }, Meteor.bindEnvironment(function processResult(err, response) { - if (err) { - console.error(`FCM error: ${err}`); - } else { - // console.log(`FCM response: ${response}`); - Notifications.update(notif._id, { $set: { notified: true, notifiedAt: new Date() } }); - } - })); - } - } - } else if (notif.type === 'web' && !notif.emailNotified) { - const user = Meteor.users.findOne({ _id: notif.userId }); - const { firstName, emailAddress } = getEmailOf(user); - - if (emailAddress) { - const img = imgUrl(notif.geo.coordinates[1], notif.geo.coordinates[0]); - // const url = imgUrl(notif.geo.coordinates[1], notif.geo.coordinates[0]); - const fireUrl = `${Meteor.absoluteUrl('fire/')}${notif.sealed}`; - // const fireHtmlUrl = `${i18n.t('Más información sobre este fuego')}`; - // TODO get _id of fire - // const fireTextUrl = `${i18n.t('Más información sobre este fuego')}:\n${fireUrl}`; - // FIXME use our map as url and static map as img - moment.locale(user.lang); - // moment user tz ? - const message = `${trim(notif.content)} (${i18n.t('fireDetectedAt', { when: dateLongFormat(notif.when) })}).`; - - // TODO Comunes Address - - const emailOpts = { - to: emailAddress, - // userName: firstName, - // sendAt: new Date(), - subject: subjectTruncate.apply(message), - // text: `${message}\n\n${fireTextUrl}\n\n`, - // template: '

{{appName}}

{{{html}}}', - lang: user.lang, - template: 'new-fire', - templateVars: { - applicationName: i18n.t('AppName'), - firstName, - message, - fireUrl, - img, - subsUrl: Meteor.absoluteUrl('subscriptions') - } - }; - sendEmail(emailOpts).catch((error) => { - throw new Meteor.Error('500', `${error}`); - }); - // sendMail(emailOpts, true); - Notifications.update(notif._id, { $set: { emailNotified: true, emailNotifiedAt: new Date() } }); - } - } - } - - Notifications.find().observe({ - added: function notifAdded(notif) { - process(notif); - }, - changed: function notifChanged(updatedNotif, oldNotif) { - process(updatedNotif); - } - }); });