{
  "id": "F21cPOX1N5YFiNsm",
  "name": "Daily News Digest: RSS & Youtube to Telegram (Text + Audio)",
  "tags": [],
  "nodes": [
    {
      "id": "c5337aed-9584-4930-8617-34ee6072203b",
      "name": "Set - Mark YouTube",
      "type": "n8n-nodes-base.set",
      "position": [
        1056,
        720
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "7fddd203-3144-4d14-af88-6608b0b5778c",
              "name": "sourceType",
              "type": "string",
              "value": "=youtube"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "c8c80f3f-aded-4e02-ace6-eb65e7ead061",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1184,
        -112
      ],
      "parameters": {
        "width": 1088,
        "height": 1360,
        "content": "## \ud83d\udcf0 Daily News Briefing\n\nThis workflow automatically collects the latest Technology, AI, and World News from multiple RSS feeds and YouTube, processes the content, removes duplicates, and sends a formatted news briefing to Telegram.\n\n### \ud83d\ude80 How it Works\n\n1. **Trigger**\n   - Runs automatically every day at the scheduled time.\n\n2. **Collect News**\n   - TechCrunch RSS\n   - The Verge RSS\n   - BBC World RSS\n   - BBC Asia RSS\n   - The Hindu RSS\n   - Latest YouTube videos (Last 24 Hours)\n\n3. **Process Data**\n   - Merge all RSS feeds.\n   - Normalize YouTube results.\n   - Tag content by source (RSS / YouTube).\n   - Remove duplicate articles.\n   - Sort news by publish date.\n\n4. **Generate Briefing**\n   - Create a clean HTML-formatted Telegram message.\n   - Automatically split long messages to fit Telegram's character limit.\n\n5. **Send to Telegram**\n   - Deliver the latest news briefing directly to your Telegram chat.\n\n---\n\n## \u2699\ufe0f Setup\n\n1. Configure credentials for:\n   - Telegram Bot API\n   - YouTube Data API\n\n2. Update your Telegram Chat ID.\n\n3. Activate the Schedule Trigger.\n\n---\n\n## \ud83d\udd27 Customization\n\n- Add or remove RSS feeds.\n- Change YouTube search keywords.\n- Modify filtering keywords inside the Code node.\n- Change the schedule time.\n- Customize the Telegram message format.\n\n---\n\n### \u2705 Output\n\nA fully automated daily news briefing containing the latest AI, Technology, and World News delivered directly to Telegram."
      },
      "typeVersion": 1
    },
    {
      "id": "e47f3ae7-e7a4-4d80-a411-0ac54dc41386",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        -112
      ],
      "parameters": {
        "color": 7,
        "width": 2496,
        "height": 1344,
        "content": "## 1. Input Sources\nFetches data from multiple sources.\n*Tip: Replace these RSS URLs and YouTube keywords to match your personal interests.*"
      },
      "typeVersion": 1
    },
    {
      "id": "37e2590a-76ce-4e36-b886-abec78cb7a2d",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        832,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 912,
        "height": 288,
        "content": "## 2. Data Preparation\nCleans HTML tags, deduplicates repeated articles, and mixes sources based on a defined ratio."
      },
      "typeVersion": 1
    },
    {
      "id": "ff8a1993-ed21-48a7-bb8e-35962e515d06",
      "name": "\u23f0 Daily Trigger (7 AM)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        64,
        496
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 7
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "06392563-483e-4089-8791-682a31dbaf78",
      "name": "\ud83d\udcf0 TechCrunch RSS",
      "type": "n8n-nodes-base.rssFeedRead",
      "onError": "continueRegularOutput",
      "position": [
        336,
        144
      ],
      "parameters": {
        "url": "https://techcrunch.com/tag/feed/",
        "options": {
          "ignoreSSL": true
        }
      },
      "retryOnFail": false,
      "typeVersion": 1.2
    },
    {
      "id": "496c9a32-46fa-4e42-9d6b-aeb122c76d62",
      "name": "\ud83d\udcf0 The Verge RSS",
      "type": "n8n-nodes-base.rssFeedRead",
      "onError": "continueRegularOutput",
      "position": [
        384,
        320
      ],
      "parameters": {
        "url": "https://www.theverge.com/rss/index.xml",
        "options": {
          "ignoreSSL": true
        }
      },
      "retryOnFail": false,
      "typeVersion": 1.2
    },
    {
      "id": "ded0e824-805b-4825-beda-fb5a3c0da36a",
      "name": "\ud83c\udf0d BBC World RSS",
      "type": "n8n-nodes-base.rssFeedRead",
      "onError": "continueRegularOutput",
      "position": [
        480,
        496
      ],
      "parameters": {
        "url": "http://feeds.bbci.co.uk/news/world/rss.xml",
        "options": {
          "ignoreSSL": true
        }
      },
      "retryOnFail": false,
      "typeVersion": 1.2
    },
    {
      "id": "216f17f1-71c0-43bd-9ad0-04d47fcce56e",
      "name": "\ud83c\udf0f BBC Asia RSS",
      "type": "n8n-nodes-base.rssFeedRead",
      "onError": "continueRegularOutput",
      "position": [
        608,
        896
      ],
      "parameters": {
        "url": "http://feeds.bbci.co.uk/news/world/asia/rss.xml",
        "options": {
          "ignoreSSL": true
        }
      },
      "retryOnFail": false,
      "typeVersion": 1.2
    },
    {
      "id": "c8cbfda1-a624-4fe0-a19e-33e9b12fbd36",
      "name": "The Hindu RSS",
      "type": "n8n-nodes-base.rssFeedRead",
      "onError": "continueRegularOutput",
      "position": [
        608,
        1088
      ],
      "parameters": {
        "url": "https://www.thehindu.com/news/national/feeder/default.rss",
        "options": {
          "ignoreSSL": true
        }
      },
      "retryOnFail": false,
      "typeVersion": 1.2
    },
    {
      "id": "f96a2bfb-2aeb-4324-9beb-b10b28811380",
      "name": "\ud83d\udd00 Merge RSS Sources",
      "type": "n8n-nodes-base.merge",
      "position": [
        864,
        464
      ],
      "parameters": {
        "numberInputs": 4
      },
      "typeVersion": 3.2
    },
    {
      "id": "03f70e4f-9b2b-44b4-81f2-074fa92a4582",
      "name": "\ud83c\udfa5 Search YouTube (24h)",
      "type": "n8n-nodes-base.youTube",
      "position": [
        608,
        704
      ],
      "parameters": {
        "limit": 15,
        "filters": {
          "q": "AI, Tech News, Artificial Intelligence , Politic ",
          "regionCode": "IN",
          "publishedAfter": "={{ $now.setZone('America/New_York').minus({ hours: 24 }).toISO() }}"
        },
        "options": {
          "order": "date"
        },
        "resource": "video"
      },
      "typeVersion": 1
    },
    {
      "id": "56f94068-2d2c-4f4d-8fa8-900d70cd66bd",
      "name": "\u2699\ufe0f Normalize & Filter YouTube",
      "type": "n8n-nodes-base.code",
      "position": [
        832,
        720
      ],
      "parameters": {
        "jsCode": "// ====== CONFIG ======\nconst ONLY_LAST_HOURS = 24;\n\n// Keyword \"Positive\" (Must match at least one)\nconst INCLUDE_REGEX = [\n  /\\bai\\b/i,\n  /\\bartificial intelligence\\b/i,\n  /\\btech\\b/i,\n  /\\bllm\\b/i,\n  /\\bchatgpt\\b/i,\n  /\\bgemini\\b/i,\n  /\\bopenai\\b/i,\n  /\\bautomation\\b/i,\n  /\\brobotics\\b/i,\n  /\\bmachine learning\\b/i,\n];\n\n// Keyword \"Negative\" (If matched, exclude item)\nconst EXCLUDE_REGEX = [\n  /\\bcrypto\\b/i,\n  /\\bbitcoin\\b/i,\n  /\\bcoin\\b/i,\n  /\\binvestment\\b/i,\n  /\\bprice prediction\\b/i,\n  /\\bxrp\\b/i,\n  /\\bgiveaway\\b/i,\n  /\\blive stream\\b/i, // Often low quality or just looping music\n];\n\n// ======================================\n\nconst now = new Date();\nconst cutoff = new Date(now.getTime() - ONLY_LAST_HOURS * 60 * 60 * 1000);\n\nfunction pickThumb(snippet) {\n  return (\n    snippet?.thumbnails?.high?.url ||\n    snippet?.thumbnails?.medium?.url ||\n    snippet?.thumbnails?.default?.url ||\n    ''\n  );\n}\n\nfunction matchAny(regexList, text) {\n  return regexList.some((re) => re.test(text));\n}\n\nconst out = [];\n\nfor (const item of $input.all()) {\n  const j = item.json;\n\n  const videoId = j?.id?.videoId || '';\n  const snippet = j?.snippet || {};\n\n  const title = snippet?.title || '';\n  const description = snippet?.description || '';\n  const channelTitle = snippet?.channelTitle || '';\n  const publishedAt = snippet?.publishedAt || snippet?.publishTime || '';\n\n  if (!videoId || !title) continue;\n\n  // Time filter (if publishedAt parse is possible)\n  if (publishedAt) {\n    const d = new Date(publishedAt);\n    if (!Number.isNaN(d.getTime()) && d < cutoff) continue;\n  }\n\n  // Content filter\n  const haystack = `${title}\\n${description}\\n${channelTitle}`;\n  if (!matchAny(INCLUDE_REGEX, haystack)) continue;\n  if (matchAny(EXCLUDE_REGEX, haystack)) continue;\n\n  out.push({\n    json: {\n      title,\n      link: `https://www.youtube.com/watch?v=${videoId}`,\n      description,\n      source: `YouTube - ${channelTitle || 'Unknown'}`,\n      publishedAt,\n      thumbnail: pickThumb(snippet),\n      channelTitle,\n      videoId,\n    }\n  });\n}\n\nreturn out;"
      },
      "typeVersion": 2
    },
    {
      "id": "d6a856e5-492b-4a62-9bc5-b1bac445f0cb",
      "name": "\ud83c\udff7\ufe0f Tag Source: RSS",
      "type": "n8n-nodes-base.set",
      "position": [
        1056,
        496
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "2ef4eb06-d6fb-4ccb-a5c7-24e10fd4734e",
              "name": "sourceType",
              "type": "string",
              "value": "=rss"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "181f36c8-8791-409b-8dc0-b19cd9567a7b",
      "name": "\ud83e\uddf9 Clean & Remove Duplicates",
      "type": "n8n-nodes-base.code",
      "position": [
        1504,
        608
      ],
      "parameters": {
        "jsCode": "// ===== CONFIG =====\nconst MAX_ITEMS = 10000; // high limit = get all\n\n// ==================\n\nfunction cleanText(str) {\n  return String(str || '')\n    .replace(/<[^>]*>/g, ' ')     // remove HTML\n    .replace(/&#\\d+;/g, ' ')      // remove encoded chars\n    .replace(/\\s+/g, ' ')\n    .trim();\n}\n\nfunction parseDate(v) {\n  const d = new Date(v);\n  return isNaN(d.getTime()) ? null : d.toISOString();\n}\n\n// ===== MAIN =====\nconst seen = new Set();\nconst articles = [];\n\nfor (const item of $input.all()) {\n  const j = item.json || {};\n\n  // \u274c skip error objects\n  if (j.error) continue;\n\n  const title = cleanText(j.title);\n  const link = (j.link || '').split('?')[0]; // remove tracking params\n  const description = cleanText(j.contentSnippet || j.description || '');\n  const publishedAt = parseDate(j.isoDate || j.pubDate || j.date);\n\n  if (!title || !link) continue;\n\n  // \ud83d\udd01 remove duplicates\n  if (seen.has(link)) continue;\n  seen.add(link);\n\n  // \ud83c\udf0d detect source\n  let source = \"Unknown\";\n  if (link.includes(\"bbc.com\")) source = \"BBC\";\n  else if (link.includes(\"theverge.com\")) source = \"The Verge\";\n  else if (link.includes(\"nytimes.com\")) source = \"NYTimes\";\n  else if (link.includes(\"reuters.com\")) source = \"Reuters\";\n\n  articles.push({\n    title,\n    description,\n    link,\n    source,\n    publishedAt\n  });\n}\n\n// \ud83d\udd52 sort latest first\narticles.sort((a, b) => new Date(b.publishedAt) - new Date(a.publishedAt));\n\n// ===== OUTPUT =====\nreturn [{\n  json: {\n    total: articles.length,\n    articles: articles.slice(0, MAX_ITEMS)\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "9393adba-77b7-4b4b-9a54-be950ec11ea5",
      "name": "\ud83d\udcdd Build Telegram Brief",
      "type": "n8n-nodes-base.code",
      "position": [
        1728,
        608
      ],
      "parameters": {
        "jsCode": "const item = $input.first().json;\n\n// \u2705 build newsContext from articles if not present\nlet raw = item.newsContext;\n\nif (!raw && Array.isArray(item.articles)) {\n  raw = item.articles\n    .map(a => `${a.title} (${a.link})`)\n    .join('\\n');\n}\n\n// \u274c still invalid\nif (!raw || typeof raw !== \"string\") {\n  throw new Error(\"newsContext must be a string\");\n}\n\n// ===== PROCESS =====\n\n// 1) Extract URLs\nconst urlRegex = /(https?:\\/\\/[^\\s)]+)/g;\n\n// 2) Split lines\nconst lines = raw.split('\\n').map(l => l.trim()).filter(Boolean);\n\n// 3) Build structured news\nlet news = []; // \u2705 IMPORTANT (fix for your error)\n\nfor (let line of lines) {\n  const urls = line.match(urlRegex);\n\n  if (urls) {\n    let title = line.replace(urlRegex, '').trim();\n\n    title = title\n      .replace(/\\[.*?\\]/, '')\n      .replace(/:\\s*$/, '')\n      .trim();\n\n    if (title.length > 80) {\n      title = title.slice(0, 77) + '...';\n    }\n\n    news.push({\n      title,\n      url: urls[0]\n    });\n  }\n}\n\n// ===== SEND ALL (with Telegram limit fix) =====\n\nconst MAX_LENGTH = 4000;\nlet messages = [];\n\nlet currentMessage = `<b>Morning Briefing</b>\\n\\n`;\n\nfor (const n of news) {\n  const line = `\u2022 <a href=\"${n.url}\">${n.title}</a>\\n`;\n\n  if ((currentMessage + line).length > MAX_LENGTH) {\n    messages.push(currentMessage);\n    currentMessage = '';\n  }\n\n  currentMessage += line;\n}\n\nif (currentMessage) {\n  messages.push(currentMessage);\n}\n\n// Title\nconst today = new Date().toISOString().split('T')[0];\nconst title = `${today} Morning Briefing`;\n\n// \u2705 return multiple messages\nreturn messages.map(msg => ({\n  json: {\n    title,\n    telegram_body: msg\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "cecf7aac-396c-4bd5-a8a2-211397c73ac5",
      "name": "\ud83d\udcf2 Send Telegram Brief",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1952,
        608
      ],
      "parameters": {
        "text": "={{ $json.telegram_body }}",
        "chatId": "123456",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "93c0b8fd-2b46-47ab-8e49-581938a29864",
      "name": "\ud83d\udd00 Merge RSS + YouTube",
      "type": "n8n-nodes-base.merge",
      "position": [
        1280,
        608
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "168e0a55-bffa-40f7-8275-678ebde879e4",
      "name": "\ud83d\udd00 Merge Tech Sources",
      "type": "n8n-nodes-base.merge",
      "position": [
        608,
        160
      ],
      "parameters": {},
      "typeVersion": 3.2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "2a4fe5a8-3fe8-4612-878d-256ef043fd40",
  "nodeGroups": [],
  "connections": {
    "The Hindu RSS": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Merge RSS Sources",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "\ud83c\udf0f BBC Asia RSS": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Merge RSS Sources",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Set - Mark YouTube": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Merge RSS + YouTube",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "\ud83c\udf0d BBC World RSS": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Merge RSS Sources",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "\ud83d\udcf0 The Verge RSS": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Merge Tech Sources",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "\ud83d\udcf0 TechCrunch RSS": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Merge Tech Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd00 Merge RSS Sources": {
      "main": [
        [
          {
            "node": "\ud83c\udff7\ufe0f Tag Source: RSS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udff7\ufe0f Tag Source: RSS": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Merge RSS + YouTube",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd00 Merge Tech Sources": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Merge RSS Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u23f0 Daily Trigger (7 AM)": {
      "main": [
        [
          {
            "node": "\ud83d\udcf0 TechCrunch RSS",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83d\udcf0 The Verge RSS",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83c\udf0d BBC World RSS",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83c\udfa5 Search YouTube (24h)",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83c\udf0f BBC Asia RSS",
            "type": "main",
            "index": 0
          },
          {
            "node": "The Hindu RSS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcf2 Send Telegram Brief": {
      "main": [
        []
      ]
    },
    "\ud83d\udd00 Merge RSS + YouTube": {
      "main": [
        [
          {
            "node": "\ud83e\uddf9 Clean & Remove Duplicates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83c\udfa5 Search YouTube (24h)": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Normalize & Filter YouTube",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcdd Build Telegram Brief": {
      "main": [
        [
          {
            "node": "\ud83d\udcf2 Send Telegram Brief",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\uddf9 Clean & Remove Duplicates": {
      "main": [
        [
          {
            "node": "\ud83d\udcdd Build Telegram Brief",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Normalize & Filter YouTube": {
      "main": [
        [
          {
            "node": "Set - Mark YouTube",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}