From 9567ac6d306833a18e523e25c5b716d9c28b09c2 Mon Sep 17 00:00:00 2001 From: AhmedHamed3699 Date: Sat, 21 Dec 2024 19:34:01 +0200 Subject: [PATCH 1/2] feat(messages): added mentions --- docs/api/chat.swagger.ts | 20 ++++++++++++----- docs/api/sockets.swagger.ts | 3 +++ src/controllers/chatController.ts | 2 +- src/services/chatService.ts | 14 +++++++----- src/sockets/chats.ts | 37 +++++++++---------------------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/docs/api/chat.swagger.ts b/docs/api/chat.swagger.ts index d397fd8..177256e 100644 --- a/docs/api/chat.swagger.ts +++ b/docs/api/chat.swagger.ts @@ -124,6 +124,20 @@ * items: * type: string * description: IDs of users blocked by this member. + * unreadMessages: + * type: array + * items: + * type: object + * properties: + * chatId: + * type: string + * description: Unique identifier of the chat this message belongs to. + * unreadMessagesCount: + * type: string + * description: Number of unread messages in the chat. + * isMentioned: + * type: boolean + * description: Indicates if the user is mentioned in the chat. * lastMessages: * type: array * items: @@ -171,12 +185,6 @@ * type: string * format: date-time * description: Timestamp when the message was sent. - * __v: - * type: integer - * description: Version key. - * id: - * type: string - * description: Alias for `_id`. * 401: * description: User is not logged in or the request is invalid. * content: diff --git a/docs/api/sockets.swagger.ts b/docs/api/sockets.swagger.ts index 785f66b..b031547 100644 --- a/docs/api/sockets.swagger.ts +++ b/docs/api/sockets.swagger.ts @@ -45,6 +45,9 @@ * isForward: * type: boolean * description: Indicates if the message is forwarded. + * isAnouncement: + * type: boolean + * description: Indicates if the message is an anouncement. * responses: * 200: * description: Message sent successfully. diff --git a/src/controllers/chatController.ts b/src/controllers/chatController.ts index 2fbbbdb..61fd401 100644 --- a/src/controllers/chatController.ts +++ b/src/controllers/chatController.ts @@ -45,7 +45,7 @@ export const getAllChats = catchAsync( 'username screenFirstName screenLastName phoneNumber photo status isAdmin stories blockedUsers' ), getLastMessage(allChats), - getUnreadMessages(allChats, userId), + getUnreadMessages(allChats, user), ]); res.status(200).json({ diff --git a/src/services/chatService.ts b/src/services/chatService.ts index cc92021..f3405f7 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -25,17 +25,21 @@ export const getLastMessage = async (chats: any) => { return lastMessages; }; -export const getUnreadMessages = async ( - chats: any, - userId: mongoose.Types.ObjectId -) => +export const getUnreadMessages = async (chats: any, user: any) => Promise.all( chats.map(async (chat: any) => ({ chatId: chat.chat._id, unreadMessagesCount: await Message.countDocuments({ chatId: chat.chat._id, - readBy: { $nin: [userId] }, + readBy: { $nin: [user._id] }, }), + isMentioned: + (await Message.exists({ + chatId: chat.chat._id, + readBy: { $nin: [user._id] }, + senderId: { $ne: user._id }, + content: new RegExp(`@${user.username}`, 'i'), + })) !== null, })) ); diff --git a/src/sockets/chats.ts b/src/sockets/chats.ts index d8f661f..1faad14 100644 --- a/src/sockets/chats.ts +++ b/src/sockets/chats.ts @@ -121,7 +121,7 @@ const handleAddMembers = async ( chat?.members.push({ user: userId, Role: 'member' }); const userWasMember = user.chats.some((c: any) => c.chat.equals(chatId)); if (!userWasMember) - User.findByIdAndUpdate( + await User.findByIdAndUpdate( userId, { $push: { chats: { chat: chatId } } }, { new: true } @@ -263,35 +263,20 @@ const handleDeleteGroupChannel = async ( senderId: any ) => { const { chatId } = data; - const chat = await Chat.findById(chatId); - if (!chat || chat.isDeleted) - return ack({ - success: false, - message: 'Could not delete the group', - error: 'no chat found with the provided id', - }); + const chat = await GroupChannel.findById(chatId); + const func = await check(chat, ack, senderId, { + chatType: ['group', 'channel'], + checkAdmin: true, + }); + if (!func) return func; - const chatMembers = chat.members; - const isCreator = chatMembers.some( - (member) => member.user.toString() === senderId && member.Role === 'admin' + await User.updateMany( + { _id: { $in: chat.members.map((m: any) => m.user) } }, + { $pull: { chats: { chat: chatId } } } ); - if (!isCreator) - return ack({ - success: false, - message: 'Could not delete the group', - error: 'you are not authorized to delete the group', - }); - - chatMembers.map(async (member: any) => { - await informSessions( - io, - member.user, - { chatId }, - 'DELETE_GROUP_CHANNEL_SERVER' - ); - }); + socket.to(chatId).emit('DELETE_GROUP_CHANNEL_SERVER', { chatId }); chat.members = []; chat.isDeleted = true; From 9b630c418315bd9e57d18af11cc7b94a63c2beee Mon Sep 17 00:00:00 2001 From: Peter Safwat <118059454+PeterSafwatHBK@users.noreply.github.com> Date: Sat, 21 Dec 2024 19:44:43 +0200 Subject: [PATCH 2/2] Update messages.ts --- src/sockets/messages.ts | 163 +++++++++++++++------------------------- 1 file changed, 62 insertions(+), 101 deletions(-) diff --git a/src/sockets/messages.ts b/src/sockets/messages.ts index 9431798..8164c3a 100644 --- a/src/sockets/messages.ts +++ b/src/sockets/messages.ts @@ -4,14 +4,16 @@ import IMessage from '@base/types/message'; import Message from '@models/messageModel'; import { enableDestruction } from '@services/chatService'; import Chat from '@base/models/chatModel'; -import { check, informSessions, updateDraft } from './MessagingServices'; - +import { check, updateDraft } from './MessagingServices'; +import detectInappropriateContent from '@services/googleAIService'; +import GroupChannel from '@base/models/groupChannelModel'; +import { group } from 'console'; interface PinUnPinMessageData { chatId: string | Types.ObjectId; messageId: string | Types.ObjectId; } -const handleMessaging = async ( +export const handleMessaging = async ( io: any, socket: Socket, data: any, @@ -19,7 +21,7 @@ const handleMessaging = async ( senderId: string ) => { let { media, content, contentType, parentMessageId } = data; - const { chatId, chatType, isReply, isForward, isAnnouncement } = data; + const { chatId, chatType, isReply, isForward } = data; if ( (!isForward && @@ -44,10 +46,8 @@ const handleMessaging = async ( const chat = await Chat.findById(chatId); const func = await check(chat, ack, senderId, { newMessageIsReply: isReply, - content, - sendMessage: true, }); - if (!func) return; + if (func) return func; let parentMessage; if (isForward || isReply) { @@ -64,18 +64,26 @@ const handleMessaging = async ( parentMessageId = undefined; } } - + let isAppropriate = true; + console.log(chatType, chatId) + const g= await GroupChannel.findById(chatId); + console.log(g); + console.log(g.isFilterd); + if ((chatType === 'group' || chatType==='channel') && g.isFilterd===true) { + console.log("innnnnnnnnnnnnn") + isAppropriate = await detectInappropriateContent(content); + } const message = new Message({ - media, content, contentType, isForward, senderId, chatId, parentMessageId, - isAnnouncement, + isAppropriate, // Set the isAppropriate property based on the content check }); - + + console.log(message); await message.save(); if (parentMessage && isReply && chatType === 'channel') { @@ -84,22 +92,7 @@ const handleMessaging = async ( } await updateDraft(io, senderId, chatId, ''); - socket.to(chatId).emit('RECEIVE_MESSAGE', message, async (res: any) => { - if (res.success && res.userId !== senderId) { - if (res.isRead && !message.readBy.includes(res.userId)) { - message.readBy.push(res.userId); - } else if (!message.deliveredTo.includes(res.userId)) { - message.deliveredTo.push(res.userId); - } - message.save(); - informSessions( - io, - senderId, - message, - res.isRead ? 'MESSAGE_READ_SERVER' : 'MESSAGE_DELIVERED' - ); - } - }); + socket.to(chatId).emit('RECEIVE_MESSAGE', message); const res = { messageId: message._id, }; @@ -107,7 +100,11 @@ const handleMessaging = async ( ack({ success: true, message: 'Message sent successfully', res }); }; -const handleEditMessage = async (socket: Socket, data: any, ack: Function) => { +export const handleEditMessage = async ( + socket: Socket, + data: any, + ack: Function +) => { const { messageId, content, chatId } = data; if (!messageId || !content) return ack({ @@ -140,7 +137,7 @@ const handleEditMessage = async (socket: Socket, data: any, ack: Function) => { }); }; -const handleDeleteMessage = async ( +export const handleDeleteMessage = async ( socket: Socket, data: any, ack: Function @@ -159,77 +156,47 @@ const handleDeleteMessage = async ( message: 'Failed to delete the message', error: 'no message found with the provided id', }); - socket.to(chatId).emit('DELETE_MESSAGE_SERVER', message); + socket.to(chatId).emit('DELETE_MESSAGE_SERVER', messageId); ack({ success: true, message: 'Message deleted successfully' }); }; -const handleReadMessage = async ( - io: Server, - socket: Socket, - data: any, - ack: Function, - userId: string -) => { - const { chatId } = data; - const messages = await Message.find({ - chatId, - senderId: { $ne: userId }, - readBy: { $nin: [userId] }, - }); - if (!messages) - return ack({ - success: true, - message: 'No messages to read', - }); - messages.forEach(async (message: IMessage) => { - message.deliveredTo = message.deliveredTo.filter( - (id) => id.toString() !== userId - ); - message.readBy.push(new Types.ObjectId(userId)); - message.save(); - informSessions( - io, - message.senderId.toString(), - message, - 'MESSAGE_READ_SERVER' - ); - }); - ack({ success: true, message: 'Message read successfully' }); -}; +async function handlePinMessage(socket: Socket, data: PinUnPinMessageData) { + try { + // Make a message pinned + const message = await Message.findById(data.messageId); + if (!message) { + //TODO: Make a global socket event for the client to send errors to + return; + } -const handlePinMessage = async ( - socket: Socket, - data: PinUnPinMessageData, - ack: Function -) => { - const message = await Message.findById(data.messageId); - if (!message) { - return ack({ success: false, message: 'Failed to pin message' }); + message.isPinned = true; + await message.save(); + + // Send an event to all online chat users to pin a message. + socket.to(data.chatId.toString()).emit('PIN_MESSAGE_SERVER', data); + } catch (err) { + //TODO: Make a global socket event for the client to send errors to } +} - message.isPinned = true; - await message.save(); +async function handleUnPinMessage(socket: Socket, data: PinUnPinMessageData) { + try { + // Make a message unpinned + const message = await Message.findById(data.messageId); + if (!message) { + //TODO: Make a global socket event for the client to send errors to + return; + } - socket.to(data.chatId.toString()).emit('PIN_MESSAGE_SERVER', data); - ack({ success: true, message: 'Message pinned successfully' }); -}; + message.isPinned = false; + await message.save(); -const handleUnPinMessage = async ( - socket: Socket, - data: PinUnPinMessageData, - ack: Function -) => { - const message = await Message.findById(data.messageId); - if (!message) { - return ack({ success: false, message: 'Failed to unpin message' }); + // Send an event to all online chat users to unpin a message. + socket.to(data.chatId.toString()).emit('UNPIN_MESSAGE_SERVER', data); + } catch (err) { + //TODO: Make a global socket event for the client to send errors to } - - message.isPinned = false; - await message.save(); - - socket.to(data.chatId.toString()).emit('UNPIN_MESSAGE_SERVER', data); - ack({ success: true, message: 'Message unpinned successfully' }); -}; +} async function registerMessagesHandlers( io: Server, @@ -248,18 +215,12 @@ async function registerMessagesHandlers( handleDeleteMessage(socket, data, ack) ); - socket.on('MESSAGE_READ_CLIENT', (data: any, ack: Function) => { - handleReadMessage(io, socket, data, ack, userId); - }); - - socket.on('PIN_MESSAGE_CLIENT', (data: PinUnPinMessageData, ack: Function) => - handlePinMessage(socket, data, ack) + socket.on('PIN_MESSAGE_CLIENT', (data: PinUnPinMessageData) => + handlePinMessage(socket, data) ); - socket.on( - 'UNPIN_MESSAGE_CLIENT', - (data: PinUnPinMessageData, ack: Function) => - handleUnPinMessage(socket, data, ack) + socket.on('UNPIN_MESSAGE_CLIENT', (data: PinUnPinMessageData) => + handleUnPinMessage(socket, data) ); }