nodebb-plugin-mentions 4.4.0 → 4.4.1
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 +94 -39
- package/package.json +1 -1
package/library.js
CHANGED
|
@@ -11,6 +11,7 @@ const winston = require.main.require('winston');
|
|
|
11
11
|
|
|
12
12
|
const db = require.main.require('./src/database');
|
|
13
13
|
const api = require.main.require('./src/api');
|
|
14
|
+
const meta = require.main.require('./src/meta');
|
|
14
15
|
const Topics = require.main.require('./src/topics');
|
|
15
16
|
const posts = require.main.require('./src/posts');
|
|
16
17
|
const User = require.main.require('./src/user');
|
|
@@ -78,34 +79,57 @@ function getNoMentionGroups() {
|
|
|
78
79
|
return noMentionGroups;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
Mentions.notify = async function (
|
|
82
|
-
const
|
|
83
|
-
const postOwner = parseInt(postData.uid, 10);
|
|
84
|
-
const cleanedContent = Mentions.clean(postData.content, true, true, true);
|
|
85
|
-
let matches = cleanedContent.match(regex);
|
|
86
|
-
if (!matches) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
82
|
+
Mentions.notify = async function ({ post }) {
|
|
83
|
+
const postOwner = parseInt(post.uid, 10);
|
|
89
84
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
85
|
+
let uidsToNotify;
|
|
86
|
+
let groupsToNotify;
|
|
87
|
+
if (utils.isNumber(post.pid)) {
|
|
88
|
+
const cleanedContent = Mentions.clean(post.content, true, true, true);
|
|
89
|
+
let matches = cleanedContent.match(regex);
|
|
90
|
+
if (!matches) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
const noMentionGroups = getNoMentionGroups();
|
|
95
|
+
matches = _.uniq(matches.map(match => slugify(match))).filter(match => match && !noMentionGroups.includes(match));
|
|
96
|
+
if (!matches.length) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
([uidsToNotify, groupsToNotify] = await Promise.all([
|
|
101
|
+
getUidsToNotify(matches),
|
|
102
|
+
getGroupsToNotify(matches),
|
|
103
|
+
]));
|
|
104
|
+
} else if (post._activitypub) { // ActivityPub
|
|
105
|
+
const { tag } = post._activitypub;
|
|
106
|
+
groupsToNotify = []; // cannot mention groups for now
|
|
107
|
+
|
|
108
|
+
if (tag.length) {
|
|
109
|
+
const slugs = tag.reduce((slugs, tag) => {
|
|
110
|
+
if (tag.type === 'Mention') {
|
|
111
|
+
const [slug, hostname] = tag.name.slice(1).split('@');
|
|
112
|
+
if (hostname === nconf.get('url_parsed').hostname) {
|
|
113
|
+
slugs.push(slug);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return slugs;
|
|
117
|
+
}, []);
|
|
118
|
+
|
|
119
|
+
uidsToNotify = slugs.length ? await db.sortedSetScores('userslug:uid', slugs) : [];
|
|
120
|
+
} else {
|
|
121
|
+
uidsToNotify = [];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
100
124
|
|
|
101
125
|
if (!uidsToNotify.length && !groupsToNotify.length) {
|
|
102
126
|
return;
|
|
103
127
|
}
|
|
104
128
|
|
|
105
129
|
const [topic, userData, topicFollowers] = await Promise.all([
|
|
106
|
-
Topics.getTopicFields(
|
|
107
|
-
User.getUserFields(
|
|
108
|
-
Mentions._settings.disableFollowedTopics === 'on' ? Topics.getFollowers(
|
|
130
|
+
Topics.getTopicFields(post.tid, ['title', 'cid']),
|
|
131
|
+
User.getUserFields(post.uid, ['username']),
|
|
132
|
+
Mentions._settings.disableFollowedTopics === 'on' ? Topics.getFollowers(post.tid) : [],
|
|
109
133
|
]);
|
|
110
134
|
const { displayname } = userData;
|
|
111
135
|
const title = entitiesDecode(topic.title);
|
|
@@ -116,8 +140,8 @@ Mentions.notify = async function (data) {
|
|
|
116
140
|
);
|
|
117
141
|
|
|
118
142
|
if (Mentions._settings.privilegedDirectReplies === 'on') {
|
|
119
|
-
const toPid = await posts.getPostField(
|
|
120
|
-
uids = await filterPrivilegedUids(uids,
|
|
143
|
+
const toPid = await posts.getPostField(post.pid, 'toPid');
|
|
144
|
+
uids = await filterPrivilegedUids(uids, post.cid, toPid);
|
|
121
145
|
}
|
|
122
146
|
|
|
123
147
|
const groupMemberUids = {};
|
|
@@ -133,20 +157,20 @@ Mentions.notify = async function (data) {
|
|
|
133
157
|
});
|
|
134
158
|
});
|
|
135
159
|
|
|
136
|
-
const filteredUids = await filterUidsAlreadyMentioned(uids,
|
|
160
|
+
const filteredUids = await filterUidsAlreadyMentioned(uids, post.pid);
|
|
137
161
|
if (filteredUids.length) {
|
|
138
|
-
await sendNotificationToUids(
|
|
139
|
-
await db.setAdd(`mentions:pid:${
|
|
162
|
+
await sendNotificationToUids(post, filteredUids, 'user', `[[notifications:user-mentioned-you-in, ${displayname}, ${titleEscaped}]]`);
|
|
163
|
+
await db.setAdd(`mentions:pid:${post.pid}:uids`, filteredUids);
|
|
140
164
|
}
|
|
141
165
|
|
|
142
166
|
for (let i = 0; i < groupsToNotify.length; ++i) {
|
|
143
167
|
if (groupsToNotify[i] && groupsToNotify[i].name && groupsToNotify[i].members) {
|
|
144
168
|
const memberUids = groupsToNotify[i].members;
|
|
145
169
|
const groupName = groupsToNotify[i].name;
|
|
146
|
-
const groupMentionSent = await db.isSetMember(`mentions:pid:${
|
|
170
|
+
const groupMentionSent = await db.isSetMember(`mentions:pid:${post.pid}:groups`, groupName);
|
|
147
171
|
if (!groupMentionSent && memberUids.length) {
|
|
148
|
-
await sendNotificationToUids(
|
|
149
|
-
await db.setAdd(`mentions:pid:${
|
|
172
|
+
await sendNotificationToUids(post, memberUids, groupName, `[[notifications:user-mentioned-group-in, ${displayname} , ${groupName}, ${titleEscaped}]]`);
|
|
173
|
+
await db.setAdd(`mentions:pid:${post.pid}:groups`, groupName);
|
|
150
174
|
}
|
|
151
175
|
}
|
|
152
176
|
}
|
|
@@ -295,7 +319,7 @@ async function createNotification(postData, nidType, notificationText) {
|
|
|
295
319
|
pid: postData.pid,
|
|
296
320
|
tid: postData.tid,
|
|
297
321
|
from: postData.uid,
|
|
298
|
-
path: `/post/${postData.pid}`,
|
|
322
|
+
path: `/post/${encodeURIComponent(postData.pid)}`,
|
|
299
323
|
topicTitle: title ? utils.decodeHTMLEntities(title) : title,
|
|
300
324
|
importance: 6,
|
|
301
325
|
});
|
|
@@ -315,8 +339,8 @@ function removePunctuationSuffix(string) {
|
|
|
315
339
|
return string.replace(/[!?.]*$/, '');
|
|
316
340
|
}
|
|
317
341
|
|
|
318
|
-
|
|
319
|
-
|
|
342
|
+
function getMatches(content, isMarkdown = false) {
|
|
343
|
+
const splitContent = utility.split(content, isMarkdown, false, true);
|
|
320
344
|
let matches = [];
|
|
321
345
|
splitContent.forEach((cleanedContent, i) => {
|
|
322
346
|
if ((i % 2) === 0) {
|
|
@@ -324,6 +348,33 @@ Mentions.parseRaw = async (content) => {
|
|
|
324
348
|
}
|
|
325
349
|
});
|
|
326
350
|
|
|
351
|
+
return { splitContent, matches };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
Mentions.getMatches = async (content) => {
|
|
355
|
+
// Exported method only accepts markdown, also filters out dupes and matches to ensure slugs exist
|
|
356
|
+
let { matches } = getMatches(content, true);
|
|
357
|
+
matches = await filterMatches(matches);
|
|
358
|
+
const ids = await Promise.all(matches.map(async m => User.getUidByUserslug(m.slice(1).toLowerCase())));
|
|
359
|
+
matches = matches.map((slug, idx) => (ids[idx] ? {
|
|
360
|
+
id: ids[idx],
|
|
361
|
+
slug,
|
|
362
|
+
} : null)).filter(Boolean);
|
|
363
|
+
|
|
364
|
+
return new Set(matches);
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
async function filterMatches(matches) {
|
|
368
|
+
matches = Array.from(new Set(matches));
|
|
369
|
+
const exists = await Promise.all(matches.map(match => meta.userOrGroupExists(match.slice(1))));
|
|
370
|
+
|
|
371
|
+
return matches.filter((m, i) => exists[[i]]);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
Mentions.parseRaw = async (content) => {
|
|
375
|
+
// Note: Mentions.clean explicitly can't be called here because I need the content unstripped
|
|
376
|
+
let { splitContent, matches } = getMatches(content);
|
|
377
|
+
|
|
327
378
|
if (!matches.length) {
|
|
328
379
|
return content;
|
|
329
380
|
}
|
|
@@ -342,12 +393,18 @@ Mentions.parseRaw = async (content) => {
|
|
|
342
393
|
const slug = slugify(match.slice(1));
|
|
343
394
|
match = removePunctuationSuffix(match);
|
|
344
395
|
const uid = await User.getUidByUserslug(slug);
|
|
345
|
-
const
|
|
396
|
+
const { groupExists, user } = await utils.promiseParallel({
|
|
346
397
|
groupExists: Groups.existsBySlug(slug),
|
|
347
398
|
user: User.getUserFields(uid, ['uid', 'username', 'fullname']),
|
|
348
399
|
});
|
|
349
400
|
|
|
350
|
-
if (
|
|
401
|
+
if (user.uid || groupExists) {
|
|
402
|
+
let url;
|
|
403
|
+
if (user.uid) {
|
|
404
|
+
url = utils.isNumber(user.uid) ? `${nconf.get('url')}/uid/${user.uid}` : user.uid;
|
|
405
|
+
} else {
|
|
406
|
+
url = `${nconf.get('url')}/groups/${slug}`;
|
|
407
|
+
}
|
|
351
408
|
const regex = isLatinMention.test(match) ?
|
|
352
409
|
RegExp(`${parts.before}${match}${parts.after}`, 'gu') :
|
|
353
410
|
RegExp(`${parts.before}${match}`, 'gu');
|
|
@@ -364,20 +421,18 @@ Mentions.parseRaw = async (content) => {
|
|
|
364
421
|
const atIndex = match.indexOf('@');
|
|
365
422
|
const plain = match.slice(0, atIndex);
|
|
366
423
|
match = match.slice(atIndex);
|
|
367
|
-
if (
|
|
424
|
+
if (user.uid) {
|
|
368
425
|
switch (Mentions._settings.display) {
|
|
369
426
|
case 'fullname':
|
|
370
|
-
match =
|
|
427
|
+
match = user.fullname || match;
|
|
371
428
|
break;
|
|
372
429
|
case 'username':
|
|
373
|
-
match =
|
|
430
|
+
match = user.username;
|
|
374
431
|
break;
|
|
375
432
|
}
|
|
376
433
|
}
|
|
377
434
|
|
|
378
|
-
const str =
|
|
379
|
-
`<a class="plugin-mentions-user plugin-mentions-a" href="${nconf.get('url')}/uid/${results.user.uid}">${match}</a>` :
|
|
380
|
-
`<a class="plugin-mentions-group plugin-mentions-a" href="${nconf.get('url')}/groups/${slug}">${match}</a>`;
|
|
435
|
+
const str = `<a class="plugin-mentions-${user.uid ? 'user' : 'group'} plugin-mentions-a" href="${url}">${match}</a>`;
|
|
381
436
|
|
|
382
437
|
return plain + str;
|
|
383
438
|
});
|
package/package.json
CHANGED