• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

jazzband/django-push-notifications: Send push notifications to mobile devices th ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称(OpenSource Name):

jazzband/django-push-notifications

开源软件地址(OpenSource Url):

https://github.com/jazzband/django-push-notifications

开源编程语言(OpenSource Language):

Python 100.0%

开源软件介绍(OpenSource Introduction):

django-push-notifications

Jazzband GitHub Actions Code coverage

A minimal Django app that implements Device models that can send messages through APNS, FCM/GCM, WNS and WebPush.

The app implements four models: GCMDevice, APNSDevice, WNSDevice and WebPushDevice. 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, WNS or WebPush 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.

Dependencies

  • Python 3.6+
  • Django 2.2+
  • For the API module, Django REST Framework 3.7+ is required.
  • For WebPush (WP), pywebpush 1.3.0+ is required (optional). py-vapid 1.3.0+ is required for generating the WebPush private key; however this step does not need to occur on the application server.
  • For Apple Push (APNS), apns2 0.3+ is required (optional).

Setup

You can install the library directly from pypi using pip:

$ pip install django-push-notifications[WP,APNS]

Edit your settings.py file:

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: [email protected]"}
}

Note

If you need to support multiple mobile applications from a single Django application, see Multiple Application Support for details.

Note

If you are planning on running your project with APNS_USE_SANDBOX=True, then make sure you have set the development certificate as your APNS_CERTIFICATE. Otherwise the app will not be able to connect to the correct host. See settings for details.

For more information about how to generate certificates, see docs/APNS.

You can learn more about APNS certificates here.

Native Django migrations are in use. manage.py migrate will install and migrate all models.

Settings list

All settings are contained in a PUSH_NOTIFICATIONS_SETTINGS dict.

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.

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.
  • UNIQUE_REG_ID: Forces the registration_id field on all device models to be unique.

APNS settings

  • APNS_CERTIFICATE: Absolute path to your APNS certificate file. Certificates with passphrases are not supported. If iOS application was build with "Release" flag, you need to use production certificate, otherwise debug. Read more about Generation of an APNS PEM file.
  • APNS_AUTH_KEY_PATH: Absolute path to your APNS signing key file for Token-Based Authentication . Use this instead of APNS_CERTIFICATE if you are using .p8 signing key certificate.
  • APNS_AUTH_KEY_ID: The 10-character Key ID you obtained from your Apple developer account
  • APNS_TEAM_ID: 10-character Team ID you use for developing your company’s apps for iOS.
  • 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'. Default value depends on DEBUG setting of your environment: if DEBUG is True and you use production certificate, you should explicitly set APNS_USE_SANDBOX to False.

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 GCM

WNS settings

  • WNS_PACKAGE_SECURITY_KEY: TODO
  • WNS_SECRET_KEY: TODO

WP settings

  • Install:
pip install pywebpush
pip install py-vapid  (Only for generating key)
  • Getting keys:

    • Create file (claim.json) like this:
{
        "sub": "mailto: [email protected]",
        "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
  • Configure settings:
  • 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: [email protected]"}
  • 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)
  • Configure client (javascript):
// 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 () {
        if ("userAgentData" in navigator) {
                // navigator.userAgentData is not available in
                // Firefox and Safari
                const uaData = navigator.userAgentData;
                // Outputs of navigator.userAgentData.brands[n].brand are e.g.
                // Chrome: 'Google Chrome'
                // Edge: 'Microsoft Edge'
                // Opera: 'Opera'
                let browsername;
                let browserversion;
                let chromeVersion = null;
                for (var i = 0; i < uaData.brands.length; i++) {
                        let brand = uaData.brands[i].brand;
                        browserversion = uaData.brands[i].version;
                        if (brand.match(/opera|chrome|edge|safari|firefox|msie|trident/i) !== null) {
                                // If we have a chrome match, save the match, but try to find another match
                                // E.g. Edge can also produce a false Chrome match.
                                if (brand.match(/chrome/i) !== null) {
                                        chromeVersion = browserversion;
                                }
                                // If this is not a chrome match return immediately
                                else {
                                        browsername = brand.substr(brand.indexOf(' ')+1);
                                        return {
                                                name: browsername,
                                                version: browserversion
                                        }
                                }
                        }
                }
                // No non-Chrome match was found. If we have a chrome match, return it.
                if (chromeVersion !== null) {
                        return {
                                name: "chrome",
                                version: chromeVersion
                        }
                }
        }
        // If no userAgentData is not present, or if no match via userAgentData was found,
        // try to extract the browser name and version from userAgent
        const userAgent = navigator.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': btoa(String.fromCharCode.apply(null, new Uint8Array(sub.getKey('p256dh')))),
                                'auth': btoa(String.fromCharCode.apply(null, new Uint8Array(sub.getKey('auth')))),
                                'name': 'XXXXX',
                                'registration_id': registration_id
                        };
                        requestPOSTToServer(data);
                })
        }).catch(function (err) {
                console.log(':^(', err);
        });




// Example navigatorPush.service.js file

var getTitle = function (title) {
        if (title === "") {
                title = "TITLE DEFAULT";
        }
        return title;
};
var getNotificationOptions = function (message, message_tag) {
        var options = {
                body: message,
                icon: '/img/icon_120.png',
                tag: message_tag,
                vibrate: [200, 100, 200, 100, 200, 100, 200]
        };
        return options;
};

self.addEventListener('install', function (event) {
        self.skipWaiting();
});

self.addEventListener('push', function(event) {
        try {
                // Push is a JSON
                var response_json = event.data.json();
                var title = response_json.title;
                var message = response_json.message;
                var message_tag = response_json.tag;
        } catch (err) {
                // Push is a simple text
                var title = "";
                var message = event.data.text();
                var message_tag = "";
        }
        self.registration.showNotification(getTitle(title), getNotificationOptions(message, message_tag));
        // Optional: Comunicating with our js application. Send a signal
        self.clients.matchAll({includeUncontrolled: true, type: 'window'}).then(function (clients) {
                clients.forEach(function (client) {
                        client.postMessage({
                                "data": message_tag,
                                "data_title": title,
                                "data_body": message});
                        });
        });
});

// Optional: Added to that the browser opens when you click on the notification push web.
self.addEventListener('notificationclick', function(event) {
        // Android doesn't close the notification when you click it
        // See http://crbug.com/463146
        event.notification.close();
        // Check if there's already a tab open with this URL.
        // If yes: focus on the tab.
        // If no: open a tab with the URL.
        event.waitUntil(clients.matchAll({type: 'window', includeUncontrolled: true}).then(function(windowClients) {
                        for (var i = 0; i < windowClients.length; i++) {
                                var client = windowClients[i];
                                if ('focus' in client) {
                                        return client.focus();
                                }
                        }
                })
        );
});

Sending messages

FCM/GCM and APNS services have slightly different semantics. The app tries to offer a common interface for both when using the models.

from push_notifications.models import APNSDevice, GCMDevice

device = GCMDevice.objects.get(registration_id=gcm_reg_id)
# The first argument will be sent as "message" to the intent extras Bundle
# Retrieve it with intent.getExtras().getString("message")
device.send_message("You've got mail")
# If you want to customize, send an extra dict and a None message.
# the extras dict will be mapped into the intent extras Bundle.
# For dicts where all values are keys this will be sent as ur 

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap