{
  "name": "Veille annonces immobilières (RSS + IA)",
  "nodes": [
    {
      "parameters": {
        "content": "### 🏠 Veille immobilière automatique — Kit n8n\nCe workflow surveille un flux RSS d'annonces immobilières, extrait le prix et la surface de chaque annonce, filtre selon votre budget, enregistre les biens correspondants dans une Data Table puis rédige automatiquement un message de contact pour l'agence avec l'IA.\n\nTemplate issu du kit de la formation complète n8n → [shubham-sharma.fr](https://shubham-sharma.fr)",
        "height": 220,
        "width": 480,
        "color": 4
      },
      "id": "sticky-titre",
      "name": "Sticky Note — Présentation",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [-40, -400]
    },
    {
      "parameters": {
        "content": "### 📡 Source & extraction\nLe flux RSS de démo est celui de CP Immobilier. Remplacez `feedUrl` par n'importe quel flux d'annonces.\n\nLe nœud Code extrait **prix** et **surface** du texte de chaque annonce (gère « 549 900 euros », « € FAI », « honoraires inclus », les espaces insécables…). Si votre flux est différent, adaptez les regex — ou collez un exemple d'annonce dans Claude/ChatGPT et demandez-lui de mettre à jour le code.",
        "height": 300,
        "width": 460,
        "color": 7
      },
      "id": "sticky-source",
      "name": "Sticky Note — Source",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [-80, -60]
    },
    {
      "parameters": {
        "content": "### 🔀 Filtre budget\nTrois sorties : prix introuvable, sous le budget, au-dessus du budget.\n\nChangez le seuil de **400 000 €** dans la règle « Moins de 400 000 € » selon votre budget. Les annonces sans prix détecté sortent par « Prix introuvable » — branchez-y une seconde Data Table si vous voulez les garder.",
        "height": 300,
        "width": 320,
        "color": 7
      },
      "id": "sticky-filtre",
      "name": "Sticky Note — Filtre",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [420, -60]
    },
    {
      "parameters": {
        "content": "### 🗃️ Data Table « Immo » — à créer avant le premier lancement\nDans n8n → **Data Tables**, créez une table `Immo` avec 4 colonnes :\n- `prix` (number)\n- `surface` (number)\n- `contenu` (string)\n- `message` (string)\n\nPuis sélectionnez cette table dans les deux nœuds Data Table ci-dessous.",
        "height": 300,
        "width": 340,
        "color": 5
      },
      "id": "sticky-table",
      "name": "Sticky Note — Data Table",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [780, -60]
    },
    {
      "parameters": {
        "content": "### 🤖 IA — à configurer\n1. Créez une **credential Anthropic** (clé API depuis console.anthropic.com) et sélectionnez-la dans le nœud « Rédiger le message (IA) ».\n2. Choisissez votre modèle (Claude Sonnet recommandé).\n\nLe message généré est enregistré dans la colonne `message` de la table. Si le champ de sortie du nœud IA diffère chez vous, glissez-déposez le champ texte depuis le panneau de sortie dans le nœud « Enregistrer le message ».",
        "height": 320,
        "width": 420,
        "color": 3
      },
      "id": "sticky-ia",
      "name": "Sticky Note — IA",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [1160, -60]
    },
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyHour"
            }
          ]
        },
        "feedUrl": "https://www.cp-immobilier.fr/rss.xml"
      },
      "id": "node-rss-trigger",
      "name": "Annonces immo (RSS)",
      "type": "n8n-nodes-base.rssFeedReadTrigger",
      "typeVersion": 1,
      "position": [0, 60]
    },
    {
      "parameters": {
        "jsCode": "// Extrait le prix et la surface de chaque annonce du flux RSS.\n// Gère : « Prix : 549 900 euros », « 529 900 € TTC », « 498 000 FAI »,\n// « 1 199 000, honoraires inclus », les espaces insécables, « 116m² », etc.\n// Certains flux perdent le symbole €, d'où plusieurs stratégies de détection.\n\nconst NOMBRE = '(\\\\d{1,3}(?:[ .]\\\\d{3})+|\\\\d{5,})';\n\nconst extrairePrix = (texte) => {\n  if (!texte) return null;\n  const t = texte.replace(/[\\u00a0\\u202f]/g, ' ');\n  const candidats = [];\n  const collecter = (regex) => {\n    let m;\n    while ((m = regex.exec(t)) !== null) {\n      const valeur = parseInt(m[1].replace(/[ .]/g, ''), 10);\n      if (valeur >= 10000) candidats.push(valeur);\n    }\n  };\n  // 1. Un nombre qui suit le mot « prix » (ex : « Prix direct promoteur : 529 900 »)\n  collecter(new RegExp('prix[^\\\\d\\\\n]{0,40}' + NOMBRE, 'gi'));\n  // 2. Un nombre suivi de €/euros/EUR/TTC/FAI\n  collecter(new RegExp(NOMBRE + '\\\\s*(?:€|euros?\\\\b|eur\\\\b|ttc\\\\b|fai\\\\b)', 'gi'));\n  // 3. Un nombre suivi de « honoraires » ou « frais » (ex : « 1 199 000, honoraires inclus »)\n  collecter(new RegExp(NOMBRE + '\\\\s*,?\\\\s*(?:honoraires|frais)', 'gi'));\n  // On garde le plus élevé : c'est généralement le prix du bien\n  // (les petits montants type charges sont écartés par le seuil de 10 000).\n  return candidats.length ? Math.max(...candidats) : null;\n};\n\nconst extraireSurface = (texte) => {\n  if (!texte) return null;\n  const t = texte.replace(/[\\u00a0\\u202f]/g, ' ');\n  // « 116m² », « 100 m² », « 85,5 m2 »… on garde la première surface plausible\n  const m = t.match(/(\\d{2,4}(?:[.,]\\d{1,2})?)\\s*m\\s*[²2](?![\\w])/i);\n  return m ? Math.round(parseFloat(m[1].replace(',', '.'))) : null;\n};\n\nreturn $input.all().map((item) => {\n  const texte = [item.json.title, item.json.contentSnippet, item.json.content]\n    .filter(Boolean)\n    .join('\\n');\n  return {\n    json: {\n      ...item.json,\n      prix: extrairePrix(texte),\n      surface: extraireSurface(texte),\n    },\n  };\n});"
      },
      "id": "node-code-extraction",
      "name": "Extraction prix & surface",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [220, 60]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "cond-prix-introuvable",
                    "leftValue": "={{ $json.prix }}",
                    "rightValue": "",
                    "operator": {
                      "type": "number",
                      "operation": "notExists",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Prix introuvable"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "cond-sous-budget",
                    "leftValue": "={{ $json.prix }}",
                    "rightValue": 400000,
                    "operator": {
                      "type": "number",
                      "operation": "lt"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Moins de 400 000 €"
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "400 000 € et plus"
        }
      },
      "id": "node-switch-budget",
      "name": "Aiguillage par budget",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [480, 60]
    },
    {
      "parameters": {
        "operation": "insert",
        "dataTableId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "Immo — sélectionnez votre table"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "prix": "={{ $json.prix }}",
            "surface": "={{ $json.surface }}",
            "contenu": "={{ $json.contentSnippet }}"
          },
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "node-datatable-insert",
      "name": "Insérer dans la table",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1,
      "position": [840, 60]
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "claude-sonnet-4-5",
          "mode": "list",
          "cachedResultName": "Claude Sonnet 4.5"
        },
        "messages": {
          "values": [
            {
              "content": "=Je suis à la recherche d'un bien immobilier. Voici le contenu d'une annonce qui correspond à mes critères :\n\n{{ $('Extraction prix & surface').item.json.contentSnippet }}\n\nRédige un email courtois et concis à destination de l'agence : je me présente comme un acheteur sérieux, je pose 2 ou 3 questions pertinentes en fonction du contenu de l'annonce (charges, travaux, disponibilité…) et je demande une visite.\n\nFormat de sortie : uniquement le corps de l'email, sans objet, sans commentaire avant ou après.",
              "role": "user"
            }
          ]
        },
        "options": {}
      },
      "id": "node-anthropic-message",
      "name": "Rédiger le message (IA)",
      "type": "@n8n/n8n-nodes-langchain.anthropic",
      "typeVersion": 1,
      "position": [1100, 60]
    },
    {
      "parameters": {
        "operation": "update",
        "dataTableId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "Immo — sélectionnez votre table"
        },
        "matchType": "allConditions",
        "filters": {
          "conditions": [
            {
              "keyName": "id",
              "condition": "eq",
              "keyValue": "={{ $('Insérer dans la table').item.json.id }}"
            }
          ]
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "message": "={{ $json.content[0].text }}"
          },
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "node-datatable-update",
      "name": "Enregistrer le message",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1,
      "position": [1360, 60]
    }
  ],
  "connections": {
    "Annonces immo (RSS)": {
      "main": [
        [
          {
            "node": "Extraction prix & surface",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extraction prix & surface": {
      "main": [
        [
          {
            "node": "Aiguillage par budget",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aiguillage par budget": {
      "main": [
        [],
        [
          {
            "node": "Insérer dans la table",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Insérer dans la table": {
      "main": [
        [
          {
            "node": "Rédiger le message (IA)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rédiger le message (IA)": {
      "main": [
        [
          {
            "node": "Enregistrer le message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {}
}
