如何通过 Node.js 上的推送通知实现一些复杂的行为

Posted

技术标签:

【中文标题】如何通过 Node.js 上的推送通知实现一些复杂的行为【英文标题】:How to implement some sophisticated behavior, with push-notifications on Node.js 【发布时间】:2018-06-03 09:05:39 【问题描述】:

我正在试验从 Node.js 应用程序发送的推送通知。 通过一些教程和示例,我现在有了一个可以开始工作的迷你应用程序。

它的作用非常基本,当它被加载到浏览器中时,会触发一个通知,并且用户会看到一条消息弹出。

它基本上由四个文件组成: index.js、index.html、worker.js 和 client.js。

作为第一个实验,我想实现一些稍微复杂的行为。

应用应在启动时触发 A 类通知(正如它已经在做的那样),然后每 121 分钟触发一次 B 类通知。

这种事情是可能的还是不可能的?

如果可以,我该怎么做?

作为参考,我在这里放了两个相关文件:

index.js

const express = require('express'),
      webPush = require('web-push'),
      bodyParser = require('body-parser'),
      path = require('path');
const app = express();

app.use(express.static(path.join(__dirname, 'client')));
app.use(bodyParser.json());

const privateVapIdKey = process.env.privVapIdKey,
      publicVapIdKey = process.env.pubVapIdKey;

webPush.setVapidDetails(
    'mailto:myemail@example.com',
    publicVapIdKey,privateVapIdKey);

// Subscribe Route.
app.post('/subscribe',(req,res) => 
    const subscription = req.body; // Get Push Subscription Object.
    res.status(201).json(); // Send 201. Resource created.

    // Do a lot of useful things ......
    .......
    // Create the PayLoad.
    const payload = JSON.stringify(
        title:'A big title!',
        ........
    );
    // Pass Object to sendNotification.
    webPush.sendNotification(subscription,payload).catch(err => console.error(err));
);

const port = 5003;

const PORT = process.env.PORT || port;
app.listen(PORT, () => console.log(`Listening on $ PORT `));

client.js

const publicVapIdKey = 'my-secret-3453754...pubVapIdKey';

// Chec for ServiceWorker.
if ('serviceWorker' in navigator) 
    send().catch(err => console.error(err));



// Register ServiceWorker, Register Push, Send Push.
async function send() 
    console.log("Registering ServiceWorker.");
    const register = await navigator.serviceWorker.register('/worker.js', 
        scope: "/"
    );
    console.log('ServiceWorker registered.');

    console.log("Registering Push.");
    //register.pushManager.uns
    const subscription = await register.pushManager.subscribe(
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(publicVapIdKey)
    );
    console.log('Push registered.');

    console.log("Sending Push.");
    await fetch('/subscribe', 
        method: 'POST',
        body: JSON.stringify(subscription),
        headers: 
            'content-type': 'application/json'
        
    );
    console.log('Push sent.');



function urlBase64ToUint8Array(base64String) 
    const padding = '='.repeat((4 - base64String.length % 4) % 4);
    const base64 = (base64String + padding)
      .replace(/\-/g, '+')
      .replace(/_/g, '/');

    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);

    for (let i = 0; i < rawData.length; ++i) 
      outputArray[i] = rawData.charCodeAt(i);
    

    return outputArray;

【问题讨论】:

【参考方案1】:

使用 setTimeout 和 setInterval 可以实现简单的事件计时:https://nodejs.org/api/timers.html 它们的工作方式与浏览器端 javascript 相同。

如需更高级的内容,请尝试node-cron

【讨论】:

我试图用nodejs.org/api/timers.html 做点什么。问题是我无法使用 setImmediate 或 setTimeout 循环来重复操作。我一定是错过了什么。 您正在寻找setInterval。或者,您可以通过在回调中设置新的超时来递归调用setTimeout 是的,我确实使用setInterval 做了一些工作,由于某种原因,我之前尝试过的对setTimeout 的递归调用不起作用。【参考方案2】:

为了能够向端点发送通知,您需要订阅它,因此我们必须存储客户端订阅:

const subscriptions = [];

app.post('/subscribe',(req,res) => 
  const subscription = req.body;
  // FIXME: Validate subscription!!!
  subscriptions.push(subscription);
  res.status(201).json();
  webPush.sendNotification(subscription,  title: "A" );
);

现在每 121 分钟,我们会检查所有订阅并传递我们的消息 B:

const MINS = 60 * 1000;

setInterval(() => 
  for(const subscription of subscriptions) 
     webPush.sendNotification(subscription, title: "B" );
  
, 121 * MINS);

PS:您可能还应该添加一个“取消订阅”端点,否则您可能会在某些时候将通知发送到死端点

【讨论】:

取得一些进展后,我也觉得我需要一些“取消订阅”端点。我该如何设置这样的东西?【参考方案3】:

有可能!但我建议您将Admin FCM 用于服务器端,它比 web-push 更新了最新的库,并且更容易推送通知。 p>

//node.js serverside code 
const FCM = require("firebase-admin");
//fcm-push-notification.json is where all the configurations are
const serviceAccount = require("fcm-push-notification.json");
FCM.initializeApp(
    credential: SERVICE_ACCOUNT,
    databaseURL: DBURL
);
// In your Subscribe Route.
app.post('/subscribe',(req,res) => 
    FCM.messaging()
    .sendToDevice(
    DEVICE_TOKEN,
    
        data: DATA,
        notification: 
            title: "A big title!",
            body: "HELLO PUSH!"
        
    
    )
    .then(res => 
        // do something here
    )
);

这里是服务人员

// put firebase-messaging-sw.js service worker
// this is to get notified in the background when the tab is closed on not active 
(global => 
  importScripts("https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js");
  importScripts(
    "https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js"
  );
  firebase.initializeApp(
    messagingSenderId: SENDER_ID
  );
  const messaging = firebase.messaging();
  console.log("Service Worker started!");
  messaging.setBackgroundMessageHandler(payload => 
    console.log("Message received In background");
    // Customize notification here
    const notificationOptions = 
      body: "Background Message body.",
      icon: "/firebase-logo.png"
    ;
    return global.registration.showNotification("TITLE", notificationOptions);
  );
)(self);

在你的 javascript 中

//to get notified in forground just do this
import firebase from "firebase";

firebase.initializeApp(FCM_CONF);
let messaging = firebase.messaging();
messaging.usePublicVapidKey(VAPID_KEY);

messaging.onMessage(payload => 
    console.log("Message received from foreground ");
);

最后创建一个 manifest.json

//create manifest.json with content like this

  "gcm_sender_id": SENDER_ID

每 121 分钟触发一次 B 类通知。使用类似later.js

var later = require('later');
 var schedule = later.parse.text('every 121 min');

 var timer = later.setTimeout(() => 
  // fired every 121 minutes
    FCM.messaging()
    .sendToDevice(
    DEVICE_TOKEN,
    
        data: DATA,
        notification: 
            title: "A big title!",
            body: "HELLO PUSH!"
        
    
    )
 , schedule);

【讨论】:

以上是关于如何通过 Node.js 上的推送通知实现一些复杂的行为的主要内容,如果未能解决你的问题,请参考以下文章

通过 Azure 从 node.js 发送推送通知

Node.js GCM 推送通知允许的 ID 数量?

如何停止 Node.JS 通知流?

如何发送推送通知 node.js everlive

如何将 Node.js 服务器连接到 Apache 服务器?

Node.js 不推送所有通知