Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions docs/api/chat.swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions docs/api/sockets.swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/chatController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
14 changes: 9 additions & 5 deletions src/services/chatService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}))
);

Expand Down
37 changes: 11 additions & 26 deletions src/sockets/chats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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;
Expand Down
163 changes: 62 additions & 101 deletions src/sockets/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@ 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,
ack: Function,
senderId: string
) => {
let { media, content, contentType, parentMessageId } = data;
const { chatId, chatType, isReply, isForward, isAnnouncement } = data;
const { chatId, chatType, isReply, isForward } = data;

if (
(!isForward &&
Expand All @@ -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) {
Expand All @@ -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') {
Expand All @@ -84,30 +92,19 @@ 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,
};
enableDestruction(socket, message, chatId);
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({
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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)
);
}

Expand Down