Configurer les notifications Web Push à l'aide de pywebpush étape par étape

Pourquoi un autre guide?


Lorsque j'ai été chargé de rédiger un projet de notifications push, une recherche rapide a montré qu'il y avait déjà beaucoup d'articles sur la configuration des notifications push sur le hub. Voici les plus adaptés, à mon avis:

Fonctionnement de JS: notifications push Web
Notifications Web PUSH rapides et faciles 924 /
Travailleurs des services. Web push et où ils vivent

C'est très bien, mais personnellement, il me manquait vraiment un guide simple et compréhensible qui me permettrait immédiatement, pratiquement par copier-coller, de faire fonctionner tout de suite. Eh bien, en plus, parmi les manuels, il n'y a pas de python adapté pour le support.

La configuration des notifications a finalement pris trois jours et il me semble que c'est un peu trop. J'espère que mon article aidera quelqu'un à configurer des notifications push en trois heures au lieu de trois jours.
Le projet sur lequel je travaille est implémenté sur Django et je décrirai l'avancement des travaux par rapport à ce framework, mais ceux qui le souhaitent pourront facilement l'adapter à Flask ou autre.

Alors allons-y.

Nous obtenons les clés


Sans clés, naturellement, ils ne nous laisseront nulle part, alors commençons par eux. Pour générer les clés, j'ai utilisé py_vapid . Il est installé automatiquement avec pywebpush , dont nous aurons encore besoin plus tard, donc pour éviter de vous lever deux fois, nous allons commencer par pywebpush:

> bin/pip install pywebpush <   > > bin/vapid --applicationServerKey No private_key.pem file found. Do you want me to create one for you? (Y/n)Y Generating private_key.pem Generating public_key.pem Application Server Key = < Server Key> 

La valeur de clé de serveur d'applications résultante est ajoutée au fichier settings.py.

 NOTIFICATION_KEY = < Server Key> 

De plus, nous devrons passer NOTIFICATION_KEY au contexte. La façon la plus simple de le faire est d'écrire votre processeur de contexte .

Faire un travailleur de service


Employé de service, qui ne sait pas - il s'agit d'un fichier spécial qui s'exécute en arrière-plan. Nous en avons besoin pour afficher nos notifications.

