{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "e12ee8c9-9805-473f-bb14-24c6a9067c16",
      "name": "Run",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -560,
        0
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "5a5dd6b4-c144-428d-8884-2e4eac5eaf42",
      "name": "Send a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        848,
        96
      ],
      "parameters": {
        "sendTo": "={{ $json.emisor }}",
        "message": "=<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width\">\n    <title>Secret Santa</title>\n    <style>\n      body { font-family: Georgia, 'Times New Roman', Times, serif; background: #fcf8ee; color: #2c2a28; text-align: center; padding: 40px; }\n      .name { font-size: 20px; font-weight: bold; margin-top: 12px; }\n    </style>\n  </head>\n  <body>\n    <p>{{ $json.nombreEmisor }} \u2192 {{ $json.nombreReceptor }}</p>\n  </body>\n</html>\n",
        "options": {
          "appendAttribution": false
        },
        "subject": "Secret Santa "
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.1,
      "waitBetweenTries": 5000
    },
    {
      "id": "4451fa67-489d-4072-955e-4b36c3402ff7",
      "name": "Delete a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1152,
        96
      ],
      "parameters": {
        "messageId": "={{ $json.id }}",
        "operation": "delete"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.1,
      "waitBetweenTries": 5000
    },
    {
      "id": "78fa5c29-dab2-4555-9677-30a99c4732d1",
      "name": "Random",
      "type": "n8n-nodes-base.code",
      "position": [
        16,
        0
      ],
      "parameters": {
        "jsCode": "// Function node en n8n\n// Lee pares nombre->email desde las claves del $json\n// Genera emparejamientos sin repetici\u00f3n y sin autoasignaci\u00f3n\n// Salida por item: { emisor, nombreEmisor, receptor, nombreReceptor }\n\nfunction shuffle(arr) {\n  const a = arr.slice();\n  for (let i = a.length - 1; i > 0; i--) {\n    const j = Math.floor(Math.random() * (i + 1));\n    [a[i], a[j]] = [a[j], a[i]];\n  }\n  return a;\n}\n\nfunction isEmail(s) {\n  return /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$/i.test(String(s).trim());\n}\n\n// 1) Construir lista tomando CLAVES como nombres y VALORES como emails\nconst entries = Object.entries($json); // [[nombre, email], ...]\nif (entries.length < 2) {\n  throw new Error('Se requieren al menos 2 participantes (claves nombre -> valor email).');\n}\n\nlet participants = [];\nconst emailToName = new Map();\nconst seenEmails = new Set();\n\nfor (const [nameRaw, emailRaw] of entries) {\n  const name = String(nameRaw); // usar EXACTAMENTE la clave (sin modificar)\n  const email = String(emailRaw).trim();\n\n  if (!isEmail(email)) continue;               // ignorar valores no-email\n  if (seenEmails.has(email.toLowerCase())) continue; // evitar duplicados\n\n  seenEmails.add(email.toLowerCase());\n  participants.push({ name, email });\n  emailToName.set(email.toLowerCase(), name);\n}\n\nif (participants.length < 2) {\n  throw new Error('No hay suficientes emails v\u00e1lidos (m\u00ednimo 2).');\n}\n\n// 2) Derangement (nadie se asigna a s\u00ed mismo)\nconst n = participants.length;\nconst givers = participants.slice();\nlet receivers = shuffle(participants);\n\n// Romper puntos fijos\nfor (let i = 0; i < n; i++) {\n  if (givers[i].email === receivers[i].email) {\n    const j = (i + 1) % n;\n    [receivers[i], receivers[j]] = [receivers[j], receivers[i]];\n  }\n}\n\n// Reintentos defensivos\nlet attempts = 0;\nwhile (attempts < 20) {\n  let ok = true;\n  for (let i = 0; i < n; i++) {\n    if (givers[i].email === receivers[i].email) { ok = false; break; }\n  }\n  if (ok) break;\n\n  receivers = shuffle(participants);\n  for (let i = 0; i < n; i++) {\n    if (givers[i].email === receivers[i].email) {\n      const j = (i + 1) % n;\n      [receivers[i], receivers[j]] = [receivers[j], receivers[i]];\n    }\n  }\n  attempts++;\n}\n\nfor (let i = 0; i < n; i++) {\n  if (givers[i].email === receivers[i].email) {\n    throw new Error('No se pudo generar una asignaci\u00f3n v\u00e1lida. Intenta de nuevo.');\n  }\n}\n\n// 3) Salida EXACTA solicitada\nconst items = givers.map((g, i) => {\n  const r = receivers[i];\n  return {\n    json: {\n      emisor: g.email,\n      nombreEmisor: g.name,              // EXACTAMENTE la clave\n      receptor: r.email,\n      nombreReceptor: emailToName.get(r.email.toLowerCase()) || r.name\n    }\n  };\n});\n\nreturn items;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "1fa91ca3-2c66-4aa4-a9b5-8c492731ea08",
      "name": "Name to INT",
      "type": "n8n-nodes-base.code",
      "position": [
        736,
        -160
      ],
      "parameters": {
        "jsCode": "// Function node en n8n (procesa m\u00faltiples items)\n// Entrada (items): [{json:{info:\"user@example.com envi\u00f3 a user@example.com\"}}, ...]\n// Salida (un solo item): { json: { info: \"1 envi\u00f3 a 2\\n2 envi\u00f3 a 1\\n...\" } }\n\nconst emailRegex = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/gi;\n\nfunction extractTwoEmails(s) {\n  if (typeof s !== 'string') return null;\n  const found = s.match(emailRegex);\n  if (!found || found.length < 2) return null;\n  // Tomar los dos primeros emails encontrados\n  return [found[0], found[1]];\n}\n\nconst emailToId = new Map();\nlet nextId = 1;\n\nfunction getId(email) {\n  const key = String(email).toLowerCase().trim();\n  if (!emailToId.has(key)) {\n    emailToId.set(key, nextId++);\n  }\n  return emailToId.get(key);\n}\n\nconst lines = [];\n\nfor (const item of items) {\n  const info = item?.json?.info;\n  const pair = extractTwoEmails(info);\n  if (!pair) continue; // o lanza error si lo prefieres\n  const [emisorEmail, receptorEmail] = pair;\n  const idEmisor = getId(emisorEmail);\n  const idReceptor = getId(receptorEmail);\n  lines.push(`${idEmisor} envi\u00f3 a ${idReceptor}`);\n}\n\nif (lines.length === 0) {\n  throw new Error('No se encontraron l\u00edneas v\u00e1lidas con \"email envi\u00f3 a email\" en los items de entrada.');\n}\n\n// Una sola salida con todas las l\u00edneas\nreturn [{ json: { info: lines.join('<br>') } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "dc7a3870-69ea-4f77-9e56-9bed21cda23f",
      "name": "Who?",
      "type": "n8n-nodes-base.set",
      "position": [
        1488,
        96
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "3bc75295-92ec-4f10-8915-4fd5252c7266",
              "name": "info",
              "type": "string",
              "value": "={{ $('loop mails').item.json.emisor }} envi\u00f3 a {{ $('loop mails').item.json.receptor }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "bfd0a287-6f6f-4726-9346-edb5f8e1f3c9",
      "name": "Emails and name",
      "type": "n8n-nodes-base.set",
      "position": [
        -288,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a021f1e8-66a2-429f-a13e-ed677defd969",
              "name": "Jesus",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "0870c028-4134-4fab-89a7-688452652e73",
              "name": "John",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "956714b2-0a95-436a-8fff-518fb61483e0",
              "name": "Jan",
              "type": "string",
              "value": "user@example.com"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "c752fe47-a074-4b56-a797-a1f6c0201f3f",
      "name": "loop mails",
      "type": "n8n-nodes-base.splitInBatches",
      "notes": "Iterate one by one for each pairing (batch per item).",
      "position": [
        368,
        0
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "c2620662-9491-4bf4-ade1-d25d3588430b",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 288,
        "content": "Run (Manual Trigger)\nStarts the workflow manually in n8n. Used only to trigger the execution.\n\nEmails and name (Set)\nDefines the Secret Santa participants as name \u2192 email pairs, for example: {\"Jesus\":\"[example@gmail.com](mailto:example@gmail.com)\", \"John\":\"[example2@gmail.com](mailto:example2@gmail.com)\"}.\n\nRandom (Code)\nReads those names and emails, generates random pairings with no repeats or self-assignments, and outputs items like: {\"emisor\":\"[example@gmail.com](mailto:example@gmail.com)\",\"nombreEmisor\":\"Jesus\",\"receptor\":\"[example2@gmail.com](mailto:example2@gmail.com)\",\"nombreReceptor\":\"John\"}.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "ba594d5b-d910-4324-a1ee-9afb09261209",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        848,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 784,
        "height": 240,
        "content": "Send a message (Gmail)\nSends an email via Gmail to $json.emisor for the current item and returns message metadata (including id) for downstream use; retries on failure (5s) using the configured OAuth2 account.\n\nDelete a message (Gmail)\nDeletes the sent message using messageId = {{$json.id}} to keep it out of the Sent folder, ensuring privacy. Uses the same Gmail credentials and retries with a delay between attempts.\n\nWho? (Set)\nCreates a text field \u201cinfo\u201d with the pattern \u201c<emisor> sent to <receptor>\u201d using the current loop item values. Used to log a trace line for each delivery.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "624e6947-b664-4dbc-8f91-3b6200c06e80",
      "name": "Send a message results",
      "type": "n8n-nodes-base.gmail",
      "maxTries": 2,
      "position": [
        1040,
        -160
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=Hello ---,\nAll invisible friends have been completed successfully\nShipping information:<br>\n\n{{ $json.info }}",
        "options": {},
        "subject": "Amic invisible"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.1,
      "waitBetweenTries": 5000
    },
    {
      "id": "198eb429-74d6-4cac-9c6a-a9489cbb4483",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        736,
        -480
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 288,
        "content": "Name to INT (Code)\nProcesses multiple input items, extracts the two email addresses from each \u201cinfo\u201d line, assigns numeric IDs to each unique address, and builds a compact summary like \u201c1 sent to 2<br>2 sent to 3\u201d. Outputs a single consolidated item with all pairings joined in HTML format.\n\nSend a message results (Gmail)\nSends a final summary email to youemail with the subject \u201cAmic invisible\u201d, containing the aggregated info from the previous node. "
      },
      "typeVersion": 1
    },
    {
      "id": "c7b4714e-a551-4c41-817b-5c0e96726ad5",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1696,
        -64
      ],
      "parameters": {
        "color": 4,
        "width": 672,
        "height": 624,
        "content": "\n\n\n\n### **Non-Commercial Use and Modification License**\n\n**Copyright \u00a9 [2025/6] - Oseguir**\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated files, to **use, copy, modify, and redistribute** the software under the following conditions:\n\n1. **Use and modification:**\n   You may modify the source code, adapt it, or integrate it into other personal or educational projects.\n\n2. **Distribution:**\n   You may share copies or modified versions **only for free**, provided that this license notice remains intact.\n\n3. **Non-commercial restriction:**\n   Selling, renting, licensing, sublicensing, or otherwise gaining direct or indirect commercial profit from this software or its derivatives is strictly prohibited.\n\n4. **Attribution:**\n   Credit to the original author must be retained in any public redistribution or modification.\n\n5. **No warranty:**\n   This software is provided \u201cas is\u201d, without any warranty of any kind, express or implied. The author is not liable for any damages arising from its use.\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "5c8ce420-70aa-49e0-a271-e738b347cdf9",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        -336
      ],
      "parameters": {
        "color": 3,
        "width": 672,
        "height": 272,
        "content": "\n\n### **Welcome!**\n\nThanks for using this workflow \ud83d\ude4c\nIf you enjoyed it and want to discover **more creative and original automations**, visit my n8n Creator profile:\n\ud83d\udc49 **[https://n8n.io/creators/oxsr11/](https://n8n.io/creators/oxsr11/)**\n\nThere you\u2019ll find more **unique, AI-powered, and real-world inspired workflows** designed to make automation smarter and more fun.\n\n\ud83d\udc49 **[https://github.com/OXSR/](https://github.com/OXSR)**\n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Run": {
      "main": [
        [
          {
            "node": "Emails and name",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Who?": {
      "main": [
        [
          {
            "node": "loop mails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Random": {
      "main": [
        [
          {
            "node": "loop mails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "loop mails": {
      "main": [
        [
          {
            "node": "Name to INT",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Name to INT": {
      "main": [
        [
          {
            "node": "Send a message results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "Delete a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Emails and name": {
      "main": [
        [
          {
            "node": "Random",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Delete a message": {
      "main": [
        [
          {
            "node": "Who?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}