{
  "name": "journal-aggregate",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 3 * * *"
            }
          ]
        }
      },
      "id": "schedule-trigger",
      "name": "Cron-3h",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [240, 320]
    },
    {
      "parameters": {
        "url": "https://git.trans-former.fr/jules.atom",
        "options": {
          "response": {
            "response": {
              "responseFormat": "text"
            }
          },
          "timeout": 15000
        }
      },
      "id": "fetch-gitea",
      "name": "Fetch-gitea",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [460, 200]
    },
    {
      "parameters": {
        "url": "=https://feeds.behold.so/{{ $env.PUBLIC_BEHOLD_AEP || 'NOT_SET' }}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "json"
            }
          },
          "timeout": 15000
        }
      },
      "id": "fetch-behold-aep",
      "name": "Fetch-behold-aep",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [460, 320]
    },
    {
      "parameters": {
        "url": "=https://feeds.behold.so/{{ $env.PUBLIC_BEHOLD_JULESNENY || 'NOT_SET' }}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "json"
            }
          },
          "timeout": 15000
        }
      },
      "id": "fetch-behold-julesneny",
      "name": "Fetch-behold-julesneny",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [460, 440]
    },
    {
      "parameters": {
        "jsCode": "// Normalisation des 3 sources V1 vers le format JSON unifié\n// Sources : Gitéa Atom (XML), Behold @aep (JSON), Behold @julesneny (JSON)\n\nconst items = [];\n\n// ---- Helper parse Atom XML (Gitéa) ----\nfunction parseAtomGitea(xml) {\n  const out = [];\n  if (!xml || typeof xml !== 'string') return out;\n  const entries = xml.split(/<entry>/i).slice(1);\n  for (const raw of entries) {\n    const block = '<entry>' + raw.split(/<\\/entry>/i)[0] + '</entry>';\n    const title = (block.match(/<title>([\\s\\S]*?)<\\/title>/i) || [])[1] || '';\n    const updated = (block.match(/<updated>([^<]+)<\\/updated>/i) || [])[1] || '';\n    const link = (block.match(/<link[^>]*href=\"([^\"]+)\"/i) || [])[1] || '';\n    const summary = (block.match(/<summary[^>]*>([\\s\\S]*?)<\\/summary>/i) || [])[1] || '';\n    const id = (block.match(/<id>([^<]+)<\\/id>/i) || [])[1] || link || updated;\n    if (!updated) continue;\n    // Decode HTML entities basique + strip tags\n    const decode = (s) => s\n      .replace(/&lt;/g, '<')\n      .replace(/&gt;/g, '>')\n      .replace(/&quot;/g, '\"')\n      .replace(/&#34;/g, '\"')\n      .replace(/&#xA;/g, ' ')\n      .replace(/&amp;/g, '&')\n      .replace(/<[^>]+>/g, ' ')\n      .replace(/\\s+/g, ' ')\n      .trim();\n    const titreClean = decode(title);\n    const extrait = decode(summary).slice(0, 280);\n    out.push({\n      id: 'gitea-' + (id.slice(0, 80) || updated),\n      platform: 'gitea',\n      hashtag: '#stack',\n      date: new Date(updated).toISOString(),\n      titre: titreClean.slice(0, 140),\n      extrait,\n      url: link,\n      thumbnail: null,\n    });\n  }\n  return out;\n}\n\n// ---- Récupère payloads depuis les 3 nodes amont ----\nlet giteaXml = '';\ntry {\n  const giteaItems = $('Fetch-gitea').all();\n  if (giteaItems.length && giteaItems[0].json) {\n    // HTTP node typeVersion 4.1 met le body brut dans .data si responseFormat=text\n    giteaXml = giteaItems[0].json.data || giteaItems[0].json.body || '';\n    if (typeof giteaXml !== 'string') giteaXml = String(giteaXml);\n  }\n} catch (e) {\n  console.log('Gitéa fetch missing:', e.message);\n}\n\nlet beholdAep = [];\ntry {\n  const aep = $('Fetch-behold-aep').all();\n  if (aep.length && aep[0].json && Array.isArray(aep[0].json.posts)) {\n    beholdAep = aep[0].json.posts;\n  } else if (aep.length && Array.isArray(aep[0].json)) {\n    beholdAep = aep[0].json;\n  }\n} catch (e) {\n  console.log('Behold AEP fetch missing:', e.message);\n}\n\nlet beholdJulesneny = [];\ntry {\n  const j = $('Fetch-behold-julesneny').all();\n  if (j.length && j[0].json && Array.isArray(j[0].json.posts)) {\n    beholdJulesneny = j[0].json.posts;\n  } else if (j.length && Array.isArray(j[0].json)) {\n    beholdJulesneny = j[0].json;\n  }\n} catch (e) {\n  console.log('Behold Julesneny fetch missing:', e.message);\n}\n\n// ---- Normalisation ----\nfor (const it of parseAtomGitea(giteaXml)) items.push(it);\n\nfor (const post of beholdAep) {\n  if (!post || !post.id) continue;\n  const ts = post.timestamp || post.taken_at || post.date;\n  const caption = (post.caption || post.captionWithEmojis || '').toString();\n  items.push({\n    id: 'insta-aep-' + post.id,\n    platform: 'instagram',\n    hashtag: '#aep-politique',\n    date: ts ? new Date(ts).toISOString() : new Date().toISOString(),\n    titre: caption.slice(0, 100) || '@aep.politique',\n    extrait: caption.slice(0, 280),\n    url: post.permalink || post.url || 'https://instagram.com/aep.politique',\n    thumbnail: post.thumbnailUrl || post.mediaUrl || (post.sizes && post.sizes.medium && post.sizes.medium.mediaUrl) || null,\n  });\n}\n\nfor (const post of beholdJulesneny) {\n  if (!post || !post.id) continue;\n  const ts = post.timestamp || post.taken_at || post.date;\n  const caption = (post.caption || post.captionWithEmojis || '').toString();\n  items.push({\n    id: 'insta-julesneny-' + post.id,\n    platform: 'instagram',\n    hashtag: '#peinture',\n    date: ts ? new Date(ts).toISOString() : new Date().toISOString(),\n    titre: caption.slice(0, 100) || '@julesneny',\n    extrait: caption.slice(0, 280),\n    url: post.permalink || post.url || 'https://instagram.com/julesneny',\n    thumbnail: post.thumbnailUrl || post.mediaUrl || (post.sizes && post.sizes.medium && post.sizes.medium.mediaUrl) || null,\n  });\n}\n\n// ---- Tri desc + cap top 100 ----\nitems.sort((a, b) => b.date.localeCompare(a.date));\nconst top = items.slice(0, 100);\n\nconst counts = {\n  total: top.length,\n  gitea: top.filter((i) => i.platform === 'gitea').length,\n  instagram: top.filter((i) => i.platform === 'instagram').length,\n};\n\nreturn [\n  {\n    json: {\n      generatedAt: new Date().toISOString(),\n      items: top,\n      counts,\n    },\n  },\n];"
      },
      "id": "normalise",
      "name": "Normalise",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [720, 320]
    },
    {
      "parameters": {
        "operation": "toJson",
        "fieldName": "data",
        "options": {
          "format": true
        }
      },
      "id": "to-json",
      "name": "To-json-string",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [940, 320],
      "notesInFlow": false,
      "notes": "Transforme l'objet JS en string JSON pour Write Binary File"
    },
    {
      "parameters": {
        "operation": "write",
        "fileName": "/home/node/.n8n/journal/journal.json",
        "dataPropertyName": "data",
        "options": {}
      },
      "id": "write-file",
      "name": "Write-journal-json",
      "type": "n8n-nodes-base.readWriteFile",
      "typeVersion": 1,
      "position": [1160, 320]
    }
  ],
  "connections": {
    "Cron-3h": {
      "main": [
        [
          { "node": "Fetch-gitea", "type": "main", "index": 0 },
          { "node": "Fetch-behold-aep", "type": "main", "index": 0 },
          { "node": "Fetch-behold-julesneny", "type": "main", "index": 0 }
        ]
      ]
    },
    "Fetch-gitea": {
      "main": [[{ "node": "Normalise", "type": "main", "index": 0 }]]
    },
    "Fetch-behold-aep": {
      "main": [[{ "node": "Normalise", "type": "main", "index": 0 }]]
    },
    "Fetch-behold-julesneny": {
      "main": [[{ "node": "Normalise", "type": "main", "index": 0 }]]
    },
    "Normalise": {
      "main": [[{ "node": "To-json-string", "type": "main", "index": 0 }]]
    },
    "To-json-string": {
      "main": [[{ "node": "Write-journal-json", "type": "main", "index": 0 }]]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveExecutionProgress": true,
    "saveManualExecutions": true
  },
  "tags": [
    { "name": "page-cerveau" },
    { "name": "PC6" }
  ]
}
