Configure las Notificaciones Web Push usando pywebpush paso a paso

¿Por qué otra guía?


Cuando me encargaron hacer un borrador de notificaciones push, una búsqueda rápida mostró que ya había muchos artículos sobre cómo configurar notificaciones push en el centro. Aquí están los más adecuados, en mi opinión:

Cómo funciona JS: notificaciones push web
Web PUSH Notificaciones Rápido y Fácil 924 /
Trabajadores de servicio. Empuje web y dónde viven

Todo esto está bien, pero personalmente carecía de una guía simple y comprensible que me permitiera inmediatamente, prácticamente mediante copiar y pegar, hacer que todo funcione de inmediato. Bueno, además, entre los manuales no hay python adaptado para respaldo.

La configuración de las notificaciones al final tomó tres días y me parece que esto es demasiado. Espero que mi artículo ayude a alguien a configurar notificaciones push en tres horas en lugar de tres días.
El proyecto en el que estoy trabajando se implementa en Django y describiré el progreso del trabajo en relación con este marco, pero aquellos que lo deseen pueden adaptarlo fácilmente a Flask u otra cosa.

Entonces vamos.

Conseguimos las llaves


Sin llaves, naturalmente, no nos dejarán en ningún lado, así que comencemos con ellas. Para generar las claves, usé py_vapid . Se instala automáticamente junto con pywebpush , que aún necesitaremos más adelante, por lo que para evitar levantarse dos veces, comenzaremos con 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> 

El valor resultante de la clave del servidor de aplicaciones se agrega al archivo settings.py.

 NOTIFICATION_KEY = < Server Key> 

Además, tendremos que pasar NOTIFICATION_KEY al contexto. La forma más fácil de hacer esto es escribir su context_processor .

Hacer un trabajador de servicio


Trabajador de servicio, que no sabe: este es un archivo especial que se ejecuta en segundo plano. Lo necesitamos para mostrar nuestras notificaciones.

El código de trabajador de servicio más simple se vería así:

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

Y ahora necesitamos registrar a nuestro trabajador de servicio. El proceso se describe, en principio, aquí . Por lo tanto, brevemente:

 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 (); } 

Genial, puede verificar el trabajo de nuestro trabajador de servicio. Para hacer esto, vaya a Herramientas para desarrolladores en un navegador, asegúrese de que aparezca un mensaje en la consola sobre el registro exitoso del woker, vaya a la pestaña Aplicación y seleccione Service Worker a la izquierda.

imagen

Si la notificación no aparece, verifique que las notificaciones estén habilitadas en su navegador.

Bueno, ya sabemos cómo enviarnos notificaciones. Genial, sigamos adelante.

Obtenga permiso del usuario para mostrar notificaciones


Después de que el woker esté registrado, solicite permiso al usuario para mostrar notificaciones.

 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; } 

No se necesitan comentarios para este código, simplemente funciona.

Suscríbase y guarde su suscripción


La suscripción también está al frente. Puede encontrar el código de suscripción aquí , pero no se utiliza la función urlBase64ToUint8Array, por lo que codifico con ella.

 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); }); } 

(El urlBase64ToUint8Array que se usa aquí probablemente pertenece a la categoría de muletas y bicicletas, pero el intento de transcodificar los datos usando btoa no tuvo éxito, no sé por qué. Quizás alguien se lo diga).

Luego enviamos los datos recibidos al servidor. Lo tengo implementado así:

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

Bueno, entonces guardamos esto en el servidor. Puedes enderezar una línea. Sí, no intente establecer una relación de suscripción de usuario uno a uno. Parece ser obvio, pero de repente a cualquiera le gustaría.
He usado un modelo tan simple para guardar, se usará más tarde, así que lo daré:

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

El ultimo paso. Enviar un mensaje usando pywebpush


Todo está en el tutorial, no hay sutilezas especiales. Bueno, excepto que el mensaje está hecho en una estructura tal que nuestro trabajador de servicio puede analizarlo.

 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 realidad, ya puedes llamar a push_notification desde el shell de django e intentar comenzar.
En este código, sería bueno interceptar la respuesta con el estado 410. Tal respuesta dice que la suscripción ha sido cancelada, y sería bueno eliminar tales suscripciones de la base de datos.

En conclusión


De hecho, hay otra gran biblioteca django-webpush . Quizás aquellos que trabajan con Django deberían comenzar con él.

PD ¡Feliz día del programador!

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


All Articles