nodebb-plugin-mentions 4.5.2 → 4.6.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/languages/th/mentions.json +3 -0
- package/languages/th/notifications.json +7 -0
- package/library.js +69 -15
- package/package.json +7 -8
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mentions": "การถูกกล่าวถึง",
|
|
3
|
+
"user-mentioned-you-in": "<strong>%1</strong> ได้กล่าวถึงคุณใน <strong>%2</strong>",
|
|
4
|
+
"user-mentioned-you-in-room": "<strong>%1</strong> ได้กล่าวถึงคุณใน <strong class=\"text-nowrap\"><i class=\"fa %2\"></i>%3</strong>",
|
|
5
|
+
"user-mentioned-group-in": "<strong>%1</strong> ได้กล่าวถึง <strong>%2</strong> ใน <strong>%3</strong>",
|
|
6
|
+
"notificationType-mention": "เมื่อบางคนได้กล่าวถึงคุณ"
|
|
7
|
+
}
|
package/library.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
const _ = require('lodash');
|
|
6
6
|
const validator = require('validator');
|
|
7
7
|
const entitiesDecode = require('html-entities').decode;
|
|
8
|
+
const cheerio = require('cheerio');
|
|
8
9
|
|
|
9
10
|
const nconf = require.main.require('nconf');
|
|
10
11
|
const winston = require.main.require('winston');
|
|
@@ -93,7 +94,11 @@ Mentions.notify = async function ({ post }) {
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
const noMentionGroups = getNoMentionGroups();
|
|
96
|
-
matches = _.uniq(
|
|
97
|
+
matches = _.uniq(
|
|
98
|
+
matches
|
|
99
|
+
.map(match => slugify(match.slice(1)))
|
|
100
|
+
.filter(match => match && !noMentionGroups.includes(match))
|
|
101
|
+
);
|
|
97
102
|
if (!matches.length) {
|
|
98
103
|
return;
|
|
99
104
|
}
|
|
@@ -185,7 +190,8 @@ Mentions.notifyMessage = async (hookData) => {
|
|
|
185
190
|
}
|
|
186
191
|
const { message } = hookData;
|
|
187
192
|
const { roomId } = message;
|
|
188
|
-
matches = _.uniq(matches.map(slugify));
|
|
193
|
+
matches = _.uniq(matches.map(match => slugify(match.slice(1))));
|
|
194
|
+
|
|
189
195
|
const [matchedUids, roomData] = await Promise.all([
|
|
190
196
|
getUidsToNotify(matches),
|
|
191
197
|
Messaging.getRoomData(roomId),
|
|
@@ -327,11 +333,12 @@ async function createNotification(postData, nidType, notificationText) {
|
|
|
327
333
|
}
|
|
328
334
|
|
|
329
335
|
Mentions.parsePost = async (data) => {
|
|
330
|
-
|
|
336
|
+
const { postData, type } = data;
|
|
337
|
+
if (!postData.content) {
|
|
331
338
|
return data;
|
|
332
339
|
}
|
|
333
340
|
|
|
334
|
-
const parsed = await Mentions.parseRaw(
|
|
341
|
+
const parsed = await Mentions.parseRaw(postData.content, type);
|
|
335
342
|
data.postData.content = parsed;
|
|
336
343
|
return data;
|
|
337
344
|
};
|
|
@@ -340,7 +347,7 @@ function removePunctuationSuffix(string) {
|
|
|
340
347
|
return string.replace(/[!?.]*$/, '');
|
|
341
348
|
}
|
|
342
349
|
|
|
343
|
-
function getMatches(content, isMarkdown = false) {
|
|
350
|
+
async function getMatches(content, isMarkdown = false) {
|
|
344
351
|
const splitContent = utility.split(content, isMarkdown, false, true);
|
|
345
352
|
let matches = [];
|
|
346
353
|
splitContent.forEach((cleanedContent, i) => {
|
|
@@ -349,12 +356,38 @@ function getMatches(content, isMarkdown = false) {
|
|
|
349
356
|
}
|
|
350
357
|
});
|
|
351
358
|
|
|
352
|
-
|
|
359
|
+
const $ = cheerio.load(splitContent.join(''));
|
|
360
|
+
const anchors = $('a');
|
|
361
|
+
const urls = new Set();
|
|
362
|
+
Array.from(anchors).forEach((anchor) => {
|
|
363
|
+
const text = $(anchor).prop('innerText');
|
|
364
|
+
const match = text.match(regex);
|
|
365
|
+
if (match) {
|
|
366
|
+
urls.add($(anchor).attr('href'));
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Filter out urls that don't backreference to a remote id
|
|
371
|
+
const backrefs = await db.getObjectFields('remoteUrl:uid', Array.from(urls));
|
|
372
|
+
const urlAsIdExists = await db.isSortedSetMembers('usersRemote:lastCrawled', Array.from(urls));
|
|
373
|
+
const urlMap = new Map();
|
|
374
|
+
Array.from(urls).map(async (url, index) => {
|
|
375
|
+
if (backrefs[url] || urlAsIdExists[index]) {
|
|
376
|
+
urlMap.set(url, backrefs[url] || url);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
let slugs = await User.getUsersFields(Array.from(urlMap.values()), ['userslug']);
|
|
380
|
+
slugs = slugs.map(({ userslug }) => userslug);
|
|
381
|
+
Array.from(urlMap.keys()).forEach((url, idx) => {
|
|
382
|
+
urlMap.set(url, `/user/${encodeURIComponent(slugs[idx])}`);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
return { splitContent, matches, urlMap };
|
|
353
386
|
}
|
|
354
387
|
|
|
355
388
|
Mentions.getMatches = async (content) => {
|
|
356
389
|
// Exported method only accepts markdown, also filters out dupes and matches to ensure slugs exist
|
|
357
|
-
let { matches } = getMatches(content, true);
|
|
390
|
+
let { matches } = await getMatches(content, true);
|
|
358
391
|
matches = await filterMatches(matches);
|
|
359
392
|
const ids = await Promise.all(matches.map(async m => User.getUidByUserslug(m.slice(1).toLowerCase())));
|
|
360
393
|
matches = matches.map((slug, idx) => (ids[idx] ? {
|
|
@@ -372,11 +405,15 @@ async function filterMatches(matches) {
|
|
|
372
405
|
return matches.filter((m, i) => exists[[i]]);
|
|
373
406
|
}
|
|
374
407
|
|
|
375
|
-
Mentions.parseRaw = async (content) => {
|
|
408
|
+
Mentions.parseRaw = async (content, type = 'default') => {
|
|
409
|
+
if (type === 'plaintext') {
|
|
410
|
+
return content;
|
|
411
|
+
}
|
|
412
|
+
|
|
376
413
|
// Note: Mentions.clean explicitly can't be called here because I need the content unstripped
|
|
377
|
-
let { splitContent, matches } = getMatches(content);
|
|
414
|
+
let { splitContent, matches, urlMap } = await getMatches(content);
|
|
378
415
|
|
|
379
|
-
if (!matches.length) {
|
|
416
|
+
if (!matches.length && !urlMap.size) {
|
|
380
417
|
return content;
|
|
381
418
|
}
|
|
382
419
|
|
|
@@ -390,6 +427,7 @@ Mentions.parseRaw = async (content) => {
|
|
|
390
427
|
return atIndex !== 0 ? match.slice(atIndex) : match;
|
|
391
428
|
});
|
|
392
429
|
|
|
430
|
+
// Convert matches to anchor html
|
|
393
431
|
await Promise.all(matches.map(async (match) => {
|
|
394
432
|
const slug = slugify(match.slice(1));
|
|
395
433
|
match = removePunctuationSuffix(match);
|
|
@@ -397,7 +435,7 @@ Mentions.parseRaw = async (content) => {
|
|
|
397
435
|
const cid = await categories.getCidByHandle(slug);
|
|
398
436
|
const { groupExists, user, category } = await utils.promiseParallel({
|
|
399
437
|
groupExists: Groups.existsBySlug(slug),
|
|
400
|
-
user: User.getUserFields(uid, ['uid', 'username', 'fullname', 'url']),
|
|
438
|
+
user: User.getUserFields(uid, ['uid', 'username', 'userslug', 'fullname', 'url']),
|
|
401
439
|
category: categories.getCategoryFields(cid, ['slug']),
|
|
402
440
|
});
|
|
403
441
|
|
|
@@ -406,17 +444,20 @@ Mentions.parseRaw = async (content) => {
|
|
|
406
444
|
|
|
407
445
|
switch (true) {
|
|
408
446
|
case !!uid: {
|
|
409
|
-
url =
|
|
447
|
+
url = `/user/${encodeURIComponent(user.userslug)}`;
|
|
448
|
+
if (type.startsWith('activitypub') && !utils.isNumber(uid)) {
|
|
449
|
+
url = user.url || user.uid;
|
|
450
|
+
}
|
|
410
451
|
break;
|
|
411
452
|
}
|
|
412
453
|
|
|
413
454
|
case !!cid: {
|
|
414
|
-
url =
|
|
455
|
+
url = `/category/${category.slug}`;
|
|
415
456
|
break;
|
|
416
457
|
}
|
|
417
458
|
|
|
418
459
|
case !!groupExists: {
|
|
419
|
-
url =
|
|
460
|
+
url = `/groups/${slug}`;
|
|
420
461
|
break;
|
|
421
462
|
}
|
|
422
463
|
}
|
|
@@ -456,7 +497,20 @@ Mentions.parseRaw = async (content) => {
|
|
|
456
497
|
}
|
|
457
498
|
}));
|
|
458
499
|
|
|
459
|
-
|
|
500
|
+
const parsed = splitContent.join('');
|
|
501
|
+
|
|
502
|
+
// Modify existing anchors to local profile
|
|
503
|
+
const $ = cheerio.load(parsed);
|
|
504
|
+
const anchors = $('a');
|
|
505
|
+
Array.from(anchors).forEach((anchor) => {
|
|
506
|
+
const $anchor = $(anchor);
|
|
507
|
+
const url = $anchor.attr('href');
|
|
508
|
+
if (urlMap.has(url)) {
|
|
509
|
+
$anchor.attr('href', urlMap.get(url));
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
return $.html();
|
|
460
514
|
};
|
|
461
515
|
|
|
462
516
|
Mentions.clean = function (input, isMarkdown, stripBlockquote, stripCode) {
|
package/package.json
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-mentions",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"description": "NodeBB Plugin that allows users to mention other users by prepending an '@' sign to their username",
|
|
5
5
|
"main": "library.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
-
},
|
|
9
6
|
"repository": {
|
|
10
7
|
"type": "git",
|
|
11
8
|
"url": "https://github.com/julianlam/nodebb-plugin-mentions"
|
|
@@ -16,22 +13,24 @@
|
|
|
16
13
|
"username",
|
|
17
14
|
"mentions"
|
|
18
15
|
],
|
|
19
|
-
"author": "Julian Lam <julian@
|
|
20
|
-
"license": "
|
|
16
|
+
"author": "Julian Lam <julian@nodebb.org>",
|
|
17
|
+
"license": "MIT",
|
|
21
18
|
"bugs": {
|
|
22
19
|
"url": "https://github.com/julianlam/nodebb-plugin-mentions/issues"
|
|
23
20
|
},
|
|
24
21
|
"nbbpm": {
|
|
25
|
-
"compatibility": "^
|
|
22
|
+
"compatibility": "^4.0.0"
|
|
26
23
|
},
|
|
27
24
|
"dependencies": {
|
|
25
|
+
"cheerio": "^1.0.0-rc.12",
|
|
28
26
|
"html-entities": "^2.3.2",
|
|
29
27
|
"lodash": "4.17.21",
|
|
28
|
+
"sanitize-html": "^2.13.0",
|
|
30
29
|
"validator": "^13.0.0"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
33
32
|
"mocha": "10.4.0",
|
|
34
|
-
"eslint": "9.
|
|
33
|
+
"eslint": "9.3.0",
|
|
35
34
|
"eslint-config-nodebb": "0.2.1",
|
|
36
35
|
"eslint-plugin-import": "2.29.1"
|
|
37
36
|
}
|