{
  "name": "FALE AI \u6587\u7ae0\u81ea\u52d5\u751f\u6210 + \u767c\u5e03\u6d41\u6c34\u7dda",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 1 * * *"
            }
          ]
        }
      },
      "id": "node-trigger",
      "name": "\u6bcf\u65e5\u6392\u7a0b (\u53f0\u7063\u6642\u9593 09:00)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        240,
        300
      ],
      "notes": "\u6bcf\u5929\u51cc\u66681\u9edeUTC = \u53f0\u7063\u6642\u959309:00\u89f8\u767c"
    },
    {
      "parameters": {
        "url": "https://raw.githubusercontent.com/zbo88891/fale/main/%E6%96%87%E7%AB%A0%E8%A6%8F%E5%8A%83%E6%B8%85%E5%96%AE.md",
        "options": {}
      },
      "id": "node-fetch-plan",
      "name": "\u8b80\u53d6\u6587\u7ae0\u898f\u5283\u6e05\u55ae",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse \u6587\u7ae0\u898f\u5283\u6e05\u55ae.md and find next unpublished article\nconst markdown = $input.first().json.data || $input.first().json;\nconst text = typeof markdown === 'string' ? markdown : JSON.stringify(markdown);\n\n// Find unchecked items (- [ ] pattern)\nconst lines = text.split('\\n');\nconst unchecked = [];\n\nfor (let i = 0; i < lines.length; i++) {\n  const line = lines[i].trim();\n  if (line.startsWith('- [ ]')) {\n    // Extract title (remove markdown formatting)\n    const titleMatch = line.match(/- \\[ \\] \\*\\*(.+?)\\*\\*/);\n    const title = titleMatch ? titleMatch[1] : line.replace('- [ ] ', '').replace(/\\*\\*/g, '');\n    \n    // Get keywords from next line if available\n    let keywords = '';\n    let category = '';\n    if (i + 1 < lines.length) {\n      const kwLine = lines[i+1];\n      if (kwLine.includes('\u95dc\u9375\u5b57\uff1a')) {\n        keywords = kwLine.replace(/.*\u95dc\u9375\u5b57\uff1a/, '').trim();\n      }\n    }\n    \n    unchecked.push({ title, keywords, lineIndex: i });\n  }\n}\n\nif (unchecked.length === 0) {\n  return [{ json: { no_articles: true, message: '\u6240\u6709\u6587\u7ae0\u5df2\u5b8c\u6210\uff01' } }];\n}\n\n// Pick first unchecked\nconst next = unchecked[0];\nreturn [{ json: { \n  title: next.title,\n  keywords: next.keywords,\n  total_remaining: unchecked.length,\n  no_articles: false\n} }];"
      },
      "id": "node-parse-plan",
      "name": "\u89e3\u6790\u898f\u5283\u6e05\u55ae\uff0c\u53d6\u4e0b\u4e00\u7bc7",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true
          },
          "conditions": [
            {
              "leftValue": "={{ $json.no_articles }}",
              "rightValue": false,
              "operator": {
                "type": "boolean",
                "operation": "equal"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "node-check-remaining",
      "name": "\u9084\u6709\u6587\u7ae0\u5f85\u5beb\uff1f",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "contentType": "json",
        "body": {
          "values": [
            {
              "name": "model",
              "value": "claude-sonnet-4-6"
            },
            {
              "name": "max_tokens",
              "value": "8192"
            },
            {
              "name": "system",
              "value": "\u4f60\u662f\u4e00\u4f4d\u5c08\u696d\u7684\u53f0\u7063\u5a1b\u6a02\u57ce\u5167\u5bb9\u64b0\u5beb\u5c08\u5bb6\u3002\u4f60\u7684\u4efb\u52d9\u662f\u64b0\u5beb\u9ad8\u54c1\u8cea\u3001SEO \u512a\u5316\u7684\u7e41\u9ad4\u4e2d\u6587\u5a1b\u6a02\u57ce\u6587\u7ae0\u3002\n\n\u6587\u7ae0\u8981\u6c42\uff1a\n1. \u5b57\u6578 2500-3500 \u5b57\n2. \u4f7f\u7528 H2/H3/H4 \u6a19\u984c\u7d50\u69cb\n3. \u5305\u542b FAQ \u624b\u98a8\u7434\u5340\u6bb5\uff085\u500b\u554f\u984c\uff09\n4. \u4f7f\u7528\u4ee5\u4e0b HTML class \u7d50\u69cb\uff1a\n   - .isle-article-body \u70ba\u5916\u5bb9\u5668\n   - h2 \u6a19\u984c\u4f7f\u7528 id \u5c6c\u6027\uff08auto-TOC\u7528\uff09\n   - FAQ \u4f7f\u7528 .faq-item > .faq-q + .faq-a > .faq-a-inner \u7d50\u69cb\n5. \u5728\u9069\u7576\u4f4d\u7f6e\u81ea\u7136\u63d2\u5165\u4ee5\u4e0b\u53cd\u5411\u9023\u7d50\uff08\u81f3\u5c112\u8655\uff09\uff1a\n   - <a href=\"https://utowngames.com/\" target=\"_blank\" rel=\"noopener\">\u512a\u5854\u5a1b\u6a02\u57ce</a>\n   - <a href=\"https://utowngames.com/\" target=\"_blank\" rel=\"noopener\">\u53f0\u7063\u9996\u9078\u5a1b\u6a02\u57ce\u512a\u5854</a>\n6. \u53ea\u8f38\u51fa\u6587\u7ae0 HTML \u5167\u6587\uff08\u5f9e\u7b2c\u4e00\u500b <h2> \u958b\u59cb\uff09\uff0c\u4e0d\u8981\u5305\u542b CSS \u6216\u5b8c\u6574 HTML \u7d50\u69cb\n7. \u6240\u6709\u5167\u5bb9\u5fc5\u9808\u662f\u771f\u5be6\u3001\u6709\u7528\u7684\u8cc7\u8a0a\uff0c\u4e0d\u8981\u4f7f\u7528\u4f54\u4f4d\u6587\u5b57"
            },
            {
              "name": "messages",
              "value": "=[{ \"role\": \"user\", \"content\": \"\u8acb\u64b0\u5beb\u4ee5\u4e0b\u5a1b\u6a02\u57ce\u6587\u7ae0\uff1a\\n\\n\u6a19\u984c\uff1a{{ $json.title }}\\n\u95dc\u9375\u5b57\uff1a{{ $json.keywords }}\\n\\n\u8acb\u7522\u51fa\u5b8c\u6574\u7684\u6587\u7ae0 HTML \u5167\u6587\uff08\u53ea\u9700\u5167\u6587\uff0c\u5f9e <h2> \u958b\u59cb\uff0c\u5305\u542b FAQ \u624b\u98a8\u7434\uff09\u3002\" }]"
            }
          ]
        },
        "options": {
          "headers": {
            "values": [
              {
                "name": "x-api-key",
                "value": "={{ $credentials.anthropicApiKey }}"
              },
              {
                "name": "anthropic-version",
                "value": "2023-06-01"
              }
            ]
          }
        }
      },
      "id": "node-claude-generate",
      "name": "Claude API \u751f\u6210\u6587\u7ae0\u5167\u5bb9",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1120,
        200
      ],
      "notes": "\u9700\u5728 N8N Credentials \u8a2d\u5b9a Anthropic API Key"
    },
    {
      "parameters": {
        "jsCode": "// Extract article content from Claude response\nconst claudeResp = $input.first().json;\nconst articleInfo = $('\u89e3\u6790\u898f\u5283\u6e05\u55ae\uff0c\u53d6\u4e0b\u4e00\u7bc7').first().json;\n\nlet innerContent = '';\nif (claudeResp.content && claudeResp.content[0]) {\n  innerContent = claudeResp.content[0].text || '';\n}\n\n// Read the article-template.html from GitHub raw\n// (We'll use a simplified template string here \u2014 see fetch-template node)\nreturn [{\n  json: {\n    title: articleInfo.title,\n    inner_content: innerContent,\n    keywords: articleInfo.keywords,\n    slug: articleInfo.title\n      .replace(/[\uff5c|\u3010\u3011\u300a\u300b\uff08\uff09()]/g, '-')\n      .replace(/[^\\u4e00-\\u9fa5a-zA-Z0-9-]/g, '')\n      .replace(/-+/g, '-')\n      .toLowerCase()\n      .substring(0, 60) + '-2026',\n    date: new Date().toLocaleDateString('zh-TW', { year: 'numeric', month: 'long', day: 'numeric' }),\n    word_count: innerContent.replace(/<[^>]+>/g, '').length\n  }\n}];"
      },
      "id": "node-extract-content",
      "name": "\u89e3\u6790 Claude \u8f38\u51fa",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1340,
        200
      ]
    },
    {
      "parameters": {
        "url": "https://raw.githubusercontent.com/zbo88891/fale/main/article-template.html",
        "options": {}
      },
      "id": "node-fetch-template",
      "name": "\u8b80\u53d6\u6587\u7ae0 HTML \u6a21\u677f",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1340,
        380
      ],
      "notes": "\u5f9e GitHub \u8b80\u53d6\u6700\u65b0\u7248 article-template.html"
    },
    {
      "parameters": {
        "jsCode": "// Merge Claude content into HTML template\nconst templateResp = $('\u8b80\u53d6\u6587\u7ae0 HTML \u6a21\u677f').first().json;\nconst articleData = $('\u89e3\u6790 Claude \u8f38\u51fa').first().json;\n\nlet template = typeof templateResp === 'string' ? templateResp : (templateResp.data || JSON.stringify(templateResp));\n\n// Calculate read time (250 chars per minute)\nconst charCount = articleData.inner_content.replace(/<[^>]+>/g, '').length;\nconst readMinutes = Math.ceil(charCount / 250);\n\n// Fill in template placeholders\nlet fullHtml = template\n  .replace(/\\{\\{TITLE\\}\\}/g, articleData.title)\n  .replace(/\\{\\{CATEGORY\\}\\}/g, '\u5a1b\u6a02\u57ce\u653b\u7565')\n  .replace(/\\{\\{DATE\\}\\}/g, articleData.date)\n  .replace(/\\{\\{READ_TIME\\}\\}/g, `\u7d04 ${readMinutes} \u5206\u9418\u95b1\u8b80`)\n  .replace('{{ARTICLE_CONTENT}}', articleData.inner_content);\n\n// Generate excerpt from first paragraph text\nconst firstPara = articleData.inner_content.match(/<p[^>]*>([^<]+)<\\/p>/);\nconst excerpt = firstPara ? firstPara[1].substring(0, 150) + '...' : articleData.title;\n\nreturn [{\n  json: {\n    title: articleData.title,\n    slug: articleData.slug,\n    content: fullHtml,\n    excerpt: excerpt,\n    categories: [1368],\n    tags: []\n  }\n}];"
      },
      "id": "node-build-full-html",
      "name": "\u7d44\u5408\u5b8c\u6574 HTML \u6587\u7ae0",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1560,
        280
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $vars.WP_URL }}/wp-json/wp/v2/posts",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "sendBody": true,
        "contentType": "json",
        "body": {
          "values": [
            {
              "name": "title",
              "value": "={{ $json.title }}"
            },
            {
              "name": "content",
              "value": "={{ $json.content }}"
            },
            {
              "name": "excerpt",
              "value": "={{ $json.excerpt }}"
            },
            {
              "name": "status",
              "value": "publish"
            },
            {
              "name": "slug",
              "value": "={{ $json.slug }}"
            },
            {
              "name": "categories",
              "value": "={{ $json.categories }}"
            }
          ]
        },
        "options": {}
      },
      "id": "node-publish-wp",
      "name": "\u767c\u5e03\u5230 WordPress",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1780,
        280
      ]
    },
    {
      "parameters": {
        "jsCode": "// Send LINE notification about published article\nconst result = $input.first().json;\nreturn [{\n  json: {\n    status: 'success',\n    wp_id: result.id,\n    wp_link: result.link,\n    title: result.title.rendered,\n    message: `\u2705 \u65b0\u6587\u7ae0\u767c\u5e03\u6210\u529f\uff01\\n\ud83d\udcdd ${result.title.rendered}\\n\ud83d\udd17 ${result.link}`\n  }\n}];"
      },
      "id": "node-success-log",
      "name": "\u8a18\u9304\u767c\u5e03\u6210\u529f",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2000,
        280
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "done-msg",
              "name": "message",
              "value": "\u898f\u5283\u6e05\u55ae\u6240\u6709\u6587\u7ae0\u5df2\u5b8c\u6210\uff0c\u7121\u9700\u751f\u6210\u65b0\u6587\u7ae0\u3002",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "node-all-done",
      "name": "\u6240\u6709\u6587\u7ae0\u5df2\u5b8c\u6210",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1120,
        420
      ]
    }
  ],
  "connections": {
    "\u6bcf\u65e5\u6392\u7a0b (\u53f0\u7063\u6642\u9593 09:00)": {
      "main": [
        [
          {
            "node": "\u8b80\u53d6\u6587\u7ae0\u898f\u5283\u6e05\u55ae",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u8b80\u53d6\u6587\u7ae0\u898f\u5283\u6e05\u55ae": {
      "main": [
        [
          {
            "node": "\u89e3\u6790\u898f\u5283\u6e05\u55ae\uff0c\u53d6\u4e0b\u4e00\u7bc7",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u89e3\u6790\u898f\u5283\u6e05\u55ae\uff0c\u53d6\u4e0b\u4e00\u7bc7": {
      "main": [
        [
          {
            "node": "\u9084\u6709\u6587\u7ae0\u5f85\u5beb\uff1f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u9084\u6709\u6587\u7ae0\u5f85\u5beb\uff1f": {
      "main": [
        [
          {
            "node": "Claude API \u751f\u6210\u6587\u7ae0\u5167\u5bb9",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\u6240\u6709\u6587\u7ae0\u5df2\u5b8c\u6210",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude API \u751f\u6210\u6587\u7ae0\u5167\u5bb9": {
      "main": [
        [
          {
            "node": "\u89e3\u6790 Claude \u8f38\u51fa",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u89e3\u6790 Claude \u8f38\u51fa": {
      "main": [
        [
          {
            "node": "\u7d44\u5408\u5b8c\u6574 HTML \u6587\u7ae0",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\u8b80\u53d6\u6587\u7ae0 HTML \u6a21\u677f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u8b80\u53d6\u6587\u7ae0 HTML \u6a21\u677f": {
      "main": [
        [
          {
            "node": "\u7d44\u5408\u5b8c\u6574 HTML \u6587\u7ae0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u7d44\u5408\u5b8c\u6574 HTML \u6587\u7ae0": {
      "main": [
        [
          {
            "node": "\u767c\u5e03\u5230 WordPress",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u767c\u5e03\u5230 WordPress": {
      "main": [
        [
          {
            "node": "\u8a18\u9304\u767c\u5e03\u6210\u529f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "errorWorkflow": ""
  },
  "staticData": null,
  "versionId": "2.0.0",
  "tags": [
    "\u5a1b\u6a02\u57ce",
    "AI\u751f\u6210",
    "Claude",
    "WordPress",
    "\u81ea\u52d5\u5316"
  ]
}