[ { "id": "tab_rss_ingest", "type": "tab", "label": "veille-ia : rss-ingest", "disabled": false, "info": "PRD §5.1 — Poll flux RSS (HF blog, releases GitHub ComfyUI) → dédup → POST classifier.\n\nVariables d'environnement :\n- CLASSIFIER_URL (défaut http://127.0.0.1:1880/veille-ia/classifier)\n\nPour FreshRSS (API Google Reader) : remplacer le noeud « Sources RSS » par un http request authentifié vers l'instance FreshRSS_ynh.\nSources complémentaires à surveiller via changedetection + license-watch : BFL, MiniMax, Krea, Artificial Analysis, Midjourney, Runway, Suno." }, { "id": "rss_inject", "type": "inject", "z": "tab_rss_ingest", "name": "toutes les heures", "props": [{ "p": "payload" }], "repeat": "3600", "crontab": "", "once": true, "onceDelay": 30, "topic": "", "payload": "", "payloadType": "date", "x": 140, "y": 100, "wires": [["rss_sources"]] }, { "id": "rss_sources", "type": "function", "z": "tab_rss_ingest", "name": "Sources RSS", "func": "// Un message par flux. URLs vérifiées au 2026-08 :\n// - blog Hugging Face (feed.xml)\n// - releases GitHub (.atom, fonctionne pour tout dépôt)\nconst FEEDS = [\n 'https://huggingface.co/blog/feed.xml',\n 'https://github.com/comfyanonymous/ComfyUI/releases.atom'\n];\nreturn [FEEDS.map((url) => ({ url, method: 'GET' }))];", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 330, "y": 100, "wires": [["rss_requete"]] }, { "id": "rss_requete", "type": "http request", "z": "tab_rss_ingest", "name": "GET flux", "method": "use", "ret": "txt", "paytoqs": "ignore", "url": "", "tls": "", "persist": false, "proxy": "", "insecureHTTPParser": false, "authType": "", "senderr": false, "headers": [], "x": 510, "y": 100, "wires": [["rss_xml"]] }, { "id": "rss_xml", "type": "xml", "z": "tab_rss_ingest", "name": "XML → objet", "property": "payload", "attr": "$", "chr": "", "x": 670, "y": 100, "wires": [["rss_normaliser"]] }, { "id": "rss_normaliser", "type": "function", "z": "tab_rss_ingest", "name": "Normaliser + dédup", "func": "// Normalise RSS 2.0 et Atom, déduplique par guid (contexte flow).\nfunction texteDe(v) {\n if (v == null) return '';\n if (typeof v === 'string') return v;\n if (Array.isArray(v)) return texteDe(v[0]);\n if (typeof v === 'object') return v._ || v.$t || '';\n return String(v);\n}\nconst canal = msg.payload?.rss?.channel?.[0];\nconst items = canal?.item || msg.payload?.feed?.entry || [];\nconst vus = flow.get('vus') || {};\nconst nouveaux = [];\nfor (const it of items) {\n const titre = texteDe(it.title);\n const lien = texteDe(it.link) || it.link?.[0]?.$?.href || '';\n const guid = texteDe(it.guid) || texteDe(it.id) || lien;\n const texte = texteDe(it.description) || texteDe(it.summary) || texteDe(it.content);\n if (!guid || vus[guid]) continue;\n vus[guid] = Date.now();\n nouveaux.push({\n titreOriginal: titre || '(sans titre)',\n texte: `${titre}\\n\\n${texte}`,\n lien,\n typeSuggere: 'nouvelle_sortie'\n });\n}\nflow.set('vus', vus);\nif (nouveaux.length === 0) return null;\nconst classifier = env.get('CLASSIFIER_URL') || 'http://127.0.0.1:1880/veille-ia/classifier';\nreturn [nouveaux.map((n) => ({\n ...n,\n method: 'POST',\n url: classifier,\n headers: { 'content-type': 'application/json' },\n payload: n.texte\n}))];", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 860, "y": 100, "wires": [["rss_vers_classifier"]] }, { "id": "rss_vers_classifier", "type": "http request", "z": "tab_rss_ingest", "name": "POST classifier", "method": "use", "ret": "txt", "paytoqs": "ignore", "url": "", "tls": "", "persist": false, "proxy": "", "insecureHTTPParser": false, "authType": "", "senderr": false, "headers": [], "x": 1070, "y": 100, "wires": [["rss_debug"]] }, { "id": "rss_debug", "type": "debug", "z": "tab_rss_ingest", "name": "items envoyés", "active": false, "tosidebar": true, "console": false, "tostatus": true, "complete": "titreOriginal", "statusVal": "titreOriginal", "statusType": "msg", "x": 1060, "y": 160, "wires": [] } ]