A minimal Django app that implements Device models that can send messages through APNS, FCM/GCM and WNS.javascript
GCMDevice
,
APNSDevice
and
WNSDevice
. Those models share the same attributes:
name
(optional): A name for the device.active
(default True): A boolean that determines whether the device will be sent notifications.user
(optional): A foreign key to auth.User, if you wish to link the device to a specific user.device_id
(optional): A UUID for the device obtained from Android/iOS/Windows APIs, if you wish to uniquely identify it.registration_id
(required): The FCM/GCM registration id or the APNS token for the device.The app also implements an admin panel, through which you can test single and bulk notifications. Select one or more FCM/GCM, APNS or WNS devices and in the action dropdown, select "Send test message" or "Send test message in bulk", accordingly. Note that sending a non-bulk test message to more than one device will just iterate over the devices and send multiple single messages. UPDATE_ON_DUPLICATE_REG_ID: Transform create of an existing Device (based on registration id) into a update. See below Update of device with duplicate registration ID for more details.html
You can install the library directly from pypi using pip:java
$ pip install django-push-notifications
Edit your settings.py file:python
INSTALLED_APPS = ( ... "push_notifications" ) PUSH_NOTIFICATIONS_SETTINGS = { "FCM_API_KEY": "[your api key]", "GCM_API_KEY": "[your api key]", "APNS_CERTIFICATE": "/path/to/your/certificate.pem", "APNS_TOPIC": "com.example.push_test", "WNS_PACKAGE_SECURITY_ID": "[your package security id, e.g: 'ms-app://e-3-4-6234...']", "WNS_SECRET_KEY": "[your app secret key, e.g.: 'KDiejnLKDUWodsjmewuSZkk']", "WP_PRIVATE_KEY": "/path/to/your/private.pem", "WP_CLAIMS": {'sub': "mailto: development@example.com"} }
Noteandroid
If you are planning on running your project with APNS_USE_SANDBOX=True
, then make sure you have set the developmentcertificate as your APNS_CERTIFICATE
. Otherwise the app will not be able to connect to the correct host. See settings for details.ios
You can learn more about APNS certificates here.git
Native Django migrations are in use. manage.py migrate
will install and migrate all models.github
All settings are contained in a PUSH_NOTIFICATIONS_SETTINGS
dict.web
In order to use FCM/GCM, you are required to include FCM_API_KEY
or GCM_API_KEY
. For APNS, you are required to include APNS_CERTIFICATE
. For WNS, you need both the WNS_PACKAGE_SECURITY_KEY
and the WNS_SECRET_KEY
.chrome
General settings
USER_MODEL
: Your user model of choice. Eg. myapp.User
. Defaults to settings.AUTH_USER_MODEL
.UPDATE_ON_DUPLICATE_REG_ID
: Transform create of an existing Device (based on registration id) into a update. See below Update of device with duplicate registration ID for more details.APNS settings
APNS_CERTIFICATE
: Absolute path to your APNS certificate file. Certificates with passphrases are not supported.APNS_TOPIC
: The topic of the remote notification, which is typically the bundle ID for your app. If you omit this header and your APNs certificate does not specify multiple topics, the APNs server uses the certificate’s Subject as the default topic.APNS_USE_ALTERNATIVE_PORT
: Use port 2197 for APNS, instead of default port 443.APNS_USE_SANDBOX
: Use 'api.development.push.apple.com', instead of default host 'api.push.apple.com'.FCM/GCM settings
FCM_API_KEY
: Your API key for Firebase Cloud Messaging.FCM_POST_URL
: The full url that FCM notifications will be POSTed to. Defaults to https://fcm.googleapis.com/fcm/send.FCM_MAX_RECIPIENTS
: The maximum amount of recipients that can be contained per bulk message. If the registration_ids
list is larger than that number, multiple bulk messages will be sent. Defaults to 1000 (the maximum amount supported by FCM).FCM_ERROR_TIMEOUT
: The timeout on FCM POSTs.GCM_API_KEY
, GCM_POST_URL
, GCM_MAX_RECIPIENTS
, GCM_ERROR_TIMEOUT
: Same parameters for GCMWNS settings
WNS_PACKAGE_SECURITY_KEY
: TODOWNS_SECRET_KEY
: TODOWP settings
pip install pywebpush
pip install py-vapid (Only for generating key)
Getting keys:
- Create file (claim.json) like this:
{
"sub": "mailto: development@example.com", "aud": "https://android.googleapis.com" } - Generate public and private keys:
vapid --sign claim.json
No private_key.pem file found.
Do you want me to create one for you? (Y/n)Y Do you want me to create one for you? (Y/n)Y Generating private_key.pem Generating public_key.pem Include the following headers in your request: Crypto-Key: p256ecdsa=BEFuGfKKEFp-kEBMxAIw7ng8HeH_QwnH5_h55ijKD4FRvgdJU1GVlDo8K5U5ak4cMZdQTUJlkA34llWF0xHya70 Authorization: WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2FuZHJvaWQuZ29vZ2xlYXBpcy5jb20iLCJleHAiOiIxNTA4NDkwODM2Iiwic3ViIjoibWFpbHRvOiBkZXZlbG9wbWVudEBleGFtcGxlLmNvbSJ9.r5CYMs86X3JZ4AEs76pXY5PxsnEhIFJ-0ckbibmFHZuyzfIpf1ZGIJbSI7knA4ufu7Hm8RFfEg5wWN1Yf-dR2A - Generate client public key (applicationServerKey)
vapid --applicationServerKey
Application Server Key = BEFuGfKKEFp-kEBMxAIw7ng8HeH_QwnH5_h55ijKD4FRvgdJU1GVlDo8K5U5ak4cMZdQTUJlkA34llWF0xHya70
WP_PRIVATE_KEY
: Absolute path to your private certificate file: os.path.join(BASE_DIR, "private_key.pem")WP_CLAIMS
: Dictionary with the same sub info like claims file: {'sub': "mailto: development@example.com"}WP_ERROR_TIMEOUT
: The timeout on WebPush POSTs. (Optional)WP_POST_URL
: A dictionary (key per browser supported) with the full url that webpush notifications will be POSTed to. (Optional)// Utils functions: function urlBase64ToUint8Array (base64String) { var padding = '='.repeat((4 - base64String.length % 4) % 4) var base64 = (base64String + padding) .replace(/\-/g, '+') .replace(/_/g, '/') var rawData = window.atob(base64) var outputArray = new Uint8Array(rawData.length) for (var i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i) } return outputArray; } function loadVersionBrowser (userAgent) { var ua = userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; if (/trident/i.test(M[1])) { tem = /\brv[ :]+(\d+)/g.exec(ua) || []; return {name: 'IE', version: (tem[1] || '')}; } if (M[1] === 'Chrome') { tem = ua.match(/\bOPR\/(\d+)/); if (tem != null) { return {name: 'Opera', version: tem[1]}; } } M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?']; if ((tem = ua.match(/version\/(\d+)/i)) != null) { M.splice(1, 1, tem[1]); } return { name: M[0], version: M[1] }; }; var applicationServerKey = "BEFuGfKKEFp-kEBMxAIw7ng8HeH_QwnH5_h55ijKD4FRvgdJU1GVlDo8K5U5ak4cMZdQTUJlkA34llWF0xHya70"; .... // In your ready listener if ('serviceWorker' in navigator) { // The service worker has to store in the root of the app // http://stackoverflow.com/questions/29874068/navigator-serviceworker-is-never-ready var browser = loadVersionBrowser(); navigator.serviceWorker.register('navigatorPush.service.js?version=1.0.0').then(function (reg) { reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(applicationServerKey) }).then(function (sub) { var endpointParts = sub.endpoint.split('/'); var registration_id = endpointParts[endpointParts.length - 1]; var data = { 'browser': browser.name.toUpperCase(), 'p256dh':