aboutsummaryrefslogtreecommitdiff
path: root/vnext/server/sender.js
diff options
context:
space:
mode:
Diffstat (limited to 'vnext/server/sender.js')
-rw-r--r--vnext/server/sender.js136
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;
+}