diff options
Diffstat (limited to 'vnext/server/sender.js')
-rw-r--r-- | vnext/server/sender.js | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/vnext/server/sender.js b/vnext/server/sender.js new file mode 100644 index 00000000..6d88c4c0 --- /dev/null +++ b/vnext/server/sender.js @@ -0,0 +1,136 @@ +import PushNotifications from 'node-pushnotifications'; +var debug = require('debug')('sender'); +import { deleteSubscribers } from './http'; +import { formatMessage, formatTitle, formatQuote } from './common/MessageUtils'; + +const apnConfig = (production = true) => { + const apn = { + token: { + key: process.env.JUICK_APN_KEY, + keyId: process.env.JUICK_APN_KEY_ID, + teamId: process.env.JUICK_APN_TEAM_ID + }, + production: production + }; + return apn; +}; +const gcmConfig = { + id: process.env.JUICK_GCM_ID +}; + +const push = new PushNotifications({ + apn: apnConfig(true), + gcm: gcmConfig, +}); +const sandbox = new PushNotifications({ + apn: apnConfig(false), + gcm: gcmConfig +}); + +/** @type {string} */ +const application = process.env.JUICK_APN_APPLICATION || ''; + + +/** + * send notification + * + * @param {PushNotifications.RegistrationId[]} productionIds + * @param {PushNotifications.RegistrationId[]} sandboxIds + * @param {PushNotifications.Data} data + */ +export function sendNotification(productionIds, sandboxIds, data) { + [productionIds, sandboxIds].map((registrationIds, index) => { + let sender = index == 0 ? push : sandbox; + if (registrationIds && registrationIds.length) { + sender.send(registrationIds, data) + .then((results) => { + results.forEach(result => { + debug(`${result.method}: ${result.success} success, ${result.failure} failure`); + if (result.failure) { + console.error(`${result.method} failure: ${JSON.stringify(result)}`); + console.error(`Failed data: ${JSON.stringify(data)}`); + } + }); + results.filter(r => r.method === 'apn') + .forEach(async r => { + debug(`Response message: ${JSON.stringify(r.message)}`); + let badTokens = r.message.filter(m => m.errorMsg === 'BadDeviceToken').map(m => { + return { 'type': 'apns', 'token': m.regId }; + }); + if (badTokens.length > 0) { + await deleteSubscribers(badTokens); + debug(`${badTokens.length} APNS tokens deleted`); + } + }); + results.filter(r => r.method === 'gcm') + .forEach(async r => { + let badTokens = r.message.filter(m => m.errorMsg === 'NotRegistered' || m.errorMsg === 'MismatchSenderId').map(m => { + return { 'type': 'gcm', 'token': m.regId }; + }); + if (badTokens.length > 0) { + await deleteSubscribers(badTokens); + debug(`${badTokens.length} GCM tokens deleted`); + } + }); + results.filter(r => r.method === 'mpns') + .forEach(async r => { + let badTokens = r.message.filter(m => m.errorMsg === 'The channel expired.').map(m => { + return { 'type': 'mpns', 'token': m.regId }; + }); + if (badTokens.length > 0) { + await deleteSubscribers(badTokens); + debug(`${badTokens.length} MPNS tokens deleted`); + } + }); + }) + .catch((err) => { console.error(JSON.stringify(err)); }); + } + }); +} + +/** + * builds notification object + * + * @param {import('../client').SecureUser} user user + * @param {import('../client').Message} msg message + * @returns {PushNotifications.Data} notification template + */ +export function buildNotification(user, msg) { + let template = { + topic: application, + custom: { + message: msg + }, + timeToLive: 0 + }; + let { tokens, ...subscriber } = user; + if (msg.service) { + template.contentAvailable = true; + template.custom.service = true; + template.custom.user = subscriber; + } else { + const avatar = `https://i.juick.com/a/${msg.user.uid}.png`; + const title = formatTitle(msg); + const body = `${formatQuote(msg)}\n${formatMessage(msg)}`; + template.custom.mid = msg.mid; + template.custom.rid = msg.rid; + template.custom.uname = msg.user.uname; + template.custom.avatarUrl = avatar; + template.image1src = avatar; + template.text1 = title; + template.text2 = body; + template.title = title; + template.body = body; + template.badge = user.unreadCount || 0; + template.mutableContent = 1; + template.color = '#3c77aa'; + template.icon = 'ic_notification'; + template.clickAction = 'com.juick.NEW_EVENT_ACTION'; + const tag = msg.mid == 0 ? msg.user.uname : msg.mid; + template.tag = `${tag}`; + template.android_channel_id = 'default'; + } + // FIXME: wrong type definition in node-pushnotifications: title and body and not required for silent pushes + // @ts-ignore + return template; +} |