{
  "name": "DMARC Report Processor (EmailConnect + Telegram)",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "your-webhook-path",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [-320, 0],
      "id": "eb563fc4-7ba8-46d5-bf57-7f04359a9610",
      "name": "Webhook",
      "webhookId": "your-webhook-id"
    },
    {
      "parameters": {
        "url": "={{ $json.body.message.attachments[0].downloadUrl }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [-112, 0],
      "id": "b34c2ddf-4377-4b08-9838-6fb8ea3c1ec6",
      "name": "HTTP Request"
    },
    {
      "parameters": {
        "jsCode": "const zlib = require('zlib');\nconst inputItem = $input.first();\nif (!inputItem?.binary?.data) throw new Error('No binary data received');\nconst binaryData = await this.helpers.getBinaryDataBuffer(0, 'data');\nif (!binaryData || binaryData.length === 0) throw new Error('Downloaded file is empty');\n\nlet xmlContent;\nlet fileName = inputItem.binary.data.fileName || 'unknown';\nconst isZip = binaryData[0] === 0x50 && binaryData[1] === 0x4B;\nconst isGzip = binaryData[0] === 0x1F && binaryData[1] === 0x8B;\n\nif (isGzip) {\n  xmlContent = zlib.gunzipSync(binaryData).toString('utf-8');\n} else if (isZip) {\n  let offset = 0;\n  let extracted = null;\n  while (offset < binaryData.length - 4) {\n    const sig = binaryData.readUInt32LE(offset);\n    if (sig !== 0x04034b50) break;\n    const compressionMethod = binaryData.readUInt16LE(offset + 8);\n    const compressedSize = binaryData.readUInt32LE(offset + 18);\n    const fileNameLen = binaryData.readUInt16LE(offset + 26);\n    const extraLen = binaryData.readUInt16LE(offset + 28);\n    const entryName = binaryData.slice(offset + 30, offset + 30 + fileNameLen).toString('utf-8');\n    const dataStart = offset + 30 + fileNameLen + extraLen;\n    const compressedData = binaryData.slice(dataStart, dataStart + compressedSize);\n    if (entryName.endsWith('.xml')) {\n      extracted = compressionMethod === 8\n        ? zlib.inflateRawSync(compressedData).toString('utf-8')\n        : compressedData.toString('utf-8');\n      fileName = entryName;\n      break;\n    }\n    offset = dataStart + compressedSize;\n  }\n  if (!extracted) throw new Error('No .xml file found in zip archive');\n  xmlContent = extracted;\n} else {\n  xmlContent = binaryData.toString('utf-8');\n}\n\nif (!xmlContent.includes('<feedback')) throw new Error('Not a DMARC report');\n\nconst getTag = (xml, tag) => {\n  if (!xml) return '';\n  const m = xml.match(new RegExp(`<${tag}[^>]*>([\\\\s\\\\S]*?)</${tag}>`));\n  return m ? m[1].trim() : '';\n};\nconst getAllTags = (xml, tag) => {\n  if (!xml) return [];\n  const results = [];\n  const re = new RegExp(`<${tag}[^>]*>([\\\\s\\\\S]*?)</${tag}>`, 'g');\n  let m;\n  while ((m = re.exec(xml)) !== null) results.push(m[1]);\n  return results;\n};\nconst esc = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\nconst ts = (v) => { const t = parseInt(v); return isNaN(t) || t <= 0 ? null : new Date(t * 1000).toISOString(); };\n\nconst reportId = getTag(xmlContent, 'report_id') || 'unknown';\nconst source = getTag(xmlContent, 'org_name') || 'unknown';\nconst dateBegin = ts(getTag(xmlContent, 'begin'));\nconst dateEnd = ts(getTag(xmlContent, 'end'));\nconst domain = getTag(getTag(xmlContent, 'policy_published'), 'domain') || 'unknown';\nconst headerFrom = getTag(xmlContent, 'header_from') || domain;\n\nconst records = getAllTags(xmlContent, 'record');\nif (records.length === 0) throw new Error(`No records in report ${reportId}`);\n\nconst rows = [];\nlet totalMessages = 0;\nconst failures = [];\n\nfor (const record of records) {\n  const row = getTag(record, 'row');\n  const sourceIp = getTag(row, 'source_ip') || 'unknown';\n  const count = parseInt(getTag(row, 'count')) || 0;\n  const policyEval = getTag(row, 'policy_evaluated');\n  const disposition = getTag(policyEval, 'disposition') || 'none';\n  const dkimResult = getTag(policyEval, 'dkim') || 'unknown';\n  const spfResult = getTag(policyEval, 'spf') || 'unknown';\n  const authResults = getTag(record, 'auth_results');\n  const dkimDomain = getTag(getTag(authResults, 'dkim'), 'domain') || headerFrom;\n  const spfDomain = getTag(getTag(authResults, 'spf'), 'domain') || headerFrom;\n  totalMessages += count;\n  if (dkimResult !== 'pass' || spfResult !== 'pass' || disposition !== 'none') {\n    failures.push({ sourceIp, count, disposition, dkimResult, spfResult });\n  }\n  rows.push({ reportId, source, messageCount: count, dateBegin, dateEnd, sourceIp, dkimResult, spfResult, dkimDomain, spfDomain });\n}\n\nconst d = v => v ? v.split('T')[0] : '?';\nlet msg = `\\u{1F4CA} <b>DMARC Report: ${esc(domain)}</b>\\n`;\nmsg += `\\u{1F4E4} From: ${esc(source)}\\n`;\nmsg += `\\u{1F4C5} ${d(dateBegin)} to ${d(dateEnd)}\\n\\n`;\nfor (const r of rows) {\n  const icon = (r.dkimResult === 'pass' && r.spfResult === 'pass') ? '\\u2705' : '\\u274C';\n  msg += `${icon} ${r.messageCount}x from <code>${esc(r.sourceIp)}</code>\\n`;\n  msg += `    DKIM: ${esc(r.dkimResult)} | SPF: ${esc(r.spfResult)}\\n`;\n}\nmsg += `\\n\\u{1F4EC} Total: ${totalMessages} messages`;\nmsg += failures.length > 0 ? `, \\u26A0\\uFE0F ${failures.length} source(s) with issues` : ', no failures';\n\nreturn rows.map((row, i) => ({ json: { ...row, telegramMessage: i === 0 ? msg : null } }));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [96, 0],
      "id": "d395fe56-646a-467a-b00a-a12e2010a0f0",
      "name": "Parse DMARC Report"
    },
    {
      "parameters": {
        "chatId": "YOUR_TELEGRAM_CHAT_ID",
        "text": "={{ $json.telegramMessage }}",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "HTML"
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [544, 80],
      "id": "75aa9bd8-9a56-42a2-b03f-80fa4e16b99e",
      "name": "Send Telegram alert",
      "credentials": {
        "telegramApi": {
          "id": "",
          "name": "Telegram Bot"
        }
      }
    },
    {
      "parameters": {
        "dataTableId": {
          "__rl": true,
          "value": "YOUR_DATA_TABLE_ID",
          "mode": "list",
          "cachedResultName": "dmarc-reports"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "messageCount": "={{ $json.messageCount }}",
            "source": "={{ $json.source }}",
            "reportId": "={{ $json.reportId }}",
            "dateBegin": "={{ $json.dateBegin }}",
            "dateEnd": "={{ $json.dateEnd }}",
            "sourceIp": "={{ $json.sourceIp }}",
            "dkimResult": "={{ $json.dkimResult }}",
            "spfResult": "={{ $json.spfResult }}",
            "dkimDomain": "={{ $json.dkimDomain }}",
            "spfDomain": "={{ $json.spfDomain }}"
          },
          "matchingColumns": [],
          "schema": [
            { "id": "reportId", "displayName": "reportId", "type": "string" },
            { "id": "source", "displayName": "source", "type": "string" },
            { "id": "messageCount", "displayName": "messageCount", "type": "number" },
            { "id": "dateBegin", "displayName": "dateBegin", "type": "dateTime" },
            { "id": "dateEnd", "displayName": "dateEnd", "type": "dateTime" },
            { "id": "sourceIp", "displayName": "sourceIp", "type": "string" },
            { "id": "dkimResult", "displayName": "dkimResult", "type": "string" },
            { "id": "spfResult", "displayName": "spfResult", "type": "string" },
            { "id": "dkimDomain", "displayName": "dkimDomain", "type": "string" },
            { "id": "spfDomain", "displayName": "spfDomain", "type": "string" }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [320, -96],
      "id": "d6d4a7c5-1a74-40c4-92be-0769bf0448b5",
      "name": "Store in data table"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "e2c7ef47-86c1-4c83-96b2-9d5597b1f0d2",
              "leftValue": "={{ $json.telegramMessage }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [320, 96],
      "id": "434bd5cc-7d02-4bfd-a414-51f60374a824",
      "name": "Has Telegram message?"
    }
  ],
  "pinData": {
    "Webhook": [
      {
        "json": {
          "headers": {
            "host": "your-n8n-instance.com",
            "user-agent": "EmailConnect/1.0",
            "content-type": "application/json",
            "x-email-webhook": "true"
          },
          "params": {},
          "query": {},
          "body": {
            "message": {
              "sender": {
                "name": null,
                "email": "reports@someispdmarc.com"
              },
              "recipient": {
                "name": null,
                "email": "dmarc@yourdomain.com"
              },
              "subject": "Report Domain: yourdomain.com Submitter: someisp.com",
              "content": {
                "text": "This is a DMARC aggregate report for yourdomain.com\n\n1 records.\n1 passed.\n0 failed.\n"
              },
              "date": "2026-02-17T14:27:18.000Z",
              "attachments": [
                {
                  "filename": "someisp.com!yourdomain.com!1771027200!1771113599.xml.gz",
                  "contentType": "application/gzip",
                  "size": 530,
                  "downloadUrl": "https://app.emailconnect.eu/attachments/example/download",
                  "status": "completed",
                  "storage": "s3",
                  "uploadType": "sync"
                }
              ]
            },
            "domainId": "your-domain-id",
            "aliasId": "your-alias-id"
          },
          "webhookUrl": "https://your-n8n-instance.com/webhook/your-webhook-path",
          "executionMode": "production"
        }
      }
    ]
  },
  "connections": {
    "Webhook": { "main": [[{ "node": "HTTP Request", "type": "main", "index": 0 }]] },
    "HTTP Request": { "main": [[{ "node": "Parse DMARC Report", "type": "main", "index": 0 }]] },
    "Parse DMARC Report": { "main": [[{ "node": "Store in data table", "type": "main", "index": 0 }, { "node": "Has Telegram message?", "type": "main", "index": 0 }]] },
    "Has Telegram message?": { "main": [[{ "node": "Send Telegram alert", "type": "main", "index": 0 }]] }
  },
  "active": false,
  "settings": { "executionOrder": "v1" },
  "meta": { "templateCredsSetupCompleted": false },
  "tags": []
}
