邮件 → tg 公开
高级
这是一个Personal Productivity, Multimodal AI领域的自动化工作流,包含 16 个节点。主要使用 Code, Gmail, Telegram, SplitInBatches, TelegramTrigger 等节点。 按需邮件摘要:从Gmail到Telegram(使用GPT-4.1-mini)
前置要求
- •Google 账号和 Gmail API 凭证
- •Telegram Bot Token
- •OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "nCr0DSvP0NQHbFEn",
"meta": {
"instanceId": "93438a4980e64a583ea2325b4dc5eae4c4c531c30461a48c79a4b262b5c53893",
"templateCredsSetupCompleted": true
},
"name": "邮件 → tg 公开",
"tags": [
{
"id": "TIMIlK5hNxVuDAlg",
"name": "public",
"createdAt": "2025-08-11T13:46:00.810Z",
"updatedAt": "2025-08-11T13:46:00.810Z"
}
],
"nodes": [
{
"id": "54461eef-b4f1-4e29-aa87-674842f889e4",
"name": "获取多条消息",
"type": "n8n-nodes-base.gmail",
"position": [
384,
64
],
"webhookId": "22795477-c7f5-4c56-90aa-dde033c00e5f",
"parameters": {
"filters": {
"q": "=(from:____@____.com) OR (from:____@____.com) OR (from:____@____.com -\"____\") after:{{ $json.dateString }}"
},
"operation": "getAll",
"returnAll": true
},
"credentials": {
"gmailOAuth2": {
"id": "sqe7HFlBnGaSLwH9",
"name": "Gmail account"
}
},
"typeVersion": 2.1
},
{
"id": "2673b554-b1b8-422e-8075-1cedbd5d6235",
"name": "Telegram 触发器",
"type": "n8n-nodes-base.telegramTrigger",
"position": [
-64,
64
],
"webhookId": "82fb11e5-394d-4a57-a1ef-5eae17c4c1bd",
"parameters": {
"updates": [
"message"
],
"additionalFields": {
"chatIds": "your_tg_id"
}
},
"credentials": {
"telegramApi": {
"id": "ki2yZMrnSvQn6aN0",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "cff69481-a8da-4ea1-b78a-3e6d94b54493",
"name": "遍历项目",
"type": "n8n-nodes-base.splitInBatches",
"position": [
608,
64
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "37f59023-026e-44f5-b2b2-f0e37fe8c6d8",
"name": "获取一条消息",
"type": "n8n-nodes-base.gmail",
"position": [
832,
64
],
"webhookId": "4a621c46-2308-40f2-b56e-c6ec6ddf950d",
"parameters": {
"simple": false,
"options": {},
"messageId": "={{ $json.id }}",
"operation": "get"
},
"credentials": {
"gmailOAuth2": {
"id": "sqe7HFlBnGaSLwH9",
"name": "Gmail account"
}
},
"typeVersion": 2.1
},
{
"id": "16d90be1-d6fb-487e-a836-643021da12e1",
"name": "获取天数",
"type": "n8n-nodes-base.code",
"position": [
160,
64
],
"parameters": {
"jsCode": "// input: daysAgo (number of days)\nconst daysAgo = parseInt($json[\"message\"][\"text\"], 10);\nconst date = new Date();\ndate.setDate(date.getDate() - daysAgo);\nconst yyyy = date.getFullYear();\nconst mm = String(date.getMonth() + 1).padStart(2, '0');\nconst dd = String(date.getDate()).padStart(2, '0');\nreturn [{ dateString: `${yyyy}/${mm}/${dd}` }];"
},
"typeVersion": 2
},
{
"id": "79126930-ced7-40e2-947b-466e9b1417fa",
"name": "获取消息数据",
"type": "n8n-nodes-base.code",
"position": [
1056,
64
],
"parameters": {
"jsCode": "function extractHtml(payload) {\n if (!payload) return '';\n if (payload.body && payload.body.data) {\n return Buffer.from(payload.body.data, 'base64').toString('utf-8');\n }\n function findHtmlPart(parts) {\n for (const part of parts) {\n if (part.mimeType === 'text/html' && part.body && part.body.data) {\n return Buffer.from(part.body.data, 'base64').toString('utf-8');\n }\n if (part.parts) {\n const result = findHtmlPart(part.parts);\n if (result) return result;\n }\n }\n return '';\n }\n if (payload.parts) {\n const html = findHtmlPart(payload.parts);\n if (html) return html;\n }\n return '';\n}\n\nlet html = $json.html || '';\nif (!html && $json.payload) {\n html = extractHtml($json.payload);\n}\nif (!html && $json.text) {\n html = $json.text;\n}\n\n// Clean 'from': keep only the sender's name\nlet from = $json.from?.text || $json.from || '';\nconst match = from.match(/^\\\"?([^\\\"<]+)\\\"?\\s*<[^>]+>$/);\nif (match) {\n from = match[1].trim();\n}\n\n// Convert date to DD.MM.YYYY format\nlet date = $json.date || '';\nlet formattedDate = date;\nif (date) {\n const d = new Date(date);\n const day = String(d.getDate()).padStart(2, '0');\n const month = String(d.getMonth() + 1).padStart(2, '0');\n const year = d.getFullYear();\n formattedDate = `${day}.${month}.${year}`;\n}\n\nconst subject = $json.subject || '';\n\nreturn [{\n html,\n subject,\n from,\n date: formattedDate\n}];\n"
},
"typeVersion": 2
},
{
"id": "7a00feb1-4fb2-43a3-8ea3-6c023e138a4c",
"name": "合并",
"type": "n8n-nodes-base.code",
"position": [
832,
-128
],
"parameters": {
"jsCode": "/**\n * This code is intended for use in the \"Code\" node in n8n.\n * It merges the 'topics' arrays from multiple incoming items into a single array.\n *\n * Incoming data (items) have the following structure:\n * [\n * { json: { message: { content: { topics: [...] } } } },\n * { json: { message: { content: { topics: [...] } } } },\n * ...\n * ]\n */\n\n// We use the flatMap method, which is a combination of map and flat.\n// It iterates over each item (item) in the incoming array (items),\n// extracts the 'topics' array and immediately flattens all arrays into one.\nconst allTopics = items.flatMap(item => {\n // We use optional chaining (?.) for safe access to the nested property.\n // This prevents an error if 'message' or 'content' is missing in any of the items.\n // If the path is not found, return an empty array [], so flatMap can work correctly.\n return item.json?.message?.content?.topics || [];\n});\n\n// The \"Code\" node must return an array of objects.\n// We return one object containing the 'json' property with our merged 'topics' array.\n// This result will be available at the output of the node for further use.\nreturn [{\n json: {\n topics: allTopics\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "222dd34e-19b9-46aa-98ba-8f014457fbd4",
"name": "创建 TG 消息",
"type": "n8n-nodes-base.code",
"position": [
1056,
-128
],
"parameters": {
"jsCode": "const topics = $json.topics;\nconst list = topics.map((t, idx) =>\n `${idx + 1}. *${t.title}*\\n\\n${t.descr}\\n\\n${t.subject}\\n→ ${t.from} - ${t.date}`\n).join('\\n\\n');\n\nreturn [{ json: { message: `${list}` } }];\n"
},
"typeVersion": 2
},
{
"id": "de2c745d-e575-4670-ac3a-fbdd6e5ba6a2",
"name": "清理",
"type": "n8n-nodes-base.code",
"position": [
1568,
-128
],
"parameters": {
"jsCode": "/**\n * This code is intended for use in the \"Code\" node in n8n.\n * It prepares text for sending to Telegram in 'HTML' mode.\n * 1. Fixes \"broken\" formatting (*...* and _..._) by adding a closing symbol if their count is odd.\n * 2. Converts *text* to <b>text</b> and _text_ to <i>text</i>, even if the text spans multiple lines.\n * 3. Escapes basic HTML characters (<, >, &) for safety.\n *\n * IMPORTANT: In the Telegram node, Parse Mode must be set to HTML.\n *\n * The code processes EACH incoming item in the array.\n */\n\nconst correctedItems = items.map(item => {\n // FIXED: Use the 'text' key that comes from the \"Split\" node.\n let text = item.json.text;\n\n if (!text) {\n return item;\n }\n\n // --- STEP 1: Fix unbalanced formatting characters ---\n const fixUnbalanced = (str, char) => {\n // Use new RegExp to create a dynamic regular expression\n const count = (str.match(new RegExp(`\\\\${char}`, 'g')) || []).length;\n if (count % 2 !== 0) {\n return str + char;\n }\n return str;\n };\n\n text = fixUnbalanced(text, '*');\n text = fixUnbalanced(text, '_');\n\n // --- STEP 2: Convert to safe HTML ---\n // First, escape basic characters in the entire text so they don't conflict with tags.\n let safeText = text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n\n // Now replace Markdown with HTML tags.\n // Use the 's' (dotAll) flag so '.' also matches newline characters.\n // This is important for your multi-line italics.\n safeText = safeText\n .replace(/\\*(.*?)\\*/gs, '<b>$1</b>')\n .replace(/_(.*?)_/gs, '<i>$1</i>');\n\n\n // Update the 'text' field in the current item.\n // In the Telegram node, use {{ $json.text }}\n item.json.text = safeText;\n\n // Return the modified item.\n return item;\n});\n\n// Return the full array of processed items.\nreturn correctedItems;\n"
},
"typeVersion": 2
},
{
"id": "57984a3f-ccb0-4e14-bac8-4d83ad89bb44",
"name": "拆分",
"type": "n8n-nodes-base.code",
"position": [
1280,
-128
],
"parameters": {
"jsCode": "const CHUNK = 3500;\nconst txt = $json.message;\nconst parts = txt.match(/[\\s\\S]{1,3500}/g) || [];\nreturn parts.map(p => ({ json: { text: p } }));\n"
},
"typeVersion": 2
},
{
"id": "f1b7cbb8-7ae7-4699-97c2-354a0b82684b",
"name": "清洁",
"type": "n8n-nodes-base.code",
"position": [
1280,
64
],
"parameters": {
"jsCode": "/**\n * This code is intended for use in the \"Code\" node in n8n.\n * Its task is to prepare data from the Gmail node for the next node (LLM).\n * It does not parse HTML, it only passes it along with other fields.\n *\n * Incoming data (items) is the result of the Gmail node.\n * [\n * { json: { html: \"...\", subject: \"...\", from: \"...\", date: \"DD.MM.YYYY\" } }\n * ]\n *\n * The code converts the date from \"DD.MM.YYYY\" to \"MM.DD\" and passes\n * all the necessary fields (html, subject, from, date) to the next step.\n */\n\n// Use .map() to transform each incoming item.\nconst results = items.map(item => {\n // Extract the required fields from the incoming JSON.\n const { html, subject, from, date: originalDate } = item.json;\n\n // Check for required fields.\n if (!html || !originalDate) {\n // If there is no data, return null to filter this item later.\n return null;\n }\n\n // 1. Split the date string into components (day, month, year).\n const [day, month] = originalDate.split('.').slice(0, 2);\n\n // 2. Form a new string in the format \"MM.DD\".\n const newDate = `${month}.${day}`;\n\n // 3. Return a new object in the structure expected by\n // the next node (LLM).\n return {\n json: {\n html,\n subject,\n from,\n date: newDate\n }\n };\n});\n\n// Filter out empty results and return the array for the next node.\nreturn results.filter(item => item !== null);\n"
},
"typeVersion": 2
},
{
"id": "a9eb6447-cc67-4364-9994-bcdb37824980",
"name": "发送消息",
"type": "n8n-nodes-base.telegram",
"position": [
1856,
-128
],
"webhookId": "e96216d9-8481-459f-a004-1572609f86d4",
"parameters": {
"text": "={{ $json.text }}\n",
"chatId": "your_tg_id",
"additionalFields": {
"parse_mode": "HTML",
"appendAttribution": false,
"disable_web_page_preview": true
}
},
"credentials": {
"telegramApi": {
"id": "ki2yZMrnSvQn6aN0",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "452b5f0c-db00-4cfe-9229-c61162476228",
"name": "向模型发送消息",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1504,
136
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini",
"cachedResultName": "GPT-4.1-MINI"
},
"options": {},
"messages": {
"values": [
{
"content": "=You are a specialist in analyzing email newsletters. Create a brief summary of the email in JSON format:\n\n`{ \"topics\": [ { \"title\": ..., \"descr\": ..., \"subject\": ..., \"from\": ..., \"date\": ... } ] }`\n\nFor each news item within a block, create a separate topic with a brief one-sentence description, even if they are listed or under a single headline. Do not combine them into one topic.\n\nHowever, if the email is from ____, combine each block (for example, \"____\") by listing the topics in the description.\n\nIf the email is from ____, ignore the sections \"____\" and \"____\".\n\nAll values in your JSON must be in ____ language, except for the subject. The subject—{{ $json.subject }}—must remain untranslated.\n\nHere is the subject: {{ $json.subject }}\nHere is the from: {{ $json.from }}\nHere is the date: {{ $json.date }}\n\nHere is the email:\n{{ $json.html }}"
}
]
},
"jsonOutput": true
},
"credentials": {
"openAiApi": {
"id": "UZtYaio4OAcJGlV9",
"name": "OpenAi account"
}
},
"typeVersion": 1.8
},
{
"id": "fc2f7ecd-5a40-4f15-8a4f-055280b762e3",
"name": "便签",
"type": "n8n-nodes-base.stickyNote",
"position": [
-64,
-224
],
"parameters": {
"color": 4,
"width": 320,
"height": 240,
"content": "## 试试这个!"
},
"typeVersion": 1
},
{
"id": "e9af2dfe-5a7b-4a55-8d78-39d82048a7b4",
"name": "便签1",
"type": "n8n-nodes-base.stickyNote",
"position": [
592,
16
],
"parameters": {
"color": 7,
"width": 1168,
"height": 272,
"content": "## 遍历每条消息"
},
"typeVersion": 1
},
{
"id": "27bbab62-1efb-4384-9d22-829a6515e855",
"name": "便签2",
"type": "n8n-nodes-base.stickyNote",
"position": [
816,
-192
],
"parameters": {
"color": 7,
"width": 880,
"height": 192,
"content": "## 清理文本并形成最终消息"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "56e514d0-6916-476f-992d-246d29f02f11",
"connections": {
"Clean": {
"main": [
[
{
"node": "Message a model",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Create TG message",
"type": "main",
"index": 0
}
]
]
},
"Split": {
"main": [
[
{
"node": "Sanitize",
"type": "main",
"index": 0
}
]
]
},
"Get days": {
"main": [
[
{
"node": "Get many messages",
"type": "main",
"index": 0
}
]
]
},
"Sanitize": {
"main": [
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
]
]
},
"Get a message": {
"main": [
[
{
"node": "Get message data",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
],
[
{
"node": "Get a message",
"type": "main",
"index": 0
}
]
]
},
"Message a model": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Get message data": {
"main": [
[
{
"node": "Clean",
"type": "main",
"index": 0
}
]
]
},
"Telegram Trigger": {
"main": [
[
{
"node": "Get days",
"type": "main",
"index": 0
}
]
]
},
"Create TG message": {
"main": [
[
{
"node": "Split",
"type": "main",
"index": 0
}
]
]
},
"Get many messages": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 个人效率, 多模态 AI
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
邮件→公开邮件
使用GPT-4.1-mini从Gmail创建每日新闻简报摘要
Code
Gmail
Split In Batches
+3
13 节点Vlad Arbatov
个人效率
完整的 B2B 销售流程:Apollo 潜在客户生成、Mailgun 外展和 AI 回复管理
完整的 B2B 销售流程:Apollo 潜在客户生成、Mailgun 外展和 AI 回复管理
If
Set
Code
+26
116 节点Paul
内容创作
从 Telegram 语音消息自动生成电子邮件
使用Whisper、GPT-4和Gmail将Telegram语音消息转换为专业电子邮件
Wait
Gmail
Telegram
+6
11 节点Baptiste Fort
个人效率
Telegram 优先的个人 AI 助手
多工具个人助手,集成Telegram、Grok-4、Gmail、日历和记忆功能
If
Merge
Airtable
+13
23 节点Vlad Arbatov
AI 聊天机器人
💥 使用NanoBanana、Seedream 4、ChatGPT Image和Veo 3自动化视频广告 - VIDE
使用AI(NanoBanana、Seedream、GPT-4o、Veo 3)自动化和发布视频广告活动
Set
Code
Wait
+16
63 节点Dr. Firas
内容创作
GPT-4驱动的冷邮件工作流,包含完全定制的3封邮件跟进
使用GPT-4、Mailgun和Supabase自动化个性化冷邮件序列
If
Set
Code
+22
100 节点Paul
客户培育