Envoi de notifications programmées pour les publications n8n vers Gmail
Ceci est unPersonal Productivity, AI Summarizationworkflow d'automatisation du domainecontenant 16 nœuds.Utilise principalement des nœuds comme Set, Code, Html, Gmail, HttpRequest. Résumé des notes de version n8n par IA avec notification (via Gmail et GPT-5-Mini)
- •Compte Google et informations d'identification Gmail API
- •Peut nécessiter les informations d'identification d'authentification de l'API cible
- •Clé API OpenAI
Nœuds utilisés (16)
Catégorie
{
"id": "z301sXBDAZd8EDCw",
"meta": {
"instanceId": "2e134e0f94da86d43329d3d8d3284f5c8c835c03cb84be5ca46f772b2cf7a103",
"templateCredsSetupCompleted": true
},
"name": "Send scheduled n8n release notes notifications to 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": "Modèle de chat 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": "Structured Output Parser",
"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": "Déclencheur planifié",
"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": "Extract HTML for updates",
"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": "Format Data and Filtrer based on Trigger",
"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": "Format Data",
"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": "Generate HTML template",
"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": "Résumer n8n Update Data",
"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": "Note adhésive",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
-352
],
"parameters": {
"width": 368,
"height": 528,
"content": "## n8n Release Notifications Workflow\n\n**Stay updated with the latest n8n releases automatically!**\n\n## What this workflow does:\n- Runs on a schedule (configurable)\n- Fetches latest release notes from n8n GitHub\n- Uses AI to summarize key features and bug fixes\n- Sends formatted email notifications via Gmail\n\n## Setup required:\n1. **Schedule Trigger** - Adjust frequency if needed\n2. **OpenAI credentials** - Add your API key or connect different LLM\n3. **Gmail credentials** - Connect your Google account\n4. **Recipient email** - Set the \"To\" address in the Gmail node"
},
"typeVersion": 1
},
{
"id": "be26a459-0049-46a8-b4a7-4efd9549a30c",
"name": "Note adhésive1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-296,
-128
],
"parameters": {
"color": 7,
"width": 176,
"height": 304,
"content": "### 1. Schedule Trigger\n\nSet your preferred frequency."
},
"typeVersion": 1
},
{
"id": "1baae255-a154-46bd-af66-fdaed8ad6d95",
"name": "Note adhésive2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-88,
-224
],
"parameters": {
"color": 7,
"width": 656,
"height": 400,
"content": "### 2. Get n8n release notes and filter\n\n- Get all release notes\n- Filters releases based on timeframe\n- Formats data\n\nNo configuration needed - it's automatic!"
},
"typeVersion": 1
},
{
"id": "9f097a51-1167-4c61-a17d-8c574140c0bd",
"name": "Get n8n release notes",
"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": "Note adhésive3",
"type": "n8n-nodes-base.stickyNote",
"position": [
608,
-272
],
"parameters": {
"color": 7,
"width": 288,
"height": 640,
"content": "### AI Summarization\n\nOpenAI analyzes release notes and extracts:\n- Important bug fixes\n- New features\n\n**⚠️Setup Required:**\nAdd your OpenAI API credentials here or connect a different LLM provider."
},
"typeVersion": 1
},
{
"id": "7d414dfc-9171-4bf2-9e79-64f9e52b637e",
"name": "Note adhésive4",
"type": "n8n-nodes-base.stickyNote",
"position": [
960,
-144
],
"parameters": {
"color": 7,
"width": 384,
"height": 304,
"content": "### Format for Email\n\nBuild an HTML template for email."
},
"typeVersion": 1
},
{
"id": "f1260b97-38b0-4791-baa4-08a4a4aad868",
"name": "Note adhésive5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1392,
-224
],
"parameters": {
"color": 7,
"height": 384,
"content": "### Send Email\n\nSend via Gmail.\n\n**⚠️ Setup Required:**\n1. Add Gmail credentials\n2. Set recipient email address in the \"To\" field"
},
"typeVersion": 1
},
{
"id": "fafd7a5e-0e9a-49ec-b097-6019d068c1cd",
"name": "Send a message",
"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": {
"d60758f6-313d-4d95-a5a4-6f22df51f4d0": {
"main": [
[
{
"node": "796aa3f1-6f22-4583-b6cc-ace9616c3fe1",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "9f097a51-1167-4c61-a17d-8c574140c0bd",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "Summarize n8n Update Data",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"9f097a51-1167-4c61-a17d-8c574140c0bd": {
"main": [
[
{
"node": "7a1ec69a-dc91-4365-9686-9d0b2a361736",
"type": "main",
"index": 0
}
]
]
},
"796aa3f1-6f22-4583-b6cc-ace9616c3fe1": {
"main": [
[
{
"node": "fafd7a5e-0e9a-49ec-b097-6019d068c1cd",
"type": "main",
"index": 0
}
]
]
},
"7a1ec69a-dc91-4365-9686-9d0b2a361736": {
"main": [
[
{
"node": "Format Data and Filter based on Trigger",
"type": "main",
"index": 0
}
]
]
},
"a3e60adf-ed32-4f6b-9903-39094c5697d3": {
"ai_outputParser": [
[
{
"node": "Summarize n8n Update Data",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Summarize n8n Update Data": {
"main": [
[
{
"node": "d60758f6-313d-4d95-a5a4-6f22df51f4d0",
"type": "main",
"index": 0
}
]
]
},
"Format Data and Filter based on Trigger": {
"main": [
[
{
"node": "Summarize n8n Update Data",
"type": "main",
"index": 0
}
]
]
}
}
}Comment utiliser ce workflow ?
Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.
Dans quelles scénarios ce workflow est-il adapté ?
Avancé - Productivité personnelle, Résumé IA
Est-ce payant ?
Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.
Workflows recommandés
Jeff Huera
@jeffh30Partager ce workflow