nodebb-plugin-admin-chats 1.1.0 → 2.1.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/.vscode/settings.json +3 -0
- package/client.js +398 -103
- package/library.js +503 -39
- package/package.json +1 -1
- package/plugin.json +66 -17
- package/public/languages/en-GB/admin-chats.json +9 -0
- package/public/languages/he/admin-chats.json +9 -0
package/library.js
CHANGED
|
@@ -2,16 +2,81 @@
|
|
|
2
2
|
|
|
3
3
|
const User = require.main.require('./src/user');
|
|
4
4
|
const Messaging = require.main.require('./src/messaging');
|
|
5
|
+
const ChatsAPI = require.main.require('./src/api/chats');
|
|
5
6
|
const db = require.main.require('./src/database');
|
|
7
|
+
const meta = require.main.require('./src/meta');
|
|
8
|
+
const privileges = require.main.require('./src/privileges');
|
|
9
|
+
const helpers = require.main.require('./src/controllers/helpers');
|
|
6
10
|
|
|
7
11
|
const plugin = {};
|
|
8
12
|
const LOCK_PREFIX = '[admin-chat-lock]';
|
|
9
13
|
const LOCKED_MESSAGE_TEXT = '🔒 חדר זה ננעל ע"י המנהלים.';
|
|
10
|
-
|
|
14
|
+
|
|
15
|
+
async function getLockedActionMessage(uid) {
|
|
16
|
+
try {
|
|
17
|
+
const userData = await User.getUserData(uid);
|
|
18
|
+
let userLang = userData && userData.settings && userData.userLang;
|
|
19
|
+
|
|
20
|
+
// If user doesn't have specific language setting, use forum default
|
|
21
|
+
if (!userLang) {
|
|
22
|
+
const meta = require.main.require('./src/meta');
|
|
23
|
+
userLang = meta.config.defaultLang || 'en-GB';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (userLang === 'he' || userLang === 'he-IL') {
|
|
27
|
+
return 'לא ניתן לבצע פעולה זו בחדר נעול';
|
|
28
|
+
} else {
|
|
29
|
+
return 'This action cannot be performed in a locked room.';
|
|
30
|
+
}
|
|
31
|
+
} catch (err) {
|
|
32
|
+
// Default to English if we can't determine language
|
|
33
|
+
return 'This action cannot be performed in a locked room.';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const ADMIN_CHAT_PAGE_SIZE = 30;
|
|
37
|
+
const ADMIN_CHAT_SCAN_SIZE = 100;
|
|
11
38
|
|
|
12
39
|
plugin.init = async function (params) {
|
|
13
|
-
registerRoutes(params.router, params.middleware);
|
|
40
|
+
registerRoutes(params.app, params.router, params.middleware);
|
|
14
41
|
overrideMessagingFunctions();
|
|
42
|
+
overrideChatsApi();
|
|
43
|
+
overrideCoreChatRedirect(params.controllers);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
plugin.addScripts = async function (data) {
|
|
47
|
+
let isHebrew = false;
|
|
48
|
+
|
|
49
|
+
const uid = data.req && data.req.uid;
|
|
50
|
+
if (uid) {
|
|
51
|
+
try {
|
|
52
|
+
const userData = await User.getUserData(uid);
|
|
53
|
+
let userLang = userData && userData.settings && userData.userLang;
|
|
54
|
+
|
|
55
|
+
// If user doesn't have specific language setting, use forum default
|
|
56
|
+
if (!userLang) {
|
|
57
|
+
const meta = require.main.require('./src/meta');
|
|
58
|
+
userLang = meta.config.defaultLang || 'en-GB';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
isHebrew = userLang === 'he' || userLang === 'he-IL';
|
|
62
|
+
} catch (err) {
|
|
63
|
+
// Default to English if error
|
|
64
|
+
isHebrew = false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Only send the lockedAction translation from server
|
|
69
|
+
const translations = {
|
|
70
|
+
lockedAction: isHebrew ? 'לא ניתן לבצע פעולה זו בחדר נעול' : 'This action cannot be performed in a locked room.',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
data.scripts = data.scripts || [];
|
|
74
|
+
data.scripts.push({
|
|
75
|
+
src: false,
|
|
76
|
+
script: `window.adminChatsTranslations = ${JSON.stringify(translations)};`
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return data;
|
|
15
80
|
};
|
|
16
81
|
|
|
17
82
|
plugin.addProfileLink = async function (data) {
|
|
@@ -34,7 +99,7 @@ plugin.addProfileLink = async function (data) {
|
|
|
34
99
|
});
|
|
35
100
|
}
|
|
36
101
|
} catch (e) {
|
|
37
|
-
|
|
102
|
+
// Silent error handling
|
|
38
103
|
}
|
|
39
104
|
return data;
|
|
40
105
|
};
|
|
@@ -72,6 +137,49 @@ plugin.canReply = async function (payload) {
|
|
|
72
137
|
return payload;
|
|
73
138
|
};
|
|
74
139
|
|
|
140
|
+
plugin.filterMessagingSend = async function (payload) {
|
|
141
|
+
const uid = getCallerUid(payload);
|
|
142
|
+
const roomId = getRoomId(payload);
|
|
143
|
+
|
|
144
|
+
if (!uid || !roomId) {
|
|
145
|
+
return payload;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (await User.isAdministrator(uid)) {
|
|
149
|
+
return payload;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (await isRoomLocked(roomId)) {
|
|
153
|
+
const message = await getLockedActionMessage(uid);
|
|
154
|
+
throw new Error(message);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return payload;
|
|
158
|
+
};
|
|
159
|
+
plugin.filterAddUsersToRoom = async function (payload) {
|
|
160
|
+
if (await canNonAdminModifyLockedRoom(payload.uid, payload.roomId)) {
|
|
161
|
+
const message = await getLockedActionMessage(payload.uid);
|
|
162
|
+
throw new Error(message);
|
|
163
|
+
}
|
|
164
|
+
return payload;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
plugin.filterRemoveUsersFromRoom = async function (payload) {
|
|
168
|
+
if (await canNonAdminModifyLockedRoom(payload.uid, payload.roomId)) {
|
|
169
|
+
const message = await getLockedActionMessage(payload.uid);
|
|
170
|
+
throw new Error(message);
|
|
171
|
+
}
|
|
172
|
+
return payload;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
plugin.filterRenameRoom = async function (payload) {
|
|
176
|
+
if (await canNonAdminModifyLockedRoom(payload.uid, payload.roomId)) {
|
|
177
|
+
const message = await getLockedActionMessage(payload.uid);
|
|
178
|
+
throw new Error(message);
|
|
179
|
+
}
|
|
180
|
+
return payload;
|
|
181
|
+
};
|
|
182
|
+
|
|
75
183
|
plugin.canGetMessages = async function (payload) {
|
|
76
184
|
const isAdmin = await User.isAdministrator(payload.callerUid);
|
|
77
185
|
if (isAdmin) {
|
|
@@ -104,11 +212,12 @@ plugin.onLoadRoom = async function (payload) {
|
|
|
104
212
|
|
|
105
213
|
const isAdmin = await User.isAdministrator(uid);
|
|
106
214
|
const lockData = await getRoomLockData(room.roomId);
|
|
107
|
-
|
|
215
|
+
const isLockedForUser = lockData.isLocked && !isAdmin;
|
|
108
216
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
217
|
+
room.adminChatLock = lockData;
|
|
218
|
+
room.messages = ensureFirstVisibleMessageHeader(buildRoomMessagesWithLockNotice(room.messages, lockData));
|
|
219
|
+
room.canReply = room.canReply && !isLockedForUser;
|
|
220
|
+
room.showUserInput = room.showUserInput && !isLockedForUser;
|
|
112
221
|
|
|
113
222
|
if (!isAdmin) {
|
|
114
223
|
return payload;
|
|
@@ -138,16 +247,26 @@ plugin.onLoadRoom = async function (payload) {
|
|
|
138
247
|
msg.index = count - index - 1;
|
|
139
248
|
});
|
|
140
249
|
|
|
141
|
-
room.messages =
|
|
250
|
+
room.messages = ensureFirstVisibleMessageHeader(buildRoomMessagesWithLockNotice(messages.reverse(), lockData));
|
|
142
251
|
|
|
143
252
|
room.messages.forEach((msg, index) => {
|
|
144
253
|
if (index === 0) {
|
|
145
254
|
msg.newSet = true;
|
|
146
255
|
} else {
|
|
147
256
|
const prevMsg = room.messages[index - 1];
|
|
148
|
-
|
|
257
|
+
const prevTime = parseInt(prevMsg && prevMsg.timestamp, 10) || 0;
|
|
258
|
+
const currentTime = parseInt(msg && msg.timestamp, 10) || 0;
|
|
259
|
+
|
|
260
|
+
msg.newSet = !!(
|
|
261
|
+
currentTime > prevTime + Messaging.newMessageCutoff ||
|
|
262
|
+
parseInt(msg.fromuid, 10) !== parseInt(prevMsg.fromuid, 10) ||
|
|
263
|
+
prevMsg.system ||
|
|
264
|
+
msg.toMid
|
|
265
|
+
);
|
|
149
266
|
}
|
|
150
267
|
});
|
|
268
|
+
|
|
269
|
+
room.messages = ensureFirstVisibleMessageHeader(room.messages);
|
|
151
270
|
}
|
|
152
271
|
|
|
153
272
|
room.isAdmin = true;
|
|
@@ -158,6 +277,43 @@ plugin.onLoadRoom = async function (payload) {
|
|
|
158
277
|
return payload;
|
|
159
278
|
};
|
|
160
279
|
|
|
280
|
+
function overrideCoreChatRedirect(controllers) {
|
|
281
|
+
const chatsController = controllers && controllers.accounts && controllers.accounts.chats;
|
|
282
|
+
if (!chatsController || typeof chatsController.redirectToChat !== 'function' || chatsController.redirectToChat._adminChatsWrapped) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const originalRedirectToChat = chatsController.redirectToChat;
|
|
287
|
+
const wrappedRedirectToChat = async function (req, res, next) {
|
|
288
|
+
if (req && req.uid && await User.isAdministrator(req.uid)) {
|
|
289
|
+
return await renderAdminChatsPage(req, res, next);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return await originalRedirectToChat.call(this, req, res, next);
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
wrappedRedirectToChat._adminChatsWrapped = true;
|
|
296
|
+
chatsController.redirectToChat = wrappedRedirectToChat;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function overrideChatsApi() {
|
|
300
|
+
if (!ChatsAPI || typeof ChatsAPI.kick !== 'function' || ChatsAPI.kick._adminChatLockWrapped) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const originalKick = ChatsAPI.kick;
|
|
305
|
+
const wrappedKick = async function (caller, data) {
|
|
306
|
+
if (caller && !await User.isAdministrator(caller.uid) && data && data.roomId && Array.isArray(data.uids) && data.uids.length === 1 && parseInt(data.uids[0], 10) === parseInt(caller.uid, 10) && await isRoomLocked(data.roomId)) {
|
|
307
|
+
const message = await getLockedActionMessage(caller.uid);
|
|
308
|
+
throw new Error(message);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return await originalKick.call(this, caller, data);
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
wrappedKick._adminChatLockWrapped = true;
|
|
315
|
+
ChatsAPI.kick = wrappedKick;
|
|
316
|
+
}
|
|
161
317
|
function overrideMessagingFunctions() {
|
|
162
318
|
const originalCanEdit = Messaging.canEdit;
|
|
163
319
|
const originalCanDelete = Messaging.canDelete;
|
|
@@ -168,6 +324,13 @@ function overrideMessagingFunctions() {
|
|
|
168
324
|
if (await User.isAdministrator(uid)) {
|
|
169
325
|
return true;
|
|
170
326
|
}
|
|
327
|
+
|
|
328
|
+
const roomId = await getMessageRoomId(messageId);
|
|
329
|
+
if (roomId && await isRoomLocked(roomId)) {
|
|
330
|
+
const message = await getLockedActionMessage(uid);
|
|
331
|
+
throw new Error(message);
|
|
332
|
+
}
|
|
333
|
+
|
|
171
334
|
return await originalCanEdit(messageId, uid);
|
|
172
335
|
};
|
|
173
336
|
|
|
@@ -175,6 +338,13 @@ function overrideMessagingFunctions() {
|
|
|
175
338
|
if (await User.isAdministrator(uid)) {
|
|
176
339
|
return true;
|
|
177
340
|
}
|
|
341
|
+
|
|
342
|
+
const roomId = await getMessageRoomId(messageId);
|
|
343
|
+
if (roomId && await isRoomLocked(roomId)) {
|
|
344
|
+
const message = await getLockedActionMessage(uid);
|
|
345
|
+
throw new Error(message);
|
|
346
|
+
}
|
|
347
|
+
|
|
178
348
|
return await originalCanDelete(messageId, uid);
|
|
179
349
|
};
|
|
180
350
|
|
|
@@ -197,9 +367,9 @@ function overrideMessagingFunctions() {
|
|
|
197
367
|
return await originalCanReply.apply(this, args);
|
|
198
368
|
};
|
|
199
369
|
}
|
|
200
|
-
}
|
|
370
|
+
}
|
|
201
371
|
|
|
202
|
-
function registerRoutes(router, middleware) {
|
|
372
|
+
function registerRoutes(app, router, middleware) {
|
|
203
373
|
if (!router) {
|
|
204
374
|
return;
|
|
205
375
|
}
|
|
@@ -209,6 +379,56 @@ function registerRoutes(router, middleware) {
|
|
|
209
379
|
routeMiddleware.push(middleware.ensureLoggedIn);
|
|
210
380
|
}
|
|
211
381
|
|
|
382
|
+
const pageMiddlewares = [
|
|
383
|
+
middleware.autoLocale,
|
|
384
|
+
middleware.applyBlacklist,
|
|
385
|
+
middleware.authenticateRequest,
|
|
386
|
+
middleware.redirectToHomeIfBanned,
|
|
387
|
+
middleware.maintenanceMode,
|
|
388
|
+
middleware.registrationComplete,
|
|
389
|
+
middleware.pluginHooks,
|
|
390
|
+
...routeMiddleware,
|
|
391
|
+
middleware.pageView,
|
|
392
|
+
].filter(Boolean);
|
|
393
|
+
|
|
394
|
+
const pageController = async (req, res, next) => {
|
|
395
|
+
try {
|
|
396
|
+
await renderAdminChatsPage(req, res, next);
|
|
397
|
+
} catch (err) {
|
|
398
|
+
next(err);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
const pageRouter = app || router;
|
|
403
|
+
pageRouter.get('/chats/:roomId?/:index?', middleware.busyCheck, pageMiddlewares, middleware.buildHeader, pageController);
|
|
404
|
+
|
|
405
|
+
router.get('/api/admin-chats', ...routeMiddleware, async (req, res) => {
|
|
406
|
+
try {
|
|
407
|
+
if (!await assertAdminChatsAccess(req, res)) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const start = Math.max(0, parseInt(req.query.start, 10) || 0);
|
|
412
|
+
const data = await getAdminRecentChats(req.uid, start, ADMIN_CHAT_PAGE_SIZE);
|
|
413
|
+
res.json(data);
|
|
414
|
+
} catch (err) {
|
|
415
|
+
res.status(500).json({ status: { code: 'error', message: err.message } });
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
router.get('/api/admin-chats/page/:roomId?/:index?', ...routeMiddleware, async (req, res) => {
|
|
420
|
+
try {
|
|
421
|
+
if (!await assertAdminChatsAccess(req, res)) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const payload = await buildAdminChatsPayload(req);
|
|
426
|
+
res.json(payload);
|
|
427
|
+
} catch (err) {
|
|
428
|
+
res.status(500).json({ status: { code: 'error', message: err.message } });
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
212
432
|
router.post('/api/admin-chats/:roomId/lock', ...routeMiddleware, async (req, res) => {
|
|
213
433
|
try {
|
|
214
434
|
const actingUid = req.uid;
|
|
@@ -237,12 +457,248 @@ function registerRoutes(router, middleware) {
|
|
|
237
457
|
lockData,
|
|
238
458
|
});
|
|
239
459
|
} catch (err) {
|
|
240
|
-
console.error('[Super Admin Chats] Error updating room lock:', err);
|
|
241
460
|
return res.status(500).json({ status: { code: 'error', message: err.message } });
|
|
242
461
|
}
|
|
243
462
|
});
|
|
244
463
|
}
|
|
245
464
|
|
|
465
|
+
async function renderAdminChatsPage(req, res, next) {
|
|
466
|
+
if (meta.config.disableChat) {
|
|
467
|
+
return next();
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (!await assertAdminChatsAccess(req, res)) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const payload = await buildAdminChatsPayload(req);
|
|
475
|
+
if (req.params.roomId && !payload.roomId) {
|
|
476
|
+
return next();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
res.render('chats', payload);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async function assertAdminChatsAccess(req, res) {
|
|
483
|
+
if (!req.uid || !await User.isAdministrator(req.uid)) {
|
|
484
|
+
helpers.notAllowed(req, res);
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
async function buildAdminChatsPayload(req) {
|
|
492
|
+
const userslug = await User.getUserField(req.uid, 'userslug');
|
|
493
|
+
const [recentChats, publicRooms, privateRoomCount] = await Promise.all([
|
|
494
|
+
getAdminRecentChats(req.uid, 0, ADMIN_CHAT_PAGE_SIZE),
|
|
495
|
+
Messaging.getPublicRooms(req.uid, req.uid),
|
|
496
|
+
getPrivateRoomCount(),
|
|
497
|
+
]);
|
|
498
|
+
|
|
499
|
+
const payload = {
|
|
500
|
+
title: '[[pages:chats]]',
|
|
501
|
+
uid: req.uid,
|
|
502
|
+
userslug,
|
|
503
|
+
adminAllChats: true,
|
|
504
|
+
rooms: recentChats.rooms,
|
|
505
|
+
nextStart: recentChats.nextStart,
|
|
506
|
+
publicRooms: publicRooms || [],
|
|
507
|
+
privateRoomCount,
|
|
508
|
+
bodyClasses: ['page-user-chats'],
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
const roomId = parseInt(req.params.roomId, 10) || 0;
|
|
512
|
+
if (!roomId) {
|
|
513
|
+
return payload;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const roomPayload = await buildAdminChatRoomPayload(req.uid, roomId, req.params.index);
|
|
517
|
+
if (!roomPayload) {
|
|
518
|
+
return payload;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return {
|
|
522
|
+
...payload,
|
|
523
|
+
...roomPayload,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
async function buildAdminChatRoomPayload(uid, roomId, indexParam) {
|
|
528
|
+
let start = 0;
|
|
529
|
+
let scrollToIndex = null;
|
|
530
|
+
|
|
531
|
+
if (indexParam) {
|
|
532
|
+
const msgCount = await db.getObjectField(`chat:room:${roomId}`, 'messageCount');
|
|
533
|
+
start = Math.max(0, parseInt(msgCount, 10) - parseInt(indexParam, 10) - 49);
|
|
534
|
+
scrollToIndex = Math.min(msgCount, Math.max(0, parseInt(indexParam, 10) || 1));
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const room = await Messaging.loadRoom(uid, {
|
|
538
|
+
uid,
|
|
539
|
+
roomId,
|
|
540
|
+
start,
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
if (!room) {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const [canViewInfo, canUploadImage, canUploadFile] = await privileges.global.can([
|
|
548
|
+
'view:users:info', 'upload:post:image', 'upload:post:file',
|
|
549
|
+
], uid);
|
|
550
|
+
|
|
551
|
+
room.title = room.roomName || room.usernames || '[[pages:chats]]';
|
|
552
|
+
room.bodyClasses = ['page-user-chats', 'chat-loaded'];
|
|
553
|
+
room.canViewInfo = canViewInfo;
|
|
554
|
+
room.canUpload = (canUploadImage || canUploadFile) && (meta.config.maximumFileSize > 0 || room.isAdmin);
|
|
555
|
+
room.scrollToIndex = scrollToIndex;
|
|
556
|
+
|
|
557
|
+
return room;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
async function getPrivateRoomCount() {
|
|
561
|
+
const [totalCount, publicCount] = await Promise.all([
|
|
562
|
+
db.sortedSetCard('chat:rooms'),
|
|
563
|
+
db.sortedSetCard('chat:rooms:public'),
|
|
564
|
+
]);
|
|
565
|
+
|
|
566
|
+
return Math.max(0, (parseInt(totalCount, 10) || 0) - (parseInt(publicCount, 10) || 0));
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async function getAdminRecentChats(uid, start, limit) {
|
|
570
|
+
let cursor = Math.max(0, parseInt(start, 10) || 0);
|
|
571
|
+
const roomPairs = [];
|
|
572
|
+
|
|
573
|
+
while (roomPairs.length < limit) {
|
|
574
|
+
const roomIds = await db.getSortedSetRevRange('chat:rooms', cursor, cursor + ADMIN_CHAT_SCAN_SIZE - 1);
|
|
575
|
+
if (!roomIds.length) {
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const rooms = await Messaging.getRoomsData(roomIds);
|
|
580
|
+
rooms.forEach((room, index) => {
|
|
581
|
+
if (room && !room.public && roomPairs.length < limit) {
|
|
582
|
+
roomPairs.push({
|
|
583
|
+
roomId: roomIds[index],
|
|
584
|
+
room,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
cursor += roomIds.length;
|
|
590
|
+
if (roomIds.length < ADMIN_CHAT_SCAN_SIZE) {
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const rooms = roomPairs.map(item => item.room);
|
|
596
|
+
const roomIds = roomPairs.map(item => item.roomId);
|
|
597
|
+
|
|
598
|
+
await enrichAdminRecentRooms(uid, roomIds, rooms);
|
|
599
|
+
|
|
600
|
+
return {
|
|
601
|
+
rooms,
|
|
602
|
+
nextStart: cursor,
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
async function enrichAdminRecentRooms(uid, roomIds, rooms) {
|
|
607
|
+
if (!roomIds.length) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const roomUsers = await Promise.all(roomIds.map(roomId => Messaging.getUidsInRoom(roomId, 0, -1)));
|
|
612
|
+
const uniqueUids = [...new Set(roomUsers.flat().filter(Boolean))];
|
|
613
|
+
const userMap = new Map();
|
|
614
|
+
|
|
615
|
+
if (uniqueUids.length) {
|
|
616
|
+
const users = await User.getUsersFields(uniqueUids, [
|
|
617
|
+
'uid', 'username', 'userslug', 'displayname', 'picture', 'status', 'lastonline',
|
|
618
|
+
]);
|
|
619
|
+
uniqueUids.forEach((memberUid, index) => {
|
|
620
|
+
const userData = users[index];
|
|
621
|
+
if (userData) {
|
|
622
|
+
userData.status = User.getStatus(userData);
|
|
623
|
+
userMap.set(String(memberUid), userData);
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const teasers = await Promise.all(roomIds.map(roomId => getAdminRoomTeaser(roomId)));
|
|
629
|
+
|
|
630
|
+
rooms.forEach((room, index) => {
|
|
631
|
+
if (!room) {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
room.users = (roomUsers[index] || [])
|
|
636
|
+
.map(memberUid => userMap.get(String(memberUid)))
|
|
637
|
+
.filter(Boolean);
|
|
638
|
+
room.groupChat = room.userCount > 2;
|
|
639
|
+
room.unread = false;
|
|
640
|
+
room.teaser = teasers[index];
|
|
641
|
+
room.lastUser = room.users[0];
|
|
642
|
+
room.usernames = Messaging.generateUsernames(room, uid);
|
|
643
|
+
room.participantsLabel = buildParticipantsLabel(room.users);
|
|
644
|
+
room.icon = Messaging.getRoomIcon(room);
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function buildParticipantsLabel(users) {
|
|
649
|
+
const list = Array.isArray(users) ? users.filter(Boolean) : [];
|
|
650
|
+
if (!list.length) {
|
|
651
|
+
return '';
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const names = list.map(user => user.displayname || user.username).filter(Boolean);
|
|
655
|
+
if (names.length <= 5) {
|
|
656
|
+
return names.join(', ');
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return `${names.slice(0, 5).join(', ')} +${names.length - 5}`;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
async function getAdminRoomTeaser(roomId) {
|
|
663
|
+
const mids = await db.getSortedSetRevRange(`chat:room:${roomId}:mids`, 0, 19);
|
|
664
|
+
if (!mids.length) {
|
|
665
|
+
return null;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const teaser = (await Messaging.getMessagesFields(mids, ['fromuid', 'content', 'timestamp', 'deleted', 'system']))
|
|
669
|
+
.find(message => message && !message.deleted && !message.system && message.fromuid);
|
|
670
|
+
|
|
671
|
+
if (!teaser) {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const teaserUser = await User.getUserFields(teaser.fromuid, [
|
|
676
|
+
'uid', 'username', 'userslug', 'displayname', 'picture', 'status', 'lastonline',
|
|
677
|
+
]);
|
|
678
|
+
|
|
679
|
+
if (teaserUser) {
|
|
680
|
+
teaser.user = teaserUser;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
teaser.content = String(teaser.content || '')
|
|
684
|
+
.replace(/<[^>]*>/g, '')
|
|
685
|
+
.trim();
|
|
686
|
+
teaser.roomId = roomId;
|
|
687
|
+
|
|
688
|
+
return teaser;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
async function canNonAdminModifyLockedRoom(uid, roomId) {
|
|
692
|
+
if (!uid || !roomId) {
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (await User.isAdministrator(uid)) {
|
|
697
|
+
return false;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return await isRoomLocked(roomId);
|
|
701
|
+
}
|
|
246
702
|
async function roomExists(roomId) {
|
|
247
703
|
const roomData = await db.getObject(`chat:room:${roomId}`);
|
|
248
704
|
return !!roomData;
|
|
@@ -256,7 +712,6 @@ async function lockRoom(roomId, actingUid) {
|
|
|
256
712
|
lockedAt: now,
|
|
257
713
|
});
|
|
258
714
|
|
|
259
|
-
await addLockSystemMessage(roomId, actingUid, true);
|
|
260
715
|
return await getRoomLockData(roomId);
|
|
261
716
|
}
|
|
262
717
|
|
|
@@ -267,29 +722,19 @@ async function unlockRoom(roomId, actingUid) {
|
|
|
267
722
|
lockedAt: 0,
|
|
268
723
|
});
|
|
269
724
|
|
|
270
|
-
await addLockSystemMessage(roomId, actingUid, false);
|
|
271
725
|
return await getRoomLockData(roomId);
|
|
272
726
|
}
|
|
273
727
|
|
|
274
|
-
async function addLockSystemMessage(roomId, actingUid, isLocked) {
|
|
275
|
-
if (typeof Messaging.addSystemMessage !== 'function') {
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const messageText = isLocked ? LOCK_PREFIX + ' ' + LOCKED_MESSAGE_TEXT : LOCK_PREFIX + ' ' + UNLOCKED_MESSAGE_TEXT;
|
|
280
|
-
|
|
281
|
-
try {
|
|
282
|
-
await Messaging.addSystemMessage(messageText, actingUid, roomId);
|
|
283
|
-
} catch (err) {
|
|
284
|
-
console.error('[Super Admin Chats] Error adding lock system message:', err);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
728
|
async function isRoomLocked(roomId) {
|
|
289
729
|
const field = await db.getObjectField(`chat:room:${roomId}:adminLock`, 'isLocked');
|
|
290
730
|
return field === '1' || field === 1 || field === true;
|
|
291
731
|
}
|
|
292
732
|
|
|
733
|
+
async function getMessageRoomId(messageId) {
|
|
734
|
+
const roomId = await db.getObjectField(`message:${messageId}`, 'roomId');
|
|
735
|
+
return parseInt(roomId, 10) || 0;
|
|
736
|
+
}
|
|
737
|
+
|
|
293
738
|
async function getRoomLockData(roomId) {
|
|
294
739
|
const data = await db.getObject(`chat:room:${roomId}:adminLock`) || {};
|
|
295
740
|
const lockedBy = parseInt(data.lockedBy, 10) || 0;
|
|
@@ -303,20 +748,25 @@ async function getRoomLockData(roomId) {
|
|
|
303
748
|
};
|
|
304
749
|
}
|
|
305
750
|
|
|
306
|
-
function
|
|
751
|
+
function ensureFirstVisibleMessageHeader(messages) {
|
|
307
752
|
const list = Array.isArray(messages) ? messages.slice() : [];
|
|
308
|
-
|
|
309
|
-
|
|
753
|
+
const firstUserMessageIndex = list.findIndex(msg => msg && !msg.system);
|
|
754
|
+
|
|
755
|
+
if (firstUserMessageIndex !== -1) {
|
|
756
|
+
list[firstUserMessageIndex].newSet = true;
|
|
310
757
|
}
|
|
311
758
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
759
|
+
return list;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function buildRoomMessagesWithLockNotice(messages, lockData) {
|
|
763
|
+
const cleanedMessages = stripAdminLockMessages(messages);
|
|
764
|
+
if (!lockData || !lockData.isLocked || !lockData.lockedAt) {
|
|
765
|
+
return cleanedMessages;
|
|
316
766
|
}
|
|
317
767
|
|
|
318
|
-
|
|
319
|
-
content
|
|
768
|
+
cleanedMessages.push({
|
|
769
|
+
content: `${LOCK_PREFIX} ${LOCKED_MESSAGE_TEXT}`,
|
|
320
770
|
fromuid: lockData.lockedBy,
|
|
321
771
|
uid: lockData.lockedBy,
|
|
322
772
|
timestamp: lockData.lockedAt,
|
|
@@ -325,13 +775,26 @@ function appendLockNotice(messages, lockData) {
|
|
|
325
775
|
newSet: true,
|
|
326
776
|
});
|
|
327
777
|
|
|
328
|
-
|
|
778
|
+
cleanedMessages.sort((left, right) => {
|
|
329
779
|
const leftTime = parseInt(left && (left.timestamp || left.datetime), 10) || 0;
|
|
330
780
|
const rightTime = parseInt(right && (right.timestamp || right.datetime), 10) || 0;
|
|
331
781
|
return leftTime - rightTime;
|
|
332
782
|
});
|
|
333
783
|
|
|
334
|
-
return
|
|
784
|
+
return cleanedMessages;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
function stripAdminLockMessages(messages) {
|
|
788
|
+
const list = Array.isArray(messages) ? messages.slice() : [];
|
|
789
|
+
return list.filter(msg => !isAdminLockMessage(msg));
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function isAdminLockMessage(msg) {
|
|
793
|
+
const content = String(msg && msg.content || '');
|
|
794
|
+
return content.includes('admin-chat-lock') ||
|
|
795
|
+
content.includes(LOCKED_MESSAGE_TEXT) ||
|
|
796
|
+
content.includes('נפתח מחדש ע"י המנהלים') ||
|
|
797
|
+
content.includes('modules:chat.system.[admin-chat-lock]');
|
|
335
798
|
}
|
|
336
799
|
|
|
337
800
|
function guessRoomIdUidFromArgs(args) {
|
|
@@ -370,3 +833,4 @@ function getRoomId(payload) {
|
|
|
370
833
|
}
|
|
371
834
|
|
|
372
835
|
module.exports = plugin;
|
|
836
|
+
|