8
n8n 中文网amn8n.com

私密圣诞老人分配与Gmail通知系统及验证

中级

这是一个自动化工作流,包含 14 个节点。主要使用 Set, Code, Gmail, ManualTrigger, SplitInBatches 等节点。 私密圣诞老人分配与Gmail通知系统及验证

前置要求
  • Google 账号和 Gmail API 凭证

分类

-
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "meta": {
    "instanceId": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "e12ee8c9-9805-473f-bb14-24c6a9067c16",
      "name": "运行",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -560,
        0
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "5a5dd6b4-c144-428d-8884-2e4eac5eaf42",
      "name": "发送消息",
      "type": "n8n-nodes-base.gmail",
      "position": [
        848,
        96
      ],
      "webhookId": "05f2826d-da1c-4276-b739-9e64cc90916e",
      "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 }} → {{ $json.nombreReceptor }}</p>\n  </body>\n</html>\n",
        "options": {
          "appendAttribution": false
        },
        "subject": "Secret Santa "
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "a8B1Lydmlh203F5C",
          "name": "Gmail account"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.1,
      "waitBetweenTries": 5000
    },
    {
      "id": "4451fa67-489d-4072-955e-4b36c3402ff7",
      "name": "删除消息",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1152,
        96
      ],
      "webhookId": "935be7db-8f05-4eea-8cca-15eacb06f7e5",
      "parameters": {
        "messageId": "={{ $json.id }}",
        "operation": "delete"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "a8B1Lydmlh203F5C",
          "name": "Gmail account"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.1,
      "waitBetweenTries": 5000
    },
    {
      "id": "78fa5c29-dab2-4555-9677-30a99c4732d1",
      "name": "随机",
      "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ón y sin autoasignación\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álidos (mínimo 2).');\n}\n\n// 2) Derangement (nadie se asigna a sí 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ón válida. 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": "姓名转数字",
      "type": "n8n-nodes-base.code",
      "position": [
        736,
        -160
      ],
      "parameters": {
        "jsCode": "// Function node en n8n (procesa múltiples items)\n// Entrada (items): [{json:{info:\"a@x.com envió a b@y.com\"}}, ...]\n// Salida (un solo item): { json: { info: \"1 envió a 2\\n2 envió 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ó a ${idReceptor}`);\n}\n\nif (lines.length === 0) {\n  throw new Error('No se encontraron líneas válidas con \"email envió a email\" en los items de entrada.');\n}\n\n// Una sola salida con todas las líneas\nreturn [{ json: { info: lines.join('<br>') } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "dc7a3870-69ea-4f77-9e56-9bed21cda23f",
      "name": "谁?",
      "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ó a {{ $('loop mails').item.json.receptor }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "bfd0a287-6f6f-4726-9346-edb5f8e1f3c9",
      "name": "邮箱和姓名",
      "type": "n8n-nodes-base.set",
      "position": [
        -288,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a021f1e8-66a2-429f-a13e-ed677defd969",
              "name": "Jesus",
              "type": "string",
              "value": "example@gmail.com"
            },
            {
              "id": "0870c028-4134-4fab-89a7-688452652e73",
              "name": "John",
              "type": "string",
              "value": "example2@gmail.com"
            },
            {
              "id": "956714b2-0a95-436a-8fff-518fb61483e0",
              "name": "Jan",
              "type": "string",
              "value": "example3@gmail.com"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "c752fe47-a074-4b56-a797-a1f6c0201f3f",
      "name": "循环邮件",
      "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": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 288,
        "content": "运行(手动触发器)"
      },
      "typeVersion": 1
    },
    {
      "id": "ba594d5b-d910-4324-a1ee-9afb09261209",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        848,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 784,
        "height": 240,
        "content": "发送消息结果"
      },
      "typeVersion": 1
    },
    {
      "id": "624e6947-b664-4dbc-8f91-3b6200c06e80",
      "name": "姓名转数字(代码)",
      "type": "n8n-nodes-base.gmail",
      "maxTries": 2,
      "position": [
        1040,
        -160
      ],
      "webhookId": "3c1f591b-2e4f-4780-9097-c1484e90221a",
      "parameters": {
        "sendTo": "youremail@gmail.com",
        "message": "=Hello ---,\nAll invisible friends have been completed successfully\nShipping information:<br>\n\n{{ $json.info }}",
        "options": {},
        "subject": "Amic invisible"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "a8B1Lydmlh203F5C",
          "name": "Gmail account"
        }
      },
      "retryOnFail": true,
      "typeVersion": 2.1,
      "waitBetweenTries": 5000
    },
    {
      "id": "198eb429-74d6-4cac-9c6a-a9489cbb4483",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        736,
        -480
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 288,
        "content": ""
      },
      "typeVersion": 1
    },
    {
      "id": "c7b4714e-a551-4c41-817b-5c0e96726ad5",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1696,
        -64
      ],
      "parameters": {
        "color": 4,
        "width": 672,
        "height": 624,
        "content": ""
      },
      "typeVersion": 1
    },
    {
      "id": "5c8ce420-70aa-49e0-a271-e738b347cdf9",
      "name": "便签4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        -336
      ],
      "parameters": {
        "color": 3,
        "width": 672,
        "height": 272,
        "content": "合并PDF"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {},
  "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
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

中级

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
中级
节点数量14
分类-
节点类型6
难度说明

适合有一定经验的用户,包含 6-15 个节点的中等复杂度工作流

作者
Oriol Seguí

Oriol Seguí

@oxsr11

Completion of a higher degree on the way to university (computer engineering)

外部链接
在 n8n.io 查看

分享此工作流