メール→公開メール
中級
これはPersonal Productivity, Multimodal AI分野の自動化ワークフローで、13個のノードを含みます。主にCode, Gmail, SplitInBatches, ScheduleTrigger, OpenAiなどのノードを使用。 GPT-4.1-miniを使ってGmailから毎日のニュース要約を作成
前提条件
- •Googleアカウント + Gmail API認証情報
- •OpenAI API Key
ワークフロープレビュー
ノード接続関係を可視化、ズームとパンをサポート
ワークフローをエクスポート
以下のJSON設定をn8nにインポートして、このワークフローを使用できます
{
"id": "4LGHvpa2PjTNwGc1",
"meta": {
"instanceId": "93438a4980e64a583ea2325b4dc5eae4c4c531c30461a48c79a4b262b5c53893",
"templateCredsSetupCompleted": true
},
"name": "email → email public",
"tags": [
{
"id": "TIMIlK5hNxVuDAlg",
"name": "public",
"createdAt": "2025-08-11T13:46:00.810Z",
"updatedAt": "2025-08-11T13:46:00.810Z"
}
],
"nodes": [
{
"id": "8636c298-0e5a-494d-9bd1-beace2be380c",
"name": "複数のメッセージを取得",
"type": "n8n-nodes-base.gmail",
"position": [
368,
64
],
"webhookId": "e1b897aa-1e4d-4880-8be0-df0c5f5d577c",
"parameters": {
"filters": {
"q": "=(from:____@____.com) OR (from:____@____.com) OR (from:____@____.com -\"____\") after:{{ $now.minus({ days: 1 }).toFormat('yyyy/MM/dd') }}"
},
"operation": "getAll",
"returnAll": true
},
"credentials": {
"gmailOAuth2": {
"id": "sqe7HFlBnGaSLwH9",
"name": "Gmail account"
}
},
"typeVersion": 2.1
},
{
"id": "d77aad78-85fa-4dd1-9227-6636b023dc04",
"name": "アイテムをループ処理",
"type": "n8n-nodes-base.splitInBatches",
"position": [
592,
64
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "3f688cd5-ac4b-4965-a9d4-2220cee11440",
"name": "メッセージを取得",
"type": "n8n-nodes-base.gmail",
"position": [
816,
64
],
"webhookId": "309187bc-88e5-4174-8aea-e3be7dc0b20e",
"parameters": {
"simple": false,
"options": {},
"messageId": "={{ $json.id }}",
"operation": "get"
},
"credentials": {
"gmailOAuth2": {
"id": "sqe7HFlBnGaSLwH9",
"name": "Gmail account"
}
},
"typeVersion": 2.1
},
{
"id": "104254b9-51ea-4655-9e49-38dd3b200929",
"name": "メッセージデータを取得",
"type": "n8n-nodes-base.code",
"position": [
1040,
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": "31e5402b-7146-4955-ae7b-ee4dd3ecc583",
"name": "マージ",
"type": "n8n-nodes-base.code",
"position": [
816,
-128
],
"parameters": {
"jsCode": "/**\n * This code is intended for use in the \"Code\" node in n8n.\n * It merges the 'topics' arrays from several 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 element (item) in the incoming array (items),\n// extracts the 'topics' array and immediately flattens all collected 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 element.\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 on the node's output for further use.\nreturn [{\n json: {\n topics: allTopics\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "df46e906-1a21-47c3-9168-900ee58c3730",
"name": "クリーンアップ",
"type": "n8n-nodes-base.code",
"position": [
1264,
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) — this 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 \"MM.DD\" format.\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": "4bd967a6-cd5c-4225-a785-fda4f227979a",
"name": "テンプレートを作成",
"type": "n8n-nodes-base.code",
"position": [
1040,
-128
],
"parameters": {
"jsCode": "/**\n * This code is intended for use in the \"Code\" node in n8n.\n * It takes an array of topics and creates a single,\n * well-formatted HTML email for sending.\n *\n * This node replaces the nodes \"Formatting\", \"Split\", and \"Sanitizer\".\n */\n\n// Get the array of topics from the previous node.\nconst topics = $json.topics;\n\n// Check if there are topics to process.\nif (!Array.isArray(topics) || topics.length === 0) {\n return []; // If there are no topics, stop the workflow.\n}\n\n// --- Create HTML markup for each news item ---\nconst topicsHtml = topics.map((topic, index) => {\n // Escape basic HTML characters in the data to avoid breaking the markup.\n const escapeHtml = (unsafe) => {\n if (!unsafe) return '';\n return unsafe\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\\\"/g, \""\")\n .replace(/'/g, \"'\");\n };\n\n const title = escapeHtml(topic.title);\n const descr = escapeHtml(topic.descr).replace(/\\n/g, '<br>'); // Preserve line breaks in the description\n const subject = escapeHtml(topic.subject);\n const from = escapeHtml(topic.from);\n const date = escapeHtml(topic.date);\n\n // Build a nice HTML block for a single news item.\n return `\n <div style=\"margin-bottom: 24px; padding-bottom: 16px; border-bottom: 1px solid #eeeeee;\">\n <h3 style=\"margin: 0 0 8px 0; font-size: 18px; color: #1a1a1a;\">\n ${index + 1}. ${title}\n </h3>\n <p style=\"margin: 0 0 12px 0; font-size: 16px; color: #333333; line-height: 1.5;\">\n ${descr}\n </p>\n <p style=\"margin: 0; font-size: 14px; color: #777777; font-style: italic;\">\n ${subject}<br>\n → ${from} - ${date}\n </p>\n </div>\n `;\n}).join(''); // Join all HTML blocks into a single string.\n\n// --- Wrap everything in a full HTML document with styles ---\nconst finalHtml = `\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Ваш новостной дайджест</title>\n </head>\n <body style=\"margin: 0; padding: 0; background-color: #f7f7f7; font-family: Arial, sans-serif;\">\n <table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n <tr>\n <td align=\"center\" style=\"padding: 20px;\">\n <table width=\"600\" border=\"0\" cellpadding=\"20\" cellspacing=\"0\" style=\"background-color: #ffffff; border-radius: 8px;\">\n <tr>\n <td>\n <h1 style=\"text-align: center; color: #1a1a1a;\">Новостной дайджест</h1>\n ${topicsHtml}\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </body>\n </html>\n`;\n\n// Return the result. In the \"Send Email\" node use {{ $json.htmlBody }}\nreturn [{ json: { htmlBody: finalHtml } }];\n"
},
"typeVersion": 2
},
{
"id": "57cc3b99-7a91-492f-8d5c-cbf0da079d16",
"name": "メッセージを送信",
"type": "n8n-nodes-base.gmail",
"position": [
1264,
-128
],
"webhookId": "74e87896-9cca-4dc0-98bb-7e8f712c5d01",
"parameters": {
"sendTo": "your-email@example.com",
"message": "={{ $json.htmlBody }}",
"options": {},
"subject": "Your-subject"
},
"credentials": {
"gmailOAuth2": {
"id": "sqe7HFlBnGaSLwH9",
"name": "Gmail account"
}
},
"typeVersion": 2.1
},
{
"id": "363983e4-7be4-4319-8fdb-dd8ff39200e5",
"name": "スケジュールトリガー",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
144,
64
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 16
}
]
}
},
"typeVersion": 1.2
},
{
"id": "06729a53-d537-4a80-8aed-b1032b858390",
"name": "付箋",
"type": "n8n-nodes-base.stickyNote",
"position": [
144,
-208
],
"parameters": {
"color": 4,
"width": 320,
"height": 240,
"content": "## Try this out!\nSend a number to your Telegram bot (e.g., 2) and get a neatly formatted digest of all Gmail newsletters received since that date. Each email is summarized by an LLM into concise topics, merged into a single Telegram message, automatically split into chunks to fit Telegram limits, and safely formatted as HTML."
},
"typeVersion": 1
},
{
"id": "00d59259-5d92-458d-9b8e-bc7bf0f58979",
"name": "付箋1",
"type": "n8n-nodes-base.stickyNote",
"position": [
576,
0
],
"parameters": {
"color": 7,
"width": 1152,
"height": 256,
"content": "## Iterates over each message"
},
"typeVersion": 1
},
{
"id": "351eb40c-ba0a-4ea6-9f14-d7eabbf956e7",
"name": "付箋2",
"type": "n8n-nodes-base.stickyNote",
"position": [
800,
-192
],
"parameters": {
"color": 7,
"width": 656,
"height": 192,
"content": "## Clean up the text and forms the final message"
},
"typeVersion": 1
},
{
"id": "bddb5d4d-629a-4bfd-bf26-b60451949144",
"name": "モデルにメッセージ",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1488,
144
],
"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
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "53fdca83-a1b5-4866-bed8-0b3322de6bc5",
"connections": {
"df46e906-1a21-47c3-9168-900ee58c3730": {
"main": [
[
{
"node": "bddb5d4d-629a-4bfd-bf26-b60451949144",
"type": "main",
"index": 0
}
]
]
},
"31e5402b-7146-4955-ae7b-ee4dd3ecc583": {
"main": [
[
{
"node": "4bd967a6-cd5c-4225-a785-fda4f227979a",
"type": "main",
"index": 0
}
]
]
},
"3f688cd5-ac4b-4965-a9d4-2220cee11440": {
"main": [
[
{
"node": "104254b9-51ea-4655-9e49-38dd3b200929",
"type": "main",
"index": 0
}
]
]
},
"4bd967a6-cd5c-4225-a785-fda4f227979a": {
"main": [
[
{
"node": "57cc3b99-7a91-492f-8d5c-cbf0da079d16",
"type": "main",
"index": 0
}
]
]
},
"d77aad78-85fa-4dd1-9227-6636b023dc04": {
"main": [
[
{
"node": "31e5402b-7146-4955-ae7b-ee4dd3ecc583",
"type": "main",
"index": 0
}
],
[
{
"node": "3f688cd5-ac4b-4965-a9d4-2220cee11440",
"type": "main",
"index": 0
}
]
]
},
"bddb5d4d-629a-4bfd-bf26-b60451949144": {
"main": [
[
{
"node": "d77aad78-85fa-4dd1-9227-6636b023dc04",
"type": "main",
"index": 0
}
]
]
},
"104254b9-51ea-4655-9e49-38dd3b200929": {
"main": [
[
{
"node": "df46e906-1a21-47c3-9168-900ee58c3730",
"type": "main",
"index": 0
}
]
]
},
"363983e4-7be4-4319-8fdb-dd8ff39200e5": {
"main": [
[
{
"node": "8636c298-0e5a-494d-9bd1-beace2be380c",
"type": "main",
"index": 0
}
]
]
},
"8636c298-0e5a-494d-9bd1-beace2be380c": {
"main": [
[
{
"node": "d77aad78-85fa-4dd1-9227-6636b023dc04",
"type": "main",
"index": 0
}
]
]
}
}
}よくある質問
このワークフローの使い方は?
上記のJSON設定コードをコピーし、n8nインスタンスで新しいワークフローを作成して「JSONからインポート」を選択、設定を貼り付けて認証情報を必要に応じて変更してください。
このワークフローはどんな場面に適していますか?
中級 - 個人の生産性, マルチモーダルAI
有料ですか?
このワークフローは完全無料です。ただし、ワークフローで使用するサードパーティサービス(OpenAI APIなど)は別途料金が発生する場合があります。
関連ワークフロー
デイリーブリーフィング - タスクとイベント
Gmailを使用してTodoist、GoogleカレンダーおよびGPT-4oによる自動化された毎日のサマリー
Code
Gmail
Merge
+
Code
Gmail
Merge
18 ノードAK Pasnoor
個人の生産性
毎日のセキュリティニュース
毎日の技術・サイバーセキュリティブリーフィング:RSS、OpenAI GPT-4o、Gmail を使用
If
Code
Gmail
+
If
Code
Gmail
19 ノードCalistus Christian
個人の生産性
OpenAIアシスタントを基にしたGmail自動返信ドラフト生成
OpenAIアシスタントを使ったGmail自動返信ドレフト生成
Set
Code
Gmail
+
Set
Code
Gmail
23 ノードHichul
チケット管理
YouTube 動画に基づく自律ブログ公開
YouTube 動画から ChatGPT、Sheets、Apify、Pexels、WordPress を使用してブログの自主公開
If
Set
Code
+
If
Set
Code
80 ノードOriol Seguí
コンテンツ作成
完全な B2B セールスフロー:Apollo リード生成、Mailgun 外信、および AI 返信管理
完全なB2Bセールスフロー:Apolloリード生成、Mailgunアウト Reach、AI返信管理
If
Set
Code
+
If
Set
Code
116 ノードPaul
コンテンツ作成
ソーシャルメディア用にPerplexityとOpenAIを使用してAIニュース動画コンテンツのアイデアを作成
PerplexityとOpenAIを使ってAIニュース動画のソーシャメディアコンテンツアイデアを作成
Set
Code
Gmail
+
Set
Code
Gmail
18 ノードGain FLow AI
コンテンツ作成