From 12c0c2eff74423a6ebdcf0238344453eefd98724 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sun, 30 Nov 2025 11:18:54 +0330 Subject: [PATCH] chat --- src/IOC/ioc.config.ts | 7 +--- src/modules/chatbot/chatbot.gateway.ts | 27 ++++++++----- .../chatbot/models/chat-message.model.ts | 29 -------------- .../chatbot/models/chat-session.model.ts | 26 ------------ .../chatbot/providers/chatbot.service.ts | 15 ++++--- .../repositories/chat-message.repository.ts | 37 ----------------- .../repositories/chat-session.repository.ts | 40 ------------------- 7 files changed, 29 insertions(+), 152 deletions(-) delete mode 100644 src/modules/chatbot/models/chat-message.model.ts delete mode 100644 src/modules/chatbot/models/chat-session.model.ts delete mode 100644 src/modules/chatbot/repositories/chat-message.repository.ts delete mode 100644 src/modules/chatbot/repositories/chat-session.repository.ts diff --git a/src/IOC/ioc.config.ts b/src/IOC/ioc.config.ts index 0eb7b51..804b469 100644 --- a/src/IOC/ioc.config.ts +++ b/src/IOC/ioc.config.ts @@ -54,13 +54,11 @@ import { ChatService } from "../modules/chat/chat.service"; import { ChatRepo, createChatRepo } from "../modules/chat/repository/chat.repository"; import { ChatMessageRepo, createChatMessageRepo } from "../modules/chat/repository/message.repository"; import { WsAuthService } from "../modules/chat/wsAuth.service"; +import { ChatbotGateway } from "../modules/chatbot/chatbot.gateway"; import { ChatbotService } from "../modules/chatbot/providers/chatbot.service"; import { DataContextService } from "../modules/chatbot/providers/data-context.service"; import { LLMService } from "../modules/chatbot/providers/llm.service"; import { WebSocketAuthService } from "../modules/chatbot/providers/websocket-auth.service"; -import { ChatbotGateway } from "../modules/chatbot/chatbot.gateway"; -import { ChatSessionRepository, createChatSessionRepository } from "../modules/chatbot/repositories/chat-session.repository"; -import { ChatMessageRepository, createChatMessageRepository } from "../modules/chatbot/repositories/chat-message.repository"; import { ContactUsRepo, CreateContactUsRepo } from "../modules/contact-us/contactUs.repository"; import { ContactUsService } from "../modules/contact-us/contactUs.service"; import { CouponRepo, CouponUsageRepo, createCouponRepo, createCouponUsageRepo } from "../modules/Coupon/coupon.repository"; @@ -291,9 +289,6 @@ const containerModules = new AsyncContainerModule(async (bind) => { bind(IOCTYPES.AboutUsRepo).toDynamicValue(CreateAboutUsRepo).inSingletonScope(); bind(IOCTYPES.SiteSettingRepo).toDynamicValue(CreateSiteSettingRepo).inSingletonScope(); bind(IOCTYPES.NewsletterRepo).toDynamicValue(CreateNewsletterRepo).inSingletonScope(); - bind(IOCTYPES.ChatbotChatSessionRepository).toDynamicValue(createChatSessionRepository).inSingletonScope(); - bind(IOCTYPES.ChatbotChatMessageRepository).toDynamicValue(createChatMessageRepository).inSingletonScope(); - // #endregion }); export { containerModules }; diff --git a/src/modules/chatbot/chatbot.gateway.ts b/src/modules/chatbot/chatbot.gateway.ts index 09ab37f..fee9f52 100644 --- a/src/modules/chatbot/chatbot.gateway.ts +++ b/src/modules/chatbot/chatbot.gateway.ts @@ -279,12 +279,6 @@ export class ChatbotGateway { return; } - // The bot response will be generated asynchronously by the service - // We'll need to emit it when it's ready - // For now, we'll rely on the service's internal async generation - // In a production system, you might want to use Redis pub/sub or similar - // to notify when the bot response is ready - // Emit that bot is typing socket.emit(WEBSOCKET_EVENTS.BOT_RESPONSE_START, { status: "success", @@ -292,9 +286,24 @@ export class ChatbotGateway { timestamp: new Date().toISOString(), } as WebSocketResponse); - // Note: The actual bot response will be generated by ChatbotService.generateBotResponse - // which runs asynchronously. In a real implementation, you might want to use - // Redis pub/sub or a message queue to notify when the response is ready + // Generate bot response and wait for it + const botMessage = await this.chatbotService.generateBotResponse(sessionId, userMessageId, ulid); + + if (botMessage) { + // Emit bot response to the client + socket.emit(WEBSOCKET_EVENTS.BOT_RESPONSE, { + status: "success", + data: botMessage, + timestamp: new Date().toISOString(), + } as WebSocketResponse); + + // Also emit to others in the session (if any) + socket.to(`session_${sessionId}`).emit(WEBSOCKET_EVENTS.BOT_RESPONSE, { + status: "success", + data: botMessage, + timestamp: new Date().toISOString(), + } as WebSocketResponse); + } } catch (error) { this.logger.error("Error generating bot response", error); socket.emit(WEBSOCKET_EVENTS.BOT_RESPONSE, { diff --git a/src/modules/chatbot/models/chat-message.model.ts b/src/modules/chatbot/models/chat-message.model.ts deleted file mode 100644 index d954429..0000000 --- a/src/modules/chatbot/models/chat-message.model.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Schema, model } from "mongoose"; - -import { IChatMessage, MessageStatus, MessageType } from "./Abstraction/IChatMessage"; - -const chatMessageSchema = new Schema( - { - content: { type: String, required: true }, - type: { type: String, enum: MessageType, required: true }, - status: { type: String, enum: MessageStatus, default: MessageStatus.SENT }, - session: { type: Schema.Types.ObjectId, ref: "ChatSession", required: true }, - sender: { type: Schema.Types.ObjectId, ref: "User", default: null }, - metadata: { type: Schema.Types.Mixed, default: null }, - responseToId: { type: String, default: null }, - tokensUsed: { type: Number, default: 0 }, - }, - { - timestamps: true, - toJSON: { virtuals: true, versionKey: false }, - id: false, - }, -); - -chatMessageSchema.index({ session: 1, createdAt: -1 }); -chatMessageSchema.index({ session: 1, createdAt: 1 }); - -const ChatMessageModel = model("ChatbotMessage", chatMessageSchema); - -export { ChatMessageModel, MessageStatus, MessageType }; - diff --git a/src/modules/chatbot/models/chat-session.model.ts b/src/modules/chatbot/models/chat-session.model.ts deleted file mode 100644 index 5a90227..0000000 --- a/src/modules/chatbot/models/chat-session.model.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Schema, model } from "mongoose"; - -import { ChatSessionStatus, IChatSession } from "./Abstraction/IChatSession"; - -const chatSessionSchema = new Schema( - { - title: { type: String, required: true, maxlength: 255 }, - user: { type: Schema.Types.ObjectId, ref: "User", required: true }, - status: { type: String, enum: ChatSessionStatus, default: ChatSessionStatus.ACTIVE }, - context: { type: Schema.Types.Mixed, default: null }, - lastMessageAt: { type: Date, default: null }, - }, - { - timestamps: true, - toJSON: { virtuals: true, versionKey: false }, - id: false, - }, -); - -chatSessionSchema.index({ user: 1, lastMessageAt: -1 }); -chatSessionSchema.index({ status: 1 }); - -const ChatSessionModel = model("ChatSession", chatSessionSchema); - -export { ChatSessionModel, ChatSessionStatus }; - diff --git a/src/modules/chatbot/providers/chatbot.service.ts b/src/modules/chatbot/providers/chatbot.service.ts index 071c574..c7ad6d9 100644 --- a/src/modules/chatbot/providers/chatbot.service.ts +++ b/src/modules/chatbot/providers/chatbot.service.ts @@ -8,8 +8,8 @@ import { IOCTYPES } from "../../../IOC/ioc.types"; import { CacheService } from "../../../utils/cache.service"; import { SendMessageDto } from "../DTO/send-message.dto"; import { IChatContext } from "../interfaces/chatbot.interface"; -import { MessageStatus, MessageType } from "../models/chat-message.model"; -import { ChatSessionStatus } from "../models/chat-session.model"; +import { MessageStatus, MessageType } from "../models/Abstraction/IChatMessage"; +import { ChatSessionStatus } from "../models/Abstraction/IChatSession"; interface CachedSession { id: string; @@ -170,8 +170,7 @@ export class ChatbotService { session.lastMessageAt = now; await this.cacheService.setAsync(sessionKey, JSON.stringify(session), this.CACHE_TTL_SECONDS); - // Generate bot response asynchronously using OpenAI service - this.generateBotResponse(sendDto.sessionId, messageId, ulid); + // Note: Bot response will be generated by the gateway after emitting user message return this.mapMessageToDto(userMessage); } @@ -240,7 +239,11 @@ export class ChatbotService { }; } - private async generateBotResponse(sessionId: string, userMessageId: string, ulid: string): Promise { + async generateBotResponse( + sessionId: string, + userMessageId: string, + ulid: string, + ): Promise | null> { try { // Get messages from cache const messagesKey = this.getMessagesCacheKey(sessionId); @@ -316,6 +319,7 @@ export class ChatbotService { await this.cacheService.setAsync(sessionKey, JSON.stringify(session), this.CACHE_TTL_SECONDS); this.logger.info(`Generated bot response for session ${sessionId}`); + return this.mapMessageToDto(botMessage); } catch (error) { this.logger.error(`Failed to generate bot response for session ${sessionId}`, error); @@ -339,6 +343,7 @@ export class ChatbotService { messages.push(errorMessage); await this.cacheService.setAsync(messagesKey, JSON.stringify(messages), this.CACHE_TTL_SECONDS); + return this.mapMessageToDto(errorMessage); } } diff --git a/src/modules/chatbot/repositories/chat-message.repository.ts b/src/modules/chatbot/repositories/chat-message.repository.ts deleted file mode 100644 index 3540430..0000000 --- a/src/modules/chatbot/repositories/chat-message.repository.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { BaseRepository } from "../../../common/base/repository"; -import { ChatMessageModel } from "../models/chat-message.model"; -import { IChatMessage, MessageStatus } from "../models/Abstraction/IChatMessage"; - -class ChatMessageRepository extends BaseRepository { - constructor() { - super(ChatMessageModel); - } - - async findBySessionWithPagination(sessionId: string, offset = 0, limit = 50) { - return this.model - .find({ session: sessionId }) - .populate("sender") - .sort({ createdAt: 1 }) - .skip(offset) - .limit(limit) - .exec(); - } - - async getConversationHistory(sessionId: string, limit = 20) { - return this.model - .find({ session: sessionId }) - .sort({ createdAt: -1 }) - .limit(limit) - .exec(); - } - - async markAsRead(messageIds: string[]) { - return this.model.updateMany({ _id: { $in: messageIds } }, { status: MessageStatus.READ }); - } -} - -function createChatMessageRepository(): ChatMessageRepository { - return new ChatMessageRepository(); -} - -export { ChatMessageRepository, createChatMessageRepository }; diff --git a/src/modules/chatbot/repositories/chat-session.repository.ts b/src/modules/chatbot/repositories/chat-session.repository.ts deleted file mode 100644 index 83fb807..0000000 --- a/src/modules/chatbot/repositories/chat-session.repository.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { BaseRepository } from "../../../common/base/repository"; -import { ChatSessionModel, ChatSessionStatus } from "../models/chat-session.model"; -import { IChatSession } from "../models/Abstraction/IChatSession"; - -class ChatSessionRepository extends BaseRepository { - constructor() { - super(ChatSessionModel); - } - - async findByUserWithMessages(userId: string, limit = 10) { - return this.model - .find({ user: userId }) - .populate("messages") - .sort({ lastMessageAt: -1 }) - .limit(limit) - .exec(); - } - - async findActiveByUser(userId: string) { - return this.model - .find({ user: userId, status: ChatSessionStatus.ACTIVE }) - .populate("messages") - .sort({ lastMessageAt: -1 }) - .exec(); - } - - async updateLastMessageTime(sessionId: string) { - return this.model.findByIdAndUpdate(sessionId, { lastMessageAt: new Date() }, { new: true }); - } - - async findByUserAndId(sessionId: string, userId: string) { - return this.model.findOne({ _id: sessionId, user: userId }).populate("messages").populate("messages.sender").exec(); - } -} - -function createChatSessionRepository(): ChatSessionRepository { - return new ChatSessionRepository(); -} - -export { ChatSessionRepository, createChatSessionRepository };