E-Mail → Öffentliche E-Mail
Dies ist ein Personal Productivity, Multimodal AI-Bereich Automatisierungsworkflow mit 13 Nodes. Hauptsächlich werden Code, Gmail, SplitInBatches, ScheduleTrigger, OpenAi und andere Nodes verwendet. Tägliche Nachrichtenbrief-Zusammenfassungen aus Gmail mit GPT-4.1-mini erstellen
- •Google-Konto + Gmail API-Anmeldedaten
- •OpenAI API Key
Verwendete Nodes (13)
{
"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": "Viele Nachrichten abrufen",
"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": "Über Elemente iterieren",
"type": "n8n-nodes-base.splitInBatches",
"position": [
592,
64
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "3f688cd5-ac4b-4965-a9d4-2220cee11440",
"name": "Eine Nachricht abrufen",
"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": "Nachrichtendaten abrufen",
"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": "Zusammenführen",
"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": "Bereinigen",
"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": "Vorlage erstellen",
"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": "Nachricht senden",
"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": "Zeitplan-Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
144,
64
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 16
}
]
}
},
"typeVersion": 1.2
},
{
"id": "06729a53-d537-4a80-8aed-b1032b858390",
"name": "Notizzettel",
"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": "Notizzettel1",
"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": "Notizzettel2",
"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": "Ein Modell anfragen",
"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
}
]
]
}
}
}Wie verwende ich diesen Workflow?
Kopieren Sie den obigen JSON-Code, erstellen Sie einen neuen Workflow in Ihrer n8n-Instanz und wählen Sie "Aus JSON importieren". Fügen Sie die Konfiguration ein und passen Sie die Anmeldedaten nach Bedarf an.
Für welche Szenarien ist dieser Workflow geeignet?
Fortgeschritten - Persönliche Produktivität, Multimodales KI
Ist es kostenpflichtig?
Dieser Workflow ist völlig kostenlos. Beachten Sie jedoch, dass Drittanbieterdienste (wie OpenAI API), die im Workflow verwendet werden, möglicherweise kostenpflichtig sind.
Verwandte Workflows
Vlad Arbatov
@vladzimaDiesen Workflow teilen