Le code de travailleur de service le plus simple ressemblerait à ceci:

 self.addEventListener('push', function(event) { var message = JSON.parse(event.data.text()); // event.waitUntil( self.registration.showNotification(message.title, { body: message.body, }) ); }); 

Et maintenant, nous devons enregistrer notre travailleur de service. Le processus est, en principe, décrit ici . Par conséquent, brièvement:

 function checkWorkerAndPushManager () { if (!('serviceWorker' in navigator)) { console.log('Workers are not supported.'); return; } if (!('PushManager' in window)) { console.log('Push notifications are not supported.'); return; } } function registerWorker () { window.addEventListener('load', function () { navigator.serviceWorker.register('/static/js/sw.js').then(function (registration) { console.log('ServiceWorker registration successful'); }, function (err) { console.log('ServiceWorker registration failed: ', err); return; }); }); return true; } var supported = checkWorkerAndPushManager(); if (supported){ var worker = registerWorker (); } 

Très bien, vous pouvez vérifier le travail de notre technicien. Pour ce faire, accédez aux outils de développement dans un navigateur, assurez-vous qu'un message s'affiche sur la console à propos de l'enregistrement réussi du woker et accédez à l'onglet Application et sélectionnez Service Worker sur la gauche.

image

Si la notification n'apparaît pas, vérifiez que les notifications sont activées dans votre navigateur.

Eh bien, nous savons déjà comment nous envoyer des notifications. Cool, passons.

Obtenez la permission de l'utilisateur pour afficher les notifications


Une fois le woker enregistré, demandez à l'utilisateur la permission d'afficher les notifications.

 function requestPermission() { return new Promise(function(resolve, reject) { const permissionResult = Notification.requestPermission(function(result) { //       . resolve(result); }); if (permissionResult) { permissionResult.then(resolve, reject); } }) .then(function(permissionResult) { if (permissionResult !== 'granted') { console.log(permissionResult); throw new Error('Permission not granted.'); } }); return true; } 

Aucun commentaire n'est nécessaire pour ce code, il fonctionne juste.

Abonnez-vous et enregistrez votre abonnement


L'abonnement est également à l'avant. Vous pouvez trouver le code d'abonnement ici , mais il n'y a pas de fonction urlBase64ToUint8Array utilisée, donc je code avec.

 NOTIFICATION_KEY = '{{ NOTIFICATION_KEY }}; function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - base64String.length % 4) % 4); const base64 = (base64String + padding) .replace(/\-/g, '+') .replace(/_/g, '/') ; const rawData = window.atob(base64); return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0))); } function subscribeUserToPush(key) { return navigator.serviceWorker.register('/static/path/to/js/sw.js') .then(function(registration) { var subscribeOptions = { userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(key), }; return registration.pushManager.subscribe(subscribeOptions) }) .then(function(pushSubscription) { sendSubscriptionToBackEnd(pushSubscription); }); } 

(Le tableau urlBase64ToUint8Array utilisé ici est probablement de la catégorie des béquilles et des vélos, mais une tentative de transcodage des données à l'aide de btoa n'a pas réussi, je ne sais pas pourquoi. Peut-être que quelqu'un vous le dira).

Ensuite, nous envoyons les données reçues au serveur. Je l'ai implémenté comme ceci:

 function sendSubscriptionToBackEnd(subscription) { $.post( SAVE_REGISTRATION_URL, { 'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val(), //,     {% csrf_token %}. 'registration_data': JSON.stringify(subscription) } ); } 

Eh bien, nous enregistrons cela sur le serveur. Vous pouvez juste à côté de la ligne. Oui, n'essayez pas d'établir une relation d'abonnement utilisateur un à un. Cela semble évident, mais tout d'un coup, tout le monde voudrait.
J'ai utilisé un modèle si simple pour la sauvegarde, il sera utilisé plus tard, je lui donnerai donc:

 class UserSubscription(models.Model): subscription = models.CharField(max_length=500) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='subscriptions') 

La dernière étape. Envoi d'un message à l'aide de pywebpush


Tout est dans le tutoriel, pas de subtilités particulières. Eh bien, sauf que le message est fait dans une telle structure que notre technicien peut l'analyser.

 import json from pywebpush import webpush, WebPushException from django.conf import settings from .models import UserSubscription def push_notification(user_id): user_subscriptions = UserSubscription.objects.filter(user_id=notification.user_id) for subscription in user_subscriptions: data = json.dumps({ 'title': 'Hello', 'body': 'there', }) try: webpush( subscription_info=json.loads(subscription.subscription), data=data, vapid_private_key='./private_key.pem', vapid_claims={ 'sub': 'mailto:{}'.format(settings.ADMIN_EMAIL), } ) notification.sent = True notification.save() except WebPushException as ex: print('I\'m sorry, Dave, but I can\'t do that: {}'.format(repr(ex))) print(ex) # Mozilla returns additional information in the body of the response. if ex.response and ex.response.json(): extra = ex.response.json() print('Remote service replied with a {}:{}, {}', extra.code, extra.errno, extra.message ) 

En fait, vous pouvez déjà appeler push_notification à partir du shell django et essayer de démarrer.
Dans ce code, il serait bien d'intercepter la réponse avec le statut 410. Une telle réponse dit que l'abonnement a été annulé, et il serait bien de supprimer ces abonnements de la base de données.

En conclusion


En fait, il existe une autre excellente bibliothèque django-webpush . Peut-être que ceux qui travaillent avec Django devraient commencer par cela.

PS Joyeux programmeur!

Source: https://habr.com/ru/post/fr423193/


All Articles