Node Push Notifications
A node.js module for interfacing with Apple Push Notification, Google Cloud Messaging, Windows Push Notification and Amazon Device Messaging services.
Changes from source
- Shuts down APN client when the send is complete, fixing a memory leak
- Allow sending APN pushes with just a badge number, that don't also trigger a sound / vibrate the device
NOTE: Version 1.x has completely been redesigned to be compatible with new apn 2.x.
Installation
npm install node-pushnotifications --save
Features
- Powerful and intuitive.
- Multi platform push notifications.
- Automatically detects destination device type.
- Unified error handling.
- Written in ES6, compatible with ES5 through babel transpilation.
Usage
1. Import and setup push module:
Include the settings for each device type. You should only include the settings for the devices that you expect to have. I.e. if your app is only available for android or for ios, you should only include gcm
or apn
respectively.
const settings = {
gcm: {
id: null,
...
},
apn: {
token: {
key: './certs/key.p8',
keyId: 'ABCD',
teamId: 'EFGH',
},
...
},
adm: {
client_id: null,
client_secret: null,
...
},
wns: {
client_id: null,
client_secret: null,
notificationMethod: 'sendTileSquareBlock',
...
}
};
const PushNotifications = require('node-pushnotifications');
const push = new PushNotifications(settings);
iOS: It is recomended to use provider authentication tokens. You need the .p8 certificate that you can obtain in your account memebership. You should ask for an Apple Push Notification Authentication Key (Sandbox & Production) or Apple Push Notification service SSL (Sandbox & Production). However, you can also use certificates. See node-apn to see how to prepare cert.pem and key.pem.
### 2. Define destination device ID. You can send to multiple devices, independently of platform, creating an array with different destination device IDs.
const registrationIds = 'INSERT_YOUR_DEVICE_ID';
const registrationIds = [];
registrationIds.push('INSERT_YOUR_DEVICE_ID');
registrationIds.push('INSERT_OTHER_DEVICE_ID');
Android: If you provide more than 1.000 registration tokens, they will automatically be splitted in 1.000 chunks (see this issue in gcm repo)
3. Create a JSON object with a title and message and send the notification.
Both title
and body
fields are required (or alert
for ios). The other fields are optional (see below how the message is created for each device type):
const data = {
title: 'New push notification',
body: 'Powered by AppFeel',
custom: {
sender: 'AppFeel',
},
priority: 'high',
collapseKey: '',
contentAvailable: true,
delayWhileIdle: true,
restrictedPackageName: '',
dryRun: false,
icon: '',
tag: '',
color: '',
clickAction: '',
locKey: '',
bodyLocArgs: '',
titleLocKey: '',
titleLocArgs: '',
retries: 1,
encoding: '',
badge: 2,
sound: 'ping.aiff',
alert: {},
titleLocKey: '',
titleLocArgs: '',
launchImage: '',
action: '',
topic: '',
category: '',
contentAvailable: '',
mdm: '',
urlArgs: '',
truncateAtWordEnd: true,
mutableContent: 0,
expiry: Math.floor(Date.now() / 1000) + 28 * 86400,
timeToLive: 28 * 86400,
headers: [],
launch: '',
duration: '',
consolidationKey: 'my notification',
};
push.send(registrationIds, data, (err, result) => {
if (err) {
console.log(err);
} else {
console.log(result);
}
});
push.send(registrationIds, data)
.then((results) => { ... })
.catch((err) => { ... });
err
will be null if all went fine, will return the error otherwise.result
will contain an array with the following objects (one object for each device type found in device registration id's):
[
{
method: 'gcm',
multicastId: [],
success: 0,
failure: 0,
message: [{
messageId: '',
regId: value,
error: new Error('unknown'),
}],
},
{
method: 'apn',
...
},
{
method: 'wns',
...
},
{
method: 'adm',
...
},
]
GCM
NOTE: If you provide more than 1.000 registration tokens, they will automatically be splitted in 1.000 chunks (see this issue in gcm repo)
The following parameters are used to create a GCM message. See https://developers.google.com/cloud-messaging/http-server-ref#table5 for more info:
let custom;
if (typeof data.custom === 'string') {
custom = {
message: data.custom,
};
} else if (typeof data.custom === 'object') {
custom = Object.assign({}, data.custom);
} else {
custom = {
data: data.custom,
};
}
custom.title = custom.title || data.title || '';
custom.message = custom.message || data.body || '';
custom.sound = custom.sound || data.sound || undefined;
custom.icon = custom.icon || data.icon || undefined;
custom.msgcnt = custom.msgcnt || data.badge || undefined;
if (opts.phonegap === true && data.contentAvailable) {
custom['content-available'] = 1;
}
const message = new gcm.Message({
collapseKey: data.collapseKey,
priority: data.priority === 'normal' ? data.priority : 'high',
contentAvailable: data.contentAvailable || false,
delayWhileIdle: data.delayWhileIdle || false,
timeToLive: data.expiry - Math.floor(Date.now() / 1000) || data.timeToLive || 28 * 86400,
restrictedPackageName: data.restrictedPackageName,
dryRun: data.dryRun || false,
data: data.custom,
notification: {
title: data.title,
body: data.body,
icon: data.icon,
sound: data.sound,
badge: data.badge,
tag: data.tag,
color: data.color,
click_action: data.clickAction || data.category,
body_loc_key: data.locKey,
body_loc_args: data.locArgs,
title_loc_key: data.titleLocKey,
title_loc_args: data.titleLocArgs,
},
}
data is the parameter in push.send(registrationIds, data)
Note: parameters are duplicated in data and in notification, so in fact they are being send as:
data: {
title: 'title',
message: 'body',
sound: 'mySound.aiff',
icon: undefined,
msgcnt: undefined
sender: 'appfeel-test',
},
notification: {
title: 'title',
body: 'body',
icon: undefined,
sound: 'mySound.aiff',
badge: undefined,
tag: undefined,
color: undefined,
click_action: undefined,
body_loc_key: undefined,
body_loc_args: undefined,
title_loc_key: undefined,
title_loc_args: undefined
}
In that way, they can be accessed in android in the following two ways:
String title = extras.getString("title");
title = title != null ? title : extras.getString("gcm.notification.title");
PhoneGap compatibility mode
In case your app is written with Cordova / Ionic and you are using the PhoneGap PushPlugin,
you can use the phonegap
setting in order to adapt to the recommended behaviour described in
https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/PAYLOAD.md#android-behaviour.
const settings = {
gcm: {
id: '<yourId>',
phonegap: true
}
}
APN
The following parameters are used to create an APN message:
{
retryLimit: data.retries || -1,
expiry: data.expiry || ((data.timeToLive || 28 * 86400) + Math.floor(Date.now() / 1000)),
priority: data.priority === 'normal' ? 5 : 10,
encoding: data.encoding,
payload: data.custom || {},
badge: data.badge,
sound: data.sound || 'ping.aiff',
alert: data.alert || {
title: data.title,
body: data.body,
'title-loc-key': data.titleLocKey,
'title-loc-args': data.titleLocArgs,
'loc-key': data.locKey,
'loc-args': data.locArgs,
'launch-image': data.launchImage,
action: data.action,
},
topic: data.topic,
category: data.category || data.clickAction,
contentAvailable: data.contentAvailable,
mdm: data.mdm,
urlArgs: data.urlArgs,
truncateAtWordEnd: data.truncateAtWordEnd,
collapseId: data.collapseKey,
mutableContent: data.mutableContent || 0,
}
data is the parameter in push.send(registrationIds, data)
WNS
The following fields are used to create a WNS message:
const notificationMethod = settings.wns.notificationMethod;
const opts = Object.assign({}, settings.wns);
opts.headers = data.headers || opts.headers;
opts.launch = data.launch || opts.launch;
opts.duration = data.duration || opts.duration;
delete opts.notificationMethod;
delete data.headers;
delete data.launch;
delete data.duration;
wns[notificationMethod](regId, data, opts, (err, response) => { ... });
data is the parameter in push.send(registrationIds, data)
Note: Please keep in mind that if data.accessToken
is supplied, each push notification will be sent after the previous one has been responded. This is because Microsoft may send a new accessToken
in the response and it should be used in successive requests. This can slow down the whole process depending on the number of devices to send.
ADM
The following parameters are used to create an ADM message:
const data = Object.assign({}, _data);
const consolidationKey = data.consolidationKey;
const expiry = data.expiry;
const timeToLive = data.timeToLive;
delete data.consolidationKey;
delete data.expiry;
delete data.timeToLive;
const ADMmesssage = {
expiresAfter: expiry - Math.floor(Date.now() / 1000) || timeToLive || 28 * 86400,
consolidationKey,
data,
};
data is the parameter in push.send(registrationIds, data)
Resources
LICENSE
The MIT License (MIT)
Copyright (c) 2016 AppFeel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Made in Barcelona with <3 and Code