{
  "name": "RTK-AG01 | The Scout - Source Monitor",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "name": "Schedule: Every 15 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const sources = [\n  // Arabic\n  { name: 'Al-Jazeera Arabic', url: 'https://www.aljazeera.net/rss/all.xml', lang: 'ar', credibility: 7, type: 'broadcaster' },\n  { name: 'BBC Arabic', url: 'https://feeds.bbci.co.uk/arabic/rss.xml', lang: 'ar', credibility: 9, type: 'broadcaster' },\n  { name: 'Al-Arabiya', url: 'https://www.alarabiya.net/tools/rss', lang: 'ar', credibility: 6, type: 'broadcaster' },\n  { name: 'Al-Quds Al-Arabi', url: 'https://www.alquds.co.uk/feed/', lang: 'ar', credibility: 7, type: 'newspaper' },\n  { name: 'Asharq Al-Awsat', url: 'https://aawsat.com/api/v1/news', lang: 'ar', credibility: 7, type: 'newspaper' },\n  { name: 'Mada Masr', url: 'https://www.madamasr.com/en/feed/', lang: 'ar', credibility: 9, type: 'independent' },\n  { name: 'Daraj Media', url: 'https://daraj.media/feed/', lang: 'ar', credibility: 8, type: 'independent' },\n  { name: 'Raseef22', url: 'https://raseef22.net/feed/', lang: 'ar', credibility: 8, type: 'independent' },\n  { name: 'Al-Mayadeen', url: 'https://www.almayadeen.net/rss/all.xml', lang: 'ar', credibility: 5, type: 'broadcaster' },\n  { name: 'Middle East Eye Arabic', url: 'https://www.middleeasteye.net/ar/rss', lang: 'ar', credibility: 7, type: 'digital' },\n  // Hebrew (English editions)\n  { name: 'Haaretz English', url: 'https://www.haaretz.com/cmlink/1.628765', lang: 'he-en', credibility: 9, type: 'newspaper' },\n  { name: 'Jerusalem Post', url: 'https://www.jpost.com/Rss/RssFeedsMiddleEast.aspx', lang: 'he-en', credibility: 7, type: 'newspaper' },\n  { name: 'Times of Israel', url: 'https://www.timesofisrael.com/feed/', lang: 'he-en', credibility: 8, type: 'digital' },\n  { name: 'Ynet English', url: 'https://www.ynetnews.com/category/3082', lang: 'he-en', credibility: 7, type: 'newspaper' },\n  // Turkish\n  { name: 'TRT World', url: 'https://www.trtworld.com/rss', lang: 'tr-en', credibility: 6, type: 'broadcaster' },\n  { name: 'Daily Sabah', url: 'https://www.dailysabah.com/rss', lang: 'tr-en', credibility: 6, type: 'newspaper' },\n  { name: 'Bianet', url: 'https://bianet.org/english/rss', lang: 'tr-en', credibility: 8, type: 'independent' },\n  // Persian\n  { name: 'Radio Farda', url: 'https://www.radiofarda.com/api/zrqiteoum', lang: 'fa-en', credibility: 8, type: 'broadcaster' },\n  { name: 'IranWire', url: 'https://iranwire.com/feed/', lang: 'fa-en', credibility: 8, type: 'independent' },\n  { name: 'Iran International', url: 'https://www.iranintl.com/en/rss', lang: 'fa-en', credibility: 7, type: 'broadcaster' },\n  // Western\n  { name: 'Reuters Middle East', url: 'https://feeds.reuters.com/reuters/MENANews', lang: 'en', credibility: 9, type: 'wire' },\n  { name: 'AP Middle East', url: 'https://rsshub.app/ap/topics/middle-east', lang: 'en', credibility: 9, type: 'wire' },\n  { name: 'The Guardian Middle East', url: 'https://www.theguardian.com/world/middleeast/rss', lang: 'en', credibility: 8, type: 'newspaper' },\n  { name: 'Foreign Policy', url: 'https://foreignpolicy.com/feed/', lang: 'en', credibility: 8, type: 'magazine' },\n  { name: 'Middle East Eye', url: 'https://www.middleeasteye.net/rss', lang: 'en', credibility: 7, type: 'digital' },\n  { name: 'Al-Monitor', url: 'https://www.al-monitor.com/rss', lang: 'en', credibility: 8, type: 'digital' },\n  { name: 'ICG', url: 'https://www.crisisgroup.org/rss.xml', lang: 'en', credibility: 9, type: 'thinktank' },\n  { name: 'Carnegie MENA', url: 'https://carnegie-mec.org/rss', lang: 'en', credibility: 9, type: 'thinktank' },\n  { name: 'Responsible Statecraft', url: 'https://responsiblestatecraft.org/feed/', lang: 'en', credibility: 7, type: 'digital' }\n];\n\nreturn sources.map(s => ({ json: s }));"
      },
      "name": "Code: Define Sources Registry",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {
          "timeout": 10000,
          "response": {
            "response": {
              "responseFormat": "text"
            }
          }
        }
      },
      "name": "HTTP: Fetch RSS Feed",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        480,
        300
      ],
      "continueOnFail": true
    },
    {
      "parameters": {
        "jsCode": "const source = $input.first().json;\nconst body = $input.first().json.body || '';\n\nif (!body || body.length < 100) {\n  return [{ json: { error: true, source: source.name, reason: 'Empty or failed response' } }];\n}\n\n// Simple XML RSS parser\nconst items = [];\nconst itemRegex = /<item>(.*?)<\\/item>/gs;\nconst titleRegex = /<title><!\\[CDATA\\[(.+?)\\]\\]><\\/title>|<title>(.+?)<\\/title>/s;\nconst linkRegex = /<link>(.+?)<\\/link>|<link\\s[^>]*href=\"([^\"]+)\"/;\nconst pubDateRegex = /<pubDate>(.+?)<\\/pubDate>/;\nconst descRegex = /<description><!\\[CDATA\\[(.+?)\\]\\]><\\/description>|<description>(.+?)<\\/description>/s;\n\nlet match;\nwhile ((match = itemRegex.exec(body)) !== null) {\n  const item = match[1];\n  const titleMatch = titleRegex.exec(item);\n  const linkMatch = linkRegex.exec(item);\n  const dateMatch = pubDateRegex.exec(item);\n  const descMatch = descRegex.exec(item);\n\n  if (!titleMatch) continue;\n\n  const title = (titleMatch[1] || titleMatch[2] || '').trim().replace(/<[^>]+>/g, '');\n  const link = (linkMatch ? (linkMatch[1] || linkMatch[2]) : '').trim();\n  const pubDate = dateMatch ? dateMatch[1] : new Date().toISOString();\n  const description = (descMatch ? (descMatch[1] || descMatch[2]) : '').trim().replace(/<[^>]+>/g, '').substring(0, 500);\n\n  if (title.length < 10) continue;\n\n  // Hash for deduplication\n  const hashInput = title.substring(0, 80).toLowerCase().replace(/\\s+/g, '');\n  let hash = 0;\n  for (let i = 0; i < hashInput.length; i++) {\n    hash = ((hash << 5) - hash) + hashInput.charCodeAt(i);\n    hash |= 0;\n  }\n\n  items.push({\n    source_name: source.name,\n    source_lang: source.lang,\n    source_credibility: source.credibility,\n    source_type: source.type,\n    title,\n    link,\n    description,\n    pubDate,\n    ingestedAt: new Date().toISOString(),\n    dedup_hash: Math.abs(hash).toString(36),\n    processed: false\n  });\n\n  if (items.length >= 10) break; // Max 10 per source per run\n}\n\nif (items.length === 0) {\n  return [{ json: { error: true, source: source.name, reason: 'No items parsed' } }];\n}\n\nreturn items.map(item => ({ json: item }));"
      },
      "name": "Code: Parse RSS Items",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        720,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.error }}",
              "value2": false
            }
          ]
        }
      },
      "name": "IF: Valid Item?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        960,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.SUPABASE_URL }}/rest/v1/raw_articles",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "={{ $env.SUPABASE_KEY }}"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.SUPABASE_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "resolution=ignore-duplicates"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "body": "={{ $json }}"
      },
      "name": "Supabase: Store Raw Article",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        1200,
        200
      ],
      "continueOnFail": true
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.N8N_WEBHOOK_AG02 }}",
        "sendBody": true,
        "contentType": "json",
        "body": "={{ $json }}"
      },
      "name": "Trigger: Send to AG-02 Filter",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        1440,
        200
      ],
      "continueOnFail": true
    }
  ],
  "connections": {
    "Schedule: Every 15 Minutes": {
      "main": [
        [
          {
            "node": "Code: Define Sources Registry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Define Sources Registry": {
      "main": [
        [
          {
            "node": "HTTP: Fetch RSS Feed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP: Fetch RSS Feed": {
      "main": [
        [
          {
            "node": "Code: Parse RSS Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Parse RSS Items": {
      "main": [
        [
          {
            "node": "IF: Valid Item?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF: Valid Item?": {
      "main": [
        [
          {
            "node": "Supabase: Store Raw Article",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Supabase: Store Raw Article": {
      "main": [
        [
          {
            "node": "Trigger: Send to AG-02 Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "errorWorkflow": "RTK-AG15-TechOps"
  },
  "tags": [
    "rethink",
    "intelligence",
    "scout",
    "ag01"
  ]
}