8
n8n 中文网amn8n.com

发送定时n8n发布说明通知到Gmail

高级

这是一个Personal Productivity, AI Summarization领域的自动化工作流,包含 16 个节点。主要使用 Set, Code, Html, Gmail, HttpRequest 等节点。 基于AI的n8n发布说明摘要通知(通过Gmail与GPT-5-Mini)

前置要求
  • Google 账号和 Gmail API 凭证
  • 可能需要目标 API 的认证凭证
  • OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "z301sXBDAZd8EDCw",
  "meta": {
    "instanceId": "2e134e0f94da86d43329d3d8d3284f5c8c835c03cb84be5ca46f772b2cf7a103",
    "templateCredsSetupCompleted": true
  },
  "name": "发送定时 n8n 发布说明通知到 Gmail",
  "tags": [
    {
      "id": "W3G9QjFn5zCWN18M",
      "name": "Notifications",
      "createdAt": "2025-10-27T20:32:25.385Z",
      "updatedAt": "2025-10-27T20:32:25.385Z"
    },
    {
      "id": "dF0HqEuaD5GnFCI0",
      "name": "n8n",
      "createdAt": "2025-10-27T20:32:30.037Z",
      "updatedAt": "2025-10-27T20:32:30.037Z"
    },
    {
      "id": "CF6Q37w8HHllwE7l",
      "name": "gmail",
      "createdAt": "2025-10-27T20:32:34.939Z",
      "updatedAt": "2025-10-27T20:32:34.939Z"
    }
  ],
  "nodes": [
    {
      "id": "09d6bcf3-9cf2-40da-83b5-89833fe663d6",
      "name": "OpenAI 聊天模型",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        648,
        224
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-5-mini",
          "cachedResultName": "gpt-5-mini"
        },
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "a3e60adf-ed32-4f6b-9903-39094c5697d3",
      "name": "结构化输出解析器",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        776,
        224
      ],
      "parameters": {
        "jsonSchemaExample": "{\n\t\"releaseBugFixes\": [\"\"],\n\t\"releaseFeatures\": [\"\"]\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "d3d79b32-6405-405a-864a-4953f824ca6b",
      "name": "定时触发器",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -256,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "executeOnce": false,
      "notesInFlow": false,
      "typeVersion": 1.2,
      "alwaysOutputData": false
    },
    {
      "id": "7a1ec69a-dc91-4365-9686-9d0b2a361736",
      "name": "提取更新内容的 HTML",
      "type": "n8n-nodes-base.html",
      "position": [
        192,
        0
      ],
      "parameters": {
        "options": {},
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "sections",
              "cssSelector": "section",
              "returnArray": true,
              "returnValue": "html"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "160368b0-4fcd-4eca-b478-4154d9aa35c9",
      "name": "格式化数据并根据触发器筛选",
      "type": "n8n-nodes-base.code",
      "position": [
        416,
        0
      ],
      "parameters": {
        "jsCode": "const cheerio = require('cheerio');\n\n// Get the array of section HTML strings\nconst allSections = $input.first().json.sections;\n\n// Get schedule trigger configuration\nconst scheduleParams = $(\"Schedule Trigger\").params;\nlet checkWindowHours = 24; // Default fallback\n\n// Calculate the time window based on schedule trigger settings\nif (scheduleParams?.rule?.interval?.[0]) {\n  const interval = scheduleParams.rule.interval[0];\n  const field = interval.field;\n  \n  if (field === 'days' && interval.daysInterval) {\n    checkWindowHours = interval.daysInterval * 24;\n  } else if (field === 'hours' && interval.hoursInterval) {\n    checkWindowHours = interval.hoursInterval;\n  } else if (field === 'minutes' && interval.minutesInterval) {\n    checkWindowHours = (interval.minutesInterval / 60);\n  } else if (field === 'seconds' && interval.secondsInterval) {\n    checkWindowHours = (interval.secondsInterval / 3600);\n  } else if (field === 'weeks' && interval.weeksInterval) {\n    checkWindowHours = interval.weeksInterval * 7 * 24;\n  } else if (field === 'months' && interval.monthsInterval) {\n    checkWindowHours = interval.monthsInterval * 30 * 24;\n  }\n}\n\nconsole.log(`Using time window of ${checkWindowHours} hours based on schedule trigger`);\n\n// Get current date/time\nconst now = DateTime.now();\n\nconst results = [];\n\n// Process each section HTML string\nallSections.forEach((sectionHtml, index) => {\n  // Load this section's HTML with cheerio\n  const $ = cheerio.load(sectionHtml);\n  \n  // Get the relative time element's datetime attribute\n  const relativeTime = $('relative-time');\n  if (relativeTime.length === 0) return;\n  \n  const dateTimeAttr = relativeTime.attr('datetime');\n  if (!dateTimeAttr) return;\n  \n  // Parse the ISO datetime (format: \"2025-10-27T12:23:36Z\")\n  const releaseDateTime = DateTime.fromISO(dateTimeAttr);\n  \n  if (!releaseDateTime.isValid) {\n    console.log(`Failed to parse datetime: ${dateTimeAttr}`);\n    return;\n  }\n  \n  // Check if within the dynamic time window based on schedule trigger\n  const diffInHours = now.diff(releaseDateTime, 'hours').hours;\n  if (diffInHours > checkWindowHours || diffInHours < 0) return;\n  \n  // Extract releaseVersion from first h2\n  const firstH2 = $('h2').first();\n  const releaseVersion = firstH2.text().trim();\n  \n  // Format releaseDate\n  const releaseDate = releaseDateTime.toFormat('d MMMM, yyyy HH:mm');\n  \n  // Extract releaseLink from div.markdown-body > h2 > a\n  const markdownBody = $('div.markdown-body');\n \n  const releaseLink2 = $('div.flex-1 span.f1 a.Link--primary').attr('href') || '';\n  const releaseLink = releaseLink2 ? ('https://github.com/'+releaseLink2) :''\n  \n  // Extract bug fixes and features\n  let releaseBugFixes = [];\n  let releaseFeatures = [];\n  \n  if (markdownBody.length > 0) {\n    markdownBody.find('h3').each((i, h3Element) => {\n      const h3 = $(h3Element);\n      const h3Text = h3.text().trim();\n      \n      // Find Bug Fixes\n      if (h3Text === 'Bug Fixes' || h3Text.includes('Bug Fix')) {\n        let nextEl = h3.next();\n        // Look for next ul, but stop at next heading\n        while (nextEl.length > 0 && !nextEl.is('ul')) {\n          if (nextEl.is('h2, h3')) break;\n          nextEl = nextEl.next();\n        }\n        if (nextEl.is('ul')) {\n          nextEl.find('li').each((j, li) => {\n            releaseBugFixes.push($(li).text().trim());\n          });\n        }\n      }\n      \n      // Find Features\n      if (h3Text === 'Features' || h3Text.includes('Feature')) {\n        let nextEl = h3.next();\n        // Look for next ul, but stop at next heading\n        while (nextEl.length > 0 && !nextEl.is('ul')) {\n          if (nextEl.is('h2, h3')) break;\n          nextEl = nextEl.next();\n        }\n        if (nextEl.is('ul')) {\n          nextEl.find('li').each((j, li) => {\n            releaseFeatures.push($(li).text().trim());\n          });\n        }\n      }\n    });\n  }\n  \n  results.push({\n    releaseVersion,\n    releaseDate,\n    releaseLink,\n    releaseBugFixes,\n    releaseFeatures\n  });\n});\n\n// Return results in n8n format\nreturn results.map(item => ({ json: item }));"
      },
      "typeVersion": 2
    },
    {
      "id": "d60758f6-313d-4d95-a5a4-6f22df51f4d0",
      "name": "格式化数据",
      "type": "n8n-nodes-base.set",
      "position": [
        992,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "38260412-2d4d-4f9e-ab33-1839ea1e5b6a",
              "name": "releaseVersion",
              "type": "string",
              "value": "={{ $('Format Data and Filter based on Trigger').item.json.releaseVersion }}"
            },
            {
              "id": "601016bd-9724-4608-bd41-cd4127c21268",
              "name": "releaseDate",
              "type": "string",
              "value": "={{ $('Format Data and Filter based on Trigger').item.json.releaseDate }}"
            },
            {
              "id": "70fdeddb-fd07-439c-8ec8-a2b7b9bfc1d4",
              "name": "releaseLink",
              "type": "string",
              "value": "={{ $('Format Data and Filter based on Trigger').item.json.releaseLink }}"
            },
            {
              "id": "dda79d93-e36a-40b2-9874-5564f6d4336f",
              "name": "output.releaseFeatures",
              "type": "array",
              "value": "={{ $json.output.releaseFeatures }}"
            },
            {
              "id": "ae95cbf9-531e-4a3b-8d59-cb5162209d03",
              "name": "output.releaseBugFixes",
              "type": "array",
              "value": "={{ $json.output.releaseBugFixes }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "796aa3f1-6f22-4583-b6cc-ace9616c3fe1",
      "name": "生成 HTML 模板",
      "type": "n8n-nodes-base.code",
      "position": [
        1216,
        0
      ],
      "parameters": {
        "jsCode": "// Get the input data\nconst items = $input.all();\n\n// Start building the HTML\nlet html = `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>n8n Daily Update</title>\n    <style>\n        body {\n            font-family: Arial, sans-serif;\n            line-height: 1.6;\n            color: #333;\n            max-width: 800px;\n            margin: 0 auto;\n            padding: 20px;\n            background-color: #f4f4f4;\n        }\n        .container {\n            background-color: #ffffff;\n            border-radius: 8px;\n            padding: 30px;\n            box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n        }\n        .header {\n            text-align: center;\n            border-bottom: 3px solid #d62854;\n            padding-bottom: 20px;\n            margin-bottom: 30px;\n        }\n        .header h1 {\n            color: #d62854;\n            margin: 0;\n            font-size: 28px;\n        }\n        .header p {\n            color: #666;\n            margin: 10px 0 0 0;\n        }\n        .release {\n            margin-bottom: 40px;\n            border-left: 4px solid #d62854;\n            padding-left: 20px;\n        }\n        .release-header {\n            margin-bottom: 15px;\n        }\n        .release-version {\n            font-size: 22px;\n            font-weight: bold;\n            color: #333;\n            margin: 0 0 5px 0;\n        }\n        .release-date {\n            color: #666;\n            font-size: 14px;\n            margin: 0 0 10px 0;\n        }\n        .release-link {\n            display: inline-block;\n            color: #d62854;\n            text-decoration: none;\n            font-size: 14px;\n            font-weight: 500;\n        }\n        .release-link:hover {\n            text-decoration: underline;\n        }\n        .section {\n            margin-top: 20px;\n        }\n        .section-title {\n            font-size: 16px;\n            font-weight: bold;\n            color: #d62854;\n            margin-bottom: 10px;\n            text-transform: uppercase;\n            letter-spacing: 0.5px;\n        }\n        .items-list {\n            margin: 0;\n            padding-left: 20px;\n        }\n        .items-list li {\n            margin-bottom: 10px;\n            color: #555;\n        }\n        .no-items {\n            color: #999;\n            font-style: italic;\n        }\n        .footer {\n            text-align: center;\n            margin-top: 40px;\n            padding-top: 20px;\n            border-top: 1px solid #ddd;\n            color: #666;\n            font-size: 12px;\n        }\n    </style>\n</head>\n<body>\n    <div class=\"container\">\n        <div class=\"header\">\n            <h1>🚀 n8n Updates</h1>\n            <p>Latest releases, features, and bug fixes</p>\n        </div>\n`;\n\n// Loop through each release\nitems.forEach((item) => {\n    const data = item.json;\n    const version = data.releaseVersion || 'Unknown Version';\n    const date = data.releaseDate || 'Unknown Date';\n    const link = data.releaseLink || '#';\n    const features = data.output?.releaseFeatures || [];\n    const bugFixes = data.output?.releaseBugFixes || [];\n\n    html += `\n        <div class=\"release\">\n            <div class=\"release-header\">\n                <h2 class=\"release-version\">${version}</h2>\n                <p class=\"release-date\">📅 ${date}</p>\n                <a href=\"${link}\" class=\"release-link\" target=\"_blank\">View on GitHub →</a>\n            </div>\n    `;\n\n    // Add Features section\n    html += `<div class=\"section\"><div class=\"section-title\">🎉 New Features</div>`;\n    \n    if (features.length > 0) {\n        html += `<ul class=\"items-list\">`;\n        features.forEach(feature => {\n            html += `<li>${feature}</li>`;\n        });\n        html += `</ul>`;\n    } else {\n        html += `<p class=\"no-items\">No new features in this release</p>`;\n    }\n    html += `</div>`;\n\n    // Add Bug Fixes section\n    html += `<div class=\"section\"><div class=\"section-title\">🔧 Bug Fixes</div>`;\n    \n    if (bugFixes.length > 0) {\n        html += `<ul class=\"items-list\">`;\n        bugFixes.forEach(fix => {\n            html += `<li>${fix}</li>`;\n        });\n        html += `</ul>`;\n    } else {\n        html += `<p class=\"no-items\">No bug fixes in this release</p>`;\n    }\n    html += `</div></div>`;\n});\n\n// Return the HTML\nreturn [{ json: { html } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "4d8a12ea-5ae3-4e4a-9e93-df5dc53cdb06",
      "name": "汇总 n8n 更新数据",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        640,
        0
      ],
      "parameters": {
        "text": "=Give me a summary of the Release Bug Fixes and Release Features as a list of important releases. Include only the important ones, and summarize as much as possible. Use simple language.\n\n{releaseBugFixes: {{ $json.releaseBugFixes }},\nreleaseFeatures: {{ $json.releaseFeatures }}\n} ",
        "options": {
          "maxIterations": 3
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 3
    },
    {
      "id": "12e67c90-bc13-458d-a45b-14ab5881d31c",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -688,
        -352
      ],
      "parameters": {
        "width": 368,
        "height": 528,
        "content": "## n8n 发布通知工作流"
      },
      "typeVersion": 1
    },
    {
      "id": "be26a459-0049-46a8-b4a7-4efd9549a30c",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -296,
        -128
      ],
      "parameters": {
        "color": 7,
        "width": 176,
        "height": 304,
        "content": "### 1. 定时触发器"
      },
      "typeVersion": 1
    },
    {
      "id": "1baae255-a154-46bd-af66-fdaed8ad6d95",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -88,
        -224
      ],
      "parameters": {
        "color": 7,
        "width": 656,
        "height": 400,
        "content": "### 2. 获取 n8n 发布说明并筛选"
      },
      "typeVersion": 1
    },
    {
      "id": "9f097a51-1167-4c61-a17d-8c574140c0bd",
      "name": "获取 n8n 发布说明",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -32,
        0
      ],
      "parameters": {
        "url": "https://github.com/n8n-io/n8n/releases",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "a9dcc608-56f9-4e94-ab8a-2401c1675484",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        -272
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 640,
        "content": "### AI 摘要生成"
      },
      "typeVersion": 1
    },
    {
      "id": "7d414dfc-9171-4bf2-9e79-64f9e52b637e",
      "name": "便签4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        960,
        -144
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 304,
        "content": "### 邮件格式化"
      },
      "typeVersion": 1
    },
    {
      "id": "f1260b97-38b0-4791-baa4-08a4a4aad868",
      "name": "便签5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1392,
        -224
      ],
      "parameters": {
        "color": 7,
        "height": 384,
        "content": "### 发送邮件"
      },
      "typeVersion": 1
    },
    {
      "id": "fafd7a5e-0e9a-49ec-b097-6019d068c1cd",
      "name": "发送消息",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1456,
        0
      ],
      "webhookId": "405dc9ef-79d6-493a-963f-08ecead7bd06",
      "parameters": {
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "=n8n Updates -  {{ $now.format('DDD') }}"
      },
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "0f73b185-10b8-4ab7-87a8-c441fd924d13",
  "connections": {
    "Format Data": {
      "main": [
        [
          {
            "node": "Generate HTML template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get n8n release notes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Summarize n8n Update Data",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get n8n release notes": {
      "main": [
        [
          {
            "node": "Extract HTML for updates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate HTML template": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract HTML for updates": {
      "main": [
        [
          {
            "node": "Format Data and Filter based on Trigger",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Summarize n8n Update Data",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Summarize n8n Update Data": {
      "main": [
        [
          {
            "node": "Format Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Data and Filter based on Trigger": {
      "main": [
        [
          {
            "node": "Summarize n8n Update Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

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

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

高级 - 个人效率, AI 摘要总结

需要付费吗?

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

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

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

外部链接
在 n8n.io 查看

分享此工作流