AutomationFlowsData & Sheets › Tarım Haberleri (ai + Http)

Tarım Haberleri (ai + Http)

Tarım Haberleri (AI + HTTP). Uses rssFeedRead, httpRequest, postgres. Event-driven trigger; 26 nodes.

Event trigger★★★★☆ complexity26 nodesRSS Feed ReadHTTP RequestPostgres
Data & Sheets Trigger: Event Nodes: 26 Complexity: ★★★★☆ Added:

This workflow follows the HTTP Request → Postgres recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Tar\u0131m Haberleri (AI + HTTP)",
  "nodes": [
    {
      "parameters": {},
      "id": "manual-trigger",
      "name": "Manuel Ba\u015flat",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "url": "https://www.bloomberght.com/rss"
      },
      "id": "rss-feed-1",
      "name": "RSS Feed (Bloomberg HT)",
      "type": "n8n-nodes-base.rssFeedRead",
      "typeVersion": 1,
      "position": [
        460,
        180
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "url": "https://www.aa.com.tr/tr/rss/default?cat=ekonomi"
      },
      "id": "rss-feed-2",
      "name": "RSS Feed (Anadolu Ajans\u0131)",
      "type": "n8n-nodes-base.rssFeedRead",
      "typeVersion": 1,
      "position": [
        460,
        280
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "url": "https://www.trthaber.com/sondakika_articles.rss"
      },
      "id": "rss-feed-3",
      "name": "RSS Feed (TRT Haber)",
      "type": "n8n-nodes-base.rssFeedRead",
      "typeVersion": 1,
      "position": [
        460,
        380
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "mode": "append",
        "mergeByFields": {
          "values": null
        },
        "options": {}
      },
      "id": "merge-rss-1",
      "name": "Merge RSS 1",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        860,
        230
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "mode": "append",
        "mergeByFields": {
          "values": null
        },
        "options": {}
      },
      "id": "merge-rss-2",
      "name": "Merge RSS 2",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        1060,
        300
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "return $input.all();"
      },
      "id": "collect-all-sources",
      "name": "T\u00fcm Kaynaklar\u0131 Topla",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1460,
        380
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const items = [];\nconst allItems = $input.all();\nconst maxItems = 120;\n\nfor (let i = 0; i < Math.min(maxItems, allItems.length); i++) {\n  const data = allItems[i].json;\n\n  const baslik = String(data.title || data.baslik || '').trim();\n  let icerik = String(data.contentSnippet || data.description || data.icerik || '').trim();\n  const link = String(data.link || data.guid || '').trim();\n\n  // RSS'ten gelen g\u00f6rsel URL'lerini yakala\n  let rss_gorsel = null;\n\n  // enclosure tag'inden g\u00f6rsel\n  if (data.enclosure && data.enclosure.url) {\n    rss_gorsel = data.enclosure.url;\n  }\n  // media:content tag'inden g\u00f6rsel\n  else if (data['media:content'] && data['media:content'].url) {\n    rss_gorsel = data['media:content'].url;\n  }\n  // media:thumbnail tag'inden g\u00f6rsel\n  else if (data['media:thumbnail'] && data['media:thumbnail'].url) {\n    rss_gorsel = data['media:thumbnail'].url;\n  }\n  // media:content width ile en b\u00fcy\u00fck g\u00f6rseli al\n  else if (data['media:content'] && Array.isArray(data['media:content'])) {\n    let maxWidth = 0;\n    for (const m of data['media:content']) {\n      const w = parseInt(m.width) || 0;\n      if (w > maxWidth && m.url) {\n        maxWidth = w;\n        rss_gorsel = m.url;\n      }\n    }\n  }\n  // Bildirim'deki g\u00f6rsel\n  else if (data.image && data.image.url) {\n    rss_gorsel = data.image.url;\n  }\n\n  icerik = icerik.replace(/<[^>]*>/g, '').trim();\n\n  items.push({\n    json: {\n      baslik,\n      icerik,\n      link,\n      yayin_tarihi: data.pubDate || data.isoDate || data.yayin_tarihi || new Date().toISOString(),\n      kaynak_adi: data.sourceName || data.kaynak || '',\n      rss_gorsel_url: rss_gorsel,\n    }\n  });\n}\n\nreturn items;"
      },
      "id": "preprocess",
      "name": "\u00d6n \u0130\u015fleme",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1660,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const inputItems = $input.all();\nconst seen = new Set();\nconst output = [];\n\nfunction normalizeTitle(v) {\n  return String(v || '')\n    .toLowerCase()\n    .normalize('NFKD')\n    .replace(/[\\u0300-\\u036f]/g, '')\n    .replace(/[^a-z0-9\\s]/g, ' ')\n    .replace(/\\s+/g, ' ')\n    .trim();\n}\n\nfunction normalizeLink(v) {\n  const raw = String(v || '').trim();\n  if (!raw) return '';\n  try {\n    const u = new URL(raw);\n    u.hash = '';\n    const paramsToDelete = [\n      'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n      'gclid', 'fbclid', 'yclid', 'mc_cid', 'mc_eid'\n    ];\n    for (const p of paramsToDelete) u.searchParams.delete(p);\n    return u.toString();\n  } catch (e) {\n    return raw;\n  }\n}\n\nfor (const item of inputItems) {\n  const link = normalizeLink(item.json.link);\n  const title = normalizeTitle(item.json.baslik || item.json.title);\n  if (!link || !title) continue;\n\n  const key = `${link}::${title}`;\n  if (seen.has(key)) continue;\n\n  seen.add(key);\n  output.push({\n    json: {\n      ...item.json,\n      link\n    }\n  });\n}\n\nreturn output;"
      },
      "id": "dedupe",
      "name": "Tekille\u015ftir (Link + Ba\u015fl\u0131k)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1860,
        300
      ]
    },
    {
      "parameters": {
        "url": "={{ $json.link }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
            },
            {
              "name": "Accept",
              "value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
            },
            {
              "name": "Accept-Language",
              "value": "tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7"
            },
            {
              "name": "Cache-Control",
              "value": "no-cache"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "responseFormat": "text"
            }
          },
          "timeout": 15000
        }
      },
      "id": "fetch-detail-page",
      "name": "Haber Detay Sayfas\u0131n\u0131 \u0130ndir",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2040,
        120
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const items = [];\n\nfunction stripHtml(raw) {\n  return String(raw || '')\n    .replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n    .replace(/<style[\\s\\S]*?<\\/style>/gi, ' ')\n    .replace(/<[^>]+>/g, ' ')\n    .replace(/&nbsp;|&#160;/gi, ' ')\n    .replace(/&amp;/gi, '&')\n    .replace(/&quot;/gi, String.fromCharCode(34))\n    .replace(/&apos;|&#39;/gi, String.fromCharCode(39))\n    .replace(/&lt;/gi, '<')\n    .replace(/&gt;/gi, '>')\n    .replace(/\\s+/g, ' ')\n    .trim();\n}\n\nfor (const rawItem of $input.all()) {\n  const src = rawItem?.json || {};\n  const htmlRaw = String(src.html || '');\n  const detailText = stripHtml(htmlRaw);\n  const detay = detailText;\n\n  items.push({\n    json: {\n      baslik: String(src.baslik || '').trim(),\n      icerik: String(src.icerik || '').trim(),\n      detay_icerik: detay || String(src.icerik || '').trim(),\n      link: String(src.link || '').trim(),\n      gorsel_url: src.gorsel_url ? String(src.gorsel_url).trim() : null,\n      yayin_tarihi: src.yayin_tarihi || new Date().toISOString(),\n      kaynak_adi: String(src.kaynak_adi || '').trim(),\n    },\n  });\n}\n\nreturn items;"
      },
      "id": "prepare-detail-content",
      "name": "Haber Detay\u0131n\u0131 Haz\u0131rla",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2240,
        120
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "authentication": "none",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "={{ $env.OPENAI_API_KEY ? ('Bearer ' + $env.OPENAI_API_KEY) : '' }}"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": null
        },
        "specifyBody": "json",
        "jsonBody": "={{ {\n  \"model\": \"gpt-4o-mini\",\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"Sen \u00e7ok kat\u0131 bir tar\u0131m haberi do\u011frulama uzman\u0131s\u0131n. SADECE haberin ana konusu do\u011frudan tar\u0131m/hayvanc\u0131l\u0131k/g\u0131da \u00fcretimi/ziraat/\u00e7ift\u00e7ilik/tohumculuk/g\u00fcbreleme/sulama/hasat/tar\u0131msal destek/tar\u0131m politikas\u0131/tar\u0131m teknolojisi ise tarim_haberi=true ver. Dolayl\u0131, kenar, isim benzerli\u011fi veya ba\u011flam d\u0131\u015f\u0131 kullan\u0131mda false ver. Jeopolitik, diplomasi, askeri, adli, su\u00e7, genel ekonomi, enerji, savunma, d\u0131\u015f politika haberleri false olmal\u0131. \u00c7IKTI SADECE JSON olmal\u0131: {\\\"tarim_haberi\\\": true/false, \\\"confidence\\\": 0-1 aras\u0131 say\u0131, \\\"gerekce\\\": \\\"k\u0131sa sebep\\\", \\\"ana_konu\\\": \\\"tek sat\u0131r konu\\\"}.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": `Ba\u015fl\u0131k: ${$json.baslik}\\n\\n\u00d6zet: ${$json.icerik}\\n\\nDetay Metin: ${$json.detay_icerik}\\n\\nKarar ver: Bu haberin ana konusu do\u011frudan tar\u0131m m\u0131?`\n    }\n  ],\n  \"temperature\": 0,\n  \"max_tokens\": 180,\n  \"response_format\": { \"type\": \"json_object\" }\n} }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 5,
              "batchInterval": 500
            }
          }
        }
      },
      "id": "ai-tarim-check",
      "name": "AI Tar\u0131m Kontrol\u00fc (OpenAI)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2440,
        120
      ]
    },
    {
      "parameters": {
        "jsCode": "const items = [];\nconst sourceItems = $items('Haber Detay\u0131n\u0131 Haz\u0131rla', 0, 0) || [];\n\nfor (let i = 0; i < $input.all().length; i++) {\n  const item = $input.all()[i];\n  const pairedIndex = Number(item?.pairedItem?.item);\n  const src = Number.isInteger(pairedIndex) ? (sourceItems[pairedIndex]?.json || {}) : (sourceItems[i]?.json || {});\n\n  let tarimHaberi = false;\n  let gerekce = '';\n  let anaKonu = '';\n  let confidence = 0;\n\n  try {\n    const aiContent = item.json?.choices?.[0]?.message?.content || '{}';\n    const parsed = JSON.parse(aiContent);\n\n    const rawBool = parsed.tarim_haberi;\n    const boolValue = rawBool === true || String(rawBool || '').toLowerCase() === 'true';\n\n    const confNum = Number(parsed.confidence);\n    confidence = Number.isFinite(confNum) ? Math.max(0, Math.min(1, confNum)) : 0;\n\n    tarimHaberi = boolValue && confidence >= 0.8;\n    gerekce = String(parsed.gerekce || '').trim();\n    anaKonu = String(parsed.ana_konu || '').trim();\n  } catch (e) {\n    tarimHaberi = false;\n  }\n\n  items.push({\n    json: {\n      ...item.json,\n      tarim_haberi: tarimHaberi,\n      ai_confidence: confidence,\n      ai_gerekce: gerekce,\n      ai_ana_konu: anaKonu,\n      baslik: String(src.baslik || '').trim(),\n      icerik: String(src.icerik || '').trim(),\n      detay_icerik: String(src.detay_icerik || '').trim(),\n      link: String(src.link || '').trim(),\n      yayin_tarihi: src.yayin_tarihi || new Date().toISOString(),\n      kaynak_adi: String(src.kaynak_adi || '').trim(),\n      gorsel_url: src.gorsel_url ? String(src.gorsel_url).trim() : null\n    }\n  });\n}\n\nreturn items;"
      },
      "id": "parse-ai-response",
      "name": "AI Cevab\u0131n\u0131 Parse Et",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2640,
        120
      ]
    },
    {
      "parameters": {
        "jsCode": "const blockedTopic = /(jeopolitik|askeri|savunma|diplomasi|adli|su\u00e7|bahis|operasyon|d\u0131\u015f politika|genel ekonomi|nato|iha|kamikaze|f\u00fcze)/i;\n\nconst out = [];\nfor (const item of $input.all()) {\n  const isTarim = item.json.tarim_haberi === true;\n  const conf = Number(item.json.ai_confidence ?? 0);\n  const reasonText = `${item.json.ai_ana_konu || ''} ${item.json.ai_gerekce || ''}`;\n\n  if (!isTarim) continue;\n  if (!Number.isFinite(conf) || conf < 0.85) continue;\n  if (blockedTopic.test(reasonText)) continue;\n\n  out.push(item);\n}\n\nreturn out;"
      },
      "id": "ai-decision-gate",
      "name": "AI Sonu\u00e7 Kap\u0131s\u0131",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2840,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "authentication": "none",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "={{ $env.OPENAI_API_KEY ? ('Bearer ' + $env.OPENAI_API_KEY) : '' }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\n  \"model\": \"gpt-4o-mini\",\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"Sen tar\u0131m haberlerinde uzman bir edit\u00f6r ve s\u0131n\u0131fland\u0131r\u0131c\u0131s\u0131n. Metni T\u00fcrk\u00e7e dilbilgisine uygun \u015fekilde d\u00fczelt. SADECE ge\u00e7erli JSON d\u00f6nd\u00fcr. Kategoriyi a\u015fa\u011f\u0131daki listeden B\u0130REB\u0130R se\u00e7mek zorundas\u0131n: Genel Tar\u0131m, Hayvanc\u0131l\u0131k, Devlet Destekleri, Tar\u0131m Teknolojileri, Piyasa Fiyatlar\u0131, Meteoroloji, Organik Tar\u0131m, Tohum ve Bitki, Tar\u0131m Makineleri, Su \u00dcr\u00fcnleri, Ar\u0131c\u0131l\u0131k, Tar\u0131m Politikalar\u0131. Liste d\u0131\u015f\u0131 de\u011fer \u00fcretme.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": `Ba\u015fl\u0131k: ${$json.baslik}\\n\\n\u0130\u00e7erik: ${$json.icerik}\\n\\nAI Ana Konu: ${$json.ai_ana_konu || ''}\\n\\nAI Gerek\u00e7e: ${$json.ai_gerekce || ''}\\n\\nBu tar\u0131m haberinin ba\u015fl\u0131\u011f\u0131n\u0131 ve \u00f6zetini d\u00fczelt ve uygun kategoriyi se\u00e7. SADECE \u015fu JSON format\u0131nda yan\u0131t ver: {\"baslik\": \"d\u00fczeltilmi\u015f ba\u015fl\u0131k\", \"ozet\": \"d\u00fczeltilmi\u015f \u00f6zet max 400 karakter\", \"kategori\": \"listeden tek kategori\"}`\n    }\n  ],\n  \"temperature\": 0.1,\n  \"max_tokens\": 450,\n  \"response_format\": { \"type\": \"json_object\" }\n} }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 5,
              "batchInterval": 500
            }
          }
        }
      },
      "id": "ai-format",
      "name": "AI Format D\u00fczenleme (OpenAI)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3040,
        120
      ]
    },
    {
      "parameters": {
        "jsCode": "const items = [];\nconst sourceItems = $items('AI Sonu\u00e7 Kap\u0131s\u0131', 0, 0) || [];\n\nconst allowedCategories = new Set([\n  'Genel Tar\u0131m',\n  'Hayvanc\u0131l\u0131k',\n  'Devlet Destekleri',\n  'Tar\u0131m Teknolojileri',\n  'Piyasa Fiyatlar\u0131',\n  'Meteoroloji',\n  'Organik Tar\u0131m',\n  'Tohum ve Bitki',\n  'Tar\u0131m Makineleri',\n  'Su \u00dcr\u00fcnleri',\n  'Ar\u0131c\u0131l\u0131k',\n  'Tar\u0131m Politikalar\u0131',\n]);\n\nfunction normalizeCategory(v) {\n  const raw = String(v || '').trim();\n  if (!raw) return '';\n  for (const c of allowedCategories) {\n    if (raw.toLowerCase() === c.toLowerCase()) return c;\n  }\n  return '';\n}\n\nfunction keywordFallbackCategory(text) {\n  const t = String(text || '').toLowerCase();\n  if (/\\b(hayvan|hayvanc\u0131l\u0131k|s\u00fct|besi|ah\u0131r|yem)\\b/.test(t)) return 'Hayvanc\u0131l\u0131k';\n  if (/\\b(destek|te\u015fvik|hibe|ipard|subvansiyon)\\b/.test(t)) return 'Devlet Destekleri';\n  if (/\\b(teknoloji|dijital|ak\u0131ll\u0131|otomasyon|drone)\\b/.test(t)) return 'Tar\u0131m Teknolojileri';\n  if (/\\b(fiyat|piyasa|borsa|ihracat|ithalat)\\b/.test(t)) return 'Piyasa Fiyatlar\u0131';\n  if (/\\b(hava|ya\u011f\u0131\u015f|don|kurakl\u0131k|iklim|meteoroloji|uyar\u0131)\\b/.test(t)) return 'Meteoroloji';\n  if (/\\b(organik|s\u00fcrd\u00fcr\u00fclebilir|ekolojik)\\b/.test(t)) return 'Organik Tar\u0131m';\n  if (/\\b(tohum|fide|bitki|ekim|hasat)\\b/.test(t)) return 'Tohum ve Bitki';\n  if (/\\b(trakt\u00f6r|bi\u00e7erd\u00f6ver|ekipman|makine)\\b/.test(t)) return 'Tar\u0131m Makineleri';\n  if (/\\b(bal\u0131k|akuak\u00fclt\u00fcr|su \u00fcr\u00fcnleri)\\b/.test(t)) return 'Su \u00dcr\u00fcnleri';\n  if (/\\b(ar\u0131|ar\u0131c\u0131l\u0131k|bal)\\b/.test(t)) return 'Ar\u0131c\u0131l\u0131k';\n  if (/\\b(y\u00f6netmelik|tebli\u011f|mevzuat|politika)\\b/.test(t)) return 'Tar\u0131m Politikalar\u0131';\n  return 'Genel Tar\u0131m';\n}\n\n\nfor (let i = 0; i < $input.all().length; i++) {\n  const item = $input.all()[i];\n  const pairedIndex = Number(item?.pairedItem?.item);\n  const src = Number.isInteger(pairedIndex) ? (sourceItems[pairedIndex]?.json || {}) : (sourceItems[i]?.json || {});\n\n  let baslik = String(src.baslik || '').trim();\n  let ozet = String(src.icerik || '').trim();\n  let kategori = 'Genel Tar\u0131m';\n\n  try {\n    const aiContent = item.json?.choices?.[0]?.message?.content || '{}';\n    const parsed = JSON.parse(aiContent);\n    baslik = String(parsed.baslik || baslik).trim();\n    ozet = String(parsed.ozet || ozet).trim();\n    const aiCategory = normalizeCategory(parsed.kategori);\n    if (aiCategory) kategori = aiCategory;\n  } catch (e) {\n    // AI parse fail olursa kaynak metni kullan\n  }\n\n  if (ozet.length > 500) ozet = ozet.substring(0, 497) + '...';\n  if (!allowedCategories.has(kategori)) kategori = keywordFallbackCategory(`${baslik} ${ozet}`);\n\n  const link = String(src.link || '').trim();\n  let kaynak = String(src.kaynak_adi || '').trim();\n  if (!kaynak) {\n    if (link.includes('bloomberght')) kaynak = 'Bloomberg HT';\n    else if (link.includes('aa.com.tr')) kaynak = 'Anadolu Ajans\u0131';\n    else if (link.includes('trthaber')) kaynak = 'TRT Haber';\n    else kaynak = 'Genel';\n  }\n\n  let gorsel_url = null;\n\n  baslik = baslik.replace(/'/g, \"''\");\n  ozet = ozet.replace(/'/g, \"''\");\n  \n\n  // Kategoriye g\u00f6re g\u00f6rsel URL'i ayarla\n  gorsel_url = {\"Genel Tar\\u0131m\": \"/assets/images/category/GenelTarim-1.jpg\", \"Hayvanc\\u0131l\\u0131k\": \"/assets/images/category/Hayvancilik-1.jpg\", \"Devlet Destekleri\": \"/assets/images/category/DevletDestekleri-1.jpg\", \"Tar\\u0131m Teknolojileri\": \"/assets/images/category/TarimTeknolokileri-1.jpg\", \"Piyasa Fiyatlar\\u0131\": \"/assets/images/category/PiyasaFiyatlari-1.jpg\", \"Meteoroloji\": \"/assets/images/category/Metoroloji-1.jpg\", \"Organik Tar\\u0131m\": \"/assets/images/category/OrganikTarim-1.jpg\", \"Tohum ve Bitki\": \"/assets/images/category/TohumVeBitki-1.jpg\", \"Tar\\u0131m Makineleri\": \"/assets/images/category/Tar\\u0131mMakineleri-1.jpg\", \"Su \\u00dcr\\u00fcnleri\": \"/assets/images/category/Su\\u00dcr\\u00fcnleri-1.jpg\", \"Ar\\u0131c\\u0131l\\u0131k\": \"/assets/images/category/Aricilik-1.jpg\", \"Tar\\u0131m Politikalar\\u0131\": \"/assets/images/category/TarimPolitikalar\\u0131-1.jpg\"}[kategori] || null;\n\n  items.push({\n    json: {\n      baslik,\n      ozet,\n      detay_icerik: (src.detay_icerik_temiz || src.detay_icerik || '').replace(/'/g, \"''\"),\n      link,\n      gorsel_url,\n      kaynak,\n      kategori,\n      yayin_tarihi: src.yayin_tarihi || new Date().toISOString(),\n      aktif: true\n    }\n  });\n}\n\nreturn items;"
      },
      "id": "kategorize",
      "name": "Kategorize ve Haz\u0131rla",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3240,
        120
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO tarim_haberler (baslik, ozet, detay_icerik, link, gorsel_url, kaynak, kategori, yayin_tarihi, aktif, ai_confidence, ai_ana_konu)\nVALUES ('{{ $json.baslik }}', '{{ $json.ozet }}', '{{ $json.detay_icerik }}', '{{ $json.link }}', '{{ $json.gorsel_url }}', '{{ $json.kaynak }}', '{{ $json.kategori }}', '{{ $json.yayin_tarihi }}', {{ $json.aktif }}, {{ $json.ai_confidence || 0 }}, '{{ $json.ai_ana_konu }}')\nON CONFLICT (link) DO UPDATE SET\n  baslik = EXCLUDED.baslik,\n  ozet = EXCLUDED.ozet,\n  detay_icerik = EXCLUDED.detay_icerik,\n  gorsel_url = EXCLUDED.gorsel_url,\n  kaynak = EXCLUDED.kaynak,\n  kategori = EXCLUDED.kategori,\n  yayin_tarihi = EXCLUDED.yayin_tarihi,\n  aktif = EXCLUDED.aktif,\n  ai_confidence = EXCLUDED.ai_confidence,\n  ai_ana_konu = EXCLUDED.ai_ana_konu,\n  guncellenme_tarihi = CURRENT_TIMESTAMP,\n  okunma_sayisi = tarim_haberler.okunma_sayisi\nRETURNING id;",
        "options": {}
      },
      "id": "postgres",
      "name": "PostgreSQL",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        3440,
        120
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const items = [];\nconst sourceItems = $items('Tekille\u015ftir (Link + Ba\u015fl\u0131k)', 0, 0) || [];\n\nfor (let i = 0; i < $input.all().length; i++) {\n  const rawItem = $input.all()[i];\n  const pairedIndex = Number(rawItem?.pairedItem?.item);\n  const src = Number.isInteger(pairedIndex) ? (sourceItems[pairedIndex]?.json || {}) : (sourceItems[i]?.json || {});\n\n  const htmlRaw = typeof rawItem?.json === 'string'\n    ? rawItem.json\n    : String(rawItem?.json?.body || rawItem?.json?.data || rawItem?.json?.response || rawItem?.json?.content || '');\n\n  items.push({\n    json: {\n      baslik: String(src.baslik || '').trim(),\n      icerik: String(src.icerik || '').trim(),\n      link: String(src.link || '').trim(),\n      yayin_tarihi: src.yayin_tarihi || new Date().toISOString(),\n      kaynak_adi: String(src.kaynak_adi || '').trim(),\n      html: htmlRaw,\n    },\n  });\n}\n\nreturn items;"
      },
      "id": "prepare-html-input",
      "name": "HTML Girdi Haz\u0131rla",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2240,
        300
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "mode": "rules",
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.link }}",
                    "rightValue": "aa.com.tr",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "AA"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.link }}",
                    "rightValue": "bloomberght.com",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Bloomberg"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.link }}",
                    "rightValue": "trthaber.com",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "TRT"
            }
          ]
        },
        "options": {
          "fallbackOutput": "none",
          "ignoreCase": true,
          "allMatchingOutputs": false
        }
      },
      "id": "switch-by-domain",
      "name": "Siteye G\u00f6re Y\u00f6nlendir",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.4,
      "position": [
        2440,
        300
      ]
    },
    {
      "parameters": {
        "operation": "extractHtmlContent",
        "sourceData": "json",
        "dataPropertyName": "html",
        "extractionValues": {
          "values": [
            {
              "key": "gorsel_raw",
              "cssSelector": "meta[name=\"twitter:image\"]",
              "returnValue": "attribute",
              "attribute": "content",
              "returnArray": false
            }
          ]
        },
        "options": {
          "trimValues": true,
          "cleanUpText": true
        }
      },
      "id": "extract-aa-image",
      "name": "HTML Extract (AA Twitter Image)",
      "type": "n8n-nodes-base.html",
      "typeVersion": 1.2,
      "position": [
        2660,
        180
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "extractHtmlContent",
        "sourceData": "json",
        "dataPropertyName": "html",
        "extractionValues": {
          "values": [
            {
              "key": "gorsel_raw",
              "cssSelector": "meta[property=\"og:image\"]",
              "returnValue": "attribute",
              "attribute": "content",
              "returnArray": false
            }
          ]
        },
        "options": {
          "trimValues": true,
          "cleanUpText": true
        }
      },
      "id": "extract-og-image",
      "name": "HTML Extract (OG Image)",
      "type": "n8n-nodes-base.html",
      "typeVersion": 1.2,
      "position": [
        2660,
        360
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "mode": "append",
        "mergeByFields": {
          "values": null
        },
        "options": {}
      },
      "id": "merge-image-branches",
      "name": "G\u00f6rsel Kanallar\u0131n\u0131 Birle\u015ftir",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        2860,
        260
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const out = [];\nconst sourceItems = $items('HTML Girdi Haz\u0131rla', 0, 0) || [];\n\n// Varsay\u0131lan g\u00f6rsel YOK - sadece ger\u00e7ek g\u00f6rseller kullan\u0131lacak\n\nfunction firstMatch(html, patterns) {\n  const text = String(html || '');\n  for (const re of patterns) {\n    const m = text.match(re);\n    if (m && m[1]) return m[1].trim();\n  }\n  return '';\n}\n\nfunction fallbackImageFromHtml(html, pageUrl) {\n  const host = String(pageUrl || '').toLowerCase();\n\n  if (host.includes('aa.com.tr')) {\n    const aaFigure = firstMatch(html, [\n      /<figure[^>]*>[\\s\\S]*?<img[^>]+src=[\"']([^\"']+)[\"']/i,\n      /<img[^>]+class=[\"'][^\"']*(?:haber|article|news)[^\"']*[\"'][^>]+src=[\"']([^\"']+)[\"']/i,\n    ]);\n    if (aaFigure) return aaFigure;\n  }\n\n  return firstMatch(html, [\n    /<meta[^>]+property=[\"']og:image[\"'][^>]+content=[\"']([^\"']+)[\"']/i,\n    /<meta[^>]+content=[\"']([^\"']+)[\"'][^>]+property=[\"']og:image[\"']/i,\n    /<meta[^>]+name=[\"']twitter:image[\"'][^>]+content=[\"']([^\"']+)[\"']/i,\n    /<meta[^>]+content=[\"']([^\"']+)[\"'][^>]+name=[\"']twitter:image[\"']/i,\n    /<img[^>]+src=[\"']([^\"']+)[\"'][^>]*>/i,\n  ]);\n}\n\nfunction normalizeImageUrl(raw, pageUrl) {\n  const value = String(raw || '').trim();\n  if (!value) return null;\n\n  const fromSrcset = value.split(',')[0]?.trim().split(/\\s+/)[0] || value;\n\n  if (fromSrcset.startsWith('//')) return `https:${fromSrcset}`;\n  if (/^https?:\\/\\//i.test(fromSrcset)) return fromSrcset;\n\n  try {\n    return new URL(fromSrcset, pageUrl || undefined).toString();\n  } catch {\n    return null;\n  }\n}\n\nfor (let i = 0; i < $input.all().length; i++) {\n  const item = $input.all()[i];\n  const pairedIndex = Number(item?.pairedItem?.item);\n  const src = Number.isInteger(pairedIndex) ? (sourceItems[pairedIndex]?.json || {}) : (sourceItems[i]?.json || {});\n\n  // \u00d6nce RSS g\u00f6rseli dene (en h\u0131zl\u0131, genellikle en kaliteli)\n  let gorselUrl = normalizeImageUrl(src.rss_gorsel_url, src.link);\n\n  // Yoksa web sayfas\u0131ndan og:image \u00e7ek\n  if (!gorselUrl) {\n    gorselUrl = normalizeImageUrl(fallbackImageFromHtml(src.html, src.link), src.link);\n  }\n\n  // Hi\u00e7biri yoksa gorsel_url NULL olarak kalacak (Flutter'da placeholder g\u00f6sterilir)\n\n  out.push({\n    json: {\n      baslik: String(src.baslik || '').trim(),\n      icerik: String(src.icerik || '').trim(),\n      link: String(src.link || '').trim(),\n      yayin_tarihi: src.yayin_tarihi || new Date().toISOString(),\n      kaynak_adi: String(src.kaynak_adi || '').trim(),\n      html: String(src.html || ''),\n      gorsel_url: gorselUrl,\n    },\n  });\n}\n\nreturn out;"
      },
      "id": "normalize-image-url",
      "name": "G\u00f6rsel URL Normalize Et",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3040,
        260
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const items = [];\n\nfor (const rawItem of $input.all()) {\n  const src = rawItem?.json || {};\n  const detay_icerik = String(src.detay_icerik || '').trim();\n\n  if (!detay_icerik || detay_icerik === 'undefined' || detay_icerik.length < 50) {\n    items.push({ json: { ...src, detay_icerik_temiz: '' } });\n    continue;\n  }\n\n  // K\u0131sa metinler i\u00e7in direkt kullan\n  if (detay_icerik.length < 500) {\n    items.push({ json: { ...src, detay_icerik_temiz: detay_icerik } });\n    continue;\n  }\n\n  // Uzun metinleri temizle - navigasyon, reklam, men\u00fc \u00e7\u0131kar\n  const temizMetin = detay_icerik\n    // RSS veya site adlar\u0131 kald\u0131r\n    .replace(/Son Dakika Haberleri\\s*-->\\s*SICAK G\u00dcNDEM[^]*?EKONOM\u0130 SPOR/i, '')\n    .replace(/-->.*?$/gm, '')\n    // Fazla bo\u015fluklar\u0131 temizle\n    .replace(/\\s+/g, ' ')\n    .trim();\n\n  // \u0130lk 3000 karakteri al (AI'\u0131n i\u015fleyebilece\u011fi limit)\n  const sinirliMetin = temizMetin.slice(0, 3000);\n\n  items.push({ json: { ...src, detay_icerik_temiz: sinirliMetin } });\n}\n\nreturn items;"
      },
      "id": "ai-content-clean",
      "name": "\u0130\u00e7erik Temizle (Pre-AI)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2440,
        120
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "authentication": "none",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "={{ $env.OPENAI_API_KEY ? ('Bearer ' + $env.OPENAI_API_KEY) : '' }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\n  \"model\": \"gpt-4o-mini\",\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"Sen bir haber metni temizleme uzman\u0131s\u0131n. Verilen haber metninden sadece ana i\u00e7eri\u011fi \u00e7\u0131kar, navigasyon men\u00fcleri, reklamlar, site logolar\u0131, sosyal medya linkleri, ilgili haber ba\u015fl\u0131klar\u0131, g\u00f6rsel alt etiketleri gibi gereksiz k\u0131s\u0131mlar\u0131 kald\u0131r. SADECE temizlenmi\u015f haber metnini d\u00f6nd\u00fcr, ba\u015fka bir \u015fey ekleme.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"Bu haber metnini temizle:\\n\\n\" + $json.detay_icerik_temiz\n    }\n  ],\n  \"temperature\": 0.1,\n  \"max_tokens\": 2000\n} }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 1500
            }
          }
        }
      },
      "id": "ai-clean-openai",
      "name": "AI \u0130\u00e7erik Temizle (OpenAI)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2640,
        120
      ]
    },
    {
      "parameters": {
        "jsCode": "const items = [];\nconst sourceItems = $items('\u0130\u00e7erik Temizle (Pre-AI)', 0, 0) || [];\n\nfor (let i = 0; i < $input.all().length; i++) {\n  const item = $input.all()[i];\n  const pairedIndex = Number(item?.pairedItem?.item);\n  const src = Number.isInteger(pairedIndex) ? (sourceItems[pairedIndex]?.json || {}) : (sourceItems[i]?.json || {});\n\n  let temizIcerik = src.detay_icerik_temiz || '';\n\n  try {\n    const aiContent = item.json?.choices?.[0]?.message?.content || '';\n    if (aiContent && aiContent.trim().length > 20) {\n      temizIcerik = aiContent.trim();\n    }\n  } catch (e) {\n    // AI parse fail olursa pre-AI temizlenmi\u015f metni kullan\n  }\n\n  items.push({\n    json: {\n      ...src,\n      detay_icerik_temiz: temizIcerik\n    }\n  });\n}\n\nreturn items;"
      },
      "id": "parse-ai-clean",
      "name": "AI Temiz Cevab\u0131 Parse Et",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2840,
        120
      ]
    }
  ],
  "connections": {
    "Manuel Ba\u015flat": {
      "main": [
        [
          {
            "node": "RSS Feed (Bloomberg HT)",
            "type": "main",
            "index": 0
          },
          {
            "node": "RSS Feed (Anadolu Ajans\u0131)",
            "type": "main",
            "index": 0
          },
          {
            "node": "RSS Feed (TRT Haber)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RSS Feed (Bloomberg HT)": {
      "main": [
        [
          {
            "node": "Merge RSS 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RSS Feed (Anadolu Ajans\u0131)": {
      "main": [
        [
          {
            "node": "Merge RSS 1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "RSS Feed (TRT Haber)": {
      "main": [
        [
          {
            "node": "Merge RSS 2",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge RSS 1": {
      "main": [
        [
          {
            "node": "Merge RSS 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge RSS 2": {
      "main": [
        [
          {
            "node": "T\u00fcm Kaynaklar\u0131 Topla",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "T\u00fcm Kaynaklar\u0131 Topla": {
      "main": [
        [
          {
            "node": "\u00d6n \u0130\u015fleme",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u00d6n \u0130\u015fleme": {
      "main": [
        [
          {
            "node": "Tekille\u015ftir (Link + Ba\u015fl\u0131k)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tekille\u015ftir (Link + Ba\u015fl\u0131k)": {
      "main": [
        [
          {
            "node": "Haber Detay Sayfas\u0131n\u0131 \u0130ndir",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Haber Detay Sayfas\u0131n\u0131 \u0130ndir": {
      "main": [
        [
          {
            "node": "HTML Girdi Haz\u0131rla",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Haber Detay\u0131n\u0131 Haz\u0131rla": {
      "main": [
        [
          {
            "node": "\u0130\u00e7erik Temizle (Pre-AI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Tar\u0131m Kontrol\u00fc (OpenAI)": {
      "main": [
        [
          {
            "node": "AI Cevab\u0131n\u0131 Parse Et",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Cevab\u0131n\u0131 Parse Et": {
      "main": [
        [
          {
            "node": "AI Sonu\u00e7 Kap\u0131s\u0131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Sonu\u00e7 Kap\u0131s\u0131": {
      "main": [
        [
          {
            "node": "AI Format D\u00fczenleme (OpenAI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Format D\u00fczenleme (OpenAI)": {
      "main": [
        [
          {
            "node": "Kategorize ve Haz\u0131rla",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Kategorize ve Haz\u0131rla": {
      "main": [
        [
          {
            "node": "PostgreSQL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTML Girdi Haz\u0131rla": {
      "main": [
        [
          {
            "node": "Siteye G\u00f6re Y\u00f6nlendir",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Siteye G\u00f6re Y\u00f6nlendir": {
      "main": [
        [
          {
            "node": "HTML Extract (AA Twitter Image)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "HTML Extract (OG Image)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "HTML Extract (OG Image)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTML Extract (AA Twitter Image)": {
      "main": [
        [
          {
            "node": "G\u00f6rsel Kanallar\u0131n\u0131 Birle\u015ftir",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTML Extract (OG Image)": {
      "main": [
        [
          {
            "node": "G\u00f6rsel Kanallar\u0131n\u0131 Birle\u015ftir",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "G\u00f6rsel Kanallar\u0131n\u0131 Birle\u015ftir": {
      "main": [
        [
          {
            "node": "G\u00f6rsel URL Normalize Et",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "G\u00f6rsel URL Normalize Et": {
      "main": [
        [
          {
            "node": "Haber Detay\u0131n\u0131 Haz\u0131rla",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u0130\u00e7erik Temizle (Pre-AI)": {
      "main": [
        [
          {
            "node": "AI \u0130\u00e7erik Temizle (OpenAI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI \u0130\u00e7erik Temizle (OpenAI)": {
      "main": [
        [
          {
            "node": "AI Temiz Cevab\u0131 Parse Et",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Temiz Cevab\u0131 Parse Et": {
      "main": [
        [
          {
            "node": "AI Tar\u0131m Kontrol\u00fc (OpenAI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsfromsameowner",
    "vars": {
      "inheritFromParent": true
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Tarım Haberleri (AI + HTTP). Uses rssFeedRead, httpRequest, postgres. Event-driven trigger; 26 nodes.

Source: https://github.com/kocakburhan/TarimAI_Phase2/blob/1add4ab9e96232b9af97f3c02937560617d405fa/n8n/tarim-haberleri-ai-http-multi.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

This workflow acts as a junior finance research analyst for a UK boutique M&A or corporate finance team. It listens for Slack messages, classifies the request, gathers company or market data, and prod

HTTP Request, Google Drive, Google Docs +5
Data & Sheets

Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.

N8N Nodes Evolution Api, Redis, HTTP Request +3
Data & Sheets

Cancelacion_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 46 nodes.

Execute Workflow Trigger, Redis, HTTP Request +3
Data & Sheets

dummy_client - Shopify abandoned carts. Uses httpRequest, shopifyTrigger, whatsApp, supabase. Event-driven trigger; 25 nodes.

HTTP Request, Shopify Trigger, WhatsApp +2
Data & Sheets

AI Money Tracker Chatbot. Uses telegramTrigger, postgres, googleSheets, telegram. Event-driven trigger; 24 nodes.

Telegram Trigger, Postgres, Google Sheets +2