Courriel -> Courriel public

Intermédiaire

Ceci est unPersonal Productivity, Multimodal AIworkflow d'automatisation du domainecontenant 13 nœuds.Utilise principalement des nœuds comme Code, Gmail, SplitInBatches, ScheduleTrigger, OpenAi. Créer un résumé d'un bulletin d'actualités quotidien à partir de Gmail avec GPT-4.1-mini

Prérequis
  • Compte Google et informations d'identification Gmail API
  • Clé API OpenAI
Aperçu du workflow
Visualisation des connexions entre les nœuds, avec support du zoom et du déplacement
Exporter le workflow
Copiez la configuration JSON suivante dans n8n pour importer et utiliser ce workflow
{
  "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": "Obtenir plusieurs messages",
      "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": "Boucler sur les éléments",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        592,
        64
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "3f688cd5-ac4b-4965-a9d4-2220cee11440",
      "name": "Obtenir un message",
      "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": "Obtenir les données du message",
      "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": "Fusionner",
      "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": "Nettoyer",
      "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": "Créer un modèle",
      "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, \"&amp;\")\n         .replace(/</g, \"&lt;\")\n         .replace(/>/g, \"&gt;\")\n         .replace(/\\\"/g, \"&quot;\")\n         .replace(/'/g, \"&#039;\");\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": "Envoyer un message",
      "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": "Déclencheur Planifié",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        144,
        64
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 16
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "06729a53-d537-4a80-8aed-b1032b858390",
      "name": "Note adhésive",
      "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": "Note adhésive1",
      "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": "Note adhésive2",
      "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": "Message a model",
      "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
          }
        ]
      ]
    }
  }
}
Foire aux questions

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é ?

Intermédiaire - Productivité personnelle, IA Multimodale

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.

Informations sur le workflow
Niveau de difficulté
Intermédiaire
Nombre de nœuds13
Catégorie2
Types de nœuds6
Description de la difficulté

Adapté aux utilisateurs expérimentés, avec des workflows de complexité moyenne contenant 6-15 nœuds

Liens externes
Voir sur n8n.io

Partager ce workflow

Catégories

Catégories: 34