{
  "name": "TxMeter alerts to Telegram",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "txmeter",
        "responseMode": "responseNode",
        "options": {
          "rawBody": true
        }
      },
      "id": "4a1f6c0e-1b51-4c8e-9a51-6f1c2d7e0001",
      "name": "TxMeter webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        0
      ],
      "webhookId": "6d2b8f3a-5c47-4e2a-8f0b-7e3c1a9d0001"
    },
    {
      "parameters": {
        "jsCode": "// X-TxMeter-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>\n// The HMAC covers the exact bytes TxMeter sent, so it is checked on the raw\n// body, never on re-serialised JSON.\nconst item = $input.first();\nconst header = item.json.headers['x-txmeter-signature'] || '';\nconst sig = Object.fromEntries(header.split(',').map((part) => part.split('=')));\nconst raw = (await this.helpers.getBinaryDataBuffer(0, 'data')).toString('utf8');\nlet payload = null;\ntry {\n  payload = JSON.parse(raw);\n} catch (e) {}\nreturn [{ json: { t: sig.t || '', v1: sig.v1 || '', signed: (sig.t || '') + '.' + raw, payload } }];"
      },
      "id": "4a1f6c0e-1b51-4c8e-9a51-6f1c2d7e0002",
      "name": "Read signature",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        0
      ]
    },
    {
      "parameters": {
        "action": "hmac",
        "type": "SHA256",
        "value": "={{ $json.signed }}",
        "dataPropertyName": "expected",
        "secret": "PASTE_YOUR_SIGNING_SECRET",
        "encoding": "hex"
      },
      "id": "4a1f6c0e-1b51-4c8e-9a51-6f1c2d7e0008",
      "name": "Compute signature",
      "type": "n8n-nodes-base.crypto",
      "typeVersion": 1,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "c1a0e7d2-0000-4000-8000-000000000001",
              "leftValue": "={{ $json.v1 }}",
              "rightValue": "={{ $json.expected }}",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "c1a0e7d2-0000-4000-8000-000000000003",
              "leftValue": "={{ Math.abs($now.toSeconds() - Number($json.t)) }}",
              "rightValue": 300,
              "operator": {
                "type": "number",
                "operation": "lte"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "4a1f6c0e-1b51-4c8e-9a51-6f1c2d7e0003",
      "name": "Signed by TxMeter?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        660,
        0
      ]
    },
    {
      "parameters": {
        "respondWith": "noData",
        "options": {
          "responseCode": 200
        }
      },
      "id": "4a1f6c0e-1b51-4c8e-9a51-6f1c2d7e0004",
      "name": "Accept",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        880,
        -100
      ]
    },
    {
      "parameters": {
        "respondWith": "noData",
        "options": {
          "responseCode": 401
        }
      },
      "id": "4a1f6c0e-1b51-4c8e-9a51-6f1c2d7e0005",
      "name": "Reject",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        880,
        100
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "c1a0e7d2-0000-4000-8000-000000000002",
              "leftValue": "={{ $json.payload.type }}",
              "rightValue": "alert.created",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "4a1f6c0e-1b51-4c8e-9a51-6f1c2d7e0006",
      "name": "Alert, not a test?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1100,
        -100
      ]
    },
    {
      "parameters": {
        "chatId": "PASTE_YOUR_CHAT_ID",
        "text": "={{ $json.payload.data.alert.severity === 'critical' ? 'CRITICAL: ' : '' }}{{ $json.payload.data.alert.title }}\n{{ $json.payload.data.address.label || $json.payload.data.address.address }} on {{ $json.payload.data.address.chain }}\n{{ ($json.payload.data.event || $json.payload.data.address).explorer_url }}",
        "additionalFields": {
          "appendAttribution": false,
          "disable_web_page_preview": true
        }
      },
      "id": "4a1f6c0e-1b51-4c8e-9a51-6f1c2d7e0007",
      "name": "Send to Telegram",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1320,
        -100
      ]
    }
  ],
  "connections": {
    "TxMeter webhook": {
      "main": [
        [
          {
            "node": "Read signature",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read signature": {
      "main": [
        [
          {
            "node": "Compute signature",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute signature": {
      "main": [
        [
          {
            "node": "Signed by TxMeter?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Signed by TxMeter?": {
      "main": [
        [
          {
            "node": "Accept",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Reject",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Accept": {
      "main": [
        [
          {
            "node": "Alert, not a test?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert, not a test?": {
      "main": [
        [
          {
            "node": "Send to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {}
}
