8
n8n 中文网amn8n.com

模板HP - 内容排期

高级

这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 24 个节点。主要使用 Set, Code, Wait, Merge, HttpRequest 等节点。 使用GPT-4、Apify和Google Sheets自动生成Instagram内容排期

前置要求
  • 可能需要目标 API 的认证凭证
  • Google Sheets API 凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "78RdoKSIQITTYT8u",
  "meta": {
    "instanceId": "a9966e7d53853abbaaeed78ba2b9971c959f5792b2cccdff75eb461951503a7f",
    "templateCredsSetupCompleted": true
  },
  "name": "模板 HP - 内容排期",
  "tags": [
    {
      "id": "qGxXgaGLRNM9h4LO",
      "name": "template",
      "createdAt": "2025-08-04T16:37:34.891Z",
      "updatedAt": "2025-08-04T16:37:34.891Z"
    }
  ],
  "nodes": [
    {
      "id": "99e295bb-f026-4641-a96a-098461e70cf7",
      "name": "当点击\"测试工作流\"时",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1960,
        40
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "b220a61b-3bce-49db-a5cc-0b56e0480f39",
      "name": "AI 智能助手 - 内容排期",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        720,
        -20
      ],
      "parameters": {
        "text": "=Write an Instagram post based on the inputs below.\n\nPillar: {{ $json.Pillar }}\nFormat: {{ $json.Format }}\nHoliday: {{ $json.Holiday }}\nSeason: {{ $json.SeasonStage }}\nBlog Title and URL (if applicable): {{ $json.Examples }}\nBlog Description (if any): {{ $json.Description }}\n\n\n🧠 Instructions:\n1. If the Holiday is not \"None\"\nWeave the occasion into the caption and visual description with elegant emotional framing and thematic imagery.\nE.g., a father and child for Father’s Day, subtle red-white-blue palette for July 4th.\nMatch tone to the holiday — nostalgic, celebratory, or heartfelt.\n\n2. If the Pillar is “Posts based on blogs”\nUse the blog title and description to inspire a visually or emotionally powerful idea.\n\nDo not summarize the blog.\n\nYou may end with a soft call to action — but do NOT use “See more in our latest journal.”\n\nInclude a CTA if the blog content or description clearly invites it.\n\n3. If the Pillar is \"testimonials”\n\nOnly begin a caption in the testimonial pillar with [add personalized sentence], not any other pillar. \n\nDo not use quotation marks unless a real quote is supplied.\n\nFocus on the tone of the testimonial (e.g., appreciation, transformation, confidence), not the exact wording.\n \n\n3. Use SeasonStage only to shape tone and atmosphere — not always as direct mention.\nLet the stage of the season influence the emotional feel, not dominate the caption.\nAvoid starting every post with “In summer…” or “As spring begins…”.\nInstead, use:\n\nEnvironmental cues (e.g., light, textures, locations, routines)\n\nEmotional themes (e.g., renewal, warmth, intimacy)\n\nTime-sensitive urgency sparingly (e.g., “final weeks for fall bookings” in mid-October)\n\n💡 You do not need to include the season name unless it adds meaningful context or urgency.\n\n4. Use a tone that is elegant, sincere, and emotionally grounded — aligned with Hartshorn Portraiture’s brand: a boutique photography studio creating framed, luxury portrait artwork for affluent families. \n\nPrioritize clarity and connection over poetic language. Avoid metaphors, clichés, or overly descriptive scenes. Write how a thoughtful human would speak. Let the subject speak for itself — your words should complement, not compete.\n\nKeep it natural, succinct (2–4 sentences), and tailored to the post’s format and intent. Vary your message — make sure it feels fresh, specific, and distinctive from other captions.\n\n5. Visual Description\nDescribe the layout, emotion, or structure of the visual clearly.\nUse variety and specificity (e.g., quote overlay on a candid moment, elegant close-up of frames, side-by-side client transformation).\n\n📦 Output Format:\nCaption\n[Your caption here — 2–4 sentences, emotionally grounded, with clarity and variety]\n\nVisual Description\n[1 sentence describing the image or structure. Be specific.]\n\nHashtags\nInclude ALL brand hashtags:\n#hartshornportraiture #jcmoms #hobokenmommies #njfamilyphotographer #hobokenfamilyphotographer #hobokennj #hobokenmoms #jcmommies #jcfamilyphotographer #jerseycityphotography #hobokenphotographer\nAdd 2 additional relevant local or theme-based hashtags (e.g., #hobokenweekend, #NJbeachportraits)",
        "options": {
          "systemMessage": "=You are a luxury brand copywriter for Hartshorn Portraiture, a boutique photography studio in Hoboken, NJ that creates wall-ready portrait artwork for affluent families who value connection, tradition, and timeless beauty. Your voice is elegant, warm, sincere, and human — never overly poetic, sentimental, or abstract. Captions should sound like something a real person would write, with subtle luxury cues and emotional intelligence.\n\nDo not use flowery or exaggerated phrases.  Avoid repeating sentence structures or relying on vague themes without a clear visual or client connection. \n\nYour job is to generate:\n\nA caption (Instagram-ready, 2–4 sentences)\n\nA visual description (1–2 clear lines describing the image or post structure)\n\n📌 Caption Guidelines\nWrite 2–4 emotionally powerful sentences\n\nUse varied tone across captions: poetic, editorial, warm, reflective, celebratory\n\nNever repeat exact sentence patterns. Avoid empty adjectives. Favor simple, well-crafted language that speaks directly to the client’s values: family, permanence, beauty, and a home filled with meaningful art.\nUse no more than 1 subtle seasonal detail or metaphor per caption, and only if it adds meaning\n\nDo not use quote marks (\"...\") unless an actual quote is provided.\n\n🔁 Content Variation\n\nDon’t default to “family” as the emotional core in every caption. Instead, rotate across:\n\n✨ Artistry & Craftsmanship – Mac’s eye, framing, archival prints, quality\n\n🏡 Interior Impact – how artwork transforms space\n\n🌱 Milestones – maternity, personal portraits, growth\n\n💬 Client Testimonials – If the Pillar is testimonial-based:\n- Begin the caption with this exact placeholder (do not replace it): [add personalized sentence]\n- Do not use quotation marks unless quoting a real client\n- Reflect the tone of the testimonial (e.g., appreciation, confidence, transformation), not the literal language\n- Focus on the client’s emotional journey or experience, not specific wording\n\n🕰️ Timelessness & Legacy – but not always via parenthood/family framing\n\nWhen family is the focus, vary your emotional angle (joy, strength, nostalgia, etc.) and avoid repetitive tones.\n\n🗓️ Seasonal Strategy (SeasonStage = {{ $json.SeasonStage }})\nUse SeasonStage to guide tone, emotion, and visual mood — not to mention the season name in every caption. Let seasonal cues emerge through imagery and energy, not explicit labels.\n\n✅ Use SeasonStage to:\n\nAdjust urgency and emotional pace (e.g., “final weeks to book”)\n\nShape imagery and mood (e.g., “golden evening light” vs. “sun-drenched beach”)\n\n❌ Do not:\n\nMention the season name (“summer,” “spring,” etc.) in every caption\n\nRepeat seasonal clichés (“As summer begins…” “This spring…”)\n\n⚠️ Limit explicit seasonal mentions to once every 4–5 captions unless the post is directly seasonal (e.g., Mother’s Day, holiday gifting)\n\n🧠 Emotional & Visual Hooks (optional ways to begin a post)\nYou may open a caption with:\n\nA vivid moment of interaction (“The way he reached for her hand mid-laugh…”)\n\nA home setting or design cue (“This foyer now holds three generations of smiles.”)\n\n\n🖼️ Visual Description Guidelines\n\nYou may describe the post’s format if relevant — e.g., “quote overlay,” “side-by-side transformation,” or “carousel of three candid moments.”\n\nAvoid values/emotions like “precious,” “timeless,” “beautiful connection”\n\nDescribe exactly what’s visible:\n\n“Client reviewing prints in the studio”\n\n“Framed black-and-white portrait above the fireplace”\n\n“Photo collage with a quote overlay”\n\nKeep descriptions brief, neutral in tone, and focused on layout or subject matter.\n\n🏷️ Hashtag Rules\nEnd every post with 13–15 hashtags:\n\nAlways include ALL brand hashtags:\n#hartshornportraiture #jcmoms #hobokenmommies #njfamilyphotographer #hobokenfamilyphotographer #hobokennj #hobokenmoms #jcmommies #jcfamilyphotographer #jerseycityphotography #hobokenphotographer\n\nAdd 2–3 custom/local hashtags based on content or location:\n\ne.g., #NJbeachportraits, #hobokenweekend, #hobokenstudio"
        },
        "promptType": "define"
      },
      "typeVersion": 1.8
    },
    {
      "id": "cc5be169-0f5f-4ff1-8cea-11bb1325970a",
      "name": "遍历项目",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        140,
        -40
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "8d8bb837-d650-488c-bb03-ce865fba2a6e",
      "name": "无操作,不执行任何动作",
      "type": "n8n-nodes-base.noOp",
      "position": [
        460,
        -260
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "ae777421-894a-4013-8278-c6ff1c150e10",
      "name": "等待",
      "type": "n8n-nodes-base.wait",
      "position": [
        1620,
        -20
      ],
      "webhookId": "b5167d12-ee8b-49a0-836b-944b40e1a32a",
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "8f2da55a-4f97-4c80-beaa-2f6877d5ad36",
      "name": "合并",
      "type": "n8n-nodes-base.merge",
      "position": [
        -440,
        -40
      ],
      "parameters": {},
      "typeVersion": 3.1
    },
    {
      "id": "e2f55596-4f66-437e-8509-4f3714285b65",
      "name": "参考",
      "type": "@n8n/n8n-nodes-langchain.toolCode",
      "position": [
        940,
        200
      ],
      "parameters": {
        "name": "Description_Reference",
        "jsCode": "const references = [\n  {\n    title: \"Framed Portrait in Home\",\n    description: \"Framed portrait hanging in a cozy home interior, placed above a mantel or sofa.\",\n    themes: [\"Framing\", \"Interior\", \"Wall Art\"]\n  },\n  {\n    title: \"Framing Overlay\",\n    description: \"A blurred frame photo with a text overlay that says 'custom framing.'\",\n    themes: [\"Framing\", \"Product\", \"Behind the Scenes\"]\n  },\n  {\n    title: \"Father’s Day Collage\",\n    description: \"A collage of 2–3 photos with 'Happy Father’s Day' text on top.\",\n    themes: [\"Father’s Day\", \"Holiday\"]\n  },\n  {\n    title: \"Testimonial Quote\",\n    description: \"Blurred image with a client review placed in elegant text overlay.\",\n    themes: [\"Testimonial\", \"Client Quote\"]\n  },\n  {\n    title: \"Mac Behind the Scenes\",\n    description: \"Photo of Mac working during a shoot — camera in hand, lights or backdrops visible.\",\n    themes: [\"Artist Insight\", \"Behind the Scenes\"]\n  },\n  {\n    title: \"Beach Portrait Styling\",\n    description: \"Carousel of beach portraits with text like 'what to wear' or 'color palette tips.'\",\n    themes: [\"Beach\", \"Style Guide\", \"Educational\"]\n  },\n  {\n    title: \"Studio Portrait\",\n    description: \"Neutral-tone studio portrait with soft lighting and minimal background.\",\n    themes: [\"Studio\", \"Interior\"]\n  },\n  {\n    title: \"Summer Promo\",\n    description: \"Ad-style post with text like 'Celebrate Summer' or 'Now Booking Beach Sessions.'\",\n    themes: [\"Seasonal\", \"Summer\", \"Ad\"]\n  },\n  {\n    title: \"Holiday Message\",\n    description: \"One image with a decorative border and greeting for July 4th, New Year, etc.\",\n    themes: [\"Holiday\", \"Seasonal\", \"Card\"]\n  },\n  {\n    title: \"Consultation Snapshot\",\n    description: \"A client and photographer reviewing prints or digital proofs in the studio.\",\n    themes: [\"Client Experience\", \"Behind the Scenes\", \"Studio\"]\n  },\n  {\n    title: \"Archival Printing\",\n    description: \"Close-up of the printer or archival paper roll in use, highlighting quality.\",\n    themes: [\"Printing\", \"Product\", \"Behind the Scenes\"]\n  },\n  {\n    title: \"Custom Albums\",\n    description: \"Photo of a hands flipping through a custom photo album on a wood table.\",\n    themes: [\"Albums\", \"Products\", \"Details\"]\n  },\n  {\n    title: \"Wall Display Mockup\",\n    description: \"Mockup of a wall gallery with 2–4 portrait prints in a clean, staged room.\",\n    themes: [\"Interior\", \"Framing\", \"Gallery\"]\n  },\n  {\n    title: \"Tools of the Trade\",\n    description: \"Flatlay of cameras, lenses, and accessories on a studio desk.\",\n    themes: [\"Artist Insight\", \"Tools\", \"Studio\"]\n  },\n  {\n    title: \"Gift Card Promo\",\n    description: \"Photo of a wrapped gift with an HP card insert, styled like a holiday present.\",\n    themes: [\"Gifts\", \"Promo\", \"Product\"]\n  }\n];\n\n// Matching logic\nconst input = query.toLowerCase();\n\nlet match = references.find(ref =>\n  ref.themes.some(theme => input.includes(theme.toLowerCase()))\n);\n\nif (!match) {\n  match = references[Math.floor(Math.random() * references.length)];\n}\n\nreturn match.description;\n",
        "description": "帮助 AI 智能助手创建帖子的视觉描述。让它创建一个具有类似结构、清晰度和简洁性的描述。"
      },
      "typeVersion": 1.1
    },
    {
      "id": "b96217b2-3298-4f1e-bb8f-601728ed5986",
      "name": "提取标题、描述、URL",
      "type": "n8n-nodes-base.code",
      "position": [
        -1440,
        180
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst output = [];\n\nfor (const item of items) {\n  const metadata = item.json.metadata || {};\n  const url = metadata.canonicalUrl || item.json.url || '';\n  const title = metadata.title || '';\n  const description = metadata.description || '';\n  \n  // Optional: Filter out any posts without a title\n  if (title && url) {\n    output.push({\n      json: {\n        URL: url,\n        Title: title,\n        Description: description\n      }\n    });\n  }\n}\n\nreturn output;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "2d435a34-ac65-4d26-8c09-fe87afcc6e71",
      "name": "输入偏好的博客月份",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1240,
        180
      ],
      "parameters": {
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "Fujtlc6eM7k9s5AV",
          "name": "Google Sheets account 3"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "1390ac63-087a-49b0-b0ee-de7679160e36",
      "name": "抓取博客文章",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1640,
        180
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "c1bce033-228d-44f0-a60e-26dc4068e696",
      "name": "等待以防止请求限制",
      "type": "n8n-nodes-base.wait",
      "position": [
        -1020,
        180
      ],
      "webhookId": "a57b14a3-b7a8-4359-b3d7-aa69f72d1123",
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "52b5306c-b81a-43b2-a778-63f86af543b0",
      "name": "读取偏好的博客月份",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -820,
        180
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "Fujtlc6eM7k9s5AV",
          "name": "Google Sheets account 3"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.5
    },
    {
      "id": "e78d082b-711b-442a-aad3-b72b1a8b1744",
      "name": "读取数据",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1240,
        -200
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "Fujtlc6eM7k9s5AV",
          "name": "Google Sheets account 3"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "ed563c5f-0c00-4c5e-9ed8-fed7d329cce9",
      "name": "提取信息",
      "type": "n8n-nodes-base.code",
      "position": [
        -200,
        -40
      ],
      "parameters": {
        "jsCode": "const startDate = new Date(\"2025-07-07\");\nconst endDate = new Date(\"2026-01-30\");\n\nconst daysOfWeek = [1, 3, 5]; // Monday, Wednesday, Friday\nconst fallbackPillars = [\"Family Images\", \"Behind-the-Scenes\"];\nconst serviceSubThemes = [\n  \"Beach\", \"Park\", \"Family\", \"Maternity\", \"Newborns\",\n  \"Headshots\", \"Studio\", \"Beauty\", \"Frames\",\n  \"Photo albums\", \"Cards\", \"Birthdays\", \"Anniversaries\"\n];\nconst processSubThemes = [\n  \"Images of studio layout/exterior of studio\",\n  \"Printer + archival inks\",\n  \"Frame options/process\",\n  \"Photo of camera/lighting equipment used to prove quality\",\n  \"Photo presentation and selection\",\n  \"Photo of consultation (blurred client + focus on HP employee)\"\n];\n\nconst items = $input.all();\nconst contentRows = items.filter(r => r.json.Pillar);\nconst blogPosts = items.filter(r => r.json.URL);\n\nconst output = [];\nconst lastUsedDates = {};\nconst serviceUsedMonths = {};\nconst processUsedMonths = {};\nlet serviceSubThemeIndex = 0;\nlet processSubThemeIndex = 0;\nlet lastFallbackUsed = null;\n\nfunction getSeasonStage(date) {\n  const month = date.getMonth() + 1; // JavaScript months are 0-based\n\n  if (month === 12 || month === 1 || month === 2) {\n    if (month === 12) return \"Early Winter\";\n    if (month === 1) return \"Mid Winter\";\n    return \"Late Winter\"; // February\n  }\n  if (month === 3 || month === 4 || month === 5) {\n    if (month === 3) return \"Early Spring\";\n    if (month === 4) return \"Mid Spring\";\n    return \"Late Spring\"; // May\n  }\n  if (month === 6 || month === 7 || month === 8) {\n    if (month === 6) return \"Early Summer\";\n    if (month === 7) return \"Mid Summer\";\n    return \"Late Summer\"; // August\n  }\n  if (month === 9 || month === 10 || month === 11) {\n    if (month === 9) return \"Early Fall\";\n    if (month === 10) return \"Mid Fall\";\n    return \"Late Fall\"; // November\n  }\n}\n\n\nfunction getMinDays(freq) {\n  const f = (freq || '').toLowerCase();\n  if (f.includes('bi-weekly')) return 14;\n  if (f.includes('weekly')) return 7;\n  if (f.includes('daily')) return 1;\n  if (f.includes('every 2 months')) return 60;\n  if (f.includes('monthly') || f.includes('every 4 weeks')) return 30;\n  return 14;\n}\n\nfunction daysBetween(d1, d2) {\n  return Math.floor((d2 - d1) / (1000 * 60 * 60 * 24));\n}\n\nfunction isScheduledDay(date) {\n  return daysOfWeek.includes(date.getDay());\n}\n\nfor (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {\n  if (!isScheduledDay(d)) continue;\n\n  const date = new Date(d);\n  const dateStr = date.toISOString().split('T')[0];\n  const dayName = date.toLocaleDateString('en-US', { weekday: 'long' });\n  const seasonStage = getSeasonStage(date);\n  const monthKey = `${date.getFullYear()}-${date.getMonth()}`;\n  const currentMonthName = date.toLocaleString('default', { month: 'long' }).toLowerCase();\n  let scheduled = false;\n\n  // Blog logic\n  const blog = blogPosts.find(b => {\n    const preferredMonth = (b.json[\"Preferred Month\"] || '').toLowerCase();\n    return preferredMonth === currentMonthName && !b.json._used;\n  });\n\n  if (blog) {\n    blog.json._used = true;\n    output.push({\n      json: {\n        Date: dateStr,\n        Day: dayName,\n        Pillar: \"Posts based on blogs\",\n        Format: \"Images\",\n        Description: blog.json.Description || '',\n        Examples: `${blog.json.Title} — ${blog.json.URL}`,\n        SeasonStage: seasonStage\n      }\n    });\n    continue;\n  }\n\n  // Regular content\n  for (const row of contentRows) {\n    const basePillar = row.json.Pillar.trim();\n    const freq = row.json.Frequency;\n    const minGap = getMinDays(freq);\n    const lastUsed = lastUsedDates[basePillar];\n    const diff = lastUsed ? daysBetween(lastUsed, date) : Infinity;\n\n    if (diff < minGap) continue;\n    if (output.length > 0 && output[output.length - 1].json.Pillar === basePillar) continue;\n\n    const post = JSON.parse(JSON.stringify(row.json)); // Deep copy\n    let finalPillar = basePillar;\n\n    if (basePillar.toLowerCase() === \"types of services offered\") {\n      if (serviceUsedMonths[monthKey]) continue;\n      const sub = serviceSubThemes[serviceSubThemeIndex % serviceSubThemes.length];\n      finalPillar = `Types of services offered – ${sub}`;\n      serviceUsedMonths[monthKey] = true;\n      serviceSubThemeIndex++;\n    }\n\n    if (basePillar.toLowerCase() === \"explanation of processes (ex: presentation, printing, installation)\") {\n      if (processUsedMonths[monthKey]) continue;\n      const sub = processSubThemes[processSubThemeIndex % processSubThemes.length];\n      finalPillar = `Explanation of processes – ${sub}`;\n      processUsedMonths[monthKey] = true;\n      processSubThemeIndex++;\n    }\n\n    output.push({\n      json: {\n        Date: dateStr,\n        Day: dayName,\n        Pillar: finalPillar,\n        Format: post[\"Content Form\"] || post[\"Format\"] || '',\n        Description: post[\"Structure\"] || '',\n        Examples: post[\"Examples\"] || '',\n        SeasonStage: seasonStage\n      }\n    });\n    lastUsedDates[basePillar] = date;\n    scheduled = true;\n    break;\n  }\n\n  // Fallback logic\n  if (!scheduled) {\n    const options = fallbackPillars.filter(p => p !== lastFallbackUsed);\n    const fallback = options[0] || fallbackPillars[0];\n    output.push({\n      json: {\n        Date: dateStr,\n        Day: dayName,\n        Pillar: fallback,\n        Format: '',\n        Description: '',\n        Examples: '',\n        SeasonStage: seasonStage\n      }\n    });\n    lastFallbackUsed = fallback;\n  }\n}\n\nreturn output;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4fd2f21e-636a-4d97-afde-76a735d76bbc",
      "name": "识别节假日",
      "type": "n8n-nodes-base.code",
      "position": [
        440,
        -20
      ],
      "parameters": {
        "jsCode": "const post = $json;\nconst date = new Date(post.Date);\nconst year = date.getFullYear();\nconst month = date.getMonth(); // 0 = Jan, 11 = Dec\nconst dateNum = date.getDate();\nconst day = date.getDay(); // 0 = Sun, 1 = Mon, ..., 6 = Sat\n\nconst formattedDate = date.toISOString().split(\"T\")[0];\n\nfunction isNthWeekday(month, weekday, n) {\n  let count = 0;\n  for (let i = 1; i <= 31; i++) {\n    const d = new Date(year, month, i);\n    if (d.getMonth() !== month) break;\n    if (d.getDay() === weekday) count++;\n    if (count === n) return i;\n  }\n  return -1;\n}\n\nfunction isLastWeekday(month, weekday) {\n  let last = -1;\n  for (let i = 1; i <= 31; i++) {\n    const d = new Date(year, month, i);\n    if (d.getMonth() !== month) break;\n    if (d.getDay() === weekday) last = i;\n  }\n  return last;\n}\n\n// Easter (hardcoded for accuracy)\nconst easterDates = {\n  2025: \"2025-04-20\",\n  2026: \"2026-04-05\",\n  2027: \"2027-03-28\"\n};\n\nlet holiday = \"None\";\n\n// Fixed-date holidays\nif (month === 0 && dateNum === 1) holiday = \"New Year’s Day\";\nelse if (month === 1 && dateNum === 14) holiday = \"Valentine’s Day\";\nelse if (month === 2 && dateNum === 17) holiday = \"St. Patrick’s Day\";\nelse if (formattedDate === easterDates[year]) holiday = \"Easter\";\nelse if (month === 5 && dateNum === 19) holiday = \"Juneteenth\";\nelse if (month === 6 && dateNum === 4) holiday = \"Independence Day\";\nelse if (month === 9 && dateNum === 11) holiday = \"Veterans Day\";\nelse if (month === 9 && dateNum === 31) holiday = \"Halloween\"; // rare edge case\nelse if (month === 11 && dateNum === 25) holiday = \"Christmas Day\";\n\n// Floating holidays\nelse if (month === 0 && dateNum === isNthWeekday(0, 1, 3)) holiday = \"Martin Luther King Jr. Day\";\nelse if (month === 1 && dateNum === isNthWeekday(1, 1, 3)) holiday = \"Presidents’ Day\";\nelse if (month === 4 && dateNum === isNthWeekday(4, 0, 2)) holiday = \"Mother’s Day\";\nelse if (month === 4 && dateNum === isLastWeekday(4, 1)) holiday = \"Memorial Day\";\nelse if (month === 5 && dateNum === isNthWeekday(5, 0, 3)) holiday = \"Father’s Day\";\nelse if (month === 8 && dateNum === isNthWeekday(8, 1, 1)) holiday = \"Labor Day\";\nelse if (month === 9 && dateNum === isNthWeekday(9, 1, 2)) holiday = \"Columbus Day\";\nelse if (month === 10 && dateNum === isNthWeekday(10, 4, 4)) holiday = \"Thanksgiving\";\n\npost.Holiday = holiday;\nreturn { json: post };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "a097c38c-d64e-41e9-92db-206304a7b62d",
      "name": "标题、描述、标签",
      "type": "n8n-nodes-base.code",
      "position": [
        1160,
        -20
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const fullText = $json.output || '';\nconst lines = fullText.split('\\n');\n\nlet caption = '';\nlet visualDescription = '';\nlet hashtags = '';\n\nlet currentSection = '';\n\nfor (const line of lines) {\n  const trimmed = line.trim();\n\n  // Normalize section header detection (handles : — or none)\n  if (/^\\*?\\*?\\s*Caption\\b/i.test(trimmed)) {\n    currentSection = 'caption';\n    continue;\n  } else if (/^\\*?\\*?\\s*Visual Description\\b/i.test(trimmed)) {\n    currentSection = 'visualDescription';\n    continue;\n  } else if (/^\\*?\\*?\\s*Hashtags\\b/i.test(trimmed)) {\n    currentSection = 'hashtags';\n    continue;\n  }\n\n  if (currentSection === 'caption') caption += trimmed + ' ';\n  else if (currentSection === 'visualDescription') visualDescription += trimmed + ' ';\n  else if (currentSection === 'hashtags') hashtags += trimmed + ' ';\n}\n\nreturn {\n  json: {\n    Caption: caption.trim(),\n    Description: visualDescription.trim(),\n    Hashtags: hashtags.trim()\n  }\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "90e6800b-0abe-4389-9368-41ce6b1bec3e",
      "name": "重命名字段",
      "type": "n8n-nodes-base.set",
      "position": [
        1380,
        -20
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cc70084f-cad3-45d8-a680-09a59fa6c87e",
              "name": "Caption",
              "type": "string",
              "value": "={{ $json.Caption }}"
            },
            {
              "id": "4e265422-f477-4a90-8043-ca6ede55d7e6",
              "name": "Hashtags",
              "type": "string",
              "value": "={{ $json.Hashtags }}"
            },
            {
              "id": "d16a69df-4b8e-464e-a19d-e8566c12897d",
              "name": "Date",
              "type": "string",
              "value": "={{ $('Loop Over Items').item.json.Date }}"
            },
            {
              "id": "7e52cc33-9c30-4f22-9260-d04d6f2d365d",
              "name": "Day",
              "type": "string",
              "value": "={{ $('Loop Over Items').item.json.Day }}"
            },
            {
              "id": "8b1956b4-e34a-46c0-9e9e-fe21a8b49ffc",
              "name": "Pillar",
              "type": "string",
              "value": "={{ $('Loop Over Items').item.json.Pillar }}"
            },
            {
              "id": "8a7356bc-3292-41e7-8f04-bc156e8090c0",
              "name": "Format",
              "type": "string",
              "value": "={{ $('Loop Over Items').item.json.Format }}"
            },
            {
              "id": "8bc6ddeb-71c2-419c-8be4-522cb7ad96e2",
              "name": "Description",
              "type": "string",
              "value": "={{ $json.Description }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "04be4907-f32c-4551-a4c5-b8de83c1b511",
      "name": "导出数据",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1860,
        -20
      ],
      "parameters": {
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "Fujtlc6eM7k9s5AV",
          "name": "Google Sheets account 3"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "1277c53a-64bc-4dd6-a100-497a37ad22d7",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3180,
        -960
      ],
      "parameters": {
        "width": 1080,
        "height": 2060,
        "content": "## 立即试用!"
      },
      "typeVersion": 1
    },
    {
      "id": "fac1861d-edea-4450-bae2-d2a4cc4a3ef5",
      "name": "便签6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2020,
        -480
      ],
      "parameters": {
        "color": 7,
        "width": 1960,
        "height": 980,
        "content": "# 1. 两组输入"
      },
      "typeVersion": 1
    },
    {
      "id": "c0cd04bd-7a71-4250-abe8-369e547956bf",
      "name": "便签7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        60,
        -480
      ],
      "parameters": {
        "color": 7,
        "width": 1960,
        "height": 980,
        "content": "# 2. 使用 GPT-4 创建标题、标签和视觉描述"
      },
      "typeVersion": 1
    },
    {
      "id": "18c9f341-d123-47d8-a049-e0a41c6ff8d8",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2020,
        -1320
      ],
      "parameters": {
        "width": 1880,
        "height": 800,
        "content": "## 输入 #1:“初始输入”"
      },
      "typeVersion": 1
    },
    {
      "id": "447abe53-d03d-4905-b1f8-e69991210d11",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2020,
        560
      ],
      "parameters": {
        "width": 1880,
        "height": 440,
        "content": "## 输入 #2:“输入(博客月份)”"
      },
      "typeVersion": 1
    },
    {
      "id": "eb712852-4cb7-4618-9437-edc2aa203017",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2080,
        -460
      ],
      "parameters": {
        "width": 1860,
        "height": 580,
        "content": "## 输出"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "cf50b336-182f-4027-9c8c-611c2d4e599c",
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "Export Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Extract Information",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read data": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "reference": {
      "ai_tool": [
        [
          {
            "node": "AI Agent - content schedule",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Export Data": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rename Fields": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Identify holiday",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Identify holiday": {
      "main": [
        [
          {
            "node": "AI Agent - content schedule",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape blog posts": {
      "main": [
        [
          {
            "node": "Extract title, description, url",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Information": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "No Operation, do nothing": {
      "main": [
        []
      ]
    },
    "Read preferred blog month": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Input preferred blog month": {
      "main": [
        [
          {
            "node": "Wait to prevent request limit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent - content schedule": {
      "main": [
        [
          {
            "node": "Caption, Description, Hashtags",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait to prevent request limit": {
      "main": [
        [
          {
            "node": "Read preferred blog month",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Caption, Description, Hashtags": {
      "main": [
        [
          {
            "node": "Rename Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract title, description, url": {
      "main": [
        [
          {
            "node": "Input preferred blog month",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking ‘Test workflow’": {
      "main": [
        [
          {
            "node": "Read data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Scrape blog posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 内容创作, 多模态 AI

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量24
分类2
节点类型12
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

外部链接
在 n8n.io 查看

分享此工作流