UNPKG

whatsapp-crm-common

Version:

Componentes compartidos para servicios de WhatsApp CRM - Common utilities and types for WhatsApp CRM system

303 lines 13.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChatRepository = void 0; const baileys_1 = require("@whiskeysockets/baileys"); const base_repository_1 = __importDefault(require("./base-repository")); const repository_errors_1 = require("../errors/repository-errors"); const logger_1 = __importDefault(require("../utils/logger")); class ChatRepository extends base_repository_1.default { /** * Guarda múltiples chats en la base de datos en una sola operación */ async guardarChatsBulk(client, chats, tenantId, agentId) { if (chats.length === 0) return; let valuesSql = []; let paramCounter = 1; let params = []; for (const chatInfo of chats) { const ultimoTimestamp = chatInfo.mensajes.length > 0 ? Number(chatInfo.mensajes[chatInfo.mensajes.length - 1] .messageTimestamp) || 0 : 0; valuesSql.push(`($${paramCounter++}, $${paramCounter++}, $${paramCounter++}, $${paramCounter++}, $${paramCounter++}, $${paramCounter++}, $${paramCounter++})`); params.push(chatInfo.chat.id, tenantId, agentId, chatInfo.chat.name || null, chatInfo.esGrupo, chatInfo.chat.unreadCount || 0, ultimoTimestamp); } const query = ` INSERT INTO chats (id, tenant_id, agent_id, name, is_group, unread_count, last_message_timestamp) VALUES ${valuesSql.join(", ")} ON CONFLICT (id, tenant_id, agent_id) DO UPDATE SET name = EXCLUDED.name, unread_count = EXCLUDED.unread_count, last_message_timestamp = EXCLUDED.last_message_timestamp, updated_at = CURRENT_TIMESTAMP `; await client.query(query, params); } /** * Procesa inserciones o actualizaciones de chats */ async handleChatUpserts(chats, tenantId, agentId) { if (chats.length === 0) return; const client = await this.pool.connect(); try { await client.query("BEGIN"); for (const chat of chats) { await this.handleSingleChatUpsert(tenantId, agentId, client, chat); } await client.query("COMMIT"); console.log(`Procesados ${chats.length} chats`); } catch (error) { await client.query("ROLLBACK"); const repositoryError = new repository_errors_1.ChatRepositoryError({ tenantId, agentId, operation: 'handleChatUpserts', details: { chatCount: chats.length, errorMessage: error instanceof Error ? error.message : String(error) } }, error instanceof Error ? error : new Error(String(error))); logger_1.default.error('Failed to handle chat upserts', repositoryError.getLogContext()); throw repositoryError; } finally { client.release(); } } /** * Procesa una inserción o actualización individual de chat */ async handleSingleChatUpsert(tenantId, agentId, client, chat) { const chatData = { id: chat.id, tenant_id: tenantId, agent_id: agentId, name: chat.name || null, is_group: (0, baileys_1.isJidGroup)(chat.id), unread_count: chat.unreadCount || 0, conversation_timestamp: chat.conversationTimestamp, archived: chat.archived || false, pinned: chat.pinned || 0, mute_end_time: chat.muteEndTime, ephemeral_expiration: chat.ephemeralExpiration, ephemeral_setting_timestamp: chat.ephemeralSettingTimestamp, end_of_history_transfer_type: chat.endOfHistoryTransferType, disappearing_mode: chat.disappearingMode ? JSON.stringify(chat.disappearingMode) : null, description: chat.description, last_message_timestamp: chat.conversationTimestamp, raw_data: JSON.stringify(chat), }; // Upsert del chat const upsertQuery = ` INSERT INTO public.chats (id, tenant_id, agent_id, name, is_group, unread_count, conversation_timestamp, archived, pinned, mute_end_time, ephemeral_expiration, ephemeral_setting_timestamp, end_of_history_transfer_type, disappearing_mode, description, last_message_timestamp, raw_data) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) ON CONFLICT (id, tenant_id, agent_id) DO UPDATE SET name = COALESCE(EXCLUDED.name, chats.name), unread_count = EXCLUDED.unread_count, conversation_timestamp = COALESCE(EXCLUDED.conversation_timestamp, chats.conversation_timestamp), archived = COALESCE(EXCLUDED.archived, chats.archived), pinned = COALESCE(EXCLUDED.pinned, chats.pinned), mute_end_time = COALESCE(EXCLUDED.mute_end_time, chats.mute_end_time), ephemeral_expiration = COALESCE(EXCLUDED.ephemeral_expiration, chats.ephemeral_expiration), ephemeral_setting_timestamp = COALESCE(EXCLUDED.ephemeral_setting_timestamp, chats.ephemeral_setting_timestamp), end_of_history_transfer_type = COALESCE(EXCLUDED.end_of_history_transfer_type, chats.end_of_history_transfer_type), disappearing_mode = COALESCE(EXCLUDED.disappearing_mode, chats.disappearing_mode), description = COALESCE(EXCLUDED.description, chats.description), last_message_timestamp = COALESCE(EXCLUDED.last_message_timestamp, chats.last_message_timestamp), raw_data = EXCLUDED.raw_data, updated_at = CURRENT_TIMESTAMP `; await client.query(upsertQuery, [ chatData.id, chatData.tenant_id, chatData.agent_id, chatData.name, chatData.is_group, chatData.unread_count, chatData.conversation_timestamp, chatData.archived, chatData.pinned, chatData.mute_end_time, chatData.ephemeral_expiration, chatData.ephemeral_setting_timestamp, chatData.end_of_history_transfer_type, chatData.disappearing_mode, chatData.description, chatData.last_message_timestamp, chatData.raw_data, ]); } /** * Procesa actualizaciones de chats */ async handleChatUpdates(tenantId, agentId, updates) { if (updates.length === 0) return; const client = await this.pool.connect(); try { await client.query("BEGIN"); for (const update of updates) { await this.handleSingleChatUpdate(tenantId, agentId, client, update); } await client.query("COMMIT"); console.log(`Procesadas ${updates.length} actualizaciones de chat`); } catch (error) { await client.query("ROLLBACK"); const repositoryError = new repository_errors_1.ChatRepositoryError({ tenantId, agentId, operation: 'handleChatUpdates', details: { updateCount: updates.length, errorMessage: error instanceof Error ? error.message : String(error) } }, error instanceof Error ? error : new Error(String(error))); logger_1.default.error('Failed to handle chat updates', repositoryError.getLogContext()); throw repositoryError; } finally { client.release(); } } /** * Procesa una actualización individual de chat */ async handleSingleChatUpdate(tenantId, agentId, client, chatUpdate) { const chatId = chatUpdate.id; if (!chatId) return; const updateFields = {}; // 1. Actualización de contador de no leídos if (typeof chatUpdate.unreadCount === "number") { updateFields.unread_count = chatUpdate.unreadCount; } // 2. Estado de archivado if (typeof chatUpdate.archived === "boolean") { updateFields.archived = chatUpdate.archived; } // 3. Estado de silenciado if (chatUpdate.muteEndTime !== undefined) { updateFields.mute_end_time = chatUpdate.muteEndTime; } // 4. Estado de fijado if (chatUpdate.pinned !== undefined) { updateFields.pinned = chatUpdate.pinned; } // 5. Timestamp de conversación if (chatUpdate.conversationTimestamp) { updateFields.conversation_timestamp = chatUpdate.conversationTimestamp; updateFields.last_message_timestamp = chatUpdate.conversationTimestamp; } // 6. Nombre del chat if (chatUpdate.name) { updateFields.name = chatUpdate.name; } // 7. Configuración efímera if (chatUpdate.ephemeralExpiration !== undefined) { updateFields.ephemeral_expiration = chatUpdate.ephemeralExpiration; } if (chatUpdate.ephemeralSettingTimestamp) { updateFields.ephemeral_setting_timestamp = chatUpdate.ephemeralSettingTimestamp; } // 8. Modo de desaparición if (chatUpdate.disappearingMode) { updateFields.disappearing_mode = JSON.stringify(chatUpdate.disappearingMode); } // 9. Descripción if (chatUpdate.description !== undefined) { updateFields.description = chatUpdate.description; } // 11. Datos raw completos updateFields.raw_data = JSON.stringify(chatUpdate); if (Object.keys(updateFields).length === 0) return; // Construir la consulta de actualización const setClause = Object.keys(updateFields) .map((key, index) => `${key} = $${index + 4}`) .join(", "); const values = [ chatId, tenantId, agentId, ...Object.values(updateFields), ]; const updateQuery = ` UPDATE public.chats SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = $1 AND tenant_id = $2 AND agent_id = $3 `; await client.query(updateQuery, values); } /** * Procesa múltiples actualizaciones de chat en lote */ async handleBulkChatUpdates(tenantId, agentId, chatUpdates) { if (chatUpdates.length === 0) return; const client = await this.pool.connect(); try { await client.query("BEGIN"); // Agrupar actualizaciones por chat ID const updatesByChat = chatUpdates.reduce((acc, update) => { if (update.id) { if (!acc[update.id]) { acc[update.id] = []; } acc[update.id].push(update); } return acc; }, {}); // Procesar cada chat con sus actualizaciones consolidadas for (const [chatId, updates] of Object.entries(updatesByChat)) { const consolidatedUpdate = this.consolidateChatUpdates(updates); await this.handleSingleChatUpdate(tenantId, agentId, client, consolidatedUpdate); } await client.query("COMMIT"); console.log(`Procesadas ${chatUpdates.length} actualizaciones de chat en lote`); } catch (error) { await client.query("ROLLBACK"); const repositoryError = new repository_errors_1.ChatRepositoryError({ tenantId, agentId, operation: 'handleBulkChatUpdates', details: { updateCount: chatUpdates.length, errorMessage: error instanceof Error ? error.message : String(error) } }, error instanceof Error ? error : new Error(String(error))); logger_1.default.error('Failed to handle bulk chat updates', repositoryError.getLogContext()); throw repositoryError; } finally { client.release(); } } /** * Consolida múltiples actualizaciones de chat en una sola */ consolidateChatUpdates(updates) { return updates.reduce((consolidated, update) => { return { ...consolidated, ...update }; }, { id: updates[0].id }); } } exports.ChatRepository = ChatRepository; //# sourceMappingURL=chat-repository.js.map