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/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
- const UNLOCKED_MESSAGE_TEXT = '🔓 חדר זה נפתח מחדש ע"י המנהלים.';
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
- console.error('[Super Admin Chats] Error adding profile link:', e);
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
- room.adminChatLock = lockData;
215
+ const isLockedForUser = lockData.isLocked && !isAdmin;
108
216
 
109
- if (lockData.isLocked) {
110
- room.messages = appendLockNotice(room.messages, lockData);
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 = appendLockNotice(messages.reverse(), lockData);
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
- msg.newSet = parseInt(msg.fromuid, 10) !== parseInt(prevMsg.fromuid, 10);
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 appendLockNotice(messages, lockData) {
751
+ function ensureFirstVisibleMessageHeader(messages) {
307
752
  const list = Array.isArray(messages) ? messages.slice() : [];
308
- if (!lockData || !lockData.isLocked || !lockData.lockedAt) {
309
- return list;
753
+ const firstUserMessageIndex = list.findIndex(msg => msg && !msg.system);
754
+
755
+ if (firstUserMessageIndex !== -1) {
756
+ list[firstUserMessageIndex].newSet = true;
310
757
  }
311
758
 
312
- const content = `${LOCK_PREFIX} ${LOCKED_MESSAGE_TEXT}`;
313
- const exists = list.some(msg => msg && msg.content === content);
314
- if (exists) {
315
- return list;
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
- list.push({
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
- list.sort((left, right) => {
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 list;
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
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-admin-chats",
3
- "version": "1.1.0",
3
+ "version": "2.1.0",
4
4
  "description": "A plugin that allows the administrator to view and manage user chats.",
5
5
  "main": "library.js",
6
6
  "dependencies": {