模板HP - 内容排期
高级
这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 24 个节点。主要使用 Set, Code, Wait, Merge, HttpRequest 等节点。 使用GPT-4、Apify和Google Sheets自动生成Instagram内容排期
前置要求
- •可能需要目标 API 的认证凭证
- •Google Sheets API 凭证
使用的节点 (24)
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 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)可能需要您自行付费。
相关工作流推荐
模板 - 博客生成
使用Google自动补全和GPT-4生成SEO优化的博客文章
Set
Code
Wait
+8
21 节点keisha kalra
内容创作
使用OpenAI、RunwayML和ElevenLabs自动化无脸短视频
使用OpenAI、RunwayML和ElevenLabs自动化无脸短视频:从脚本到社交媒体
Set
Code
Wait
+18
56 节点LeeWei
内容创作
使用 OpenAI、ElevenLabs 和 Fal.ai 自动化病毒式内容创作,适用于视频、播客和 ASMR
使用 OpenAI、ElevenLabs 和 Fal.ai 自动化病毒式内容创作,适用于视频、播客和 ASMR
Set
Code
Wait
+16
97 节点Adam Crafts
内容创作
批量SEO内容生成与带AI图片的Webflow草稿创建(模板)
使用GPT、Gemini图片和Webflow草稿创建进行批量SEO内容生成
If
Set
Code
+18
54 节点Dahiana
内容创作
使用AI和Freepik将Reddit商业痛点转换为病毒式LinkedIn内容
使用AI和Freepik将Reddit商业痛点转换为病毒式LinkedIn内容
If
Set
Code
+16
48 节点Daniel Lianes
内容创作
💥 使用NanoBanana、Seedream 4、ChatGPT Image和Veo 3自动化视频广告 - VIDE
使用AI(NanoBanana、Seedream、GPT-4o、Veo 3)自动化和发布视频广告活动
Set
Code
Wait
+16
63 节点Dr. Firas
内容创作