UNPKG

skailan-conversations

Version:

Servicio de conversaciones y mensajería para Skailan

274 lines 11.8 kB
import { MessageDisplayService, } from "../../domain/services/MessageDisplayService"; import { TelegramSenderService } from "../../domain/services/TelegramSenderService"; import { TelegramService } from "../../domain/services/TelegramService"; import { MessagePrismaRepository } from "../../infra/database/prisma/MessagePrismaRepository"; import { ConversationPrismaRepository } from "../../infra/database/prisma/ConversationPrismaRepository"; import { PrismaClient } from "@prisma/client"; export class MessageController { messageDisplayService; messageRepository; conversationRepository; telegramSenderService; constructor() { const prisma = new PrismaClient(); this.messageDisplayService = new MessageDisplayService(); this.messageRepository = new MessagePrismaRepository(prisma); this.conversationRepository = new ConversationPrismaRepository(prisma); // Crear instancia de TelegramService (necesitamos un bot token) const telegramService = new TelegramService(process.env.TELEGRAM_BOT_TOKEN || ""); this.telegramSenderService = new TelegramSenderService(telegramService, this.messageRepository, this.conversationRepository); } /** * Obtiene mensajes de una conversación con formato para visualización */ async getMessages(req, res) { try { const { conversationId } = req.params; const { page = 1, limit = 50 } = req.query; const organizationId = req.organization?.id; if (!organizationId) { res.status(400).json({ error: "Organization ID not found" }); return; } if (!conversationId) { res.status(400).json({ error: "Conversation ID is required" }); return; } // Verificar que la conversación existe y pertenece a la organización const conversation = await this.conversationRepository.findById(conversationId, organizationId); if (!conversation) { res.status(404).json({ error: "Conversation not found" }); return; } // Obtener mensajes paginados const offset = (Number(page) - 1) * Number(limit); const messages = await this.messageRepository.findByConversationId(conversationId, organizationId, Number(limit), offset); // Formatear mensajes para visualización const formattedMessages = messages.map((message) => this.messageDisplayService.formatMessageForDisplay(message)); // Obtener total de mensajes para paginación const totalMessages = await this.messageRepository.countByConversationId(conversationId, organizationId); res.json({ messages: formattedMessages, pagination: { page: Number(page), limit: Number(limit), total: totalMessages, totalPages: Math.ceil(totalMessages / Number(limit)), }, }); } catch (error) { console.error("❌ Error obteniendo mensajes:", error); res.status(500).json({ error: "Internal server error" }); } } /** * Envía un mensaje a través del canal correspondiente */ async sendMessage(req, res) { try { const sendRequest = req.body; const organizationId = req.organization?.id; if (!organizationId) { res.status(400).json({ error: "Organization ID not found" }); return; } // Verificar que la conversación existe y pertenece a la organización const conversation = await this.conversationRepository.findById(sendRequest.conversationId, organizationId); if (!conversation) { res.status(404).json({ error: "Conversation not found" }); return; } // Determinar el tipo de canal y enviar el mensaje const channelType = this.determineChannelType(conversation); if (channelType === "telegram") { await this.sendTelegramMessage(conversation, sendRequest, organizationId, res); } else { res .status(400) .json({ error: `Unsupported channel type: ${channelType}` }); } } catch (error) { console.error("❌ Error enviando mensaje:", error); res.status(500).json({ error: "Internal server error" }); } } /** * Envía mensaje específicamente a Telegram */ async sendTelegramMessage(conversation, sendRequest, organizationId, res) { try { // Obtener configuración del canal de Telegram const channelConfig = await this.getTelegramChannelConfig(organizationId); if (!channelConfig) { res.status(400).json({ error: "Telegram channel not configured" }); return; } // Extraer chat ID de la conversación const chatId = this.extractChatIdFromConversation(conversation); if (!chatId) { res.status(400).json({ error: "Invalid chat ID in conversation" }); return; } // Crear instancia del servicio de Telegram const telegramService = new TelegramService(channelConfig.botToken); // Enviar mensaje const result = await telegramService.sendMessage(chatId, sendRequest.content, { parse_mode: sendRequest.options?.parse_mode, reply_to_message_id: sendRequest.options?.reply_to_message_id, reply_markup: sendRequest.options?.inline_keyboard ? { inline_keyboard: sendRequest.options.inline_keyboard, } : undefined, }); if (result.success) { // Guardar mensaje en la base de datos const message = await this.messageRepository.create({ organizationId, conversationId: sendRequest.conversationId, senderType: "user", senderId: "system", type: sendRequest.type || "text", content: sendRequest.content, status: "sent", externalMessageId: result.messageId?.toString(), }); res.json({ success: true, message: "Message sent successfully", data: message, }); } else { res.status(400).json({ success: false, error: result.error || "Failed to send message", }); } } catch (error) { console.error("❌ Error enviando mensaje a Telegram:", error); res.status(500).json({ error: "Internal server error" }); } } /** * Marca mensajes como leídos */ async markMessagesAsRead(req, res) { try { const { conversationId } = req.params; const { messageIds } = req.body; const organizationId = req.organization?.id; if (!organizationId) { res.status(400).json({ error: "Organization ID not found" }); return; } if (!conversationId) { res.status(400).json({ error: "Conversation ID is required" }); return; } if (!Array.isArray(messageIds)) { res.status(400).json({ error: "Message IDs array is required" }); return; } // Verificar que la conversación existe y pertenece a la organización const conversation = await this.conversationRepository.findById(conversationId, organizationId); if (!conversation) { res.status(404).json({ error: "Conversation not found" }); return; } // Marcar mensajes como leídos const updatedMessages = await Promise.all(messageIds.map(async (messageId) => { return await this.messageRepository.update(messageId, { status: "read", }); })); res.json({ success: true, message: "Messages marked as read", data: updatedMessages, }); } catch (error) { console.error("❌ Error marcando mensajes como leídos:", error); res.status(500).json({ error: "Internal server error" }); } } /** * Obtiene estadísticas de mensajes de una conversación */ async getMessageStats(req, res) { try { const { conversationId } = req.params; const organizationId = req.organization?.id; if (!organizationId) { res.status(400).json({ error: "Organization ID not found" }); return; } if (!conversationId) { res.status(400).json({ error: "Conversation ID is required" }); return; } // Verificar que la conversación existe y pertenece a la organización const conversation = await this.conversationRepository.findById(conversationId, organizationId); if (!conversation) { res.status(404).json({ error: "Conversation not found" }); return; } // Obtener estadísticas const totalMessages = await this.messageRepository.countByConversationId(conversationId, organizationId); const unreadMessages = await this.messageRepository.countByConversationIdAndStatus(conversationId, organizationId, "unread"); const todayMessages = await this.messageRepository.countByConversationIdAndDate(conversationId, organizationId, new Date()); res.json({ success: true, stats: { total: totalMessages, unread: unreadMessages, today: todayMessages, }, }); } catch (error) { console.error("❌ Error obteniendo estadísticas:", error); res.status(500).json({ error: "Internal server error" }); } } /** * Determina el tipo de canal basado en la conversación */ determineChannelType(conversation) { // Lógica para determinar el tipo de canal // Por ahora, asumimos que es Telegram si no hay información específica return conversation.channelType || "telegram"; } /** * Obtiene la configuración del canal de Telegram */ async getTelegramChannelConfig(organizationId) { // Aquí deberías obtener la configuración desde la base de datos // Por ahora, retornamos una configuración de ejemplo return { botToken: process.env.TELEGRAM_BOT_TOKEN, webhookUrl: process.env.TELEGRAM_WEBHOOK_URL, }; } /** * Extrae el chat ID de la conversación */ extractChatIdFromConversation(conversation) { try { // Asumimos que el chat ID está almacenado en metadata const metadata = conversation.metadata ? JSON.parse(conversation.metadata) : {}; return metadata.chatId || null; } catch { return null; } } } //# sourceMappingURL=messageController.js.map