{
  "name": "Multi-Source AI News Digest",
  "nodes": [
    {
      "id": "8c3d6e44-a1a1-4a1a-9a1a-000000000001",
      "name": "Schedule (Daily 8AM)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        0,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      }
    },
    {
      "id": "8c3d6e44-a2a2-4a2a-9a2a-000000000002",
      "name": "Hacker News Top",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        240,
        -200
      ],
      "parameters": {
        "url": "https://hn.algolia.com/api/v1/search?tags=front_page&hitsPerPage=5",
        "options": {}
      }
    },
    {
      "id": "8c3d6e44-a3a3-4a3a-9a3a-000000000003",
      "name": "Dev.to Top",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        240,
        0
      ],
      "parameters": {
        "url": "https://dev.to/api/articles?top=1&per_page=5",
        "options": {}
      }
    },
    {
      "id": "8c3d6e44-a4a4-4a4a-9a4a-000000000004",
      "name": "GitHub Trending",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        240,
        200
      ],
      "parameters": {
        "url": "https://api.github.com/search/repositories?q=created:>{{ $now.minus({days: 2}).toFormat('yyyy-MM-dd') }}&sort=stars&order=desc&per_page=5",
        "options": {
          "response": { "response": { "neverError": true } }
        }
      }
    },
    {
      "id": "8c3d6e44-a5a5-4a5a-9a5a-000000000005",
      "name": "Normalize HN",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        480,
        -200
      ],
      "parameters": {
        "jsCode": "const hits = $input.first().json.hits || [];\nreturn hits.map(h => ({ json: {\n  source: 'Hacker News',\n  title: h.title,\n  url: h.url || `https://news.ycombinator.com/item?id=${h.objectID}`,\n  score: h.points || 0,\n  summary_seed: (h.story_text || '').slice(0, 400)\n}}));"
      }
    },
    {
      "id": "8c3d6e44-a6a6-4a6a-9a6a-000000000006",
      "name": "Normalize Dev.to",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        480,
        0
      ],
      "parameters": {
        "jsCode": "const items = $input.all().map(it => it.json);\nreturn items.map(a => ({ json: {\n  source: 'Dev.to',\n  title: a.title,\n  url: a.url,\n  score: a.positive_reactions_count || 0,\n  summary_seed: (a.description || '').slice(0, 400)\n}}));"
      }
    },
    {
      "id": "8c3d6e44-a7a7-4a7a-9a7a-000000000007",
      "name": "Normalize GitHub",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        480,
        200
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst items = (data && Array.isArray(data.items)) ? data.items : [];\nreturn items.map(r => ({ json: {\n  source: 'GitHub Trending',\n  title: `${r.full_name} \u2014 ${r.language || 'mixed'}`,\n  url: r.html_url,\n  score: r.stargazers_count || 0,\n  summary_seed: (r.description || '').slice(0, 400)\n}}));"
      }
    },
    {
      "id": "8c3d6e44-a8a8-4a8a-9a8a-000000000008",
      "name": "Merge All Sources",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        720,
        0
      ],
      "parameters": {
        "mode": "append"
      }
    },
    {
      "id": "8c3d6e44-a9a9-4a9a-9a9a-000000000009",
      "name": "Summarize Each",
      "type": "@n8n/n8n-nodes-langchain.chainSummarization",
      "typeVersion": 2.1,
      "position": [
        960,
        0
      ],
      "parameters": {
        "options": {
          "summarizationMethodAndPrompts": {
            "values": {
              "combineMapPrompt": "You are a senior tech analyst. Here is a daily tech item:\n\nSource: {{ $json.source }}\nTitle: {{ $json.title }}\nURL: {{ $json.url }}\nScore/engagement: {{ $json.score }}\nContext: \"{{ $json.summary_seed }}\"\n\nWrite ONE crisp sentence (\u2264 25 words) that tells a busy developer what this is and why it matters TODAY. Lead with the concrete thing, not with \"This story is about...\". If it is a GitHub repo, mention the language or primary domain. No hedging.\n\nONE-SENTENCE:",
              "prompt": "You are a senior tech analyst. Here is a daily tech item:\n\nSource: {{ $json.source }}\nTitle: {{ $json.title }}\nURL: {{ $json.url }}\nScore/engagement: {{ $json.score }}\nContext: \"{{ $json.summary_seed }}\"\n\nWrite ONE crisp sentence (\u2264 25 words) that tells a busy developer what this is and why it matters TODAY. Lead with the concrete thing, not with \"This story is about...\". If it is a GitHub repo, mention the language or primary domain. No hedging.\n\nONE-SENTENCE:"
            }
          }
        }
      }
    },
    {
      "id": "8c3d6e44-aaaa-4aaa-9aaa-00000000000a",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        896,
        240
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-5-mini"
        },
        "options": {}
      }
    },
    {
      "id": "8c3d6e44-abab-4aba-9aba-00000000000b",
      "name": "Combine Items",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        0
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst bySource = {};\nconst toStr = (v) => {\n  if (typeof v === 'string') return v;\n  if (v && typeof v === 'object') return v.text || v.content || v.output || JSON.stringify(v);\n  return v == null ? '' : String(v);\n};\nitems.forEach(it => {\n  const j = it.json;\n  const src = j.source || 'Other';\n  bySource[src] = bySource[src] || [];\n  const raw = j.output ?? j.response?.text ?? j.text ?? '';\n  bySource[src].push({\n    summary: toStr(raw).trim(),\n    url: j.url,\n    title: j.title,\n    score: j.score\n  });\n});\nconst date = new Date().toISOString().slice(0, 10);\nreturn [{ json: { date, bySource } }];"
      }
    },
    {
      "id": "8c3d6e44-acac-4aca-9aca-00000000000c",
      "name": "Format Slack Blocks",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1440,
        0
      ],
      "parameters": {
        "jsCode": "const { date, bySource } = $input.first().json;\nconst blocks = [\n  { type: 'header', text: { type: 'plain_text', text: `\ud83e\udde0 Daily Tech Digest \u2014 ${date}` } },\n  { type: 'context', elements: [{ type: 'mrkdwn', text: `Merged from ${Object.keys(bySource).length} sources` }] },\n  { type: 'divider' }\n];\nObject.entries(bySource).forEach(([src, items]) => {\n  blocks.push({ type: 'section', text: { type: 'mrkdwn', text: `*${src}*` } });\n  items.forEach(i => {\n    const scoreStr = i.score ? ` _(${i.score})_` : '';\n    blocks.push({ type: 'section', text: { type: 'mrkdwn', text: `\u2022 <${i.url}|${i.title}>${scoreStr}\\n${i.summary || '_no summary_'}` } });\n  });\n  blocks.push({ type: 'divider' });\n});\nblocks.push({ type: 'context', elements: [{ type: 'mrkdwn', text: '_Built with n8n \u00b7 nerdleveltech.com_' }] });\nreturn [{ json: { blocks, text: `Daily Tech Digest \u2014 ${date}` } }];"
      }
    },
    {
      "id": "8c3d6e44-adad-4ada-9ada-00000000000d",
      "name": "Post to Slack Webhook",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1680,
        0
      ],
      "parameters": {
        "method": "POST",
        "url": "https://hooks.slack.com/services/REPLACE/WITH/YOUR_WEBHOOK",
        "sendBody": true,
        "bodyContentType": "json",
        "jsonBody": "={{ JSON.stringify({ text: $json.text, blocks: $json.blocks }) }}",
        "options": {}
      }
    }
  ],
  "connections": {
    "Schedule (Daily 8AM)": {
      "main": [
        [
          {
            "node": "Hacker News Top",
            "type": "main",
            "index": 0
          },
          {
            "node": "Dev.to Top",
            "type": "main",
            "index": 0
          },
          {
            "node": "GitHub Trending",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hacker News Top": {
      "main": [
        [
          {
            "node": "Normalize HN",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dev.to Top": {
      "main": [
        [
          {
            "node": "Normalize Dev.to",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub Trending": {
      "main": [
        [
          {
            "node": "Normalize GitHub",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize HN": {
      "main": [
        [
          {
            "node": "Merge All Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Dev.to": {
      "main": [
        [
          {
            "node": "Merge All Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize GitHub": {
      "main": [
        [
          {
            "node": "Merge All Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Sources": {
      "main": [
        [
          {
            "node": "Summarize Each",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Summarize Each",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Summarize Each": {
      "main": [
        [
          {
            "node": "Combine Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Items": {
      "main": [
        [
          {
            "node": "Format Slack Blocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Slack Blocks": {
      "main": [
        [
          {
            "node": "Post to Slack Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null
}