nodebb-plugin-web-push 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/library.js CHANGED
@@ -5,6 +5,7 @@ const winston = require.main.require('winston');
5
5
  const webPush = require('web-push');
6
6
  const validator = require('validator');
7
7
 
8
+ const db = require.main.require('./src/database');
8
9
  const user = require.main.require('./src/user');
9
10
  const meta = require.main.require('./src/meta');
10
11
  const utils = require.main.require('./src/utils');
@@ -114,6 +115,11 @@ plugin.onNotificationPush = async ({ notification, uidsNotified: uids }) => {
114
115
  uids = uids.filter(uid => subs.get(uid).size);
115
116
  const userSettings = await user.getMultipleUserSettings(uids);
116
117
 
118
+ // Save recipients by nid (for use by .rescind)
119
+ const refKey = `web-push:nid:${notification.mergeId || notification.nid}:uids`;
120
+ await db.setAdd(refKey, uids);
121
+ db.pexpire(refKey, 1000 * 60 * 60 * 48); // only track last 48 hours
122
+
117
123
  let payloads = await Promise.all(uids.map(async (uid, idx) => {
118
124
  const payload = await constructPayload(notification, userSettings[idx].userLang);
119
125
  return [uid, payload];
@@ -127,12 +133,34 @@ plugin.onNotificationPush = async ({ notification, uidsNotified: uids }) => {
127
133
  await webPush.sendNotification(subscription, JSON.stringify(payload));
128
134
  } catch (e) {
129
135
  // Errored — remove subscription from user
130
- subscriptions.remove(uid, subscription);
136
+ winston.info(`[plugins/web-push] Push failed: ${e.code}; ${e.message}; statusCode: ${e.statusCode}`);
137
+ // subscriptions.remove(uid, subscription);
131
138
  }
132
139
  });
133
140
  });
134
141
  };
135
142
 
143
+ plugin.onNotificationRescind = async ({ nids }) => {
144
+ const notificationKeys = nids.map(nid => `notifications:${nid}`);
145
+ let mergeIds = await db.getObjectsFields(notificationKeys, ['mergeId']);
146
+ mergeIds = mergeIds.map(o => o.mergeId);
147
+
148
+ // Favour mergeIds over nids, then eliminate dupes
149
+ const tags = new Set(notificationKeys.map((key, i) => mergeIds[i] || key));
150
+ const recipients = await db.getSetsMembers(Array.from(tags).map(tag => `web-push:nid:${tag}:uids`));
151
+
152
+ Promise.all(Array.from(tags).map(async (tag, idx) => {
153
+ let subs = await subscriptions.list(recipients[idx]);
154
+ subs = new Set(...Object.values(Object.fromEntries(subs))); // wtf
155
+
156
+ if (subs.size) {
157
+ subs.forEach(async (subscription) => {
158
+ await webPush.sendNotification(subscription, JSON.stringify({ tag }));
159
+ });
160
+ }
161
+ }));
162
+ };
163
+
136
164
  plugin.addProfileItem = async (data) => {
137
165
  const title = await translator.translate('[[web-push:profile.label]]');
138
166
  data.links.push({
@@ -152,13 +180,17 @@ plugin.addProfileItem = async (data) => {
152
180
  return data;
153
181
  };
154
182
 
155
- async function constructPayload({ bodyShort, bodyLong, path }, language) {
183
+ async function constructPayload({ nid, mergeId, bodyShort, bodyLong, path }, language) {
184
+ let { maxLength } = await meta.settings.get('web-push');
185
+ maxLength = parseInt(maxLength, 10) || 256;
186
+
156
187
  if (!language) {
157
188
  language = meta.config.defaultLang || 'en-GB';
158
189
  }
159
190
 
160
191
  let [title, body] = await translator.translateKeys([bodyShort, bodyLong], language);
161
192
  ([title, body] = [title, body].map(str => validator.unescape(utils.stripHTMLTags(str))));
193
+ const tag = mergeId || nid;
162
194
  const url = `${nconf.get('url')}${path}`;
163
195
 
164
196
  // Handle empty bodyLong
@@ -167,9 +199,15 @@ async function constructPayload({ bodyShort, bodyLong, path }, language) {
167
199
  title = meta.config.title || 'NodeBB';
168
200
  }
169
201
 
202
+ // Truncate body if needed
203
+ if (body.length > maxLength) {
204
+ body = `${body.slice(0, maxLength)}…`;
205
+ }
206
+
170
207
  return {
171
208
  title,
172
209
  body,
210
+ tag,
173
211
  data: { url },
174
212
  };
175
213
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-web-push",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "A starter kit for quickly creating NodeBB plugins",
5
5
  "main": "library.js",
6
6
  "repository": {
package/plugin.json CHANGED
@@ -8,6 +8,7 @@
8
8
  { "hook": "filter:admin.header.build", "method": "addAdminNavigation" },
9
9
  { "hook": "filter:config.get", "method": "appendConfig" },
10
10
  { "hook": "action:notification.pushed", "method": "onNotificationPush" },
11
+ { "hook": "static:notifications.rescind", "method": "onNotificationRescind" },
11
12
  { "hook": "filter:user.profileMenu", "method": "addProfileItem" }
12
13
  ],
13
14
  "languages": "public/languages",
@@ -7,9 +7,14 @@
7
7
  <div class="mb-4">
8
8
  <h5 class="fw-bold tracking-tight settings-header">Settings</h5>
9
9
 
10
- <p class="fst-italic">
11
- There are no settings to adjust for this plugin — at this time.
12
- </p>
10
+ <div class="mb-3">
11
+ <label class="form-label" for="maxLength">Maximum length</label>
12
+ <input type="number" min="0" max="4096" id="maxLength" name="maxLength" title="Maximum message length" class="form-control" placeholder="256">
13
+ <p class="form-text">
14
+ Additional characters beyond this specified length will be truncated.
15
+ Due to a software limitation, if the message body is greater than 4096 bytes, the message itself will be an attachment in the push notification.
16
+ </p>
17
+ </div>
13
18
  </div>
14
19
  </form>
15
20