{
  "name": "ALLSoft — Reactivar clientes dormidos",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                2
              ],
              "triggerAtHour": 10
            }
          ]
        }
      },
      "name": "Cada martes a las 10",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        -620,
        300
      ],
      "notes": "Una vez por semana basta. Mandarlo a diario se siente como acoso.",
      "id": "d0rm-0001-4000-8000-000000000001"
    },
    {
      "parameters": {
        "jsCode": "// Tus clientes y su última compra. Sustituye por Google Sheets, tu CRM o\n// una consulta a la base de datos.\nconst clientes = [\n  { nombre: 'Sandra Villa',   correo: 'sandra@ejemplo.com',  ultimaCompra: 95,  compro: 'tinte y tratamiento capilar', noMolestar: false },\n  { nombre: 'Ricardo Mejía',  correo: 'ricardo@ejemplo.com', ultimaCompra: 12,  compro: 'corte',                        noMolestar: false },\n  { nombre: 'Diana Álvarez',  correo: 'diana@ejemplo.com',   ultimaCompra: 180, compro: 'keratina',                     noMolestar: true  },\n  { nombre: 'Felipe Cardona', correo: 'felipe@ejemplo.com',  ultimaCompra: 74,  compro: 'barbería y afeitado clásico',  noMolestar: false },\n  { nombre: 'Marcela Ossa',   correo: 'marcela@ejemplo.com', ultimaCompra: 61,  compro: 'manicure y pedicure',          noMolestar: false },\n];\n\nconst DIAS_DORMIDO = 60;\nconst MAXIMO_POR_ENVIO = 3; // no vaciar la base de golpe: mejor por tandas\n\nconst dormidos = clientes\n  .filter((c) => !c.noMolestar)          // respetar a quien pidió no recibir nada\n  .filter((c) => c.ultimaCompra >= DIAS_DORMIDO)\n  .sort((a, b) => b.ultimaCompra - a.ultimaCompra)  // primero los más perdidos\n  .slice(0, MAXIMO_POR_ENVIO);\n\nreturn dormidos.map((c) => ({\n  json: {\n    ...c,\n    nombre_pila: c.nombre.split(' ')[0],\n    meses: Math.round(c.ultimaCompra / 30),\n  },\n}));\n"
      },
      "name": "Clientes dormidos",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -400,
        300
      ],
      "notes": "Respeta 'no molestar' y manda como máximo 3 por tanda. Cámbialo por tu CRM o Google Sheets.",
      "id": "d0rm-0002-4000-8000-000000000002"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.groq.com/openai/v1/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer TU_GROQ_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: 'llama-3.3-70b-versatile', temperature: 0.75, max_tokens: 160, messages: [ { role: 'system', content: 'Escribes mensajes cortos para reactivar clientes de un negocio de servicios en Colombia. Maximo 45 palabras, en tu-teo, calido y sin sonar a publicidad. Menciona lo que el cliente compro la ultima vez. NUNCA inventes descuentos, promociones ni precios. No uses emojis.' }, { role: 'user', content: 'El cliente ' + $json.nombre_pila + ' no viene hace ' + $json.meses + ' meses. La ultima vez vino por: ' + $json.compro } ] }) }}",
        "options": {}
      },
      "name": "Escribir el mensaje (Groq)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -160,
        300
      ],
      "onError": "continueErrorOutput",
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 2000,
      "notes": "Escribe el cuerpo. Los descuentos, si los hay, se agregan después a mano.",
      "id": "d0rm-0003-4000-8000-000000000003"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Arma el mensaje de reactivación. Corre una vez por cliente.\n//\n// El modelo escribe el cuerpo. Lo que NO se le deja inventar: descuentos,\n// promociones ni precios. Si quieres ofrecer algo, se agrega aquí abajo con\n// un valor que decidas tú — no uno que se le ocurra al modelo.\nconst cliente = $('Clientes dormidos').item.json;\n\nlet cuerpo;\ntry {\n  cuerpo = $input.item.json.choices[0].message.content.trim();\n} catch (e) {\n  cuerpo = `Hola ${cliente.nombre_pila}, hace un tiempo que no te vemos. Nos encantaría atenderte de nuevo.`;\n}\n\ncuerpo = cuerpo.replace(/^[\"']|[\"']$/g, '').slice(0, 500);\n\n// Red de seguridad: si el modelo se inventó una promoción, se marca para\n// revisar en vez de mandarlo.\nconst inventaPromo = /(\\d+\\s*%|descuento|gratis|promoci|regalo|2x1)/i.test(cuerpo);\n\nreturn {\n  json: {\n    nombre: cliente.nombre,\n    correo: cliente.correo,\n    meses: cliente.meses,\n    compro: cliente.compro,\n    asunto: `${cliente.nombre_pila}, te extrañamos`,\n    mensaje: cuerpo,\n    revisar: inventaPromo,\n    motivo: inventaPromo ? 'El texto menciona una promoción o descuento que nadie autorizó' : '',\n  },\n};\n"
      },
      "name": "Preparar envío",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        80,
        200
      ],
      "notes": "Si el modelo se inventó una promoción, lo detecta y manda a revisar en vez de enviarlo.",
      "id": "d0rm-0004-4000-8000-000000000004"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "version": 2
          },
          "conditions": [
            {
              "id": "d1",
              "leftValue": "={{ $json.revisar }}",
              "rightValue": "={{ true }}",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "¿Se puede enviar tal cual?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        300,
        200
      ],
      "notes": "Si el texto promete algo que nadie autorizó, no sale.",
      "id": "d0rm-0005-4000-8000-000000000005"
    },
    {
      "parameters": {
        "sendTo": "={{ $json.correo }}",
        "subject": "={{ $json.asunto }}",
        "emailType": "text",
        "message": "={{ $json.mensaje }}",
        "options": {}
      },
      "name": "Enviar mensaje",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        540,
        120
      ],
      "notes": "Conecta tu Gmail. Pruébalo primero enviándotelo a ti mismo.",
      "id": "d0rm-0006-4000-8000-000000000006"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "x1",
              "name": "estado",
              "value": "revisar_texto",
              "type": "string"
            },
            {
              "id": "x2",
              "name": "cliente",
              "value": "={{ $json.nombre }}",
              "type": "string"
            },
            {
              "id": "x3",
              "name": "motivo",
              "value": "={{ $json.motivo }}",
              "type": "string"
            },
            {
              "id": "x4",
              "name": "texto",
              "value": "={{ $json.mensaje }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "name": "Revisar antes de enviar",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        540,
        300
      ],
      "notes": "Alguien lee el texto, lo corrige y decide si sale.",
      "id": "d0rm-0007-4000-8000-000000000007"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "e1",
              "name": "estado",
              "value": "error_ia",
              "type": "string"
            },
            {
              "id": "e2",
              "name": "detalle",
              "value": "={{ $json.error || 'Groq no respondió' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "name": "Avisar del fallo",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        80,
        440
      ],
      "notes": "Salida de error del nodo de IA: el flujo no se cae, avisa.",
      "id": "d0rm-0008-4000-8000-000000000008"
    }
  ],
  "connections": {
    "Cada martes a las 10": {
      "main": [
        [
          {
            "node": "Clientes dormidos",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clientes dormidos": {
      "main": [
        [
          {
            "node": "Escribir el mensaje (Groq)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Escribir el mensaje (Groq)": {
      "main": [
        [
          {
            "node": "Preparar envío",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Avisar del fallo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparar envío": {
      "main": [
        [
          {
            "node": "¿Se puede enviar tal cual?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "¿Se puede enviar tal cual?": {
      "main": [
        [
          {
            "node": "Enviar mensaje",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Revisar antes de enviar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "meta": {
    "instanceId": "allsoft-academia"
  },
  "tags": []
}
