skailan-conversations
Version:
Servicio de conversaciones y mensajería para Skailan
274 lines • 11.8 kB
JavaScript
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