{"repo": "carete16/masbaratoexpress", "files": {"src/core/AIProcessor.js": {"c8dd69bc93856782197930d1324589ecf2110911": {"t": 1785745529.5557792, "n": 10024, "h": "ef3bf55d7c0abe8e", "c": "const logger = require('../utils/logger');\nconst axios = require('axios');\n\n/**\n * AIProcessor: Optimiza títulos y descripciones para maximizar ventas\n */\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n }\n\n async generateEnhancedContent(rawTitle) {\n if (!this.apiKey) {\n this.apiKey = process.env.DEEPSEEK_API_KEY || process.env.OPENAI_API_KEY;\n }\n\n try {\n const prompt = `Actúa como un redactor experto en E-commerce y Tecnología para Colombia.\nAnaliza este producto: \"${rawTitle}\"\n\nGenera una respuesta en formato JSON con la siguiente estructura exacta:\n{\n \"title\": \"Un título CORTO (max 70 chars), VIRAL y PERSUASIVO con 1-2 emojis al inicio.\",\n \"description\": \"Una descripción de venta persuasiva de 2 párrafos cortos (max 300 chars) enfocada en beneficios, no solo características.\",\n \"specs\": \"Lista de 3 a 5 especificaciones técnicas clave (bullet points) usando emojis como viñeta.\"\n}\n\nReglas:\n1. Idioma: Español Neutro/Colombiano.\n2. Tono: Entusiasta y profesional.\n3. Formato specs: \"- 📱 Pantalla: AMOLED...\" (una por línea).\n4. SOLO responde el JSON válido, sin markdown ni explicaciones adicionales.`;\n\n // Usar DEEPSEEK si está disponible, sino volver a OPENAI\n const useDeepSeek = process.env.DEEPSEEK_API_KEY ? true : false;\n const apiUrl = useDeepSeek ? 'https://api.deepseek.com/chat/completions' : 'https://api.openai.com/v1/chat/completions';\n const apiKey = useDeepSeek ? process.env.DEEPSEEK_API_KEY : this.apiKey;\n const model = useDeepSeek ? \"deepseek-chat\" : \"gpt-3.5-turbo\";\n\n const response = await axios.post(apiUrl, {\n model: model,\n messages: [\n { role: \"system\", content: \"Eres un asistente de e-commerce que solo responde en JSON válido.\" },\n { role: \"user\", content: prompt }\n ],\n temperature: 0.7,\n max_tokens: 400\n }, {\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n 'Content-Type': 'application/json'\n },\n timeout: 12000\n });\n\n let content = response.data.choices[0].message.content.trim();\n // Limpiar markdown si la IA lo puso\n content = content.replace(/```json|```/g, '').trim();\n\n try {\n return JSON.parse(content);\n } catch (jsonErr) {\n console.error(\"Error parseando JSON IA:\", jsonErr);\n // Fallback manual si el JSON falla\n return {\n title: this.pseudoTranslate(rawTitle),\n description: \"Producto importado de alta calidad. Aprovecha esta oferta por tiempo limitado.\",\n specs: \"- ✅ Garantía de calidad\\n- 📦 Envío internacional\\n- 💯 Oportunidad única\"\n };\n }\n\n } catch (e) {\n logger.warn(`⚠️ AI Content Error: ${e.message}`);\n return {\n title: this.pseudoTranslate(rawTitle),\n description: \"\",\n specs: \"\"\n };\n }\n }\n\n async generateOptimizedTitle(rawTitle) {\n // Redirigir a la nueva lógica simplificada si solo piden título, \n // pero mantenemos el método por compatibilidad.\n const res = await this.generateEnhancedContent(rawTitle);\n return res.title;\n }\n\n pseudoTranslate(title) {\n if (!title) return \"Oferta Exclusiva\";\n let translated = title;\n\n // 0. Limpieza inicial agresiva (Marcas, modelos largos)\n translated = translated\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/gi, '')\n .replace(/\\s*-\\s*.*$/, '') // Eliminar todo después del primer guion\n .replace(/\\|\\s*.*$/, '') // Eliminar todo después del pipe\n .trim();\n\n // 1. Reemplazos de frases comunes (Orden de importancia)\n const phrases = {\n 'noise canceling': 'con cancelación de ruido',\n 'noise cancelling': 'con cancelación de ruido',\n 'wireless bluetooth': 'Bluetooth inalámbrico',\n 'smart watch': 'Reloj Inteligente',\n 'fast charging': 'Carga rápida',\n 'stainless steel': 'Acero inoxidable',\n 'limited edition': 'Edición limitada',\n 'men\\'s': 'para Hombre',\n 'women\\'s': 'para Mujer',\n 'boy\\'s': 'para Niño',\n 'girl\\'s': 'para Niña',\n 'built-in': 'integrado',\n 'high definition': 'Alta definición',\n 'waterproof': 'Impermeable',\n 'shockproof': 'Resistente a golpes'\n };\n\n for (let [eng, esp] of Object.entries(phrases)) {\n translated = translated.replace(new RegExp(eng, 'g'), esp);\n }\n\n // 2. Diccionario de palabras sueltas\n const dict = {\n 'laptop': 'Portátil', 'purifier': 'Purificador', 'cool': 'Cool', 'fan': 'Ventilador',\n 'smart': 'Inteligente', 'air': 'de Aire', 'watch': 'Reloj', 'shoes': 'Tenis',\n 'sneakers': 'Tenis', 'headphones': 'Audífonos', 'earbuds': 'Audífonos',\n 'monitor': 'Monitor', 'gaming': 'Gamer', 'shirt': 'Camisa', 'pant': 'Pantalón',\n 'keyboard': 'Teclado', 'mouse': 'Mouse', 'cordless': 'Inalámbrico',\n 'wireless': 'Inalámbrico', 'original': 'Original', 'clearance': '¡Remate!',\n 'sale': 'Oferta', 'deal': 'Ganga', 'phone': 'Celular', 'camera': 'Cámara',\n 'storage': 'Almacenamiento', 'fast': 'Rápido', 'pro': 'Pro', 'ultra': 'Ultra',\n 'black': 'Negro', 'white': 'Blanco', 'blue': 'Azul', 'red': 'Rojo',\n 'kit': 'Combo', 'pack': 'Paquete', 'new': 'Nuevo', 'off': 'de descuento',\n 'discount': 'Descuento', 'free': 'Gratis', 'shipping': 'Envío', 'tv': 'Televisor',\n 'ssd': 'Disco SSD', 'drive': 'Disco', 'leather': 'de Cuero', 'men': 'Hombre',\n 'women': 'Mujer', 'kids': 'Niños', 'sport': 'Deportivo', 'running': 'para Correr',\n 'speaker': 'Parlante', 'soundbar': 'Barra de Sonido', 'battery': 'Batería',\n 'charger': 'Cargador', 'cable': 'Cable', 'case': 'Estuche', 'tablet': 'Tablet',\n 'console': 'Consola', 'game': 'Juego', 'analog': 'Análogo', 'digital': 'Digital',\n 'multi-function': 'Multifunción', 'light': 'Luz', 'heavy': 'Pesado',\n 'and': 'y', 'for': 'para', 'with': 'con', 'set': 'Set de', 'inch': 'pulgadas'\n };\n\n let words = translated.split(' ');\n translated = words.map(w => {\n let clean = w.replace(/[^a-z-]/g, '');\n if (dict[clean]) return dict[clean];\n return w;\n }).join(' ');\n\n // Capitalizar la primera letra y limpiar espacios\n translated = translated.charAt(0).toUpperCase() + translated.slice(1);\n return translated.replace(/\\s+/g, ' ').trim();\n }\n\n cleanTitle(title) {\n if (!title) return \"Producto Exclusivo\";\n // Limpiar marcas de agua de tiendas o textos largos innecesarios\n return title\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/g, '')\n .split(' - ')[0]\n .split(' | ')[0]\n .trim();\n }\n\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n let description = deal.description || deal.originalDescription || '';\n\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n let formatted = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) formatted += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n formatted += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) formatted += `📉 AHORRO DEL ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) desc += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n desc += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) desc += `💰 AHORRA ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n desc += `${deal.title} representa una excelente oportunidad en ${storeName}. `;\n if (discount >= 30) desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n desc += `Producto verificado y en stock.\\n\\n`;\n desc += `✅ Producto auténtico\\n✅ Envío disponible\\n✅ Garantía del fabricante\\n`;\n if (deal.categoria) desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "35046869fecb5523131a563592713db74c289807": {"t": 1785753895.5796144, "n": 10024, "h": "ef3bf55d7c0abe8e", "c": "const logger = require('../utils/logger');\nconst axios = require('axios');\n\n/**\n * AIProcessor: Optimiza títulos y descripciones para maximizar ventas\n */\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n }\n\n async generateEnhancedContent(rawTitle) {\n if (!this.apiKey) {\n this.apiKey = process.env.DEEPSEEK_API_KEY || process.env.OPENAI_API_KEY;\n }\n\n try {\n const prompt = `Actúa como un redactor experto en E-commerce y Tecnología para Colombia.\nAnaliza este producto: \"${rawTitle}\"\n\nGenera una respuesta en formato JSON con la siguiente estructura exacta:\n{\n \"title\": \"Un título CORTO (max 70 chars), VIRAL y PERSUASIVO con 1-2 emojis al inicio.\",\n \"description\": \"Una descripción de venta persuasiva de 2 párrafos cortos (max 300 chars) enfocada en beneficios, no solo características.\",\n \"specs\": \"Lista de 3 a 5 especificaciones técnicas clave (bullet points) usando emojis como viñeta.\"\n}\n\nReglas:\n1. Idioma: Español Neutro/Colombiano.\n2. Tono: Entusiasta y profesional.\n3. Formato specs: \"- 📱 Pantalla: AMOLED...\" (una por línea).\n4. SOLO responde el JSON válido, sin markdown ni explicaciones adicionales.`;\n\n // Usar DEEPSEEK si está disponible, sino volver a OPENAI\n const useDeepSeek = process.env.DEEPSEEK_API_KEY ? true : false;\n const apiUrl = useDeepSeek ? 'https://api.deepseek.com/chat/completions' : 'https://api.openai.com/v1/chat/completions';\n const apiKey = useDeepSeek ? process.env.DEEPSEEK_API_KEY : this.apiKey;\n const model = useDeepSeek ? \"deepseek-chat\" : \"gpt-3.5-turbo\";\n\n const response = await axios.post(apiUrl, {\n model: model,\n messages: [\n { role: \"system\", content: \"Eres un asistente de e-commerce que solo responde en JSON válido.\" },\n { role: \"user\", content: prompt }\n ],\n temperature: 0.7,\n max_tokens: 400\n }, {\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n 'Content-Type': 'application/json'\n },\n timeout: 12000\n });\n\n let content = response.data.choices[0].message.content.trim();\n // Limpiar markdown si la IA lo puso\n content = content.replace(/```json|```/g, '').trim();\n\n try {\n return JSON.parse(content);\n } catch (jsonErr) {\n console.error(\"Error parseando JSON IA:\", jsonErr);\n // Fallback manual si el JSON falla\n return {\n title: this.pseudoTranslate(rawTitle),\n description: \"Producto importado de alta calidad. Aprovecha esta oferta por tiempo limitado.\",\n specs: \"- ✅ Garantía de calidad\\n- 📦 Envío internacional\\n- 💯 Oportunidad única\"\n };\n }\n\n } catch (e) {\n logger.warn(`⚠️ AI Content Error: ${e.message}`);\n return {\n title: this.pseudoTranslate(rawTitle),\n description: \"\",\n specs: \"\"\n };\n }\n }\n\n async generateOptimizedTitle(rawTitle) {\n // Redirigir a la nueva lógica simplificada si solo piden título, \n // pero mantenemos el método por compatibilidad.\n const res = await this.generateEnhancedContent(rawTitle);\n return res.title;\n }\n\n pseudoTranslate(title) {\n if (!title) return \"Oferta Exclusiva\";\n let translated = title;\n\n // 0. Limpieza inicial agresiva (Marcas, modelos largos)\n translated = translated\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/gi, '')\n .replace(/\\s*-\\s*.*$/, '') // Eliminar todo después del primer guion\n .replace(/\\|\\s*.*$/, '') // Eliminar todo después del pipe\n .trim();\n\n // 1. Reemplazos de frases comunes (Orden de importancia)\n const phrases = {\n 'noise canceling': 'con cancelación de ruido',\n 'noise cancelling': 'con cancelación de ruido',\n 'wireless bluetooth': 'Bluetooth inalámbrico',\n 'smart watch': 'Reloj Inteligente',\n 'fast charging': 'Carga rápida',\n 'stainless steel': 'Acero inoxidable',\n 'limited edition': 'Edición limitada',\n 'men\\'s': 'para Hombre',\n 'women\\'s': 'para Mujer',\n 'boy\\'s': 'para Niño',\n 'girl\\'s': 'para Niña',\n 'built-in': 'integrado',\n 'high definition': 'Alta definición',\n 'waterproof': 'Impermeable',\n 'shockproof': 'Resistente a golpes'\n };\n\n for (let [eng, esp] of Object.entries(phrases)) {\n translated = translated.replace(new RegExp(eng, 'g'), esp);\n }\n\n // 2. Diccionario de palabras sueltas\n const dict = {\n 'laptop': 'Portátil', 'purifier': 'Purificador', 'cool': 'Cool', 'fan': 'Ventilador',\n 'smart': 'Inteligente', 'air': 'de Aire', 'watch': 'Reloj', 'shoes': 'Tenis',\n 'sneakers': 'Tenis', 'headphones': 'Audífonos', 'earbuds': 'Audífonos',\n 'monitor': 'Monitor', 'gaming': 'Gamer', 'shirt': 'Camisa', 'pant': 'Pantalón',\n 'keyboard': 'Teclado', 'mouse': 'Mouse', 'cordless': 'Inalámbrico',\n 'wireless': 'Inalámbrico', 'original': 'Original', 'clearance': '¡Remate!',\n 'sale': 'Oferta', 'deal': 'Ganga', 'phone': 'Celular', 'camera': 'Cámara',\n 'storage': 'Almacenamiento', 'fast': 'Rápido', 'pro': 'Pro', 'ultra': 'Ultra',\n 'black': 'Negro', 'white': 'Blanco', 'blue': 'Azul', 'red': 'Rojo',\n 'kit': 'Combo', 'pack': 'Paquete', 'new': 'Nuevo', 'off': 'de descuento',\n 'discount': 'Descuento', 'free': 'Gratis', 'shipping': 'Envío', 'tv': 'Televisor',\n 'ssd': 'Disco SSD', 'drive': 'Disco', 'leather': 'de Cuero', 'men': 'Hombre',\n 'women': 'Mujer', 'kids': 'Niños', 'sport': 'Deportivo', 'running': 'para Correr',\n 'speaker': 'Parlante', 'soundbar': 'Barra de Sonido', 'battery': 'Batería',\n 'charger': 'Cargador', 'cable': 'Cable', 'case': 'Estuche', 'tablet': 'Tablet',\n 'console': 'Consola', 'game': 'Juego', 'analog': 'Análogo', 'digital': 'Digital',\n 'multi-function': 'Multifunción', 'light': 'Luz', 'heavy': 'Pesado',\n 'and': 'y', 'for': 'para', 'with': 'con', 'set': 'Set de', 'inch': 'pulgadas'\n };\n\n let words = translated.split(' ');\n translated = words.map(w => {\n let clean = w.replace(/[^a-z-]/g, '');\n if (dict[clean]) return dict[clean];\n return w;\n }).join(' ');\n\n // Capitalizar la primera letra y limpiar espacios\n translated = translated.charAt(0).toUpperCase() + translated.slice(1);\n return translated.replace(/\\s+/g, ' ').trim();\n }\n\n cleanTitle(title) {\n if (!title) return \"Producto Exclusivo\";\n // Limpiar marcas de agua de tiendas o textos largos innecesarios\n return title\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/g, '')\n .split(' - ')[0]\n .split(' | ')[0]\n .trim();\n }\n\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n let description = deal.description || deal.originalDescription || '';\n\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n let formatted = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) formatted += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n formatted += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) formatted += `📉 AHORRO DEL ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) desc += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n desc += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) desc += `💰 AHORRA ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n desc += `${deal.title} representa una excelente oportunidad en ${storeName}. `;\n if (discount >= 30) desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n desc += `Producto verificado y en stock.\\n\\n`;\n desc += `✅ Producto auténtico\\n✅ Envío disponible\\n✅ Garantía del fabricante\\n`;\n if (deal.categoria) desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "c026218c1550ad6898f3c78d2ed2c6e23283082c": {"t": 1785753896.847779, "n": 9467, "h": "3c4c6c43c9a2da58", "c": "const logger = require('../utils/logger');\nconst axios = require('axios');\n\n/**\n * AIProcessor: Optimiza títulos y descripciones para maximizar ventas\n */\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n }\n\n async generateOptimizedTitle(rawTitle) {\n // Recargar la llave por si no estaba lista en el constructor\n if (!this.apiKey) this.apiKey = process.env.OPENAI_API_KEY;\n\n if (!this.apiKey) {\n logger.warn(\"⚠️ OPENAI_API_KEY no detectada en .env. Usando traductor básico.\");\n return this.pseudoTranslate(rawTitle);\n }\n\n try {\n const prompt = `Actúa como un experto en Copywriting y Growth Hacking para un canal de ofertas que vende en Colombia.\nConvierte este título de producto en un título VIRAL, MAGNÉTICO y MUY PERSUASIVO.\n\nREGLAS:\n1. Idioma: ESPAÑOL.\n2. Longitud: Máximo 70 caracteres.\n3. Tono: Exclamativo, de oportunidad única, usando disparadores psicológicos (Curiosidad, Escasez o Ahorro).\n4. Estructura recomendada: [Emoji] [Gancho de Venta] [Beneficio/Modelo].\n5. Emojis: Usa 1 o 2 emojis llamativos.\n6. SOLO responde con el nuevo título, sin comillas.\n\nTítulo original: ${rawTitle}`;\n\n // Usar DEEPSEEK si está disponible, sino volver a OPENAI\n const useDeepSeek = process.env.DEEPSEEK_API_KEY ? true : false;\n const apiUrl = useDeepSeek ? 'https://api.deepseek.com/chat/completions' : 'https://api.openai.com/v1/chat/completions';\n const apiKey = useDeepSeek ? process.env.DEEPSEEK_API_KEY : this.apiKey;\n const model = useDeepSeek ? \"deepseek-chat\" : \"gpt-3.5-turbo\";\n\n const response = await axios.post(apiUrl, {\n model: model,\n messages: [\n { role: \"system\", content: \"Eres un experto en Copywriting para e-commerce en Colombia.\" },\n { role: \"user\", content: prompt }\n ],\n temperature: 0.7,\n max_tokens: 70\n }, {\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n 'Content-Type': 'application/json'\n },\n timeout: 8000\n });\n\n let optimized = response.data.choices[0].message.content.trim();\n // Limpiar si la IA agregó comillas o \"Título:\"\n optimized = optimized.replace(/^[\"']|[\"']$/g, '').replace(/^Título:\\s*/i, '');\n\n return optimized || this.pseudoTranslate(rawTitle);\n } catch (e) {\n // Manejo específico para cuota excedida (429)\n if (e.response && e.response.status === 429) {\n logger.warn(`⚠️ OpenAI Quota Exceeded (429). Usando traductor de emergencia.`);\n } else {\n logger.warn(`⚠️ OpenAI Title Error: ${e.message}. Usando fallback.`);\n }\n return this.pseudoTranslate(rawTitle);\n }\n }\n\n pseudoTranslate(title) {\n if (!title) return \"Oferta Exclusiva\";\n let translated = title;\n\n // 0. Limpieza inicial agresiva (Marcas, modelos largos)\n translated = translated\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/gi, '')\n .replace(/\\s*-\\s*.*$/, '') // Eliminar todo después del primer guion\n .replace(/\\|\\s*.*$/, '') // Eliminar todo después del pipe\n .trim();\n\n // 1. Reemplazos de frases comunes (Orden de importancia)\n const phrases = {\n 'noise canceling': 'con cancelación de ruido',\n 'noise cancelling': 'con cancelación de ruido',\n 'wireless bluetooth': 'Bluetooth inalámbrico',\n 'smart watch': 'Reloj Inteligente',\n 'fast charging': 'Carga rápida',\n 'stainless steel': 'Acero inoxidable',\n 'limited edition': 'Edición limitada',\n 'men\\'s': 'para Hombre',\n 'women\\'s': 'para Mujer',\n 'boy\\'s': 'para Niño',\n 'girl\\'s': 'para Niña',\n 'built-in': 'integrado',\n 'high definition': 'Alta definición',\n 'waterproof': 'Impermeable',\n 'shockproof': 'Resistente a golpes'\n };\n\n for (let [eng, esp] of Object.entries(phrases)) {\n translated = translated.replace(new RegExp(eng, 'g'), esp);\n }\n\n // 2. Diccionario de palabras sueltas\n const dict = {\n 'laptop': 'Portátil', 'purifier': 'Purificador', 'cool': 'Cool', 'fan': 'Ventilador',\n 'smart': 'Inteligente', 'air': 'de Aire', 'watch': 'Reloj', 'shoes': 'Tenis',\n 'sneakers': 'Tenis', 'headphones': 'Audífonos', 'earbuds': 'Audífonos',\n 'monitor': 'Monitor', 'gaming': 'Gamer', 'shirt': 'Camisa', 'pant': 'Pantalón',\n 'keyboard': 'Teclado', 'mouse': 'Mouse', 'cordless': 'Inalámbrico',\n 'wireless': 'Inalámbrico', 'original': 'Original', 'clearance': '¡Remate!',\n 'sale': 'Oferta', 'deal': 'Ganga', 'phone': 'Celular', 'camera': 'Cámara',\n 'storage': 'Almacenamiento', 'fast': 'Rápido', 'pro': 'Pro', 'ultra': 'Ultra',\n 'black': 'Negro', 'white': 'Blanco', 'blue': 'Azul', 'red': 'Rojo',\n 'kit': 'Combo', 'pack': 'Paquete', 'new': 'Nuevo', 'off': 'de descuento',\n 'discount': 'Descuento', 'free': 'Gratis', 'shipping': 'Envío', 'tv': 'Televisor',\n 'ssd': 'Disco SSD', 'drive': 'Disco', 'leather': 'de Cuero', 'men': 'Hombre',\n 'women': 'Mujer', 'kids': 'Niños', 'sport': 'Deportivo', 'running': 'para Correr',\n 'speaker': 'Parlante', 'soundbar': 'Barra de Sonido', 'battery': 'Batería',\n 'charger': 'Cargador', 'cable': 'Cable', 'case': 'Estuche', 'tablet': 'Tablet',\n 'console': 'Consola', 'game': 'Juego', 'analog': 'Análogo', 'digital': 'Digital',\n 'multi-function': 'Multifunción', 'light': 'Luz', 'heavy': 'Pesado',\n 'and': 'y', 'for': 'para', 'with': 'con', 'set': 'Set de', 'inch': 'pulgadas'\n };\n\n let words = translated.split(' ');\n translated = words.map(w => {\n let clean = w.replace(/[^a-z-]/g, '');\n if (dict[clean]) return dict[clean];\n return w;\n }).join(' ');\n\n // Capitalizar la primera letra y limpiar espacios\n translated = translated.charAt(0).toUpperCase() + translated.slice(1);\n return translated.replace(/\\s+/g, ' ').trim();\n }\n\n cleanTitle(title) {\n if (!title) return \"Producto Exclusivo\";\n // Limpiar marcas de agua de tiendas o textos largos innecesarios\n return title\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/g, '')\n .split(' - ')[0]\n .split(' | ')[0]\n .trim();\n }\n\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n let description = deal.description || deal.originalDescription || '';\n\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n let formatted = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) formatted += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n formatted += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) formatted += `📉 AHORRO DEL ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) desc += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n desc += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) desc += `💰 AHORRA ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n desc += `${deal.title} representa una excelente oportunidad en ${storeName}. `;\n if (discount >= 30) desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n desc += `Producto verificado y en stock.\\n\\n`;\n desc += `✅ Producto auténtico\\n✅ Envío disponible\\n✅ Garantía del fabricante\\n`;\n if (deal.categoria) desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "20e20c86e7e79336084ab13cf149841b822de071": {"t": 1785753898.5046992, "n": 8929, "h": "f345ef56888d0bfc", "c": "const logger = require('../utils/logger');\nconst axios = require('axios');\n\n/**\n * AIProcessor: Optimiza títulos y descripciones para maximizar ventas\n */\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n }\n\n async generateOptimizedTitle(rawTitle) {\n // Recargar la llave por si no estaba lista en el constructor\n if (!this.apiKey) this.apiKey = process.env.OPENAI_API_KEY;\n\n if (!this.apiKey) {\n logger.warn(\"⚠️ OPENAI_API_KEY no detectada en .env. Usando traductor básico.\");\n return this.pseudoTranslate(rawTitle);\n }\n\n try {\n const prompt = `Actúa como un experto en Copywriting y Growth Hacking para un canal de ofertas que vende en Colombia.\nConvierte este título de producto en un título VIRAL, MAGNÉTICO y MUY PERSUASIVO.\n\nREGLAS:\n1. Idioma: ESPAÑOL.\n2. Longitud: Máximo 70 caracteres.\n3. Tono: Exclamativo, de oportunidad única, usando disparadores psicológicos (Curiosidad, Escasez o Ahorro).\n4. Estructura recomendada: [Emoji] [Gancho de Venta] [Beneficio/Modelo].\n5. Emojis: Usa 1 o 2 emojis llamativos.\n6. SOLO responde con el nuevo título, sin comillas.\n\nTítulo original: ${rawTitle}`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: \"gpt-3.5-turbo\",\n messages: [{ role: \"user\", content: prompt }],\n temperature: 0.7,\n max_tokens: 60\n }, {\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json'\n },\n timeout: 5000\n });\n\n let optimized = response.data.choices[0].message.content.trim();\n // Limpiar si la IA agregó comillas o \"Título:\"\n optimized = optimized.replace(/^[\"']|[\"']$/g, '').replace(/^Título:\\s*/i, '');\n\n return optimized || this.pseudoTranslate(rawTitle);\n } catch (e) {\n // Manejo específico para cuota excedida (429)\n if (e.response && e.response.status === 429) {\n logger.warn(`⚠️ OpenAI Quota Exceeded (429). Usando traductor de emergencia.`);\n } else {\n logger.warn(`⚠️ OpenAI Title Error: ${e.message}. Usando fallback.`);\n }\n return this.pseudoTranslate(rawTitle);\n }\n }\n\n pseudoTranslate(title) {\n if (!title) return \"Oferta Exclusiva\";\n let translated = title;\n\n // 0. Limpieza inicial agresiva (Marcas, modelos largos)\n translated = translated\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/gi, '')\n .replace(/\\s*-\\s*.*$/, '') // Eliminar todo después del primer guion\n .replace(/\\|\\s*.*$/, '') // Eliminar todo después del pipe\n .trim();\n\n // 1. Reemplazos de frases comunes (Orden de importancia)\n const phrases = {\n 'noise canceling': 'con cancelación de ruido',\n 'noise cancelling': 'con cancelación de ruido',\n 'wireless bluetooth': 'Bluetooth inalámbrico',\n 'smart watch': 'Reloj Inteligente',\n 'fast charging': 'Carga rápida',\n 'stainless steel': 'Acero inoxidable',\n 'limited edition': 'Edición limitada',\n 'men\\'s': 'para Hombre',\n 'women\\'s': 'para Mujer',\n 'boy\\'s': 'para Niño',\n 'girl\\'s': 'para Niña',\n 'built-in': 'integrado',\n 'high definition': 'Alta definición',\n 'waterproof': 'Impermeable',\n 'shockproof': 'Resistente a golpes'\n };\n\n for (let [eng, esp] of Object.entries(phrases)) {\n translated = translated.replace(new RegExp(eng, 'g'), esp);\n }\n\n // 2. Diccionario de palabras sueltas\n const dict = {\n 'laptop': 'Portátil', 'purifier': 'Purificador', 'cool': 'Cool', 'fan': 'Ventilador',\n 'smart': 'Inteligente', 'air': 'de Aire', 'watch': 'Reloj', 'shoes': 'Tenis',\n 'sneakers': 'Tenis', 'headphones': 'Audífonos', 'earbuds': 'Audífonos',\n 'monitor': 'Monitor', 'gaming': 'Gamer', 'shirt': 'Camisa', 'pant': 'Pantalón',\n 'keyboard': 'Teclado', 'mouse': 'Mouse', 'cordless': 'Inalámbrico',\n 'wireless': 'Inalámbrico', 'original': 'Original', 'clearance': '¡Remate!',\n 'sale': 'Oferta', 'deal': 'Ganga', 'phone': 'Celular', 'camera': 'Cámara',\n 'storage': 'Almacenamiento', 'fast': 'Rápido', 'pro': 'Pro', 'ultra': 'Ultra',\n 'black': 'Negro', 'white': 'Blanco', 'blue': 'Azul', 'red': 'Rojo',\n 'kit': 'Combo', 'pack': 'Paquete', 'new': 'Nuevo', 'off': 'de descuento',\n 'discount': 'Descuento', 'free': 'Gratis', 'shipping': 'Envío', 'tv': 'Televisor',\n 'ssd': 'Disco SSD', 'drive': 'Disco', 'leather': 'de Cuero', 'men': 'Hombre',\n 'women': 'Mujer', 'kids': 'Niños', 'sport': 'Deportivo', 'running': 'para Correr',\n 'speaker': 'Parlante', 'soundbar': 'Barra de Sonido', 'battery': 'Batería',\n 'charger': 'Cargador', 'cable': 'Cable', 'case': 'Estuche', 'tablet': 'Tablet',\n 'console': 'Consola', 'game': 'Juego', 'analog': 'Análogo', 'digital': 'Digital',\n 'multi-function': 'Multifunción', 'light': 'Luz', 'heavy': 'Pesado',\n 'and': 'y', 'for': 'para', 'with': 'con', 'set': 'Set de', 'inch': 'pulgadas'\n };\n\n let words = translated.split(' ');\n translated = words.map(w => {\n let clean = w.replace(/[^a-z-]/g, '');\n if (dict[clean]) return dict[clean];\n return w;\n }).join(' ');\n\n // Capitalizar la primera letra y limpiar espacios\n translated = translated.charAt(0).toUpperCase() + translated.slice(1);\n return translated.replace(/\\s+/g, ' ').trim();\n }\n\n cleanTitle(title) {\n if (!title) return \"Producto Exclusivo\";\n // Limpiar marcas de agua de tiendas o textos largos innecesarios\n return title\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/g, '')\n .split(' - ')[0]\n .split(' | ')[0]\n .trim();\n }\n\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n let description = deal.description || deal.originalDescription || '';\n\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n let formatted = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) formatted += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n formatted += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) formatted += `📉 AHORRO DEL ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) desc += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n desc += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) desc += `💰 AHORRA ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n desc += `${deal.title} representa una excelente oportunidad en ${storeName}. `;\n if (discount >= 30) desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n desc += `Producto verificado y en stock.\\n\\n`;\n desc += `✅ Producto auténtico\\n✅ Envío disponible\\n✅ Garantía del fabricante\\n`;\n if (deal.categoria) desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "bd25c380a5604cd89e2a5ffbccff84eca9275898": {"t": 1785753899.8814473, "n": 8351, "h": "4112a261af044673", "c": "const logger = require('../utils/logger');\nconst axios = require('axios');\n\n/**\n * AIProcessor: Optimiza títulos y descripciones para maximizar ventas\n */\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n }\n\n async generateOptimizedTitle(rawTitle) {\n // Recargar la llave por si no estaba lista en el constructor\n if (!this.apiKey) this.apiKey = process.env.OPENAI_API_KEY;\n\n if (!this.apiKey) {\n logger.warn(\"⚠️ OPENAI_API_KEY no detectada en .env. Usando traductor básico.\");\n return this.pseudoTranslate(rawTitle);\n }\n\n try {\n const prompt = `Actúa como un experto en Copywriting y Growth Hacking para un canal de ofertas que vende en Colombia.\nConvierte este título de producto en un título VIRAL, MAGNÉTICO y MUY PERSUASIVO.\n\nREGLAS:\n1. Idioma: ESPAÑOL.\n2. Longitud: Máximo 70 caracteres.\n3. Tono: Exclamativo, de oportunidad única, usando disparadores psicológicos (Curiosidad, Escasez o Ahorro).\n4. Estructura recomendada: [Emoji] [Gancho de Venta] [Beneficio/Modelo].\n5. Emojis: Usa 1 o 2 emojis llamativos.\n6. SOLO responde con el nuevo título, sin comillas.\n\nTítulo original: ${rawTitle}`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: \"gpt-3.5-turbo\",\n messages: [{ role: \"user\", content: prompt }],\n temperature: 0.7,\n max_tokens: 60\n }, {\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json'\n },\n timeout: 5000\n });\n\n let optimized = response.data.choices[0].message.content.trim();\n // Limpiar si la IA agregó comillas o \"Título:\"\n optimized = optimized.replace(/^[\"']|[\"']$/g, '').replace(/^Título:\\s*/i, '');\n\n return optimized || this.pseudoTranslate(rawTitle);\n } catch (e) {\n logger.warn(`⚠️ OpenAI Title Error: ${e.message}. Usando fallback.`);\n return this.pseudoTranslate(rawTitle);\n }\n }\n\n pseudoTranslate(title) {\n if (!title) return \"Oferta Exclusiva\";\n\n let translated = title.toLowerCase();\n\n // 1. Reemplazos de frases comunes (Orden de importancia)\n const phrases = {\n 'noise canceling': 'con cancelación de ruido',\n 'noise cancelling': 'con cancelación de ruido',\n 'wireless bluetooth': 'Bluetooth inalámbrico',\n 'smart watch': 'Reloj Inteligente',\n 'fast charging': 'Carga rápida',\n 'stainless steel': 'Acero inoxidable',\n 'limited edition': 'Edición limitada',\n 'men\\'s': 'para Hombre',\n 'women\\'s': 'para Mujer',\n 'boy\\'s': 'para Niño',\n 'girl\\'s': 'para Niña',\n 'built-in': 'integrado',\n 'high definition': 'Alta definición',\n 'waterproof': 'Impermeable',\n 'shockproof': 'Resistente a golpes'\n };\n\n for (let [eng, esp] of Object.entries(phrases)) {\n translated = translated.replace(new RegExp(eng, 'g'), esp);\n }\n\n // 2. Diccionario de palabras sueltas\n const dict = {\n 'laptop': 'Portátil', 'purifier': 'Purificador', 'cool': 'Cool', 'fan': 'Ventilador',\n 'smart': 'Inteligente', 'air': 'de Aire', 'watch': 'Reloj', 'shoes': 'Tenis',\n 'sneakers': 'Tenis', 'headphones': 'Audífonos', 'earbuds': 'Audífonos',\n 'monitor': 'Monitor', 'gaming': 'Gamer', 'shirt': 'Camisa', 'pant': 'Pantalón',\n 'keyboard': 'Teclado', 'mouse': 'Mouse', 'cordless': 'Inalámbrico',\n 'wireless': 'Inalámbrico', 'original': 'Original', 'clearance': '¡Remate!',\n 'sale': 'Oferta', 'deal': 'Ganga', 'phone': 'Celular', 'camera': 'Cámara',\n 'storage': 'Almacenamiento', 'fast': 'Rápido', 'pro': 'Pro', 'ultra': 'Ultra',\n 'black': 'Negro', 'white': 'Blanco', 'blue': 'Azul', 'red': 'Rojo',\n 'kit': 'Combo', 'pack': 'Paquete', 'new': 'Nuevo', 'off': 'de descuento',\n 'discount': 'Descuento', 'free': 'Gratis', 'shipping': 'Envío', 'tv': 'Televisor',\n 'ssd': 'Disco SSD', 'drive': 'Disco', 'leather': 'de Cuero', 'men': 'Hombre',\n 'women': 'Mujer', 'kids': 'Niños', 'sport': 'Deportivo', 'running': 'para Correr',\n 'speaker': 'Parlante', 'soundbar': 'Barra de Sonido', 'battery': 'Batería',\n 'charger': 'Cargador', 'cable': 'Cable', 'case': 'Estuche', 'tablet': 'Tablet',\n 'console': 'Consola', 'game': 'Juego', 'analog': 'Análogo', 'digital': 'Digital',\n 'multi-function': 'Multifunción', 'light': 'Luz', 'heavy': 'Pesado',\n 'and': 'y', 'for': 'para', 'with': 'con', 'set': 'Set de', 'inch': 'pulgadas'\n };\n\n let words = translated.split(' ');\n translated = words.map(w => {\n let clean = w.replace(/[^a-z-]/g, '');\n if (dict[clean]) return dict[clean];\n return w;\n }).join(' ');\n\n // Capitalizar la primera letra y limpiar espacios\n translated = translated.charAt(0).toUpperCase() + translated.slice(1);\n return translated.replace(/\\s+/g, ' ').trim();\n }\n\n cleanTitle(title) {\n if (!title) return \"Producto Exclusivo\";\n // Limpiar marcas de agua de tiendas o textos largos innecesarios\n return title\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/g, '')\n .split(' - ')[0]\n .split(' | ')[0]\n .trim();\n }\n\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n let description = deal.description || deal.originalDescription || '';\n\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n let formatted = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) formatted += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n formatted += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) formatted += `📉 AHORRO DEL ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) desc += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n desc += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) desc += `💰 AHORRA ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n desc += `${deal.title} representa una excelente oportunidad en ${storeName}. `;\n if (discount >= 30) desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n desc += `Producto verificado y en stock.\\n\\n`;\n desc += `✅ Producto auténtico\\n✅ Envío disponible\\n✅ Garantía del fabricante\\n`;\n if (deal.categoria) desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "4013bad8450ad2a25bea9c17404489bd183d96fb": {"t": 1785753901.0856946, "n": 8137, "h": "02aa8d40cd6c946c", "c": "const logger = require('../utils/logger');\nconst axios = require('axios');\n\n/**\n * AIProcessor: Optimiza títulos y descripciones para maximizar ventas\n */\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n }\n\n async generateOptimizedTitle(rawTitle) {\n // Recargar la llave por si no estaba lista en el constructor\n if (!this.apiKey) this.apiKey = process.env.OPENAI_API_KEY;\n\n if (!this.apiKey) {\n logger.warn(\"⚠️ OPENAI_API_KEY no detectada en .env. Usando traductor básico.\");\n return this.pseudoTranslate(rawTitle);\n }\n\n try {\n const prompt = `Actúa como un experto en Growth Hacking y Ventas para un canal de ofertas.\nConvierte este título de producto aburrido en un título MAGNÉTICO y CORTO (máximo 80 caracteres) que denote urgencia o gran oportunidad. \nUsa emojis adecuados. El idioma debe ser ESPAÑOL.\nSOLO responde con el nuevo título, nada más.\n\nTítulo original: ${rawTitle}`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: \"gpt-3.5-turbo\",\n messages: [{ role: \"user\", content: prompt }],\n temperature: 0.7,\n max_tokens: 60\n }, {\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json'\n },\n timeout: 5000\n });\n\n let optimized = response.data.choices[0].message.content.trim();\n // Limpiar si la IA agregó comillas o \"Título:\"\n optimized = optimized.replace(/^[\"']|[\"']$/g, '').replace(/^Título:\\s*/i, '');\n\n return optimized || this.pseudoTranslate(rawTitle);\n } catch (e) {\n logger.warn(`⚠️ OpenAI Title Error: ${e.message}. Usando fallback.`);\n return this.pseudoTranslate(rawTitle);\n }\n }\n\n pseudoTranslate(title) {\n if (!title) return \"Oferta Exclusiva\";\n\n let translated = title.toLowerCase();\n\n // 1. Reemplazos de frases comunes (Orden de importancia)\n const phrases = {\n 'noise canceling': 'con cancelación de ruido',\n 'noise cancelling': 'con cancelación de ruido',\n 'wireless bluetooth': 'Bluetooth inalámbrico',\n 'smart watch': 'Reloj Inteligente',\n 'fast charging': 'Carga rápida',\n 'stainless steel': 'Acero inoxidable',\n 'limited edition': 'Edición limitada',\n 'men\\'s': 'para Hombre',\n 'women\\'s': 'para Mujer',\n 'boy\\'s': 'para Niño',\n 'girl\\'s': 'para Niña',\n 'built-in': 'integrado',\n 'high definition': 'Alta definición',\n 'waterproof': 'Impermeable',\n 'shockproof': 'Resistente a golpes'\n };\n\n for (let [eng, esp] of Object.entries(phrases)) {\n translated = translated.replace(new RegExp(eng, 'g'), esp);\n }\n\n // 2. Diccionario de palabras sueltas\n const dict = {\n 'laptop': 'Portátil', 'purifier': 'Purificador', 'cool': 'Cool', 'fan': 'Ventilador',\n 'smart': 'Inteligente', 'air': 'de Aire', 'watch': 'Reloj', 'shoes': 'Tenis',\n 'sneakers': 'Tenis', 'headphones': 'Audífonos', 'earbuds': 'Audífonos',\n 'monitor': 'Monitor', 'gaming': 'Gamer', 'shirt': 'Camisa', 'pant': 'Pantalón',\n 'keyboard': 'Teclado', 'mouse': 'Mouse', 'cordless': 'Inalámbrico',\n 'wireless': 'Inalámbrico', 'original': 'Original', 'clearance': '¡Remate!',\n 'sale': 'Oferta', 'deal': 'Ganga', 'phone': 'Celular', 'camera': 'Cámara',\n 'storage': 'Almacenamiento', 'fast': 'Rápido', 'pro': 'Pro', 'ultra': 'Ultra',\n 'black': 'Negro', 'white': 'Blanco', 'blue': 'Azul', 'red': 'Rojo',\n 'kit': 'Combo', 'pack': 'Paquete', 'new': 'Nuevo', 'off': 'de descuento',\n 'discount': 'Descuento', 'free': 'Gratis', 'shipping': 'Envío', 'tv': 'Televisor',\n 'ssd': 'Disco SSD', 'drive': 'Disco', 'leather': 'de Cuero', 'men': 'Hombre',\n 'women': 'Mujer', 'kids': 'Niños', 'sport': 'Deportivo', 'running': 'para Correr',\n 'speaker': 'Parlante', 'soundbar': 'Barra de Sonido', 'battery': 'Batería',\n 'charger': 'Cargador', 'cable': 'Cable', 'case': 'Estuche', 'tablet': 'Tablet',\n 'console': 'Consola', 'game': 'Juego', 'analog': 'Análogo', 'digital': 'Digital',\n 'multi-function': 'Multifunción', 'light': 'Luz', 'heavy': 'Pesado',\n 'and': 'y', 'for': 'para', 'with': 'con', 'set': 'Set de', 'inch': 'pulgadas'\n };\n\n let words = translated.split(' ');\n translated = words.map(w => {\n let clean = w.replace(/[^a-z-]/g, '');\n if (dict[clean]) return dict[clean];\n return w;\n }).join(' ');\n\n // Capitalizar la primera letra y limpiar espacios\n translated = translated.charAt(0).toUpperCase() + translated.slice(1);\n return translated.replace(/\\s+/g, ' ').trim();\n }\n\n cleanTitle(title) {\n if (!title) return \"Producto Exclusivo\";\n // Limpiar marcas de agua de tiendas o textos largos innecesarios\n return title\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/g, '')\n .split(' - ')[0]\n .split(' | ')[0]\n .trim();\n }\n\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n let description = deal.description || deal.originalDescription || '';\n\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n let formatted = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) formatted += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n formatted += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) formatted += `📉 AHORRO DEL ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) desc += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n desc += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) desc += `💰 AHORRA ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n desc += `${deal.title} representa una excelente oportunidad en ${storeName}. `;\n if (discount >= 30) desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n desc += `Producto verificado y en stock.\\n\\n`;\n desc += `✅ Producto auténtico\\n✅ Envío disponible\\n✅ Garantía del fabricante\\n`;\n if (deal.categoria) desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "f1bd26244becdb4520046a1687a68a602e279c5b": {"t": 1785753902.330139, "n": 8046, "h": "53b8c52f3c19e338", "c": "const logger = require('../utils/logger');\nconst axios = require('axios');\n\n/**\n * AIProcessor: Optimiza títulos y descripciones para maximizar ventas\n */\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n }\n\n async generateOptimizedTitle(rawTitle) {\n // Recargar la llave por si no estaba lista en el constructor\n if (!this.apiKey) this.apiKey = process.env.OPENAI_API_KEY;\n\n if (!this.apiKey) {\n logger.warn(\"⚠️ OPENAI_API_KEY no detectada en .env. Usando traductor básico.\");\n return this.pseudoTranslate(rawTitle);\n }\n\n try {\n const prompt = `Actúa como un experto en Growth Hacking y Ventas para un canal de ofertas.\nConvierte este título de producto aburrido en un título MAGNÉTICO y CORTO (máximo 80 caracteres) que denote urgencia o gran oportunidad. \nUsa emojis adecuados. El idioma debe ser ESPAÑOL.\nSOLO responde con el nuevo título, nada más.\n\nTítulo original: ${rawTitle}`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: \"gpt-3.5-turbo\",\n messages: [{ role: \"user\", content: prompt }],\n temperature: 0.7,\n max_tokens: 60\n }, {\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json'\n },\n timeout: 5000\n });\n\n let optimized = response.data.choices[0].message.content.trim();\n // Limpiar si la IA agregó comillas o \"Título:\"\n optimized = optimized.replace(/^[\"']|[\"']$/g, '').replace(/^Título:\\s*/i, '');\n\n return optimized || this.pseudoTranslate(rawTitle);\n } catch (e) {\n logger.warn(`⚠️ OpenAI Title Error: ${e.message}. Usando fallback.`);\n return this.pseudoTranslate(rawTitle);\n }\n }\n\n pseudoTranslate(title) {\n if (!title) return \"Oferta Exclusiva\";\n\n let translated = title.toLowerCase();\n\n // 1. Reemplazos de frases comunes (Orden de importancia)\n const phrases = {\n 'noise canceling': 'con cancelación de ruido',\n 'noise cancelling': 'con cancelación de ruido',\n 'wireless bluetooth': 'Bluetooth inalámbrico',\n 'smart watch': 'Reloj Inteligente',\n 'fast charging': 'Carga rápida',\n 'stainless steel': 'Acero inoxidable',\n 'limited edition': 'Edición limitada',\n 'men\\'s': 'para Hombre',\n 'women\\'s': 'para Mujer',\n 'boy\\'s': 'para Niño',\n 'girl\\'s': 'para Niña',\n 'built-in': 'integrado',\n 'high definition': 'Alta definición',\n 'waterproof': 'Impermeable',\n 'shockproof': 'Resistente a golpes'\n };\n\n for (let [eng, esp] of Object.entries(phrases)) {\n translated = translated.replace(new RegExp(eng, 'g'), esp);\n }\n\n // 2. Diccionario de palabras sueltas\n const dict = {\n 'laptop': 'Portátil', 'purifier': 'Purificador', 'cool': 'Cool', 'fan': 'Ventilador',\n 'smart': 'Inteligente', 'air': 'de Aire', 'watch': 'Reloj', 'shoes': 'Tenis',\n 'sneakers': 'Tenis', 'headphones': 'Audífonos', 'earbuds': 'Audífonos',\n 'monitor': 'Monitor', 'gaming': 'Gamer', 'shirt': 'Camisa', 'pant': 'Pantalón',\n 'keyboard': 'Teclado', 'mouse': 'Mouse', 'cordless': 'Inalámbrico',\n 'wireless': 'Inalámbrico', 'original': 'Original', 'clearance': '¡Remate!',\n 'sale': 'Oferta', 'deal': 'Ganga', 'phone': 'Celular', 'camera': 'Cámara',\n 'storage': 'Almacenamiento', 'fast': 'Rápido', 'pro': 'Pro', 'ultra': 'Ultra',\n 'black': 'Negro', 'white': 'Blanco', 'blue': 'Azul', 'red': 'Rojo',\n 'kit': 'Combo', 'pack': 'Paquete', 'new': 'Nuevo', 'off': 'de descuento',\n 'discount': 'Descuento', 'free': 'Gratis', 'shipping': 'Envío', 'tv': 'Televisor',\n 'ssd': 'Disco SSD', 'drive': 'Disco', 'leather': 'de Cuero', 'men': 'Hombre',\n 'women': 'Mujer', 'kids': 'Niños', 'sport': 'Deportivo', 'running': 'para Correr',\n 'speaker': 'Parlante', 'soundbar': 'Barra de Sonido', 'battery': 'Batería',\n 'charger': 'Cargador', 'cable': 'Cable', 'case': 'Estuche', 'tablet': 'Tablet',\n 'console': 'Consola', 'game': 'Juego', 'analog': 'Análogo', 'digital': 'Digital',\n 'multi-function': 'Multifunción', 'light': 'Luz', 'heavy': 'Pesado'\n };\n\n let words = translated.split(' ');\n translated = words.map(w => {\n let clean = w.replace(/[^a-z-]/g, '');\n if (dict[clean]) return dict[clean];\n return w;\n }).join(' ');\n\n // Capitalizar la primera letra y limpiar espacios\n translated = translated.charAt(0).toUpperCase() + translated.slice(1);\n return translated.replace(/\\s+/g, ' ').trim();\n }\n\n cleanTitle(title) {\n if (!title) return \"Producto Exclusivo\";\n // Limpiar marcas de agua de tiendas o textos largos innecesarios\n return title\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/g, '')\n .split(' - ')[0]\n .split(' | ')[0]\n .trim();\n }\n\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n let description = deal.description || deal.originalDescription || '';\n\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n let formatted = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) formatted += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n formatted += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) formatted += `📉 AHORRO DEL ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) desc += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n desc += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) desc += `💰 AHORRA ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n desc += `${deal.title} representa una excelente oportunidad en ${storeName}. `;\n if (discount >= 30) desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n desc += `Producto verificado y en stock.\\n\\n`;\n desc += `✅ Producto auténtico\\n✅ Envío disponible\\n✅ Garantía del fabricante\\n`;\n if (deal.categoria) desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "3c86e8ffdaae323553466424d9327a2f80c9daa5": {"t": 1785753906.3196602, "n": 7443, "h": "cb256fce916baa97", "c": "const logger = require('../utils/logger');\nconst axios = require('axios');\n\n/**\n * AIProcessor: Optimiza títulos y descripciones para maximizar ventas\n */\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n }\n\n async generateOptimizedTitle(rawTitle) {\n // Recargar la llave por si no estaba lista en el constructor\n if (!this.apiKey) this.apiKey = process.env.OPENAI_API_KEY;\n\n if (!this.apiKey) {\n logger.warn(\"⚠️ OPENAI_API_KEY no detectada en .env. Usando traductor básico.\");\n return this.pseudoTranslate(rawTitle);\n }\n\n try {\n const prompt = `Actúa como un experto en Growth Hacking y Ventas para un canal de ofertas.\nConvierte este título de producto aburrido en un título MAGNÉTICO y CORTO (máximo 80 caracteres) que denote urgencia o gran oportunidad. \nUsa emojis adecuados. El idioma debe ser ESPAÑOL.\nSOLO responde con el nuevo título, nada más.\n\nTítulo original: ${rawTitle}`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: \"gpt-3.5-turbo\",\n messages: [{ role: \"user\", content: prompt }],\n temperature: 0.7,\n max_tokens: 60\n }, {\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json'\n },\n timeout: 5000\n });\n\n let optimized = response.data.choices[0].message.content.trim();\n // Limpiar si la IA agregó comillas o \"Título:\"\n optimized = optimized.replace(/^[\"']|[\"']$/g, '').replace(/^Título:\\s*/i, '');\n\n return optimized || this.pseudoTranslate(rawTitle);\n } catch (e) {\n logger.warn(`⚠️ OpenAI Title Error: ${e.message}. Usando fallback.`);\n return this.pseudoTranslate(rawTitle);\n }\n }\n\n pseudoTranslate(title) {\n if (!title) return \"Oferta Exclusiva\";\n let clean = this.cleanTitle(title);\n\n // Diccionario de \"IA de Emergencia\" (Traducción rápida)\n const dict = {\n 'laptop': 'Portátil',\n 'purifier': 'Purificador',\n 'cool': 'Refrescante',\n 'fan': 'Ventilador',\n 'smart': 'Inteligente',\n 'air': 'de Aire',\n 'watch': 'Reloj',\n 'shoes': 'Tenis',\n 'sneakers': 'Tenis',\n 'headphones': 'Audífonos',\n 'earbuds': 'Audífonos Bluetooth',\n 'monitor': 'Monitor Gamer',\n 'gaming': 'para Gaming',\n 'shirt': 'Camisa',\n 'pant': 'Pantalón',\n 'keyboard': 'Teclado',\n 'mouse': 'Mouse',\n 'cordless': 'Inalámbrico',\n 'wireless': 'Inalámbrico',\n 'original': 'Original',\n 'clearance': '¡LIQUIDACIÓN!',\n 'sale': 'OFERTA',\n 'deal': 'Ganga',\n 'smartwatch': 'Reloj Inteligente',\n 'phone': 'Celular',\n 'camera': 'Cámara',\n 'storage': 'Almacenamiento',\n 'fast': 'Rápido',\n 'pro': 'Premium',\n 'ultra': 'Ultra',\n 'black': 'Negro',\n 'white': 'Blanco',\n 'blue': 'Azul',\n 'red': 'Rojo',\n 'kit': 'Combo',\n 'pack': 'Paquete',\n 'new': 'Nuevo',\n 'off': 'de descuento',\n 'discount': 'Descuento',\n 'free': 'Gratis',\n 'shipping': 'Envío',\n 'tv': 'Televisor',\n 'ssd': 'Disco SSD',\n 'drive': 'Unidad',\n 'leather': 'de Cuero',\n 'men': 'para Hombre',\n 'women': 'para Mujer',\n 'kids': 'para Niños',\n 'sport': 'Deportivo',\n 'running': 'para Correr',\n 'speaker': 'Parlante',\n 'soundbar': 'Barra de Sonido',\n 'camera': 'Cámara',\n 'battery': 'Batería',\n 'charger': 'Cargador',\n 'cable': 'Cable',\n 'case': 'Estuche',\n 'tablet': 'Tablet',\n 'console': 'Consola',\n 'game': 'Juego'\n };\n\n let words = clean.split(' ');\n let translated = words.map(w => {\n let low = w.toLowerCase().replace(/[^a-z]/g, '');\n return dict[low] || w;\n }).join(' ');\n\n return translated;\n }\n\n cleanTitle(title) {\n if (!title) return \"Producto Exclusivo\";\n // Limpiar marcas de agua de tiendas o textos largos innecesarios\n return title\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/g, '')\n .split(' - ')[0]\n .split(' | ')[0]\n .trim();\n }\n\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n let description = deal.description || deal.originalDescription || '';\n\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n let formatted = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) formatted += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n formatted += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) formatted += `📉 AHORRO DEL ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) desc += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n desc += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) desc += `💰 AHORRA ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n desc += `${deal.title} representa una excelente oportunidad en ${storeName}. `;\n if (discount >= 30) desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n desc += `Producto verificado y en stock.\\n\\n`;\n desc += `✅ Producto auténtico\\n✅ Envío disponible\\n✅ Garantía del fabricante\\n`;\n if (deal.categoria) desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "76ea7055031e77456aae409998e8b273923ca93a": {"t": 1785753907.805948, "n": 6935, "h": "3b3c5686ccffc581", "c": "const logger = require('../utils/logger');\nconst axios = require('axios');\n\n/**\n * AIProcessor: Optimiza títulos y descripciones para maximizar ventas\n */\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n }\n\n async generateOptimizedTitle(rawTitle) {\n // Recargar la llave por si no estaba lista en el constructor\n if (!this.apiKey) this.apiKey = process.env.OPENAI_API_KEY;\n\n if (!this.apiKey) {\n logger.warn(\"⚠️ OPENAI_API_KEY no detectada en .env. Usando traductor básico.\");\n return this.pseudoTranslate(rawTitle);\n }\n\n try {\n const prompt = `Actúa como un experto en Growth Hacking y Ventas para un canal de ofertas.\nConvierte este título de producto aburrido en un título MAGNÉTICO y CORTO (máximo 80 caracteres) que denote urgencia o gran oportunidad. \nUsa emojis adecuados. El idioma debe ser ESPAÑOL.\nSOLO responde con el nuevo título, nada más.\n\nTítulo original: ${rawTitle}`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: \"gpt-3.5-turbo\",\n messages: [{ role: \"user\", content: prompt }],\n temperature: 0.7,\n max_tokens: 60\n }, {\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json'\n },\n timeout: 5000\n });\n\n let optimized = response.data.choices[0].message.content.trim();\n // Limpiar si la IA agregó comillas o \"Título:\"\n optimized = optimized.replace(/^[\"']|[\"']$/g, '').replace(/^Título:\\s*/i, '');\n\n return optimized || this.pseudoTranslate(rawTitle);\n } catch (e) {\n logger.warn(`⚠️ OpenAI Title Error: ${e.message}. Usando fallback.`);\n return this.pseudoTranslate(rawTitle);\n }\n }\n\n pseudoTranslate(title) {\n if (!title) return \"Oferta Exclusiva\";\n let clean = this.cleanTitle(title);\n\n // Diccionario de \"IA de Emergencia\" (Traducción rápida)\n const dict = {\n 'laptop': 'Portátil',\n 'watch': 'Reloj',\n 'shoes': 'Tenis',\n 'sneakers': 'Tenis',\n 'headphones': 'Audífonos',\n 'earbuds': 'Audífonos Bluetooth',\n 'monitor': 'Monitor Gamer',\n 'gaming': 'para Gaming',\n 'shirt': 'Camisa',\n 'pant': 'Pantalón',\n 'keyboard': 'Teclado',\n 'mouse': 'Mouse',\n 'cordless': 'Inalámbrico',\n 'wireless': 'Inalámbrico',\n 'original': 'Original',\n 'clearance': '¡LIQUIDACIÓN!',\n 'sale': 'OFERTA',\n 'deal': 'Ganga',\n 'smartwatch': 'Reloj Inteligente',\n 'phone': 'Celular',\n 'camera': 'Cámara',\n 'storage': 'Almacenamiento',\n 'fast': 'Rápido',\n 'pro': 'Premium',\n 'ultra': 'Ultra',\n 'black': 'Negro',\n 'white': 'Blanco',\n 'blue': 'Azul',\n 'red': 'Rojo',\n 'kit': 'Combo',\n 'pack': 'Paquete',\n 'new': 'Nuevo',\n 'off': 'de descuento',\n 'discount': 'Descuento',\n 'free': 'Gratis',\n 'shipping': 'Envío',\n 'tv': 'Televisor',\n 'ssd': 'Disco SSD',\n 'drive': 'Unidad',\n 'leather': 'de Cuero',\n 'men': 'para Hombre',\n 'women': 'para Mujer',\n 'kids': 'para Niños',\n 'sport': 'Deportivo',\n 'running': 'para Correr'\n };\n\n let words = clean.split(' ');\n let translated = words.map(w => {\n let low = w.toLowerCase().replace(/[^a-z]/g, '');\n return dict[low] || w;\n }).join(' ');\n\n return translated;\n }\n\n cleanTitle(title) {\n if (!title) return \"Producto Exclusivo\";\n // Limpiar marcas de agua de tiendas o textos largos innecesarios\n return title\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/g, '')\n .split(' - ')[0]\n .split(' | ')[0]\n .trim();\n }\n\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n let description = deal.description || deal.originalDescription || '';\n\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n let formatted = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) formatted += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n formatted += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) formatted += `📉 AHORRO DEL ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) desc += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n desc += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) desc += `💰 AHORRA ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n desc += `${deal.title} representa una excelente oportunidad en ${storeName}. `;\n if (discount >= 30) desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n desc += `Producto verificado y en stock.\\n\\n`;\n desc += `✅ Producto auténtico\\n✅ Envío disponible\\n✅ Garantía del fabricante\\n`;\n if (deal.categoria) desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "9f59a0c059ccb06780178ee086f3d93fdcdc4fd1": {"t": 1785753908.941009, "n": 4694, "h": "7f6eec2b694b50f5", "c": "const logger = require('../utils/logger');\nconst axios = require('axios');\n\n/**\n * AIProcessor: Optimiza títulos y descripciones para maximizar ventas\n */\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n }\n\n async generateOptimizedTitle(rawTitle) {\n if (!this.apiKey) return this.cleanTitle(rawTitle);\n\n try {\n const prompt = `Actúa como un experto en Growth Hacking y Ventas para un canal de ofertas.\nConvierte este título de producto aburrido en un título MAGNÉTICO y CORTO (máximo 80 caracteres) que denote urgencia o gran oportunidad. \nUsa emojis adecuados. El idioma debe ser ESPAÑOL.\nSOLO responde con el nuevo título, nada más.\n\nTítulo original: ${rawTitle}`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: \"gpt-3.5-turbo\",\n messages: [{ role: \"user\", content: prompt }],\n temperature: 0.7,\n max_tokens: 60\n }, {\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json'\n },\n timeout: 5000\n });\n\n let optimized = response.data.choices[0].message.content.trim();\n // Limpiar si la IA agregó comillas o \"Título:\"\n optimized = optimized.replace(/^[\"']|[\"']$/g, '').replace(/^Título:\\s*/i, '');\n\n return optimized || this.cleanTitle(rawTitle);\n } catch (e) {\n logger.warn(`⚠️ OpenAI Title Error: ${e.message}. Usando fallback.`);\n return this.cleanTitle(rawTitle);\n }\n }\n\n cleanTitle(title) {\n if (!title) return \"Producto Exclusivo\";\n // Limpiar marcas de agua de tiendas o textos largos innecesarios\n return title\n .replace(/Amazon.com\\s*:|Wal-Mart\\s*:|eBay\\s*:/g, '')\n .split(' - ')[0]\n .split(' | ')[0]\n .trim();\n }\n\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n let description = deal.description || deal.originalDescription || '';\n\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n let formatted = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) formatted += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n formatted += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) formatted += `📉 AHORRO DEL ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title}\\n`;\n if (deal.coupon) desc += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n desc += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n if (discount > 0) desc += `💰 AHORRA ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n\n desc += `${deal.title} representa una excelente oportunidad en ${storeName}. `;\n if (discount >= 30) desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n desc += `Producto verificado y en stock.\\n\\n`;\n desc += `✅ Producto auténtico\\n✅ Envío disponible\\n✅ Garantía del fabricante\\n`;\n if (deal.categoria) desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "6b0cfb78ad2f0883c3531d6af169a66891f28a6d": {"t": 1785753910.096456, "n": 3644, "h": "c2ca6728e6abf58b", "c": "const logger = require('../utils/logger');\n\n/**\n * AIProcessor: Extrae y formatea descripciones directamente del producto\n * Sin necesidad de OpenAI - usa la info real del scraping\n */\nclass AIProcessor {\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n // Usar la descripción ya extraída del scraping o generar una básica\n let description = deal.description || deal.originalDescription || '';\n\n // Si no hay descripción del scraping, generar una profesional\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n // Limpiar y formatear la descripción extraída\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n // Limpiar HTML y caracteres especiales\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n // Limitar longitud\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n // Agregar encabezado con precio\n let formatted = `🔥 [${storeName}] ${deal.title}\\n`;\n\n if (deal.coupon) {\n formatted += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n }\n\n formatted += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n\n if (discount > 0) {\n formatted += `📉 AHORRO DEL ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n }\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title}\\n`;\n\n if (deal.coupon) {\n desc += `🎟️ APLICA CUPÓN: ${deal.coupon}\\n`;\n }\n\n desc += `💰 PRECIO: $${deal.price_offer}\\n\\n`;\n\n if (discount > 0) {\n desc += `💰 AHORRA ${discount}% - Antes: $${deal.price_official}\\n\\n`;\n }\n\n // Descripción profesional basada en el título\n desc += `${deal.title} representa una excelente oportunidad en ${storeName}. `;\n\n if (discount >= 30) {\n desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n }\n\n if (deal.coupon) {\n desc += `Asegúrate de aplicar el cupón ${deal.coupon} al finalizar tu compra. `;\n }\n\n desc += `Producto verificado y en stock.\\n\\n`;\n\n // Beneficios genéricos\n desc += `✅ Producto auténtico\\n`;\n desc += `✅ Envío disponible\\n`;\n desc += `✅ Garantía del fabricante\\n`;\n\n if (deal.categoria) {\n desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n }\n\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "a75b5cff78368515ee55307cea0e82290a5de8c8": {"t": 1785753911.500143, "n": 3351, "h": "dce7e7354d099e69", "c": "const logger = require('../utils/logger');\n\n/**\n * AIProcessor: Extrae y formatea descripciones directamente del producto\n * Sin necesidad de OpenAI - usa la info real del scraping\n */\nclass AIProcessor {\n async generateViralContent(deal) {\n try {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n // Usar la descripción ya extraída del scraping o generar una básica\n let description = deal.description || deal.originalDescription || '';\n\n // Si no hay descripción del scraping, generar una profesional\n if (!description || description.length < 50) {\n description = this.generateProfessionalDescription(deal, discount, storeName);\n } else {\n // Limpiar y formatear la descripción extraída\n description = this.formatScrapedDescription(description, deal, discount, storeName);\n }\n\n return { content: description };\n } catch (e) {\n logger.error(`Error en AIProcessor: ${e.message}`);\n return { content: this.generateProfessionalDescription(deal, 0, 'USA Store') };\n }\n }\n\n formatScrapedDescription(rawDesc, deal, discount, storeName) {\n // Limpiar HTML y caracteres especiales\n let clean = rawDesc\n .replace(/<[^>]*>/g, '')\n .replace(/\\s+/g, ' ')\n .replace(/[\\u0000-\\u001F\\u007F-\\u009F]/g, '')\n .trim();\n\n // Limitar longitud\n if (clean.length > 400) {\n clean = clean.substring(0, 397) + '...';\n }\n\n // Agregar encabezado con precio\n let formatted = `🔥 [${storeName}] ${deal.title} – $${deal.price_offer}\\n\\n`;\n\n if (discount > 0) {\n formatted += `💰 AHORRO DEL ${discount}% - Precio original: $${deal.price_official}\\n\\n`;\n }\n\n formatted += clean;\n formatted += `\\n\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return formatted;\n }\n\n generateProfessionalDescription(deal, discount, storeName) {\n let desc = `🔥 [${storeName}] ${deal.title} – $${deal.price_offer}\\n\\n`;\n\n if (discount > 0) {\n desc += `💰 AHORRA ${discount}% - Precio especial de $${deal.price_offer} `;\n desc += `(antes $${deal.price_official})\\n\\n`;\n }\n\n // Descripción profesional basada en el título\n desc += `${deal.title} representa una oportunidad excepcional en el mercado actual. `;\n\n if (discount >= 30) {\n desc += `Con un descuento del ${discount}%, este es uno de los mejores precios disponibles. `;\n }\n\n desc += `Producto verificado y en stock en ${storeName}.\\n\\n`;\n\n // Beneficios genéricos\n desc += `✅ Producto auténtico\\n`;\n desc += `✅ Envío disponible\\n`;\n desc += `✅ Garantía del fabricante\\n`;\n\n if (deal.categoria) {\n desc += `\\n📦 Categoría: ${deal.categoria}\\n`;\n }\n\n desc += `\\n⚠️ Oferta por tiempo limitado. Stock sujeto a disponibilidad.`;\n\n return desc;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "596d3da15d33e343491ddfd01bc4554397b327eb": {"t": 1785753912.856618, "n": 2473, "h": "a7744d8934f81a9d", "c": "const axios = require('axios');\nconst logger = require('../utils/logger');\nrequire('dotenv').config();\n\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n this.model = 'gpt-4o';\n }\n\n async generateViralContent(deal) {\n const storeName = (deal.tienda || 'Oferta').toUpperCase();\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n if (!this.apiKey || this.apiKey === 'tu_key_aqui') {\n return { content: this.fallbackEditorial(deal, discount, storeName) };\n }\n\n try {\n const systemPrompt = `Eres un redactor editorial experto de MasbaratoDeals. Estilo profesional y persuasivo.`;\n const userPrompt = `\nGenera un post editorial para:\n- Producto: ${deal.title}\n- Tienda: ${storeName}\n- Precio Final: $${deal.price_offer}\n- Precio Original: $${deal.price_official || '---'}\n- Descuento: ${discount}%\n\nEstructura:\n1. Título: 🔥 [${storeName}] ${deal.title} – $${deal.price_offer}\n2. Análisis de oportunidad (150 palabras).\n`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: this.model,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n max_tokens: 800\n }, {\n headers: { 'Authorization': `Bearer ${this.apiKey}` }\n });\n\n return { content: response.data.choices[0].message.content };\n } catch (error) {\n logger.error(`Error IA: ${error.message}`);\n return { content: this.fallbackEditorial(deal, discount, storeName) };\n }\n }\n\n fallbackEditorial(deal, discount, storeName) {\n return `🔥 [${storeName}] ${deal.title} – $${deal.price_offer}\n \nEste ${deal.title} representa una oportunidad excepcional en el mercado actual. Con un descuento verificado, se posiciona como una de las mejores opciones en cuanto a relación calidad-precio.\n\nLa durabilidad y el rendimiento de este modelo han sido destacados en múltiples análisis técnicos, lo que garantiza una compra segura. Para aprovechar este precio, haz clic en el botón de abajo. Stock limitado.`;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "5946473408fac339066fb780915518b3407d71c9": {"t": 1785753914.4279616, "n": 4274, "h": "06ee4f54211df612", "c": "const axios = require('axios');\nconst logger = require('../utils/logger');\nrequire('dotenv').config();\n\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n this.model = 'gpt-4o'; // Recomendado para contenido editorial de alta calidad\n }\n\n async generateViralContent(deal) {\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n // Si no hay API Key, usamos un fallback editorial básico\n if (!this.apiKey || this.apiKey === 'tu_key_aqui') {\n return { content: this.fallbackEditorial(deal, discount) };\n }\n\n try {\n const systemPrompt = `Eres un redactor editorial experto de MasbaratoDeals, un agregador de ofertas premium en español. \nTu estilo es informativo, profesional y persuasivo, similar a Wirecutter o Xataka.\nNO copies textos de terceros. Crea contenido 100% original.`;\n\n const userPrompt = `\nGenera un post editorial para el siguiente producto:\n- Producto: ${deal.title}\n- Tienda: ${deal.tienda}\n- Precio Final: $${deal.price_offer}\n- Precio Original: $${deal.price_official || 'N/A'}\n- Descuento: ${discount}%\n${deal.coupon ? `- Cupón/Código: ${deal.coupon}` : ''}\n\nREGLAS DE ESTRUCTURA (OBLIGATORIO):\n1. Título EXACTO: 🔥 [${deal.tienda.toUpperCase()}] ${deal.title} – $${deal.price_offer} (Antes $${deal.price_official || '---'}) – ${discount}%OFF\n2. Cuerpo (150-220 palabras estrictas): \n - Una introducción sobre qué es el producto.\n - Un análisis de por qué es una buena oportunidad.\n - Instrucciones de cómo aplicar el ahorro.\n - Perfil ideal del comprador.\n - Nota sobre vigencia.\n3. Disclaimer: Mención sutil de independencia.\n\nUsa español neutro. NO menciones Slickdeals.`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: this.model,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n max_tokens: 800,\n temperature: 0.7\n }, {\n headers: { 'Authorization': `Bearer ${this.apiKey}` }\n });\n\n return { content: response.data.choices[0].message.content };\n } catch (error) {\n logger.error(`Error en IAProcessor Editorial: ${error.message}`);\n return { content: this.fallbackEditorial(deal, discount) };\n }\n }\n\n fallbackEditorial(deal, discount) {\n return `🔥 [${deal.tienda.toUpperCase()}] ${deal.title} – $${deal.price_offer} (Antes $${deal.price_official || '---'}) – ${discount}%OFF\n\nEl ${deal.title} es una solución líder en su categoría, diseñada para simplificar las tareas diarias con una eficiencia superior. Este producto destaca por su durabilidad y la calidad de sus componentes, lo que lo convierte en una inversión inteligente para cualquier hogar o usuario profesional que busque resultados consistentes a largo plazo.\n\nEn cuanto a su valor de mercado, esta es una oportunidad excepcional. Normalmente, este artículo se encuentra a un precio bastante más elevado, pero gracias a la oferta actual en ${deal.tienda}, puedes adquirirlo con un ahorro del ${discount}%. Es una de las rebajas más significativas detectadas en las últimas semanas, lo que lo posiciona como una \"mejor compra\" dentro de su segmento.\n\nPara aprovechar este precio, simplemente debes seguir el enlace a la tienda oficial, donde el descuento ya está aplicado o se verá reflejado al finalizar tu pedido. No requiere códigos adicionales complejos, lo que facilita enormemente el proceso de compra. Este artículo es ideal para quienes valoran la relación calidad-precio y desean adquirir tecnología o productos de consumo masivo con garantías oficiales.\n\nTen en cuenta que estas ofertas son volátiles y están sujetas a la disponibilidad de stock en el sitio de destino. \n\n👉 Ver oferta\n💡 Precio sujeto a cambios y disponibilidad. MasbaratoDeals participa en programas de afiliación para mantener su operación independiente.`;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "3850eeb242be9505861ce2fa74d4ee1643f77970": {"t": 1785753915.545537, "n": 3130, "h": "b60c08ae76d88591", "c": "const axios = require('axios');\nconst logger = require('../utils/logger');\nrequire('dotenv').config();\n\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n this.model = 'gpt-3.5-turbo'; // O gpt-4o si tiene saldo\n }\n\n /**\n * MÉTODO INTEGRAL DE GENERACIÓN VIRAL\n * Sincronizado con CoreProcessor.js\n */\n async generateViralContent(deal) {\n // Calcular descuento si no viene pre-calculado\n const discount = (deal.price_official && deal.price_offer && deal.price_official > deal.price_offer)\n ? Math.round(((deal.price_official - deal.price_offer) / deal.price_official) * 100)\n : 0;\n\n if (!this.apiKey || this.apiKey === 'tu_key_aqui') {\n return { content: this.fallbackRewrite(deal, discount) };\n }\n\n try {\n const isHistoric = deal.is_historic_low || deal.badge === 'MÍNIMO HISTÓRICO';\n let systemPrompt = `Actúa como un Director de Marketing experto en Ventas para Telegram.`;\n\n let userPrompt = `\nOBJETIVO: Redactar una promoción irresistible.\n\nDATOS:\n- Producto: ${deal.title}\n- Precio Normal: $${deal.price_official || 'N/A'}\n- Precio Hoy: $${deal.price_offer}\n- Ahorro: ${discount}%\n- Tienda: ${deal.tienda}\n${deal.coupon ? `- CUPÓN: ${deal.coupon}` : ''}\n\nSALIDA (HTML): 🚀 ¡OFERTA EN ${deal.tienda.toUpperCase()}!\n📦 Producto: ${deal.title}\n💰 Antes: $${deal.price_official || '---'}\n🔥 HOY: $${deal.price_offer}\n📉 AHORRO: ${discount}%\n\n${deal.coupon ? `🎟️ CUPÓN: ${deal.coupon}` : ''}\n👉 VER OFERTA: [link]\n#MasbaratoDeals #OfertasUSA`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: this.model,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7\n }, {\n headers: { 'Authorization': `Bearer ${this.apiKey}` }\n });\n\n return { content: response.data.choices[0].message.content };\n } catch (error) {\n logger.error(`Error en IAProcessor: ${error.message}`);\n return { content: this.fallbackRewrite(deal, discount) };\n }\n }\n\n fallbackRewrite(deal, discount) {\n const ahorro = (deal.price_official && deal.price_official > deal.price_offer) ? (deal.price_official - deal.price_offer) : 0;\n const compHtml = deal.price_official > 0 ? `$${deal.price_official.toLocaleString()}` : '---';\n\n return `🚀 ¡NUEVA OFERTA EN ${deal.tienda.toUpperCase()}!\n\n🔥 ${deal.title.toUpperCase()}\n\n💰 Antes: ${compHtml}\n✅ PRECIO HOY: $${deal.price_offer.toLocaleString()}\n📉 DESCUENTO: ${discount}%\n\n⭐ Oferta verificada por el equipo +BARATO DEALS\n━━━━━━━━━━━━━━━━━━\n👉 COMPRA AQUÍ: ${deal.link}\n━━━━━━━━━━━━━━━━━━\n\n#MasbaratoDeals #OfertasUSA #${deal.tienda.replace(/\\s+/g, '')}`;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "15ff9c707a346938d2ae2f9664a30f7bd76ef8a0": {"t": 1785753916.950045, "n": 3880, "h": "c00e0fc5587d6041", "c": "const axios = require('axios');\nconst logger = require('../utils/logger');\nrequire('dotenv').config();\n\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n this.model = 'gpt-3.5-turbo'; // O gpt-4o si tiene saldo\n }\n\n async rewriteViral(deal, discount) {\n if (!this.apiKey || this.apiKey === 'tu_key_aqui') {\n return this.fallbackRewrite(deal, discount);\n }\n\n try {\n const isHistoric = deal.isHistoricLow;\n\n let systemPrompt = `Actúa como un Director de Marketing experto en Ventas y Psicología del Consumidor para Telegram.`;\n\n let userPrompt = `\nOBJETIVO: Redactar una promoción irresistible.\n\nDATOS:\n- Producto: ${deal.title}\n- Precio Normal: $${deal.price_official}\n- Precio Hoy: $${deal.price_offer}\n- Descuento Directo: ${discount}%\n- Tienda: ${deal.tienda}\n${deal.coupon ? `- CUPÓN: ${deal.coupon} (MUY IMPORTANTE)` : ''}\n${isHistoric ? '- CONTEXTO: ¡ES EL PRECIO MÁS BAJO DE LA HISTORIA (ALL TIME LOW)! 🔥💎' : '- Contexto: Buen descuento verificado.'}\n\nESTRATEGIA DE MENSAJE:\n1. Tono: Urgente pero profesional. \"Alguien se equivocó con este precio\".\n2. ${isHistoric ? 'DESTACA EN NEGRITA QUE ES MÍNIMO HISTÓRICO.' : 'Destaca el ahorro.'}\n3. ${deal.coupon ? 'MENCIONA EL CUPÓN CLARAMENTE PARA QUE EL USUARIO LO COPIE.' : ''}\n4. Usa emojis estratégicos (🔥, 💎, 🚨, 📉).\n\nSALIDA (Formato HTML estricto):\n${isHistoric ? '🚨 ¡MÍNIMO HISTÓRICO DETECTADO!' : '🚀 ¡[TITULO EXPLOSIVO]!'}\n\n📦 Producto: ${deal.title}\n🏢 Tienda: ${deal.tienda}\n\n💰 Antes: $${deal.price_official}\n🔥 PÁGALO POR SOLO: $${deal.price_offer}\n📉 AHORRO: ${discount}% ${isHistoric ? '(¡PRECIO JAMÁS VISTO!)' : ''}\n\n${deal.coupon ? `🎟️ USA EL CUPÓN: ${deal.coupon} (Toca para copiar)` : ''}\n\n⭐ ${isHistoric ? '💎 Oportunidad Única Verificada (ATL)' : 'Oportunidad Verificada por el equipo +BARATO'}\n━━━━━━━━━━━━━━━━━━\n👉 VER OFERTA AQUÍ: [link]\n━━━━━━━━━━━━━━━━━━\n\n#MasbaratoDeals #OfertasUSA ${isHistoric ? '#MinimoHistorico #Ganga' : '#Descuentos'}`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: this.model,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7\n }, {\n headers: { 'Authorization': `Bearer ${this.apiKey}` }\n });\n\n const content = response.data.choices[0].message.content;\n // Fallback de seguridad por si la IA alucina y olvida el cupón\n if (deal.coupon && !content.includes(deal.coupon)) {\n return content.replace('━━━━━━━━━━━━━━━━━━', `🎟️ CUPÓN EXTRA: ${deal.coupon}\\n━━━━━━━━━━━━━━━━━━`);\n }\n return content;\n } catch (error) {\n logger.error(`Error en IAProcessor: ${error.message}`);\n return this.fallbackRewrite(deal, discount);\n }\n }\n\n fallbackRewrite(deal, discount) {\n const ahorro = deal.price_official - deal.price_offer;\n const ahorroPorcentaje = discount || Math.round((ahorro / deal.price_official) * 100);\n\n return `🚀 ¡OFERTA DETECTADA EN ${deal.tienda.toUpperCase()}!\n\n🔥 ${deal.title.toUpperCase()}\n\n💰 Precio Normal: $${deal.price_official.toLocaleString()}\n✅ PRECIO HOY: $${deal.price_offer.toLocaleString()}\n📉 AHORRAS: $${ahorro.toLocaleString()} (${ahorroPorcentaje}%)\n\n⭐ Oferta exclusiva verificada por +BARATO DEALS\n━━━━━━━━━━━━━━━━━━\n👉 COMPRA AQUÍ: ${deal.link}\n━━━━━━━━━━━━━━━━━━\n\n#MasbaratoDeals #OfertasUSA #${deal.tienda.replace(/\\s+/g, '')} #Ahorro`;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "ae6c2e087ce84c82b0693c267314ac52841af652": {"t": 1785753919.2880661, "n": 3305, "h": "92a799795a047235", "c": "const axios = require('axios');\nconst logger = require('../utils/logger');\nrequire('dotenv').config();\n\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n this.model = 'gpt-3.5-turbo'; // O gpt-4o si tiene saldo\n }\n\n async rewriteViral(deal, discount) {\n if (!this.apiKey || this.apiKey === 'tu_key_aqui') {\n return this.fallbackRewrite(deal, discount);\n }\n\n try {\n const isHistoric = deal.isHistoricLow;\n\n let systemPrompt = `Actúa como un Director de Marketing experto en Ventas y Psicología del Consumidor para Telegram.`;\n\n let userPrompt = `\nOBJETIVO: Redactar una promoción irresistible.\n\nDATOS:\n- Producto: ${deal.title}\n- Precio Normal: $${deal.price_official}\n- Precio Hoy: $${deal.price_offer}\n- Descuento Directo: ${discount}%\n- Tienda: ${deal.tienda}\n${isHistoric ? '- CONTEXTO: ¡ES EL PRECIO MÁS BAJO DE LA HISTORIA (ALL TIME LOW)! 🔥💎' : '- Contexto: Buen descuento verificado.'}\n\nESTRATEGIA DE MENSAJE:\n1. Tono: Urgente pero profesional. \"Alguien se equivocó con este precio\".\n2. ${isHistoric ? 'DESTACA EN NEGRITA QUE ES MÍNIMO HISTÓRICO.' : 'Destaca el ahorro.'}\n3. Usa emojis estratégicos (🔥, 💎, 🚨, 📉).\n\nSALIDA (Formato HTML estricto):\n${isHistoric ? '🚨 ¡MÍNIMO HISTÓRICO DETECTADO!' : '🚀 ¡[TITULO EXPLOSIVO]!'}\n\n📦 Producto: ${deal.title}\n🏢 Tienda: ${deal.tienda}\n\n💰 Antes: $${deal.price_official}\n🔥 PÁGALO POR SOLO: $${deal.price_offer}\n📉 AHORRO: ${discount}% ${isHistoric ? '(¡PRECIO JAMÁS VISTO!)' : ''}\n\n⭐ ${isHistoric ? '💎 Oportunidad Única Verificada (ATL)' : 'Oportunidad Verificada por el equipo +BARATO'}\n━━━━━━━━━━━━━━━━━━\n👉 VER OFERTA AQUÍ: [link]\n━━━━━━━━━━━━━━━━━━\n\n#MasbaratoDeals #OfertasUSA ${isHistoric ? '#MinimoHistorico #Ganga' : '#Descuentos'}`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: this.model,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7\n }, {\n headers: { 'Authorization': `Bearer ${this.apiKey}` }\n });\n\n return response.data.choices[0].message.content;\n } catch (error) {\n logger.error(`Error en IAProcessor: ${error.message}`);\n return this.fallbackRewrite(deal, discount);\n }\n }\n\n fallbackRewrite(deal, discount) {\n const ahorro = deal.price_official - deal.price_offer;\n const ahorroPorcentaje = discount || Math.round((ahorro / deal.price_official) * 100);\n\n return `🚀 ¡OFERTA DETECTADA EN ${deal.tienda.toUpperCase()}!\n\n🔥 ${deal.title.toUpperCase()}\n\n💰 Precio Normal: $${deal.price_official.toLocaleString()}\n✅ PRECIO HOY: $${deal.price_offer.toLocaleString()}\n📉 AHORRAS: $${ahorro.toLocaleString()} (${ahorroPorcentaje}%)\n\n⭐ Oferta exclusiva verificada por +BARATO DEALS\n━━━━━━━━━━━━━━━━━━\n👉 COMPRA AQUÍ: ${deal.link}\n━━━━━━━━━━━━━━━━━━\n\n#MasbaratoDeals #OfertasUSA #${deal.tienda.replace(/\\s+/g, '')} #Ahorro`;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "7af472063c30690adcaebd92789dc93dc443d5ca": {"t": 1785753921.3530905, "n": 2977, "h": "0768988f4f7634ab", "c": "const axios = require('axios');\nconst logger = require('../utils/logger');\nrequire('dotenv').config();\n\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n this.model = 'gpt-3.5-turbo'; // O gpt-4o si tiene saldo\n }\n\n async rewriteViral(deal, discount) {\n if (!this.apiKey || this.apiKey === 'tu_key_aqui') {\n return this.fallbackRewrite(deal, discount);\n }\n\n try {\n const prompt = `Actúa como un Director de Marketing experto en Ventas y Psicología del Consumidor.\nTu objetivo es redactar una promoción irresistible para Telegram sobre un \"Chollazo Histórico\".\n\nESTRATEGIA DE VENTA:\n1. Usa gatillos de ESCASEZ y URGENCIA (¡Liquidación!, ¡Solo hoy!, ¡Se agotan!).\n2. Enfócate en la PRUEBA SOCIAL: Menciona que es una oportunidad verificada manualmente.\n3. El tono debe ser profesional pero electrizante, como alguien que acaba de descubrir un error de precio.\n4. Indica que este precio rompe el mercado comparado con el histórico.\n\nDATOS:\n- Producto: ${deal.title}\n- Precio Normal: $${deal.price_official}\n- Precio Hoy: $${deal.price_offer}\n- Descuento Directo: ${discount}%\n- Tienda: ${deal.tienda}\n\nSALIDA (Formato HTML):\n🚀 ¡[TITULO EXPLOSIVO]!\n\n📦 Producto: ${deal.title}\n🏢 Tienda: ${deal.tienda}\n\n💰 Antes: $${deal.price_official}\n🔥 PÁGALO POR SOLO: $${deal.price_offer}\n📉 AHORRO TOTAL: $${discount}% (Ahorras $[valor_ahorro])\n\n⭐ Oportunidad Verificada por el equipo +BARATO\n━━━━━━━━━━━━━━━━━━\n👉 VER OFERTA AQUÍ: [link]\n━━━━━━━━━━━━━━━━━━\n\n#MasbaratoDeals #OportunidadUnica #AhorroUSA`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: this.model,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0.7\n }, {\n headers: { 'Authorization': `Bearer ${this.apiKey}` }\n });\n\n return response.data.choices[0].message.content;\n } catch (error) {\n logger.error(`Error en IAProcessor: ${error.message}`);\n return this.fallbackRewrite(deal, discount);\n }\n }\n\n fallbackRewrite(deal, discount) {\n const ahorro = deal.price_official - deal.price_offer;\n const ahorroPorcentaje = discount || Math.round((ahorro / deal.price_official) * 100);\n\n return `🚀 ¡CHOLLAZO DETECTADO EN ${deal.tienda.toUpperCase()}!\n\n🔥 ${deal.title.toUpperCase()}\n\n💰 Precio Normal: $${deal.price_official.toLocaleString()}\n✅ PRECIO HOY: $${deal.price_offer.toLocaleString()}\n📉 AHORRAS: $${ahorro.toLocaleString()} (${ahorroPorcentaje}%)\n\n⭐ Oferta exclusiva verificada por +BARATO DEALS\n━━━━━━━━━━━━━━━━━━\n👉 COMPRA AQUÍ: ${deal.link}\n━━━━━━━━━━━━━━━━━━\n\n#MasbaratoDeals #OfertasUSA #${deal.tienda.replace(/\\s+/g, '')} #Ahorro`;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}, "1494214218d2a179e3b8e25ec2baf46f1c256b1e": {"t": 1785753922.599197, "n": 2483, "h": "baa6a29cd20b11bc", "c": "const axios = require('axios');\nconst logger = require('../utils/logger');\nrequire('dotenv').config();\n\nclass AIProcessor {\n constructor() {\n this.apiKey = process.env.OPENAI_API_KEY;\n this.model = 'gpt-3.5-turbo'; // O gpt-4o si tiene saldo\n }\n\n async rewriteViral(deal, discount) {\n if (!this.apiKey || this.apiKey === 'tu_key_aqui') {\n return this.fallbackRewrite(deal, discount);\n }\n\n try {\n const prompt = `Actúa como un Copywriter experto en Marketing de Afiliación y SEO de ofertas.\nTu objetivo es convertir esta oferta en un post irresistible para Telegram que maximice los CLICS.\n\nREGLAS DE ORO:\n1. Títulos con GANCHO (FOMO/Urgencia). Ej: \"¡PRECIO MÍNIMO!\", \"¡CHOLLAZO!\", \"Vuela 🚀\".\n2. Resalta el AHORRO REAL. El usuario debe sentir que pierde dinero si no compra.\n3. Usa MÁXIMO 4 emojis para mantener el profesionalismo.\n4. Idioma: Español neutro/latino.\n\nDATOS:\n- Nombre: ${deal.title}\n- Precio Antes: $${deal.price_official}\n- Precio Ahora: $${deal.price_offer}\n- Descuento: ${discount}%\n- Tienda: ${deal.tienda}\n\nSALIDA (Formato HTML):\n🔥 [TITULO CON GANCHO]\n\n🛒 Producto: ${deal.title}\n🏢 Tienda: ${deal.tienda}\n\n💰 Normal: $[precio_normal]\n✅ HOY: $[precio_oferta]\n📉 Ahorras: $[valor_ahorro] ([porcentaje]%)\n\n⚡ ¡Liquidación por tiempo limitado!\n👉 COMPRA AQUÍ: [link]\n\n#BaratoDealsNET #Oferta #Ahorro`;\n\n const response = await axios.post('https://api.openai.com/v1/chat/completions', {\n model: this.model,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0.7\n }, {\n headers: { 'Authorization': `Bearer ${this.apiKey}` }\n });\n\n return response.data.choices[0].message.content;\n } catch (error) {\n logger.error(`Error en IAProcessor: ${error.message}`);\n return this.fallbackRewrite(deal, discount);\n }\n }\n\n fallbackRewrite(deal, discount) {\n const ahorro = deal.price_official - deal.price_offer;\n return `🔥 ${deal.title.toUpperCase()} EN OFERTA\n\n🛒 Producto: ${deal.title}\n\n💰 Antes: $${deal.price_official.toLocaleString()}\n🔥 Ahora: $${deal.price_offer.toLocaleString()}\n💸 Ahorro: $${ahorro.toLocaleString()} (${discount}%)\n\n⏰ Oferta por tiempo limitado\n👉 Comprar aquí: ${deal.link}\n\n#MasbaratoDeals #Ofertas #Descuentos`;\n }\n}\n\nmodule.exports = new AIProcessor();\n"}}}}