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 +40 -2
- package/package.json +1 -1
- package/plugin.json +1 -0
- package/templates/admin/plugins/web-push.tpl +8 -3
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
|
-
|
|
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
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
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|