{
  "name": "01-api-lire-mails-du-jour",
  "nodes": [
    {
      "parameters": {
        "path": "mails-today",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        544,
        352
      ],
      "id": "d4ceb5f3-fc91-4731-8e42-7d9c7684e00e",
      "name": "Webhook"
    },
    {
      "parameters": {
        "jsCode": "// Cr\u00e9e une date correspondant \u00e0 maintenant.\nconst now = new Date()\n\n// Cr\u00e9e une copie de la date actuelle.\nconst tomorrow = new Date(now)\n\n// Ajoute un jour pour obtenir la date de demain.\ntomorrow.setDate(now.getDate() + 1)\n\n// Cr\u00e9e une fonction qui transforme une date au format Gmail.\nfunction formatForGmail(date) {\n  // R\u00e9cup\u00e8re l'ann\u00e9e.\n  const year = date.getFullYear()\n\n  // R\u00e9cup\u00e8re le mois avec deux chiffres.\n  const month = String(date.getMonth() + 1).padStart(2, '0')\n\n  // R\u00e9cup\u00e8re le jour avec deux chiffres.\n  const day = String(date.getDate()).padStart(2, '0')\n\n  // Retourne la date au format attendu par Gmail.\n  return `${year}/${month}/${day}`\n}\n\n// Cr\u00e9e une fonction qui transforme une date au format React.\nfunction formatForReact(date) {\n  // R\u00e9cup\u00e8re l'ann\u00e9e.\n  const year = date.getFullYear()\n\n  // R\u00e9cup\u00e8re le mois avec deux chiffres.\n  const month = String(date.getMonth() + 1).padStart(2, '0')\n\n  // R\u00e9cup\u00e8re le jour avec deux chiffres.\n  const day = String(date.getDate()).padStart(2, '0')\n\n  // Retourne la date au format simple.\n  return `${year}-${month}-${day}`\n}\n\n// Pr\u00e9pare la date du jour pour Gmail.\nconst todayForGmail = formatForGmail(now)\n\n// Pr\u00e9pare la date de demain pour Gmail.\nconst tomorrowForGmail = formatForGmail(tomorrow)\n\n// Pr\u00e9pare la date du jour pour React.\nconst todayForReact = formatForReact(now)\n\n// Pr\u00e9pare la recherche Gmail des mails re\u00e7us aujourd'hui.\nconst searchQuery = `in:inbox after:${todayForGmail} before:${tomorrowForGmail}`\n\n// Retourne les donn\u00e9es au n\u0153ud suivant.\nreturn [\n  {\n    json: {\n      today: todayForReact,\n      searchQuery: searchQuery\n    }\n  }\n]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        768,
        352
      ],
      "id": "deeee034-6f5f-48fa-875f-4ad82e7073e8",
      "name": "Pr\u00e9parer la recherche du jour",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "operation": "getAll",
        "returnAll": true,
        "simple": false,
        "filters": {
          "q": "=={{ $json.searchQuery }}"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        992,
        352
      ],
      "id": "d8853610-4fac-4f17-a0c8-52806623e9d5",
      "name": "R\u00e9cup\u00e9rer les mails du jour",
      "alwaysOutputData": true,
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "get",
        "messageId": "={{ $json.id }}",
        "simple": false,
        "options": {}
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        1888,
        208
      ],
      "id": "69d7be88-440e-4a0d-8a54-22d41a96393c",
      "name": "Lire le d\u00e9tail de chaque mail",
      "alwaysOutputData": true,
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// R\u00e9cup\u00e8re tous les e-mails re\u00e7us depuis le n\u0153ud Gmail pr\u00e9c\u00e9dent.\nconst items = $input.all()\n\n// Transforme une valeur quelconque en texte simple.\nfunction toText(value) {\n  // V\u00e9rifie si la valeur est absente.\n  if (value === undefined || value === null) {\n    // Retourne un texte vide.\n    return ''\n  }\n\n  // V\u00e9rifie si la valeur est d\u00e9j\u00e0 un texte.\n  if (typeof value === 'string') {\n    // Retourne directement le texte.\n    return value\n  }\n\n  // V\u00e9rifie si la valeur est un nombre.\n  if (typeof value === 'number') {\n    // Convertit le nombre en texte.\n    return String(value)\n  }\n\n  // V\u00e9rifie si la valeur est un tableau.\n  if (Array.isArray(value)) {\n    // Convertit chaque \u00e9l\u00e9ment du tableau en texte.\n    return value.map((item) => toText(item)).filter(Boolean).join(', ')\n  }\n\n  // V\u00e9rifie si la valeur est un objet.\n  if (typeof value === 'object') {\n    // V\u00e9rifie si l'objet contient un nom et une adresse e-mail.\n    if (value.name && value.email) {\n      // Retourne le nom et l'adresse.\n      return `${value.name} <${value.email}>`\n    }\n\n    // V\u00e9rifie si l'objet contient un nom et une adresse.\n    if (value.name && value.address) {\n      // Retourne le nom et l'adresse.\n      return `${value.name} <${value.address}>`\n    }\n\n    // V\u00e9rifie si l'objet contient une valeur textuelle.\n    if (value.text) {\n      // Retourne le texte.\n      return toText(value.text)\n    }\n\n    // V\u00e9rifie si l'objet contient une valeur HTML.\n    if (value.html) {\n      // Retourne le HTML.\n      return toText(value.html)\n    }\n\n    // V\u00e9rifie si l'objet contient une valeur.\n    if (value.value) {\n      // Retourne la valeur.\n      return toText(value.value)\n    }\n\n    // V\u00e9rifie si l'objet contient une adresse e-mail.\n    if (value.email) {\n      // Retourne l'adresse e-mail.\n      return toText(value.email)\n    }\n\n    // V\u00e9rifie si l'objet contient une adresse.\n    if (value.address) {\n      // Retourne l'adresse.\n      return toText(value.address)\n    }\n\n    // V\u00e9rifie si l'objet contient un body.\n    if (value.body) {\n      // Retourne le body.\n      return toText(value.body)\n    }\n\n    // V\u00e9rifie si l'objet contient des donn\u00e9es.\n    if (value.data) {\n      // Retourne les donn\u00e9es.\n      return toText(value.data)\n    }\n  }\n\n  // Retourne un texte vide si rien n'est exploitable.\n  return ''\n}\n\n// R\u00e9cup\u00e8re la premi\u00e8re valeur disponible parmi plusieurs champs possibles.\nfunction getFirstValue(object, names) {\n  // Parcourt tous les noms possibles.\n  for (const name of names) {\n    // R\u00e9cup\u00e8re la valeur du champ courant.\n    const value = toText(object?.[name])\n\n    // V\u00e9rifie si la valeur existe.\n    if (value) {\n      // Retourne la premi\u00e8re valeur trouv\u00e9e.\n      return value\n    }\n  }\n\n  // Retourne un texte vide si rien n'est trouv\u00e9.\n  return ''\n}\n\n// D\u00e9code un contenu Gmail encod\u00e9 en base64url.\nfunction decodeBase64Url(value) {\n  // V\u00e9rifie si la valeur est vide.\n  if (!value) {\n    // Retourne un texte vide.\n    return ''\n  }\n\n  // Remplace les caract\u00e8res base64url par des caract\u00e8res base64 classiques.\n  const base64 = value.replace(/-/g, '+').replace(/_/g, '/')\n\n  // D\u00e9code le contenu base64.\n  return Buffer.from(base64, 'base64').toString('utf8')\n}\n\n// D\u00e9code les entit\u00e9s HTML fr\u00e9quentes.\nfunction decodeHtmlEntities(value) {\n  // V\u00e9rifie si la valeur est vide.\n  if (!value) {\n    // Retourne un texte vide.\n    return ''\n  }\n\n  // Remplace les entit\u00e9s HTML par des caract\u00e8res normaux.\n  return value\n    .replace(/&nbsp;/gi, ' ')\n    .replace(/&zwnj;/gi, ' ')\n    .replace(/&zwj;/gi, ' ')\n    .replace(/&shy;/gi, ' ')\n    .replace(/&#8204;/g, ' ')\n    .replace(/&#8205;/g, ' ')\n    .replace(/&#173;/g, ' ')\n    .replace(/&#39;/g, \"'\")\n    .replace(/&apos;/gi, \"'\")\n    .replace(/&quot;/gi, '\"')\n    .replace(/&amp;/gi, '&')\n    .replace(/&lt;/gi, '<')\n    .replace(/&gt;/gi, '>')\n    .replace(/&eacute;/gi, '\u00e9')\n    .replace(/&egrave;/gi, '\u00e8')\n    .replace(/&ecirc;/gi, '\u00ea')\n    .replace(/&agrave;/gi, '\u00e0')\n    .replace(/&ccedil;/gi, '\u00e7')\n    .replace(/&ocirc;/gi, '\u00f4')\n    .replace(/&ugrave;/gi, '\u00f9')\n    .replace(/&#(\\d+);/g, (match, number) => String.fromCharCode(Number(number)))\n}\n\n// Nettoie un contenu HTML pour obtenir un texte lisible.\nfunction cleanHtml(value) {\n  // V\u00e9rifie si la valeur est vide.\n  if (!value) {\n    // Retourne un texte vide.\n    return ''\n  }\n\n  // D\u00e9code d'abord les entit\u00e9s HTML visibles comme &zwnj;.\n  const decodedFirst = decodeHtmlEntities(value)\n\n  // Supprime les scripts.\n  const withoutScripts = decodedFirst.replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n\n  // Supprime les styles.\n  const withoutStyles = withoutScripts.replace(/<style[\\s\\S]*?<\\/style>/gi, ' ')\n\n  // Supprime les commentaires HTML.\n  const withoutComments = withoutStyles.replace(/<!--[\\s\\S]*?-->/g, ' ')\n\n  // Remplace certaines balises par des espaces pour \u00e9viter de coller les phrases.\n  const withSpaces = withoutComments\n    .replace(/<\\/p>/gi, ' ')\n    .replace(/<br\\s*\\/?>/gi, ' ')\n    .replace(/<\\/div>/gi, ' ')\n    .replace(/<\\/tr>/gi, ' ')\n    .replace(/<\\/td>/gi, ' ')\n    .replace(/<\\/li>/gi, ' ')\n\n  // Supprime toutes les balises HTML restantes.\n  const withoutTags = withSpaces.replace(/<[^>]*>/g, ' ')\n\n  // D\u00e9code encore une fois les entit\u00e9s restantes.\n  const decodedSecond = decodeHtmlEntities(withoutTags)\n\n  // Supprime les vrais caract\u00e8res invisibles Unicode.\n  const withoutInvisible = decodedSecond.replace(/[\\u200B-\\u200D\\uFEFF\\u00AD]/g, ' ')\n\n  // Supprime les r\u00e9p\u00e9titions inutiles de la mention image.\n  const withoutTooManyImages = withoutInvisible.replace(/\\[image:\\s*[^\\]]+\\]/gi, ' ')\n\n  // Remplace plusieurs espaces par un seul espace.\n  const cleanText = withoutTooManyImages.replace(/\\s+/g, ' ').trim()\n\n  // Retourne le texte propre.\n  return cleanText\n}\n\n// Nettoie le nom de l'exp\u00e9diteur.\nfunction cleanSender(value) {\n  // Transforme la valeur en texte.\n  const sender = toText(value)\n\n  // V\u00e9rifie si l'exp\u00e9diteur est vide.\n  if (!sender) {\n    // Retourne une valeur par d\u00e9faut.\n    return 'Exp\u00e9diteur inconnu'\n  }\n\n  // Supprime les guillemets autour du nom de l'exp\u00e9diteur.\n  const cleaned = sender\n    .replace(/^\"([^\"]+)\"\\s*</, '$1 <')\n    .replace(/\\\\\"/g, '\"')\n    .trim()\n\n  // Retourne l'exp\u00e9diteur nettoy\u00e9.\n  return cleaned || 'Exp\u00e9diteur inconnu'\n}\n\n// R\u00e9cup\u00e8re un header Gmail.\nfunction getHeader(mail, headerName) {\n  // Met le nom recherch\u00e9 en minuscules.\n  const wantedName = headerName.toLowerCase()\n\n  // R\u00e9cup\u00e8re les headers dans payload.headers.\n  const payloadHeaders = mail.payload?.headers || []\n\n  // V\u00e9rifie si payload.headers est un tableau.\n  if (Array.isArray(payloadHeaders)) {\n    // Cherche le header demand\u00e9.\n    const foundHeader = payloadHeaders.find((header) => header.name?.toLowerCase() === wantedName)\n\n    // V\u00e9rifie si le header existe.\n    if (foundHeader?.value) {\n      // Retourne la valeur du header.\n      return foundHeader.value\n    }\n  }\n\n  // R\u00e9cup\u00e8re les headers directs.\n  const directHeaders = mail.headers || {}\n\n  // V\u00e9rifie si headers est un tableau.\n  if (Array.isArray(directHeaders)) {\n    // Cherche le header demand\u00e9.\n    const foundHeader = directHeaders.find((header) => header.name?.toLowerCase() === wantedName)\n\n    // V\u00e9rifie si le header existe.\n    if (foundHeader?.value) {\n      // Retourne la valeur du header.\n      return foundHeader.value\n    }\n  }\n\n  // V\u00e9rifie si headers est un objet.\n  if (typeof directHeaders === 'object' && directHeaders !== null) {\n    // Cherche le header avec le nom exact.\n    const exactValue = directHeaders[headerName]\n\n    // V\u00e9rifie si la valeur exacte existe.\n    if (exactValue) {\n      // Retourne la valeur exacte.\n      return toText(exactValue)\n    }\n\n    // Cherche le header avec le nom en minuscules.\n    const lowerValue = directHeaders[wantedName]\n\n    // V\u00e9rifie si la valeur en minuscules existe.\n    if (lowerValue) {\n      // Retourne la valeur en minuscules.\n      return toText(lowerValue)\n    }\n\n    // Cherche une cl\u00e9 qui correspond au header demand\u00e9.\n    const matchingKey = Object.keys(directHeaders).find((key) => key.toLowerCase() === wantedName)\n\n    // V\u00e9rifie si une cl\u00e9 correspond.\n    if (matchingKey) {\n      // Retourne la valeur trouv\u00e9e.\n      return toText(directHeaders[matchingKey])\n    }\n  }\n\n  // Retourne un texte vide si rien n'est trouv\u00e9.\n  return ''\n}\n\n// R\u00e9cup\u00e8re l'exp\u00e9diteur du mail.\nfunction getSender(mail) {\n  // Cherche l'exp\u00e9diteur dans les champs directs.\n  const directSender = getFirstValue(mail, [\n    'from',\n    'From',\n    'sender',\n    'Sender',\n    'expediteur',\n    'replyTo',\n    'reply_to'\n  ])\n\n  // V\u00e9rifie si un exp\u00e9diteur direct existe.\n  if (directSender) {\n    // Retourne l'exp\u00e9diteur nettoy\u00e9.\n    return cleanSender(directSender)\n  }\n\n  // Cherche l'exp\u00e9diteur dans le header From.\n  const fromHeader = getHeader(mail, 'From')\n\n  // V\u00e9rifie si le header From existe.\n  if (fromHeader) {\n    // Retourne l'exp\u00e9diteur nettoy\u00e9.\n    return cleanSender(fromHeader)\n  }\n\n  // Cherche l'exp\u00e9diteur dans le header Sender.\n  const senderHeader = getHeader(mail, 'Sender')\n\n  // V\u00e9rifie si le header Sender existe.\n  if (senderHeader) {\n    // Retourne l'exp\u00e9diteur nettoy\u00e9.\n    return cleanSender(senderHeader)\n  }\n\n  // Cherche l'exp\u00e9diteur dans le header Reply-To.\n  const replyToHeader = getHeader(mail, 'Reply-To')\n\n  // V\u00e9rifie si Reply-To existe.\n  if (replyToHeader) {\n    // Retourne l'exp\u00e9diteur nettoy\u00e9.\n    return cleanSender(replyToHeader)\n  }\n\n  // Retourne une valeur par d\u00e9faut.\n  return 'Exp\u00e9diteur inconnu'\n}\n\n// R\u00e9cup\u00e8re l'objet du mail.\nfunction getSubject(mail) {\n  // Cherche l'objet dans les champs directs.\n  const directSubject = getFirstValue(mail, ['subject', 'Subject', 'objet'])\n\n  // V\u00e9rifie si un objet direct existe.\n  if (directSubject) {\n    // Retourne l'objet direct.\n    return cleanHtml(directSubject)\n  }\n\n  // Cherche l'objet dans les headers.\n  const headerSubject = getHeader(mail, 'Subject')\n\n  // V\u00e9rifie si un objet existe dans les headers.\n  if (headerSubject) {\n    // Retourne l'objet trouv\u00e9.\n    return cleanHtml(headerSubject)\n  }\n\n  // Retourne une valeur par d\u00e9faut.\n  return 'Sans objet'\n}\n\n// Lit toutes les parties disponibles dans le payload Gmail.\nfunction getBodyFromPayload(payload) {\n  // Cr\u00e9e une liste pour les textes simples.\n  const plainTexts = []\n\n  // Cr\u00e9e une liste pour les contenus HTML.\n  const htmlTexts = []\n\n  // Cr\u00e9e une fonction interne pour parcourir le payload.\n  function readPart(part) {\n    // V\u00e9rifie si la partie existe.\n    if (!part) {\n      // Arr\u00eate la fonction.\n      return\n    }\n\n    // V\u00e9rifie si cette partie contient des donn\u00e9es.\n    if (part.body?.data) {\n      // D\u00e9code les donn\u00e9es.\n      const decoded = decodeBase64Url(part.body.data)\n\n      // V\u00e9rifie si le contenu d\u00e9cod\u00e9 existe.\n      if (decoded) {\n        // V\u00e9rifie si la partie est du texte simple.\n        if (part.mimeType === 'text/plain') {\n          // Ajoute le texte simple.\n          plainTexts.push(decoded)\n        } else {\n          // Ajoute le contenu HTML ou g\u00e9n\u00e9ral.\n          htmlTexts.push(decoded)\n        }\n      }\n    }\n\n    // R\u00e9cup\u00e8re les sous-parties.\n    const subParts = part.parts || []\n\n    // Parcourt toutes les sous-parties.\n    for (const subPart of subParts) {\n      // Lit chaque sous-partie.\n      readPart(subPart)\n    }\n  }\n\n  // Lance la lecture du payload complet.\n  readPart(payload)\n\n  // Fusionne les textes simples.\n  const plainContent = plainTexts.join(' ')\n\n  // Fusionne les contenus HTML.\n  const htmlContent = htmlTexts.join(' ')\n\n  // Nettoie le texte simple.\n  const cleanedPlain = cleanHtml(plainContent)\n\n  // Nettoie le HTML.\n  const cleanedHtml = cleanHtml(htmlContent)\n\n  // V\u00e9rifie si le texte simple est plus complet.\n  if (cleanedPlain.length >= cleanedHtml.length) {\n    // Retourne le texte simple.\n    return cleanedPlain\n  }\n\n  // Retourne le texte HTML nettoy\u00e9.\n  return cleanedHtml\n}\n\n// Formate la date du mail.\nfunction formatMailDate(mail) {\n  // R\u00e9cup\u00e8re la date directe.\n  const directDate = getFirstValue(mail, ['date', 'Date', 'receivedDate'])\n\n  // R\u00e9cup\u00e8re la date depuis les headers.\n  const headerDate = getHeader(mail, 'Date')\n\n  // R\u00e9cup\u00e8re la date interne Gmail.\n  const internalDate = mail.internalDate\n\n  // V\u00e9rifie si internalDate existe et ressemble \u00e0 un nombre.\n  if (internalDate && !isNaN(Number(internalDate))) {\n    // Transforme internalDate en date JavaScript.\n    const date = new Date(Number(internalDate))\n\n    // V\u00e9rifie si la date est valide.\n    if (!isNaN(date.getTime())) {\n      // Retourne la date en fran\u00e7ais.\n      return date.toLocaleString('fr-FR')\n    }\n  }\n\n  // Choisit la meilleure date texte disponible.\n  const rawDate = directDate || headerDate\n\n  // V\u00e9rifie si une date texte existe.\n  if (rawDate) {\n    // Transforme la date texte en date JavaScript.\n    const date = new Date(rawDate)\n\n    // V\u00e9rifie si la date est valide.\n    if (!isNaN(date.getTime())) {\n      // Retourne la date en fran\u00e7ais.\n      return date.toLocaleString('fr-FR')\n    }\n  }\n\n  // Retourne une valeur par d\u00e9faut.\n  return 'Date inconnue'\n}\n\n// R\u00e9cup\u00e8re le contenu complet du mail.\nfunction getMailContent(mail) {\n  // R\u00e9cup\u00e8re le texte direct.\n  const directText = getFirstValue(mail, ['text', 'textPlain', 'plainText', 'bodyText', 'content'])\n\n  // R\u00e9cup\u00e8re le HTML direct.\n  const directHtml = getFirstValue(mail, ['html', 'textHtml', 'bodyHtml', 'htmlBody'])\n\n  // R\u00e9cup\u00e8re le contenu document si n8n l'utilise.\n  const documentContent = getFirstValue(mail, ['document'])\n\n  // R\u00e9cup\u00e8re le contenu body si n8n l'utilise.\n  const bodyContent = getFirstValue(mail, ['body'])\n\n  // R\u00e9cup\u00e8re le contenu data si n8n l'utilise.\n  const dataContent = getFirstValue(mail, ['data'])\n\n  // R\u00e9cup\u00e8re le contenu depuis payload.\n  const payloadContent = getBodyFromPayload(mail.payload)\n\n  // R\u00e9cup\u00e8re le snippet Gmail.\n  const snippet = getFirstValue(mail, ['snippet', 'preview'])\n\n  // Pr\u00e9pare toutes les sources possibles.\n  const sources = [\n    directText,\n    directHtml,\n    documentContent,\n    bodyContent,\n    dataContent,\n    payloadContent,\n    snippet\n  ]\n\n  // Nettoie toutes les sources disponibles.\n  const cleanedSources = sources.map((source) => cleanHtml(source)).filter(Boolean)\n\n  // V\u00e9rifie si au moins une source existe.\n  if (cleanedSources.length > 0) {\n    // Trie les contenus du plus long au plus court.\n    cleanedSources.sort((a, b) => b.length - a.length)\n\n    // Retourne le contenu le plus long.\n    return cleanedSources[0]\n  }\n\n  // Retourne une valeur par d\u00e9faut.\n  return 'Contenu non disponible.'\n}\n\n// Cr\u00e9e la date actuelle.\nconst now = new Date()\n\n// R\u00e9cup\u00e8re l'ann\u00e9e actuelle.\nconst year = now.getFullYear()\n\n// R\u00e9cup\u00e8re le mois actuel avec deux chiffres.\nconst month = String(now.getMonth() + 1).padStart(2, '0')\n\n// R\u00e9cup\u00e8re le jour actuel avec deux chiffres.\nconst day = String(now.getDate()).padStart(2, '0')\n\n// Cr\u00e9e la date du jour au format React.\nconst today = `${year}-${month}-${day}`\n\n// Garde seulement les \u00e9l\u00e9ments qui ressemblent \u00e0 des e-mails.\nconst realItems = items.filter((item) => {\n  // R\u00e9cup\u00e8re le mail courant.\n  const mail = item.json\n\n  // V\u00e9rifie si le mail contient des informations utiles.\n  return mail.id || mail.messageId || mail.payload || mail.html || mail.text || mail.document || mail.snippet\n})\n\n// Transforme chaque mail Gmail en objet simple pour React.\nconst mails = realItems.map((item, index) => {\n  // R\u00e9cup\u00e8re le mail courant.\n  const mail = item.json\n\n  // R\u00e9cup\u00e8re l'exp\u00e9diteur.\n  const expediteur = getSender(mail)\n\n  // R\u00e9cup\u00e8re l'objet.\n  const objet = getSubject(mail)\n\n  // R\u00e9cup\u00e8re la date de r\u00e9ception.\n  const dateReception = formatMailDate(mail)\n\n  // R\u00e9cup\u00e8re le contenu complet.\n  const contenu = getMailContent(mail)\n\n  // Cr\u00e9e un r\u00e9sum\u00e9 court pour la carte.\n  const resume = contenu.length > 300 ? `${contenu.slice(0, 300)}...` : contenu\n\n  // Retourne le mail dans le format attendu par React.\n  return {\n    // Identifiant simple pour React.\n    id: index + 1,\n\n    // Identifiant Gmail.\n    messageId: mail.id || mail.messageId || '',\n\n    // Identifiant du thread Gmail.\n    threadId: mail.threadId || '',\n\n    // Exp\u00e9diteur du mail.\n    expediteur: expediteur,\n\n    // Objet du mail.\n    objet: objet,\n\n    // Date de r\u00e9ception.\n    dateReception: dateReception,\n\n    // R\u00e9sum\u00e9 court.\n    resume: resume,\n\n    // Contenu complet.\n    contenu: contenu,\n\n    // Statut de r\u00e9ponse.\n    statut: 'non r\u00e9pondu'\n  }\n})\n\n// Pr\u00e9pare le r\u00e9sum\u00e9 global provisoire.\nconst resumeGlobal = `${mails.length} e-mail(s) re\u00e7u(s) aujourd\u2019hui ont \u00e9t\u00e9 r\u00e9cup\u00e9r\u00e9(s) depuis Gmail.`\n\n// Retourne les donn\u00e9es finales \u00e0 React.\nreturn [\n  {\n    json: {\n      date: today,\n      resumeGlobal: resumeGlobal,\n      mails: mails\n    }\n  }\n]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2112,
        208
      ],
      "id": "45c3db80-3ddb-47d6-8235-9dc8675238d8",
      "name": "Structurer les donn\u00e9es pour React",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        4032,
        208
      ],
      "id": "6ba9776a-6bf8-439b-90d9-845472233b62",
      "name": "Respond to Webhook"
    },
    {
      "parameters": {
        "jsCode": "// R\u00e9cup\u00e8re les donn\u00e9es venant du n\u0153ud IF.\nconst data = $input.first().json\n\n// R\u00e9cup\u00e8re la liste des messages.\nconst messages = data.messages || []\n\n// Transforme chaque mail en ID propre pour le n\u0153ud Gmail suivant.\nconst preparedItems = messages.map((message) => {\n  // R\u00e9cup\u00e8re l'ID Gmail du message.\n  const messageId = String(message.id).trim()\n\n  // Retourne un item propre.\n  return {\n    json: {\n      id: messageId,\n      messageId: messageId,\n      threadId: message.threadId || ''\n    }\n  }\n})\n\n// Retourne les IDs au n\u0153ud suivant.\nreturn preparedItems"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1664,
        208
      ],
      "id": "bf0039a0-cc24-4e9f-9758-4474fe031277",
      "name": "Pr\u00e9parer les IDs des mails",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "jsCode": "// R\u00e9cup\u00e8re les donn\u00e9es pr\u00e9par\u00e9es pour React.\nconst data = $input.first().json\n\n// R\u00e9cup\u00e8re la liste des mails.\nconst mails = data.mails || []\n\n// Transforme chaque mail en texte simple pour Groq.\nconst mailsText = mails.map((mail, index) => {\n  // Retourne un bloc lisible pour chaque mail.\n  return `\nMail ${index + 1}\nExp\u00e9diteur : ${mail.expediteur}\nObjet : ${mail.objet}\nDate : ${mail.dateReception}\nR\u00e9sum\u00e9 court : ${mail.resume}\nContenu : ${mail.contenu}\n`\n}).join('\\n--------------------\\n')\n\n// Pr\u00e9pare la consigne envoy\u00e9e \u00e0 Groq.\nconst prompt = `\nTu dois analyser les e-mails re\u00e7us aujourd'hui et produire un r\u00e9sum\u00e9 global clair.\n\nR\u00e9ponds exactement avec cette structure :\n\nR\u00e9sum\u00e9 g\u00e9n\u00e9ral :\n\u00c9cris ici un court paragraphe qui r\u00e9sume la journ\u00e9e.\n\nPoints importants :\n- Point important 1\n- Point important 2\n- Point important 3\n\nActions \u00e0 faire :\n- Action 1 s'il y en a une\n- Sinon \u00e9cris : Aucune action urgente d\u00e9tect\u00e9e.\n\nContraintes :\n- R\u00e9ponds uniquement en fran\u00e7ais.\n- Ne r\u00e9p\u00e8te pas tous les mails un par un.\n- Ne mets pas de Markdown avec des ast\u00e9risques.\n- Ne mets pas de texte comme **Mail 1**.\n- Fais une r\u00e9ponse claire, courte et facile \u00e0 lire.\n\nVoici les e-mails re\u00e7us aujourd'hui :\n\n${mailsText}\n` \n\n// Retourne les donn\u00e9es originales avec le prompt ajout\u00e9.\nreturn [\n  {\n    json: {\n      date: data.date,\n      resumeGlobal: data.resumeGlobal,\n      mails: data.mails,\n      prompt: prompt\n    }\n  }\n]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2336,
        208
      ],
      "id": "02c0bbfb-9ee0-40b2-b748-cf9626c6482c",
      "name": "Pr\u00e9parer la demande Groq",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.prompt }}",
        "messages": {
          "messageValues": []
        },
        "batching": {}
      },
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.9,
      "position": [
        2560,
        96
      ],
      "id": "c9a34f73-38ab-42cc-88be-87173f47e84b",
      "name": "G\u00e9n\u00e9rer le r\u00e9sum\u00e9 global",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "model": "llama-3.1-8b-instant",
        "options": {
          "maxTokensToSample": 500,
          "temperature": 0.2
        }
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatGroq",
      "typeVersion": 1,
      "position": [
        2560,
        304
      ],
      "id": "ccc15eae-2a4b-463a-9c94-01aa18dd83ca",
      "name": "Mod\u00e8le Groq",
      "credentials": {
        "groqApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// R\u00e9cup\u00e8re la r\u00e9ponse du n\u0153ud Basic LLM Chain.\nconst aiResponse = $input.first().json\n\n// R\u00e9cup\u00e8re les donn\u00e9es originales pr\u00e9par\u00e9es avant Groq.\nconst originalData = $('Pr\u00e9parer la demande Groq').first().json\n\n// Essaie de r\u00e9cup\u00e9rer le r\u00e9sum\u00e9 dans plusieurs champs possibles.\nconst generatedSummary =\n  aiResponse.text ||\n  aiResponse.output ||\n  aiResponse.response ||\n  aiResponse.result ||\n  aiResponse.content ||\n  aiResponse.answer ||\n  aiResponse.message ||\n  ''\n\n// V\u00e9rifie si Groq a vraiment g\u00e9n\u00e9r\u00e9 un r\u00e9sum\u00e9.\nconst finalSummary = generatedSummary || originalData.resumeGlobal\n\n// Retourne le JSON final attendu par React.\nreturn [\n  {\n    json: {\n      date: originalData.date,\n      resumeGlobal: finalSummary,\n      mails: originalData.mails\n    }\n  }\n]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2912,
        208
      ],
      "id": "fe6c5837-abae-42ad-8fd7-2d2f4b81a5ed",
      "name": "Ajouter le r\u00e9sum\u00e9 Groq au JSON",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "jsCode": "// R\u00e9cup\u00e8re tous les \u00e9l\u00e9ments re\u00e7us depuis le n\u0153ud Gmail pr\u00e9c\u00e9dent.\nconst items = $input.all()\n\n// Cr\u00e9e une date avec la date actuelle.\nconst now = new Date()\n\n// R\u00e9cup\u00e8re l'ann\u00e9e actuelle.\nconst year = now.getFullYear()\n\n// R\u00e9cup\u00e8re le mois actuel avec deux chiffres.\nconst month = String(now.getMonth() + 1).padStart(2, '0')\n\n// R\u00e9cup\u00e8re le jour actuel avec deux chiffres.\nconst day = String(now.getDate()).padStart(2, '0')\n\n// Cr\u00e9e la date du jour au format attendu par React.\nconst today = `${year}-${month}-${day}`\n\n// Cr\u00e9e une liste vide pour stocker les vrais mails trouv\u00e9s.\nconst messages = []\n\n// Parcourt tous les \u00e9l\u00e9ments re\u00e7us.\nfor (const item of items) {\n  // R\u00e9cup\u00e8re les donn\u00e9es JSON de l'\u00e9l\u00e9ment actuel.\n  const data = item.json || {}\n\n  // V\u00e9rifie si l'\u00e9l\u00e9ment contient directement un vrai identifiant Gmail.\n  if (data.id) {\n    // Ajoute ce mail dans la liste.\n    messages.push(data)\n  }\n\n  // V\u00e9rifie si l'\u00e9l\u00e9ment contient une liste de messages.\n  if (Array.isArray(data.messages)) {\n    // Ajoute tous les messages trouv\u00e9s.\n    messages.push(...data.messages)\n  }\n\n  // V\u00e9rifie si l'\u00e9l\u00e9ment contient une liste data.\n  if (Array.isArray(data.data)) {\n    // Ajoute tous les \u00e9l\u00e9ments trouv\u00e9s.\n    messages.push(...data.data)\n  }\n\n  // V\u00e9rifie si l'\u00e9l\u00e9ment contient une liste items.\n  if (Array.isArray(data.items)) {\n    // Ajoute tous les \u00e9l\u00e9ments trouv\u00e9s.\n    messages.push(...data.items)\n  }\n}\n\n// Garde uniquement les messages qui ont un ID Gmail correct.\nconst validMessages = messages.filter((message) => {\n  // R\u00e9cup\u00e8re l'identifiant du message.\n  const id = message.id\n\n  // Refuse si l'identifiant est absent.\n  if (!id) {\n    return false\n  }\n\n  // Transforme l'identifiant en texte.\n  const cleanId = String(id).trim()\n\n  // Refuse les identifiants vides.\n  if (!cleanId) {\n    return false\n  }\n\n  // Refuse les valeurs invalides.\n  if (cleanId === 'undefined' || cleanId === 'null' || cleanId.includes('{{')) {\n    return false\n  }\n\n  // Accepte le message.\n  return true\n})\n\n// Retourne une seule sortie claire pour le n\u0153ud IF.\nreturn [\n  {\n    json: {\n      date: today,\n      mailCount: validMessages.length,\n      messages: validMessages,\n      resumeGlobal: validMessages.length === 0\n        ? \"Aucun e-mail re\u00e7u aujourd\u2019hui. Vous n\u2019avez aucune action \u00e0 traiter pour le moment.\"\n        : `${validMessages.length} e-mail(s) re\u00e7u(s) aujourd\u2019hui ont \u00e9t\u00e9 r\u00e9cup\u00e9r\u00e9(s) depuis Gmail.`,\n      mails: []\n    }\n  }\n]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1216,
        352
      ],
      "id": "f5ab7366-2885-4094-80b9-6032a5ff5d37",
      "name": "V\u00e9rifier s\u2019il y a des mails",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 3
          },
          "conditions": [
            {
              "id": "78944045-f3e3-43ff-9e1f-17dd35245249",
              "leftValue": "={{ $json.mailCount }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1440,
        352
      ],
      "id": "570c9feb-182d-4f19-a19e-e4b64bb6e773",
      "name": "Y a-t-il des mails ?",
      "alwaysOutputData": false
    },
    {
      "parameters": {
        "jsCode": "// R\u00e9cup\u00e8re les donn\u00e9es venant du n\u0153ud IF.\nconst data = $input.first().json\n\n// Retourne une r\u00e9ponse propre pour React.\nreturn [\n  {\n    json: {\n      date: data.date,\n      resumeGlobal: data.resumeGlobal,\n      mails: []\n    }\n  }\n]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1664,
        496
      ],
      "id": "38f90ae0-d1c6-4ae3-bf9b-9e74c74eae3d",
      "name": "R\u00e9ponse aucun mail",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "options": {
          "responseCode": 200
        }
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        2912,
        496
      ],
      "id": "3ec1d1f1-b96f-4b31-b0ba-bd560a132dcd",
      "name": "Respond to Webhook1"
    },
    {
      "parameters": {
        "jsCode": "// R\u00e9cup\u00e8re les donn\u00e9es finales venant du n\u0153ud pr\u00e9c\u00e9dent.\nconst finalData = $input.first().json\n\n// Transforme les donn\u00e9es finales en texte JSON bien format\u00e9.\nconst jsonText = JSON.stringify(finalData, null, 2)\n\n// Retourne les donn\u00e9es finales avec une version texte du fichier.\nreturn [\n  {\n    json: {\n      date: finalData.date,\n      resumeGlobal: finalData.resumeGlobal,\n      mails: finalData.mails,\n      jsonText: jsonText\n    }\n  }\n]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3136,
        208
      ],
      "id": "d0e3d15f-e7db-4d47-b9a6-93d6a4b4d9a8",
      "name": "Pr\u00e9parer le fichier JSON",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "jsCode": "// R\u00e9cup\u00e8re les donn\u00e9es finales venant du n\u0153ud pr\u00e9c\u00e9dent.\nconst finalData = $input.first().json\n\n// Transforme les donn\u00e9es finales en texte JSON bien format\u00e9.\nconst jsonText = JSON.stringify(finalData, null, 2)\n\n// Retourne les donn\u00e9es finales avec une version texte du fichier.\nreturn [\n  {\n    json: {\n      date: finalData.date,\n      resumeGlobal: finalData.resumeGlobal,\n      mails: finalData.mails,\n      jsonText: jsonText\n    }\n  }\n]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1888,
        496
      ],
      "id": "0abe5761-b968-4468-b350-4a5c9a94221e",
      "name": "Pr\u00e9parer le fichier JSON1",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "operation": "toJson",
        "options": {
          "encoding": "utf8",
          "fileName": "mails-today.json"
        }
      },
      "type": "n8n-nodes-base.convertToFile",
      "typeVersion": 1.1,
      "position": [
        3360,
        208
      ],
      "id": "97def6dc-cc76-4f15-b288-45890221ede3",
      "name": "Convertir en fichier JSON",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "operation": "toJson",
        "options": {
          "encoding": "utf8",
          "fileName": "mails-today.json"
        }
      },
      "type": "n8n-nodes-base.convertToFile",
      "typeVersion": 1.1,
      "position": [
        2112,
        496
      ],
      "id": "500a35db-de82-4ee6-88ef-32cf1c22f3f8",
      "name": "Convertir en fichier JSON1",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "operation": "write",
        "fileName": "D:/assistant_mails/data/mails-today.json",
        "options": {}
      },
      "type": "n8n-nodes-base.readWriteFile",
      "typeVersion": 1.1,
      "position": [
        3584,
        208
      ],
      "id": "6b70c60f-ef85-4d1a-afb5-2eafa25ba101",
      "name": "Sauvegarder mails-today.json",
      "alwaysOutputData": false
    },
    {
      "parameters": {
        "operation": "write",
        "fileName": "D:/assistant_mails/data/mails-today.json",
        "options": {
          "append": false
        }
      },
      "type": "n8n-nodes-base.readWriteFile",
      "typeVersion": 1.1,
      "position": [
        2336,
        496
      ],
      "id": "97e71e7c-ab9f-43e5-9a8e-283739febbd6",
      "name": "Sauvegarder mails-today.json1",
      "alwaysOutputData": false
    },
    {
      "parameters": {
        "jsCode": "// R\u00e9cup\u00e8re les donn\u00e9es pr\u00e9par\u00e9es avant la conversion en fichier.\nconst finalData = $('Pr\u00e9parer le fichier JSON').first().json\n\n// Retourne uniquement les donn\u00e9es attendues par React.\nreturn [\n  {\n    json: {\n      date: finalData.date,\n      resumeGlobal: finalData.resumeGlobal,\n      mails: finalData.mails\n    }\n  }\n]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3808,
        208
      ],
      "id": "1f8786de-493c-4b87-9a09-4fb9a57ead94",
      "name": "Retourner le JSON final",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "jsCode": "// R\u00e9cup\u00e8re les donn\u00e9es du n\u0153ud qui pr\u00e9pare la r\u00e9ponse quand il n'y a aucun mail.\nconst finalData = $('R\u00e9ponse aucun mail').first().json\n\n// Retourne uniquement les donn\u00e9es attendues par React.\nreturn [\n  {\n    json: {\n      date: finalData.date,\n      resumeGlobal: finalData.resumeGlobal,\n      mails: finalData.mails\n    }\n  }\n]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2624,
        496
      ],
      "id": "aa9ba036-f46e-42e5-a0a1-e14f3f0b213f",
      "name": "Retourner le JSON final1",
      "alwaysOutputData": false
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Pr\u00e9parer la recherche du jour",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pr\u00e9parer la recherche du jour": {
      "main": [
        [
          {
            "node": "R\u00e9cup\u00e9rer les mails du jour",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "R\u00e9cup\u00e9rer les mails du jour": {
      "main": [
        [
          {
            "node": "V\u00e9rifier s\u2019il y a des mails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lire le d\u00e9tail de chaque mail": {
      "main": [
        [
          {
            "node": "Structurer les donn\u00e9es pour React",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structurer les donn\u00e9es pour React": {
      "main": [
        [
          {
            "node": "Pr\u00e9parer la demande Groq",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pr\u00e9parer les IDs des mails": {
      "main": [
        [
          {
            "node": "Lire le d\u00e9tail de chaque mail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pr\u00e9parer la demande Groq": {
      "main": [
        [
          {
            "node": "G\u00e9n\u00e9rer le r\u00e9sum\u00e9 global",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "G\u00e9n\u00e9rer le r\u00e9sum\u00e9 global": {
      "main": [
        [
          {
            "node": "Ajouter le r\u00e9sum\u00e9 Groq au JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mod\u00e8le Groq": {
      "ai_languageModel": [
        [
          {
            "node": "G\u00e9n\u00e9rer le r\u00e9sum\u00e9 global",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Ajouter le r\u00e9sum\u00e9 Groq au JSON": {
      "main": [
        [
          {
            "node": "Pr\u00e9parer le fichier JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "V\u00e9rifier s\u2019il y a des mails": {
      "main": [
        [
          {
            "node": "Y a-t-il des mails ?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Y a-t-il des mails ?": {
      "main": [
        [
          {
            "node": "Pr\u00e9parer les IDs des mails",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "R\u00e9ponse aucun mail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "R\u00e9ponse aucun mail": {
      "main": [
        [
          {
            "node": "Pr\u00e9parer le fichier JSON1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pr\u00e9parer le fichier JSON1": {
      "main": [
        [
          {
            "node": "Convertir en fichier JSON1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pr\u00e9parer le fichier JSON": {
      "main": [
        [
          {
            "node": "Convertir en fichier JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convertir en fichier JSON": {
      "main": [
        [
          {
            "node": "Sauvegarder mails-today.json",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convertir en fichier JSON1": {
      "main": [
        [
          {
            "node": "Sauvegarder mails-today.json1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sauvegarder mails-today.json": {
      "main": [
        [
          {
            "node": "Retourner le JSON final",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sauvegarder mails-today.json1": {
      "main": [
        [
          {
            "node": "Retourner le JSON final1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retourner le JSON final1": {
      "main": [
        [
          {
            "node": "Respond to Webhook1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retourner le JSON final": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "versionId": "cf60920b-05d6-4422-8f14-d61fea3fe8fe",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "pjlmNIpG080NN455",
  "tags": []
}