{
  "id": "y46yWKUMXv5c4yXO",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AutoBlogger with SEO Optimisation",
  "tags": [],
  "nodes": [
    {
      "id": "ce8ca466-fe1c-4bf1-b1ac-4e4eedb655cd",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        48,
        -1024
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 12
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "4dcf5db1-e87c-4be1-a0a2-e95d5c5333b6",
      "name": "Research Topic \u2013 GPT",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        544,
        -416
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={{$json}}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "10641039-52eb-4cf6-8479-c358260e863c",
      "name": "Create WordPress Post",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3424,
        -640
      ],
      "parameters": {
        "url": "https://public-api.wordpress.com/wp/v2/sites/YOURSITE/posts",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ { \"title\": $(\"Get Title, Content, and Image FileName\").item.json.title, \"content\": $(\"Get Title, Content, and Image FileName\").item.json.content, \"status\": \"publish\", \"categories\": [916], \"featured_media\": $(\"Upload Image to Wordpress\").item.json.id } }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "headerParameters": {
          "parameters": [
            {}
          ]
        }
      },
      "credentials": {
        "oAuth2Api": {
          "name": "<your credential>"
        },
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "39207c98-64d0-4fbf-b568-8423de3af206",
      "name": "Upload Image to Wordpress",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3024,
        -640
      ],
      "parameters": {
        "url": "[Your.Site]",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "binaryData",
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Disposition",
              "value": "=attachment; filename=\"{{ $('Get Title, Content, and Image FileName').item.json.image_filename }}\""
            },
            {
              "name": "Content-Type",
              "value": "image/jpeg"
            }
          ]
        },
        "inputDataFieldName": "data"
      },
      "credentials": {
        "oAuth2Api": {
          "name": "<your credential>"
        },
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "ccef3219-0205-4434-9cf0-5587a64969e8",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        2512,
        -640
      ],
      "parameters": {
        "amount": 30
      },
      "typeVersion": 1.1
    },
    {
      "id": "17065be0-975e-4cd5-9499-461e06abf40e",
      "name": "Get Leonardo Image Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2672,
        -640
      ],
      "parameters": {
        "url": "=https://cloud.leonardo.ai/api/rest/v1/generations/{{ $json.generationId }}\n",
        "options": {},
        "authentication": "genericCredentialType"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "b23b38ed-4fb7-4fce-a20a-71495ef6b65e",
      "name": "Get Leonardo Image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2848,
        -640
      ],
      "parameters": {
        "url": "={{ $json.generations_by_pk.generated_images[0].url }}",
        "options": {}
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "c6c99530-e681-4fb1-9b20-33b00661a3ca",
      "name": "Add ALT to Image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        -640
      ],
      "parameters": {
        "url": "=https://public-api.wordpress.com/wp/v2/sites/YOURSITE/media/{{ $json.id }}",
        "method": "PUT",
        "options": {},
        "sendBody": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "alt_text",
              "value": "={{ $('Leonardo Prompt Creator').item.json.message.content }}"
            }
          ]
        }
      },
      "credentials": {
        "oAuth2Api": {
          "name": "<your credential>"
        },
        "httpBasicAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "ea2c2f27-2209-451d-9652-75bd2405726b",
      "name": "Leonardo: Create Post Image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2208,
        -640
      ],
      "parameters": {
        "url": "https://cloud.leonardo.ai/api/rest/v1/generations",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"prompt\": \"{{ $json.message.content }}\",\n  \"modelId\": \"6bef9f1b-29cb-40c7-b9df-32b51c1f67d3\",\n  \"width\": 1280,\n  \"height\": 720,\n  \"sd_version\": \"v2\",\n  \"num_images\": 1,\n  \"promptMagic\": true,\n  \"promptMagicStrength\": 0.5,\n  \"public\": false,\n  \"scheduler\": \"LEONARDO\",\n  \"guidance_scale\": 7\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "aa1e97d9-4806-47a4-b823-de14cb915580",
      "name": "Get Title, Content, and Image FileName",
      "type": "n8n-nodes-base.code",
      "position": [
        1712,
        -400
      ],
      "parameters": {
        "jsCode": "// ---- helpers ----\nfunction slugify(s) {\n  return String(s || '')\n    .toLowerCase()\n    .normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')\n    .replace(/[^a-z0-9]+/g, '-')\n    .replace(/^-+|-+$/g, '')\n    .slice(0, 60);\n}\n\nfunction extractSources(html) {\n  if (!html) return '';\n  if (/<h2[^>]*>Sources<\\/h2>/i.test(html)) return html;\n\n  const urls = Array.from(new Set(\n    (html.match(/https?:\\/\\/[^\\s\"')<>]+/g) || []).map(u => u.replace(/[.,)\\]]+$/,''))\n  ));\n\n  if (!urls.length) return html;\n\n  const items = urls.map(u => {\n    try {\n      const host = new URL(u).hostname.replace(/^www\\./,'');\n      return `<li><a href=\"${u}\">${host}</a></li>`;\n    } catch {\n      return `<li><a href=\"${u}\">${u}</a></li>`;\n    }\n  }).join('');\n\n  return html + `\\n\\n<h2>Sources</h2><ul>${items}</ul>`;\n}\n\n// Detect object vs string safely and only JSON.parse when it's a real string\nfunction coerceObject(j) {\n  // 1) Common places where the node already gives an OBJECT\n  if (j?.message?.parsed && typeof j.message.parsed === 'object') return j.message.parsed;\n  if (j?.choices?.[0]?.message?.parsed && typeof j.choices[0].message.parsed === 'object') {\n    return j.choices[0].message.parsed;\n  }\n  if (j?.message?.content && typeof j.message.content === 'object') return j.message.content;\n\n  // 2) Candidate content (could be string or object)\n  let cand =\n    j?.choices?.[0]?.message?.content ??\n    j?.message?.content ??\n    j?.content ??\n    '';\n\n  // If it's already an OBJECT, just use it\n  if (cand && typeof cand === 'object' && !Array.isArray(cand)) return cand;\n\n  // 3) If it's a STRING, clean & parse\n  if (typeof cand === 'string') {\n    let s = cand.trim();\n\n    // strip ```json fences if present\n    s = s.replace(/^```(?:json)?\\s*/i, '').replace(/```$/i, '');\n\n    // isolate outermost JSON object (in case there's prose around it)\n    const start = s.indexOf('{');\n    const end   = s.lastIndexOf('}');\n    if (start !== -1 && end !== -1) s = s.slice(start, end + 1);\n\n    try {\n      return JSON.parse(s);\n    } catch (e) {\n      throw new Error(\n        'Could not parse model JSON. ' +\n        'Type=' + typeof cand + ' Preview:\\n' + s.slice(0, 400)\n      );\n    }\n  }\n\n  // 4) Nothing usable\n  throw new Error('No usable payload: got ' + typeof cand);\n}\n\n// ---- MAIN ----\nconst obj = coerceObject($json);         // <- ONLY parses when needed\n\nconst title = String(obj.title || 'Untitled').trim();\nlet content = String(obj.content || '');\ncontent = extractSources(content);\n\nconst image_filename = `${slugify(title)}.jpg`;\n\nreturn [{ json: { title, content, image_filename } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e8cd2710-fd64-435b-b7cd-c3477f463a0e",
      "name": "Google News RSS",
      "type": "n8n-nodes-base.rssFeedRead",
      "position": [
        272,
        -1072
      ],
      "parameters": {
        "url": "https://news.google.com/rss/search?q=startups%20OR%20%22venture%20capital%22%20OR%20%22AI%20startup%22%20OR%20technology&hl=en-AU&gl=AU&ceid=AU:en",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "2e98667b-c420-494e-b9cd-9b984ca2c39d",
      "name": "GDELT Docs API",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        272,
        -880
      ],
      "parameters": {
        "url": "https://api.gdeltproject.org/api/v2/doc/doc?query=startups%20OR%20%22venture%20capital%22%20OR%20%22AI%20startup%22%20OR%20technology&mode=artlist&maxrecords=50&format=json&sort=DateDesc",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {}
          ]
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2
    },
    {
      "id": "5298d0dd-12a8-4207-b2b2-846f7f3333e6",
      "name": "Merge News",
      "type": "n8n-nodes-base.merge",
      "position": [
        496,
        -1024
      ],
      "parameters": {},
      "typeVersion": 2
    },
    {
      "id": "6451c13a-9efe-45b6-a007-d9a5c87d762d",
      "name": "Tone & Format Revision \u2013 GPT",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        960,
        -416
      ],
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ $json }}",
        "sendBody": true,
        "specifyBody": "=json",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {}
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "22d0a951-9259-46e9-ba85-406778059ab0",
      "name": "Within dedupe",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        -1024
      ],
      "parameters": {
        "jsCode": "// Deduplicate items by canonicalized URL (strip UTM + hash).\nconst seen = new Set();\nconst out = [];\n\nfor (const item of $input.all()) {\n  let link = (item.json.link || item.json.url || \"\").toLowerCase();\n  if (!link) continue;\n\n  link = link\n    .replace(/(\\?|&)utm_[^=]+=[^&]*/g, \"\")  // drop UTM params\n    .replace(/#.*$/, \"\");                    // drop hash\n\n  if (!seen.has(link)) {\n    seen.add(link);\n    out.push({ json: { ...item.json, link } });\n  }\n}\n\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "49e976f8-980d-456f-a1bd-2edd10c68a08",
      "name": "Top Headlines",
      "type": "n8n-nodes-base.code",
      "position": [
        944,
        -1024
      ],
      "parameters": {
        "jsCode": "function ts(x){ const d = new Date(x || 0); return isNaN(d.getTime()) ? 0 : d.getTime(); }\n\n// take all merged items and sort newest \u2192 oldest\nconst items = $input.all().map(i => i.json);\nitems.sort((a,b) => ts(b.isoDate) - ts(a.isoDate));\n\n// pick your top N\nconst TOP_N = 3;\nconst top = items.slice(0, TOP_N);\n\n// build outputs\nconst headlines = top.map(it => ({ title: it.title, link: it.link, isoDate: it.isoDate }));\nconst news_bullets = top.map(it => `\u2022 ${it.title} \u2014 ${it.link}`).join(\"\\n\");\n\nreturn [{ json: { headlines, news_bullets } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "d4a35b45-006a-4300-8c28-538c34894d93",
      "name": "Build GPT body",
      "type": "n8n-nodes-base.code",
      "position": [
        1616,
        -1024
      ],
      "parameters": {
        "jsCode": "const heads = ($json.headlines && $json.headlines.length)\n  ? $json.headlines\n  : ($node['Top Headlines'].json.headlines || []);\n\nif (!heads.length) throw new Error(\"No headlines available \u2014 stopping.\");\n\nconst style   = $json.style_guide || \"\";\nconst dos     = ($json.dos || []).map(s => \"\u2022 \" + s).join(\"\\n\");\nconst donts   = ($json.donts || []).map(s => \"\u2022 \" + s).join(\"\\n\");\nconst trusted = ($json.trustedDomains || [\"reuters.com\",\"apnews.com\",\"bbc.com\"]).join(\", \");\nconst leadList = heads.map(h => `\u2022 ${h.title} \u2014 ${h.link}`).join(\"\\n\");\n\nconst body = {\n  model: \"gpt-4o-mini\",\n  response_format: { type: \"json_object\" },\n  temperature: 0.6,\n  messages: [\n    { role: \"system\",\n      content: \"You write news-driven articles with credible citations. Never choose 'JSON' as a topic; JSON refers only to the output format. Minimum length of 1600 words, targeting between 1700 to 1800 words.\"\n     \n    },\n    { role: \"user\",\n      content:\n`USE THESE HEADLINES ONLY. PICK ONE AS THE OPENING HOOK AND LINK IT IN THE FIRST PARAGRAPH. DO NOT INVENT A TOPIC. DO NOT WRITE ABOUT 'JSON'.\n\nHEADLINES:\n${leadList}\n\nSTYLE GUIDE:\n${style}\n\nDO:\n${dos}\n\nDON'T:\n${donts}\n\nPREFER SOURCES: ${trusted}\n\nReturn ONLY one JSON object:\n{\n  \"title\": \"<headline based on the news>\",\n  \"content\": \"<HTML only: <p>, <h2>, <ul>, <ol>, <li>, <strong>, <em>, <a>>\"\n}` }\n  ]\n};\n\nreturn [{ json: body }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7d86ccf5-fded-462f-b7c6-e61b257509b8",
      "name": "Classify Headlines",
      "type": "n8n-nodes-base.code",
      "position": [
        1168,
        -1024
      ],
      "parameters": {
        "jsCode": "const heads = $json.headlines || [];\nconst text  = heads.map(h => `${h.title} ${h.link}`).join(\" \").toLowerCase();\n\nlet topic = 'startups-tech';\nif (/(security|breach|ransomware|zero[-\\s]?day|cisa|patch|exploit|credential)/i.test(text)) topic = 'cybersecurity';\nelse if (/(ai|artificial intelligence|anthropic|openai|chatgpt|llm|genai|model|deep learning)/i.test(text)) topic = 'ai-ml';\nelse if (/(economy|inflation|interest rate|fed|recession|ipo|earnings|acquisition|merger|valuation|venture capital|funding|series)/i.test(text)) topic = 'business';\nelse if (/(cloud|aws|azure|gcp|kubernetes|devops|sdk|api|framework|github|vscode)/i.test(text)) topic = 'developer-tools';\nelse if (/(health|who|cdc|sleep|diet|exercise)/i.test(text)) topic = 'health';\n\nlet angle = 'news explainer';\nif (topic === 'business' && /(ipo|earnings)/i.test(text)) angle = 'numbers breakdown';\nif (topic === 'ai-ml' && /(model|release|policy)/i.test(text)) angle = 'capability analysis';\nif (topic === 'cybersecurity' && /(breach|ransomware|zero[-\\s]?day)/i.test(text)) angle = 'incident & mitigation';\n\nconst TRUST = {\n  'ai-ml': [\"arxiv.org\",\"openai.com\",\"anthropic.com\",\"deepmind.google\",\"huggingface.co\",\"nips.cc\",\"icml.cc\",\"paperswithcode.com\",\"nist.gov\",\"reuters.com\",\"apnews.com\",\"bbc.com\"],\n  'cybersecurity': [\"cisa.gov\",\"nist.gov\",\"krebsonsecurity.com\",\"bleepingcomputer.com\",\"cloudflare.com\",\"reuters.com\",\"bbc.com\",\"thehackernews.com\"],\n  'business': [\"reuters.com\",\"apnews.com\",\"ft.com\",\"bloomberg.com\",\"oecd.org\",\"imf.org\",\"worldbank.org\",\"sec.gov\"],\n  'developer-tools': [\"github.blog\",\"aws.amazon.com\",\"azure.microsoft.com\",\"cloud.google.com\",\"kubernetes.io\",\"vercel.com\",\"react.dev\",\"golang.org\",\"python.org\",\"docs.docker.com\"],\n  'health': [\"cdc.gov\",\"who.int\",\"nhs.uk\",\"mayoclinic.org\",\"pubmed.ncbi.nlm.nih.gov\",\"heart.org\",\"sleepfoundation.org\",\"reuters.com\"],\n  'startups-tech': [\"reuters.com\",\"apnews.com\",\"techcrunch.com\",\"theverge.com\",\"wired.com\",\"bbc.com\"]\n};\n\nreturn [{\n  json: {\n    ...$json,                             // keep headlines & news_bullets\n    topic,\n    angle,\n    trustedDomains: TRUST[topic]\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "0397edc3-7806-44e5-95de-bb00bdcd2643",
      "name": "Build Tone Request",
      "type": "n8n-nodes-base.code",
      "position": [
        768,
        -416
      ],
      "parameters": {
        "jsCode": "const style = $node[\"Writing Style\"].json.style_guide || \"Human, practical, medium\u2013low formality.\";\nconst dos = ($node[\"Writing Style\"].json.dos || []).join(\"\\n\");\nconst donts = ($node[\"Writing Style\"].json.donts || []).join(\"\\n\");\n\nconst headlinesArr = $node[\"Top Headlines\"].json.headlines || [];\nconst headlineList = headlinesArr.map(h => `\u2022 ${h.title} \u2014 ${h.link}`).join(\"\\n\");\n\nconst article = $node[\"Build GPT body\"].json_output || $node[\"Build GPT body\"].json || $json;\n\nreturn [{\n  json: {\n    model: \"gpt-4o-mini\",\n    temperature: 0.4,\n    response_format: { type: \"json_object\" },\n    messages: [\n      {\n        role: \"system\",\n        content:\n          \"You are an editor. Improve clarity/flow only. Do NOT change topic, claims, facts, links, or structure. \" +\n          \"Preserve HTML tags (<p>, <h2>, <ul>, <ol>, <li>, <strong>, <em>, <a>). \" +\n          \"Return a JSON object with keys: title, content.\"\n      },\n      {\n        role: \"user\",\n        content:\n          \"STYLE GUIDE:\\n\" + style +\n          \"\\n\\nDO:\\n\" + dos +\n          \"\\n\\nDON'T:\\n\" + donts +\n          \"\\n\\nHEADLINES (for alignment, do not change topic):\\n\" + headlineList +\n          \"\\n\\nARTICLE JSON TO POLISH (keep same keys & structure):\\n\" + JSON.stringify(article)\n      }\n    ]\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4492c1bc-bc74-4fbf-83da-1d92e76f3354",
      "name": "Word Count Guard",
      "type": "n8n-nodes-base.code",
      "position": [
        1136,
        -416
      ],
      "parameters": {
        "jsCode": "// Word Count Guard (after Tone & Format)\nfunction strip(html) { return (html || '').replace(/<[^>]+>/g, ' '); }\nconst text = strip($json.content || '');\nconst words = text.trim().split(/\\s+/).filter(Boolean).length;\n\nconst MIN = 1600;\nif (words >= MIN) {\n  return [{ json: { ...$json, word_count: words } }];\n}\n\n// too short \u2192 ask GPT to expand\nreturn [{\n  json: {\n    draft: $json,\n    word_count: words,\n    need_expand: true\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4ae2e7d0-c90c-4266-ba16-b4df18ae94b0",
      "name": "If1",
      "type": "n8n-nodes-base.if",
      "position": [
        1280,
        -416
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1efd6c6f-5e54-4cc8-9bdb-a9e21ed735b5",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "=={{ $json.need_expand }}",
              "rightValue": true
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "de8bd899-92e4-4748-bbea-804b040aacf8",
      "name": "Expand Draft",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1408,
        -512
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1",
          "cachedResultName": "GPT-4.1"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "=Expand this article to exceed 1,800 words by deepening \u201cWhy it matters\u201d,\n\u201cWinners/risks\u201d, and a more detailed \u201cAction plan\u201d. Keep the same voice\nand existing links intact.\n\n{{ JSON.stringify($json.draft || $json) }}\n\n"
            }
          ]
        },
        "simplify": false,
        "jsonOutput": true
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "fa92a539-e8a8-4dd7-b723-f4b40f4ab291",
      "name": "Code",
      "type": "n8n-nodes-base.code",
      "position": [
        2368,
        -640
      ],
      "parameters": {
        "jsCode": "// Node: Keep Generation Id\nconst id =\n  $json.sdGenerationJob?.generationId ||\n  $json.generationId ||\n  $json.generations_by_pk?.id ||\n  $json.id;\n\nif (!id) throw new Error('No generationId found in previous node output');\n\nreturn [{ json: { generationId: id } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "492255f9-f46f-4cec-9619-8d3353342526",
      "name": "Writing Style",
      "type": "n8n-nodes-base.code",
      "position": [
        1392,
        -1024
      ],
      "parameters": {
        "jsCode": "const base = $input.first().json || {};\nconst style_guide = [\n  \"Use first person a few times (\u201cI\u201d), friendly and practical.\",\n  \"Medium\u2013low formality. No fluff or buzzwords.\",\n  \"Start with a concrete hook tied to one headline.\",\n  \"Explain what happened, why it matters, who it affects, and what to do next.\",\n  \"Embed 4\u20138 inline links to reputable external sources using descriptive anchor text.\",\n  \"Name the source in the sentence when you cite a fact (e.g., Reuters, WHO, CISA).\",\n  \"Clean HTML only: <p>, <h2>, <ul>, <ol>, <li>, <strong>, <em>, <a>.\",\n  \"Short paragraphs (2\u20134 sentences).\",\n  \"Include sections: What happened; Why it matters; Winners/losers or risks; Actionable plan; FAQ.\"\n].join(\"\\n\");\n\nconst do_list = [\n  \"Pick one headline as the lead and summarize it in 2\u20133 sentences with a link.\",\n  \"Pull 1\u20132 supporting links from trusted domains.\",\n  \"Add a 4\u2013step action plan tailored to operators/founders.\",\n  \"End with a grounded takeaway.\"\n];\n\nconst dont_list = [\n  \"Don\u2019t speculate without saying it\u2019s an opinion.\",\n  \"Don\u2019t add links to random low-quality blogs.\",\n  \"Don\u2019t use clickbait.\"\n];\n\nreturn [{\n  json: {\n    ...base,\n    style_guide,\n    dos: do_list,\n    donts: dont_list\n  }\n}];\n\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e1ac258d-ae70-46a4-9bc2-b6d079fc96c6",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1328,
        -1104
      ],
      "parameters": {
        "height": 208,
        "content": "Change the writing style to what you would prefer,"
      },
      "typeVersion": 1
    },
    {
      "id": "35eaa09f-a031-4015-b544-9496c6903007",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        208,
        -1168
      ],
      "parameters": {
        "height": 448,
        "content": "Pulls from the Google news RSS to extract headlines"
      },
      "typeVersion": 1
    },
    {
      "id": "35649c90-f9fe-4774-88a2-98f78c7d5f0c",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        -1120
      ],
      "parameters": {
        "color": 3,
        "width": 704,
        "height": 224,
        "content": "Removes duplicates, pulls the top headlines for the day and classifies."
      },
      "typeVersion": 1
    },
    {
      "id": "d35c8f18-3202-48c6-a2c2-7b0433192632",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1584,
        -1104
      ],
      "parameters": {
        "color": 6,
        "width": 176,
        "height": 208,
        "content": "Builds your GPT Body ready for research"
      },
      "typeVersion": 1
    },
    {
      "id": "76300c38-d17b-44f4-a340-0eb44b48c087",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        496,
        -480
      ],
      "parameters": {
        "width": 896,
        "height": 192,
        "content": "Researches, builds the narrative tone and formats the article. Then it passes through a word count guard to ensure articles are over 1600 words, if not it will pass through the expand draft."
      },
      "typeVersion": 1
    },
    {
      "id": "28fc1334-f466-4d90-8345-b30fbdda0b03",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1696,
        -480
      ],
      "parameters": {
        "width": 464,
        "height": 192,
        "content": "Extracts information from the article and writes a prompt for leonardo to create a header picture."
      },
      "typeVersion": 1
    },
    {
      "id": "c20137e7-5518-465c-92d2-988f7b16f2f8",
      "name": "Leonardo Prompt Creator",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1856,
        -400
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini",
          "cachedResultName": "GPT-4.1-MINI"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=Generate a single-line English description of an editorial-style image to visually represent the following article.\n\nThe image must follow these rules:\n- Cinematic, editorial look (not cartoonish or abstract).\n- No visible text or logos.\n- Must work for a blog post featured image in Google News and Discover (ideal size: 1200x628 px).\n- The article title is: \"{{ $json.title }}\"\n- Article content: {{ $json.content }}.\n\nOutput just the English prompt, without quotes or formatting."
            },
            {
              "role": "system",
              "content": "You are an expert in crafting AI image generation prompts for editorial and news images. You help generate cinematic, editorial-style prompts for blog images that perform well in Google News and Google Discover. Avoid any text or logos in the result."
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "82b37390-e45a-43c5-b147-54d0d894a298",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2192,
        -704
      ],
      "parameters": {
        "color": 3,
        "width": 1136,
        "height": 176,
        "content": "Creates the image and uploads to wordpress, change to your site/ blog location"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "callerPolicy": "workflowsFromSameOwner",
    "executionOrder": "v1"
  },
  "versionId": "cf90d239-f6d1-4a5b-b10f-7d0c99cb9ec7",
  "connections": {
    "If1": {
      "main": [
        [
          {
            "node": "Expand Draft",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get Title, Content, and Image FileName",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Get Leonardo Image Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge News": {
      "main": [
        [
          {
            "node": "Within dedupe",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Expand Draft": {
      "main": [
        [
          {
            "node": "Get Title, Content, and Image FileName",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Top Headlines": {
      "main": [
        [
          {
            "node": "Classify Headlines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Within dedupe": {
      "main": [
        [
          {
            "node": "Top Headlines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Writing Style": {
      "main": [
        [
          {
            "node": "Build GPT body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build GPT body": {
      "main": [
        [
          {
            "node": "Research Topic \u2013 GPT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GDELT Docs API": {
      "main": [
        [
          {
            "node": "Merge News",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Google News RSS": {
      "main": [
        [
          {
            "node": "Merge News",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add ALT to Image": {
      "main": [
        [
          {
            "node": "Create WordPress Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Google News RSS",
            "type": "main",
            "index": 0
          },
          {
            "node": "GDELT Docs API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Word Count Guard": {
      "main": [
        [
          {
            "node": "If1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Tone Request": {
      "main": [
        [
          {
            "node": "Tone & Format Revision \u2013 GPT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Headlines": {
      "main": [
        [
          {
            "node": "Writing Style",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Leonardo Image": {
      "main": [
        [
          {
            "node": "Upload Image to Wordpress",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create WordPress Post": {
      "main": [
        []
      ]
    },
    "Research Topic \u2013 GPT": {
      "main": [
        [
          {
            "node": "Build Tone Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Leonardo Prompt Creator": {
      "main": [
        [
          {
            "node": "Leonardo: Create Post Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Leonardo Image Status": {
      "main": [
        [
          {
            "node": "Get Leonardo Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Image to Wordpress": {
      "main": [
        [
          {
            "node": "Add ALT to Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Leonardo: Create Post Image": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tone & Format Revision \u2013 GPT": {
      "main": [
        [
          {
            "node": "Word Count Guard",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Title, Content, and Image FileName": {
      "main": [
        [
          {
            "node": "Leonardo Prompt Creator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}