{
  "name": "Alerta de Prazos Trello WhatsApp",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1-5"
            }
          ]
        }
      },
      "id": "6e207f29-407f-4c7e-907d-70df7e728cb1",
      "name": "Dias \u00fateis \u00e0s 08h",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        -656,
        32
      ]
    },
    {
      "parameters": {
        "url": "=https://api.trello.com/1/boards/{{ $env.TRELLO_BOARD_ID }}/cards",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "key",
              "value": "={{ $env.TRELLO_API_KEY }}"
            },
            {
              "name": "token",
              "value": "={{ $env.TRELLO_TOKEN }}"
            },
            {
              "name": "fields",
              "value": "name,due,url,idMembers,dueComplete"
            }
          ]
        },
        "options": {}
      },
      "id": "efbd20d6-18c6-40a1-bef7-f2dffa7d7d29",
      "name": "Buscar cart\u00f5es do Trello",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        -448,
        32
      ]
    },
    {
      "parameters": {
        "jsCode": "function env(name, fallback = \"\") {\n  const value = process.env[name];\n  return value === undefined || value === null || value === \"\" ? fallback : value;\n}\n\nfunction parseJsonEnv(name, fallback) {\n  const raw = env(name, \"\");\n\n  if (!raw) return fallback;\n\n  try {\n    return JSON.parse(raw);\n  } catch (error) {\n    throw new Error(`Variavel de ambiente ${name} contem JSON invalido: ${error.message}`);\n  }\n}\n\nconst CONFIG = {\n  grupoDestino: env(\"WHATSAPP_GROUP_JID\", \"120000000000000000@g.us\"),\n  diasAntecedencia: Number(env(\"DAYS_BEFORE_DUE\", \"2\")),\n  timezone: env(\"TIMEZONE\", \"America/Sao_Paulo\"),\n  fallbackContact: {\n    numero: env(\"FALLBACK_PHONE_NUMBER\", \"5500000000000\"),\n    nome: env(\"FALLBACK_CONTACT_NAME\", \"Equipe\")\n  },\n  memberContacts: parseJsonEnv(\"TRELLO_MEMBER_CONTACTS_JSON\", {\n    \"trello_member_id_here\": {\n      numero: \"5500000000000\",\n      nome: \"Responsavel\"\n    }\n  })\n};\n\nfunction normalizarNumero(numero) {\n  return String(numero || \"\")\n    .replace(/\\D/g, \"\")\n    .replace(/^55(\\d{2})9(\\d{8})$/, \"55$1$2\");\n}\n\nfunction montarContato(contato) {\n  const numero = normalizarNumero(contato.numero);\n\n  return {\n    numero,\n    nome: contato.nome || \"Equipe\",\n    jid: `${numero}@s.whatsapp.net`,\n    textoMencao: `@${numero}`\n  };\n}\n\nfunction formatarData(data) {\n  return data.toLocaleDateString(\"pt-BR\", {\n    timeZone: CONFIG.timezone,\n    day: \"2-digit\",\n    month: \"2-digit\",\n    year: \"numeric\"\n  });\n}\n\nconst agora = new Date();\nconst diasAntecedencia = Number.isFinite(CONFIG.diasAntecedencia)\n  ? CONFIG.diasAntecedencia\n  : 2;\nconst limite = new Date(agora.getTime() + diasAntecedencia * 24 * 60 * 60 * 1000);\nconst resultado = [];\n\nfor (const item of $input.all()) {\n  const card = item.json;\n\n  if (!card.due || card.dueComplete === true) continue;\n\n  const vencimento = new Date(card.due);\n  if (Number.isNaN(vencimento.getTime())) continue;\n\n  const vencido = vencimento < agora;\n  const venceEmBreve = vencimento >= agora && vencimento <= limite;\n\n  if (!vencido && !venceEmBreve) continue;\n\n  const membrosIds = Array.isArray(card.idMembers) ? card.idMembers : [];\n\n  let contatosDoCard = membrosIds\n    .map((membroId) => CONFIG.memberContacts[membroId])\n    .filter(Boolean)\n    .map(montarContato);\n\n  if (contatosDoCard.length === 0) {\n    contatosDoCard = [montarContato(CONFIG.fallbackContact)];\n  }\n\n  const nomeTarefa = card.name || \"Sem titulo\";\n  const dataFormatada = formatarData(vencimento);\n\n  const mencoesTexto = contatosDoCard\n    .map((contato) => contato.textoMencao)\n    .join(\" \");\n\n  const mencoesJid = contatosDoCard.map((contato) => contato.jid);\n  const nomes = contatosDoCard.map((contato) => contato.nome).join(\", \");\n\n  const emoji = vencido ? \"\ud83d\udd34\" : \"\ud83d\udfe1\";\n\n  const mensagem = vencido\n    ? `${emoji} Aten\u00e7\u00e3o ${mencoesTexto}, a tarefa *${nomeTarefa}* est\u00e1 *VENCIDA* desde ${dataFormatada}!`\n    : `${emoji} Aten\u00e7\u00e3o ${mencoesTexto}, a tarefa *${nomeTarefa}* est\u00e1 *VENCENDO* em ${dataFormatada}!`;\n\n  resultado.push({\n    json: {\n      name: nomeTarefa,\n      due: card.due,\n      due_formatado: dataFormatada,\n      url: card.url,\n      whatsapp_nomes: nomes,\n      whatsapp_mencoes: mencoesJid,\n      mensagem_final: mensagem,\n      payload_evolution: {\n        number: CONFIG.grupoDestino,\n        text: mensagem,\n        mentionsEveryOne: false,\n        mentioned: mencoesJid\n      }\n    }\n  });\n}\n\nif (resultado.length === 0) {\n  return [{ json: { _skip: true, mensagem_final: \"\" } }];\n}\n\nreturn resultado;"
      },
      "id": "af824171-52d9-485b-86e1-70e0fef99f6e",
      "name": "Filtrar e formatar cart\u00f5es",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -240,
        32
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "b4b4d4d1-39a7-4b9c-9c00-c99afc5821de",
              "leftValue": "={{ $json.mensagem_final }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "0d74c86e-7f4f-4906-bcef-77de8f9978c9",
      "name": "Tem mensagem para enviar?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -32,
        32
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.EVOLUTION_API_BASE_URL }}/message/sendText/{{ $env.EVOLUTION_INSTANCE_NAME }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "={{ $env.EVOLUTION_API_KEY }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.payload_evolution }}",
        "options": {}
      },
      "id": "a66cdd5d-873e-40ad-8a44-092e16b3abaf",
      "name": "Enviar WhatsApp",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        192,
        16
      ]
    },
    {
      "parameters": {
        "content": "Dispara o workflow automaticamente em dias \u00fateis \u00e0s 08h.",
        "height": 272,
        "width": 208
      },
      "id": "2d8c030a-aff2-4fdf-803f-1aaaf963e2b2",
      "name": "Nota - Agendamento",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -704,
        -80
      ]
    },
    {
      "parameters": {
        "content": "Busca cart\u00f5es do board informado em TRELLO_BOARD_ID.",
        "height": 272,
        "width": 208,
        "color": 4
      },
      "id": "9345faa2-48b7-4345-abe8-770741e7e269",
      "name": "Nota - Trello",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -496,
        -80
      ]
    },
    {
      "parameters": {
        "content": "Filtra cart\u00f5es vencidos ou pr\u00f3ximos do vencimento, identifica respons\u00e1veis e monta a mensagem.",
        "height": 272,
        "width": 208,
        "color": 6
      },
      "id": "0f8519a2-f538-4115-af08-34ea965ee974",
      "name": "Nota - Processamento",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -288,
        -80
      ]
    },
    {
      "parameters": {
        "content": "Envia a mensagem no WhatsApp usando Evolution API e vari\u00e1veis de ambiente.",
        "height": 272,
        "width": 212
      },
      "id": "7391217b-1d4c-41e8-9660-d81ae3ebc254",
      "name": "Nota - Envio",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        128,
        -80
      ]
    }
  ],
  "connections": {
    "Dias \u00fateis \u00e0s 08h": {
      "main": [
        [
          {
            "node": "Buscar cart\u00f5es do Trello",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buscar cart\u00f5es do Trello": {
      "main": [
        [
          {
            "node": "Filtrar e formatar cart\u00f5es",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filtrar e formatar cart\u00f5es": {
      "main": [
        [
          {
            "node": "Tem mensagem para enviar?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tem mensagem para enviar?": {
      "main": [
        [
          {
            "node": "Enviar WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enviar WhatsApp": {
      "main": [
        []
      ]
    }
  },
  "meta": {
    "templateCredsSetupCompleted": false
  }
}