skailan-conversations
Version:
Servicio de conversaciones y mensajería para Skailan
195 lines • 8.31 kB
JavaScript
import { Message } from "./domain/entities/Message";
import { Conversation } from "./domain/entities/Conversation";
import { MessagePrismaRepository } from "./infra/database/prisma/MessagePrismaRepository";
import { ConversationPrismaRepository } from "./infra/database/prisma/ConversationPrismaRepository";
import { PrismaClient } from "@prisma/client";
import { v4 as uuidv4 } from "uuid";
export class TelegramWebhookHandler {
messageRepository;
conversationRepository;
constructor() {
// Inicializar repositorios con PrismaClient
const prisma = new PrismaClient();
this.messageRepository = new MessagePrismaRepository(prisma);
this.conversationRepository = new ConversationPrismaRepository(prisma);
}
async handleWebhook(req, res) {
try {
console.log("📥 Telegram webhook recibido:", JSON.stringify(req.body, null, 2));
const update = req.body;
const organizationId = req.organization?.id;
if (!organizationId) {
console.error("❌ Organization ID no encontrado en el request");
res.status(400).json({ error: "Organization ID not found" });
return;
}
if (update.message) {
await this.handleMessage(update.message, organizationId);
}
else if (update.callback_query) {
await this.handleCallbackQuery(update.callback_query, organizationId);
}
// Telegram requiere una respuesta 200 OK
res.status(200).json({ ok: true });
}
catch (error) {
console.error("❌ Error en webhook de Telegram:", error);
res.status(500).json({ error: "Internal server error" });
}
}
async handleMessage(message, organizationId) {
if (!message)
return;
const { from, chat, text, message_id, photo, document, reply_to_message } = message;
console.log(`📱 Mensaje recibido de ${from.first_name} (@${from.username}): "${text}"`);
console.log(` - Chat ID: ${chat.id}`);
console.log(` - Message ID: ${message_id}`);
console.log(` - Fecha: ${new Date(message.date * 1000).toLocaleString()}`);
try {
// Buscar o crear conversación
const conversation = await this.findOrCreateConversation(chat.id, from, organizationId);
// Determinar tipo de contenido
const contentType = this.determineContentType(message);
const content = this.extractContent(message);
// Crear mensaje en la base de datos
const telegramMessage = new Message(uuidv4(), organizationId, conversation.id, "user", // senderType
`telegram_user_${from.id}`, // senderId
contentType, // type
content, // content
photo?.[0]?.file_id || document?.file_id || null, // mediaUrl
message_id.toString(), // externalMessageId
"delivered", // status
new Date(message.date * 1000), // timestamp
new Date() // createdAt
);
// Guardar mensaje
const savedMessage = await this.messageRepository.save(telegramMessage);
// Actualizar timestamp de última actividad de la conversación
await this.updateConversationLastMessage(conversation.id, organizationId);
console.log(`✅ Mensaje procesado y guardado: ${savedMessage.id}`);
}
catch (error) {
console.error("❌ Error procesando mensaje de Telegram:", error);
}
}
async handleCallbackQuery(callbackQuery, organizationId) {
if (!callbackQuery)
return;
const { from, data, message } = callbackQuery;
console.log(`🔘 Callback query recibido de ${from.first_name}: "${data}"`);
console.log(` - Chat ID: ${message.chat.id}`);
console.log(` - Message ID: ${message.message_id}`);
try {
// Buscar conversación
const conversation = await this.findConversationByChatId(message.chat.id, organizationId);
if (!conversation) {
console.error(`❌ Conversación no encontrada para chat ${message.chat.id}`);
return;
}
// Crear mensaje de callback query
const callbackMessage = new Message(uuidv4(), organizationId, conversation.id, "user", `telegram_user_${from.id}`, "callback_query", JSON.stringify({
type: "callback_query",
data: data,
originalMessageId: message.message_id,
}), null, // mediaUrl
callbackQuery.id, // externalMessageId
"delivered", new Date(), new Date());
// Guardar mensaje
await this.messageRepository.save(callbackMessage);
console.log(`✅ Callback query procesado: ID ${callbackQuery.id}`);
}
catch (error) {
console.error("❌ Error procesando callback query:", error);
}
}
/**
* Busca o crea una conversación para el chat
*/
async findOrCreateConversation(chatId, user, organizationId) {
// Buscar conversación existente
let conversation = await this.findConversationByChatId(chatId, organizationId);
if (!conversation) {
// Crear nueva conversación
conversation = new Conversation(uuidv4(), organizationId, `telegram_chat_${chatId}`, `telegram_user_${user.id}`, "open", null, // assignedToUserId
new Date(), // lastMessageAt
new Date(), // createdAt
new Date() // updatedAt
);
conversation = await this.conversationRepository.save(conversation);
console.log(`✅ Nueva conversación creada: ${conversation.id}`);
}
return conversation;
}
/**
* Busca conversación por chatId
*/
async findConversationByChatId(chatId, organizationId) {
// Implementar búsqueda por metadata
// Por ahora retornamos null para crear nueva conversación
return null;
}
/**
* Determina el tipo de contenido del mensaje
*/
determineContentType(message) {
if (message?.photo && message.photo.length > 0) {
return "image";
}
if (message?.document) {
return "document";
}
if (message?.text) {
return "text";
}
return "text";
}
/**
* Extrae el contenido del mensaje
*/
extractContent(message) {
if (message?.text) {
return message.text;
}
if (message?.photo && message.photo.length > 0) {
return JSON.stringify({
type: "image",
fileId: message.photo[0]?.file_id,
width: message.photo[0]?.width,
height: message.photo[0]?.height,
fileSize: message.photo[0]?.file_size,
});
}
if (message?.document) {
return JSON.stringify({
type: "document",
fileId: message.document.file_id,
fileName: message.document.file_name,
mimeType: message.document.mime_type,
fileSize: message.document.file_size,
});
}
return "";
}
/**
* Actualiza el timestamp de última actividad de la conversación
*/
async updateConversationLastMessage(conversationId, organizationId) {
try {
const conversation = await this.conversationRepository.findById(conversationId, organizationId);
if (conversation) {
conversation.updatedAt = new Date();
await this.conversationRepository.save(conversation);
}
}
catch (error) {
console.error("❌ Error actualizando timestamp de conversación:", error);
}
}
}
// Crear una instancia del manejador
const telegramWebhookHandler = new TelegramWebhookHandler();
// Exportar la función para usar en Express
export const handleTelegramWebhook = (req, res) => {
return telegramWebhookHandler.handleWebhook(req, res);
};
//# sourceMappingURL=telegram-webhook.js.map