Echtzeit-Flugpreis-Tracker – Sofortige Preissenkungsbenachrichtigungen per SMS und E-Mail

Fortgeschritten

Dies ist ein Miscellaneous-Bereich Automatisierungsworkflow mit 14 Nodes. Hauptsächlich werden If, Code, Cron, Gmail, Function und andere Nodes verwendet. Realtime-Flugpreis-Tracker (Aviation Stack API) – Erinnerungen per Gmail und Telegram senden

Voraussetzungen
  • Google-Konto + Gmail API-Anmeldedaten
  • Telegram Bot Token
  • Möglicherweise sind Ziel-API-Anmeldedaten erforderlich
  • Google Sheets API-Anmeldedaten

Kategorie

Workflow-Vorschau
Visualisierung der Node-Verbindungen, mit Zoom und Pan
Workflow exportieren
Kopieren Sie die folgende JSON-Konfiguration und importieren Sie sie in n8n
{
  "id": "4vQkJTdJPHnD0XUa",
  "meta": {
    "instanceId": "dd69efaf8212c74ad206700d104739d3329588a6f3f8381a46a481f34c9cc281",
    "templateCredsSetupCompleted": true
  },
  "name": "Live Flight Fare Tracker – Instant Price Drop Alerts via SMS & Email",
  "tags": [],
  "nodes": [
    {
      "id": "aa5f054a-6239-46c1-8ab3-492796e1f1c1",
      "name": "Zeitplan-Trigger",
      "type": "n8n-nodes-base.cron",
      "position": [
        -1440,
        380
      ],
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "mode": "everyX",
              "unit": "minutes",
              "value": 15
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8d308fdf-5c40-46ab-b695-a7c95710ada2",
      "name": "Flugdaten abrufen",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1220,
        380
      ],
      "parameters": {
        "url": "https://api.aviationstack.com/v1/flights",
        "options": {},
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "access_key",
              "value": "0987c6845c09876yt"
            },
            {
              "name": "dep_iata",
              "value": "JFK"
            },
            {
              "name": "arr_iata",
              "value": "LAX"
            },
            {
              "name": "limit",
              "value": "10"
            }
          ]
        }
      },
      "credentials": {
        "httpQueryAuth": {
          "id": "xA2e6hA40RZ8bzrI",
          "name": "Query Auth account - test"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "f4675a74-c27d-4b8b-bb8e-624687125b4a",
      "name": "Flugdaten verarbeiten",
      "type": "n8n-nodes-base.function",
      "position": [
        -1000,
        380
      ],
      "parameters": {
        "functionCode": "// Process flight data and check for fare changes\nconst currentFlights = items[0].json.data || [];\nconst processedFlights = [];\n\nfor (const flight of currentFlights) {\n  if (flight.flight_status === 'scheduled' && flight.departure) {\n    // Extract fare information (you may need to adapt based on API response)\n    const flightInfo = {\n      flight_number: flight.flight.iata,\n      airline: flight.airline.name,\n      departure: flight.departure.airport,\n      arrival: flight.arrival.airport,\n      departure_time: flight.departure.scheduled,\n      arrival_time: flight.arrival.scheduled,\n      // Mock fare data - replace with actual fare from your chosen API\n      current_fare: Math.floor(Math.random() * 500) + 200,\n      route: `${flight.departure.iata}-${flight.arrival.iata}`,\n      timestamp: new Date().toISOString()\n    };\n    \n    processedFlights.push(flightInfo);\n  }\n}\n\nreturn processedFlights.map(flight => ({ json: flight }));"
      },
      "executeOnce": true,
      "typeVersion": 1
    },
    {
      "id": "87b6bde3-fe0a-47a2-9410-ddb8d78cbf77",
      "name": "Prüfen, ob Benachrichtigung erforderlich",
      "type": "n8n-nodes-base.if",
      "position": [
        -340,
        380
      ],
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{ $json.fare_change }}",
              "operation": "notEqual"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "11309f53-cdc0-4cf9-b060-d55ca3e9ca74",
      "name": "Benachrichtigungsnachricht formatieren",
      "type": "n8n-nodes-base.function",
      "position": [
        -80,
        340
      ],
      "parameters": {
        "functionCode": "// Format alert message\nconst flight = items[0].json;\nconst alertType = flight.alert_type;\nconst emoji = alertType === 'PRICE_DROP' ? '📉' : '📈';\nconst alertColor = alertType === 'PRICE_DROP' ? 'good' : 'warning';\n\nconst message = {\n  email: {\n    subject: `${emoji} Flight Fare Alert: ${flight.flight_number}`,\n    html: `\n      <h2>${emoji} Fare ${alertType.replace('_', ' ')} Alert</h2>\n      <p><strong>Flight:</strong> ${flight.flight_number} (${flight.airline})</p>\n      <p><strong>Route:</strong> ${flight.departure} → ${flight.arrival}</p>\n      <p><strong>Departure:</strong> ${new Date(flight.departure_time).toLocaleString()}</p>\n      <p><strong>Previous Fare:</strong> $${flight.previous_fare}</p>\n      <p><strong>Current Fare:</strong> $${flight.current_fare}</p>\n      <p><strong>Change:</strong> $${flight.fare_change} (${flight.percentage_change}%)</p>\n      <p style=\"color: ${alertType === 'PRICE_DROP' ? 'green' : 'red'};\"><strong>Recommendation:</strong> ${alertType === 'PRICE_DROP' ? 'Consider booking now!' : 'Price increased - monitor for drops'}</p>\n    `\n  },\n  slack: {\n    text: `${emoji} Flight Fare Alert`,\n    attachments: [\n      {\n        color: alertColor,\n        fields: [\n          {\n            title: \"Flight\",\n            value: `${flight.flight_number} (${flight.airline})`,\n            short: true\n          },\n          {\n            title: \"Route\",\n            value: `${flight.departure} → ${flight.arrival}`,\n            short: true\n          },\n          {\n            title: \"Previous Fare\",\n            value: `$${flight.previous_fare}`,\n            short: true\n          },\n          {\n            title: \"Current Fare\",\n            value: `$${flight.current_fare}`,\n            short: true\n          },\n          {\n            title: \"Change\",\n            value: `$${flight.fare_change} (${flight.percentage_change}%)`,\n            short: false\n          }\n        ]\n      }\n    ]\n  },\n  sms: `${emoji} FARE ALERT: ${flight.flight_number} ${flight.departure}-${flight.arrival} fare changed from $${flight.previous_fare} to $${flight.current_fare} (${flight.percentage_change}%)`\n};\n\nreturn [{ json: { ...flight, formatted_messages: message } }];"
      },
      "typeVersion": 1
    },
    {
      "id": "026afcab-023a-4ef4-9573-28ba32edc01c",
      "name": "Benachrichtigungsaktivität protokollieren",
      "type": "n8n-nodes-base.function",
      "position": [
        460,
        400
      ],
      "parameters": {
        "functionCode": "// Log alert activity\nconst alert = items[0].json;\n\nconst logEntry = {\n  timestamp: new Date().toISOString(),\n  flight_number: alert.flight_number,\n  route: alert.route,\n  alert_type: alert.alert_type,\n  fare_change: alert.fare_change,\n  percentage_change: alert.percentage_change,\n  notification_sent: true\n};\n\nconsole.log('Fare Alert Sent:', logEntry);\n\nreturn [{ json: logEntry }];"
      },
      "typeVersion": 1
    },
    {
      "id": "374b1e8e-15b3-4109-9c46-01406c0e4ca5",
      "name": "Code",
      "type": "n8n-nodes-base.code",
      "position": [
        -580,
        380
      ],
      "parameters": {
        "jsCode": "// Get current flight data from previous node (Process Flight Data)\nconst currentFlightsItems = $('Process Flight Data').all();\n\n// Get stored fare data from Google Sheets node\nconst sheetsData = $('Previous flight data').all();\n\nconsole.log('Current flights items:', currentFlightsItems.length);\nconsole.log('Sheets data items:', sheetsData.length);\n\n// Build lookup object from Google Sheets data (remove duplicates)\nconst storedFares = {};\nconst uniqueSheetRows = new Map();\n\n// Remove duplicates from sheets data first\nfor (const row of sheetsData) {\n  const rowData = row.json;\n  if (rowData.flight_number && rowData.route) {\n    const routeKey = `${rowData.flight_number}_${rowData.route}`;\n    \n    // Keep only the first occurrence of each flight\n    if (!uniqueSheetRows.has(routeKey)) {\n      uniqueSheetRows.set(routeKey, rowData);\n      storedFares[routeKey] = parseFloat(rowData.current_fare || 0);\n    }\n  }\n}\n\nconsole.log('Stored fares lookup:', Object.keys(storedFares));\n\nconst alertFlights = [];\nconst fareUpdates = [];\n\n// Process each current flight item\nfor (const flightItem of currentFlightsItems) {\n  const flightData = flightItem.json;\n  \n  if (!flightData.flight_number || !flightData.route) {\n    console.log('Skipping invalid flight data:', flightData);\n    continue;\n  }\n  \n  const routeKey = `${flightData.flight_number}_${flightData.route}`;\n  const currentFare = parseFloat(flightData.current_fare || 0);\n  const previousFare = storedFares[routeKey];\n  \n  console.log(`Processing ${routeKey}: Current=${currentFare}, Previous=${previousFare}`);\n  \n  if (previousFare && previousFare !== currentFare && currentFare > 0) {\n    const fareChange = currentFare - previousFare;\n    const percentageChange = (fareChange / previousFare) * 100;\n    \n    console.log(`${routeKey}: Change=${fareChange}, Percentage=${percentageChange.toFixed(2)}%`);\n    \n    // Alert if fare decreased by 10% or more, or increased by 15% or more\n    if (percentageChange <= -10 || percentageChange >= 15) {\n      alertFlights.push({\n        ...flightData,\n        previous_fare: previousFare,\n        fare_change: fareChange,\n        percentage_change: Math.round(percentageChange * 100) / 100,\n        alert_type: percentageChange < 0 ? 'PRICE_DROP' : 'PRICE_INCREASE',\n        alert_message: percentageChange < 0 \n          ? `🔥 PRICE DROP: ${Math.abs(percentageChange).toFixed(1)}% decrease!` \n          : `⚠️ PRICE INCREASE: ${percentageChange.toFixed(1)}% increase!`\n      });\n      \n      console.log(`ALERT TRIGGERED for ${routeKey}: ${percentageChange < 0 ? 'DROP' : 'INCREASE'} of ${Math.abs(percentageChange).toFixed(2)}%`);\n    }\n  } else if (!previousFare) {\n    console.log(`New flight detected: ${routeKey}`);\n  } else if (currentFare <= 0) {\n    console.log(`Invalid current fare for ${routeKey}: ${currentFare}`);\n  }\n  \n  // Prepare fare updates for Google Sheets (to update stored fares)\n  if (currentFare > 0) {\n    fareUpdates.push({\n      row_number: uniqueSheetRows.get(routeKey)?.row_number || null,\n      flight_number: flightData.flight_number,\n      airline: flightData.airline || 'Unknown',\n      departure: flightData.departure || 'Unknown',\n      arrival: flightData.arrival || 'Unknown',\n      departure_time: flightData.departure_time || '',\n      arrival_time: flightData.arrival_time || '',\n      current_fare: currentFare,\n      route: flightData.route,\n      timestamp: flightData.timestamp || new Date().toISOString(),\n      last_updated: new Date().toISOString()\n    });\n  }\n}\n\nconsole.log(`Total alerts generated: ${alertFlights.length}`);\nif (alertFlights.length > 0) {\n  console.log(`Alerts for flights:`, alertFlights.map(f => f.flight_number));\n}\n\n// Store fare updates in node context for the Google Sheets update node\n$node[\"Code\"].context = { fareUpdates };\n\n// If no alerts, return empty array but still process\nif (alertFlights.length === 0) {\n  console.log('No fare alerts triggered');\n  return [];\n}\n\n// Return alert flights for notification processing\nreturn alertFlights.map(flight => ({ json: flight }));\n"
      },
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "43084297-fd73-45ae-83b8-3e31db490777",
      "name": "Telegram",
      "type": "n8n-nodes-base.telegram",
      "onError": "continueRegularOutput",
      "position": [
        180,
        480
      ],
      "webhookId": "30b417c4-a6ea-42ec-8218-c40248df36b8",
      "parameters": {
        "text": "={{ $json.formatted_messages.sms }}",
        "chatId": "123SSHSJNASB",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "3ubbGgZx2YzylQZu",
          "name": "Telegram account - test"
        }
      },
      "executeOnce": true,
      "retryOnFail": false,
      "typeVersion": 1.2
    },
    {
      "id": "b2048186-6d7a-4b76-9849-bc694592ce39",
      "name": "Workflow-Übersicht",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1440,
        60
      ],
      "parameters": {
        "width": 700,
        "height": 200,
        "content": "## ✈️ Flight Fare Tracker & Alert System\n\nThis workflow is designed to monitor flight prices and send alerts when significant fare changes occur (drops or increases). It automates the process of fetching flight data, comparing current fares against historical records, and notifying users via email or Telegram if an alert condition is met. 📉📈"
      },
      "typeVersion": 1
    },
    {
      "id": "06f7bb02-8151-43f9-b5c2-9a75555d0199",
      "name": "Datenerfassung & Verarbeitung",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1400,
        560
      ],
      "parameters": {
        "color": 4,
        "width": 600,
        "height": 300,
        "content": "### 📊 Data Ingestion & Processing\n\n1.  **Schedule Trigger**: Starts the workflow at regular intervals.\n2.  **Fetch Flight Data**: Retrieves flight information from the AviationStack API, filtering by JFK to LAX route.\n3.  **Process Flight Data**: A Function node that extracts and formats relevant flight details (flight number, airline, departure/arrival times, and a *mock* current fare). This prepares data for comparison.\n4.  **Google Sheets**: Reads existing flight fare data from a Google Sheet, which acts as a historical record."
      },
      "typeVersion": 1
    },
    {
      "id": "5b431dd1-1832-4a03-9df0-880dc17d1924",
      "name": "Preisvergleich & Benachrichtigungslogik",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -700,
        20
      ],
      "parameters": {
        "color": 3,
        "width": 600,
        "height": 280,
        "content": "### 🔍 Fare Comparison & Alert Logic\n\n1.  **Code**: This critical node compares the `current_fare` from the \"Process Flight Data\" node with the `previous_fare` fetched from \"Google Sheets\". It calculates fare changes (absolute and percentage) and determines if an alert is needed based on predefined thresholds (e.g., >=10% drop or >=15% increase).\n2.  **Check if Alert Needed**: An If node that checks if the `fare_change` calculated in the Code node is non-zero. If there's a change, it proceeds to format and send alerts."
      },
      "typeVersion": 1
    },
    {
      "id": "a29a641a-f204-463f-8764-91bfaf753f2d",
      "name": "Benachrichtigung & Protokollierung",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": 5,
        "width": 600,
        "height": 280,
        "content": "### 🔔 Notification & Logging\n\n1.  **Format Alert Message**: Prepares the alert message in different formats (email HTML, SMS) using a Function node, based on the `alert_type` (price drop/increase).\n2.  **Gmail**: Sends an email notification with the formatted alert.\n3.  **Telegram**: Sends a Telegram message with the formatted alert.\n4.  **Log Alert Activity**: A final Function node that logs details about the sent alert, useful for auditing or debugging."
      },
      "typeVersion": 1
    },
    {
      "id": "cf96cc07-5679-4ba4-86f7-5a2cfec7dae3",
      "name": "Vorherige Flugdaten",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -780,
        380
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WAwV5oNEaedbi9saba87LTkxTFbBWwWaGxsgCmda1_Q/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1WAwV5oNEaedbi9saba87LTkxTFbBWwWaGxsgCmda1_Q",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WAwV5oNEaedbi9saba87LTkxTFbBWwWaGxsgCmda1_Q/edit?usp=drivesdk",
          "cachedResultName": "fare details change logs"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "id": "ScSS2KxGQULuPtdy",
          "name": "Google Sheets- test"
        }
      },
      "executeOnce": false,
      "typeVersion": 4.6
    },
    {
      "id": "ba34df18-2c7c-46ee-9fc4-acfacb7b8fb1",
      "name": "Nachricht senden",
      "type": "n8n-nodes-base.gmail",
      "position": [
        180,
        300
      ],
      "webhookId": "2402da10-b757-4220-8399-d9dee79a3a51",
      "parameters": {
        "sendTo": "abc@gmail.com",
        "message": "={{ $json.formatted_messages.email.html }}",
        "options": {},
        "subject": "={{ $json.formatted_messages.email.subject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "PcTqvGU9uCunfltE",
          "name": "Gmail account - test"
        }
      },
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "93eb556a-c580-4639-9d7d-0b0baaa6cda4",
  "connections": {
    "374b1e8e-15b3-4109-9c46-01406c0e4ca5": {
      "main": [
        [
          {
            "node": "87b6bde3-fe0a-47a2-9410-ddb8d78cbf77",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "43084297-fd73-45ae-83b8-3e31db490777": {
      "main": [
        [
          {
            "node": "026afcab-023a-4ef4-9573-28ba32edc01c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ba34df18-2c7c-46ee-9fc4-acfacb7b8fb1": {
      "main": [
        [
          {
            "node": "026afcab-023a-4ef4-9573-28ba32edc01c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "aa5f054a-6239-46c1-8ab3-492796e1f1c1": {
      "main": [
        [
          {
            "node": "8d308fdf-5c40-46ab-b695-a7c95710ada2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "8d308fdf-5c40-46ab-b695-a7c95710ada2": {
      "main": [
        [
          {
            "node": "f4675a74-c27d-4b8b-bb8e-624687125b4a",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "f4675a74-c27d-4b8b-bb8e-624687125b4a": {
      "main": [
        [
          {
            "node": "cf96cc07-5679-4ba4-86f7-5a2cfec7dae3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "11309f53-cdc0-4cf9-b060-d55ca3e9ca74": {
      "main": [
        [
          {
            "node": "ba34df18-2c7c-46ee-9fc4-acfacb7b8fb1",
            "type": "main",
            "index": 0
          },
          {
            "node": "43084297-fd73-45ae-83b8-3e31db490777",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "cf96cc07-5679-4ba4-86f7-5a2cfec7dae3": {
      "main": [
        [
          {
            "node": "374b1e8e-15b3-4109-9c46-01406c0e4ca5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "87b6bde3-fe0a-47a2-9410-ddb8d78cbf77": {
      "main": [
        [
          {
            "node": "11309f53-cdc0-4cf9-b060-d55ca3e9ca74",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Häufig gestellte Fragen

Wie verwende ich diesen Workflow?

Kopieren Sie den obigen JSON-Code, erstellen Sie einen neuen Workflow in Ihrer n8n-Instanz und wählen Sie "Aus JSON importieren". Fügen Sie die Konfiguration ein und passen Sie die Anmeldedaten nach Bedarf an.

Für welche Szenarien ist dieser Workflow geeignet?

Fortgeschritten - Verschiedenes

Ist es kostenpflichtig?

Dieser Workflow ist völlig kostenlos. Beachten Sie jedoch, dass Drittanbieterdienste (wie OpenAI API), die im Workflow verwendet werden, möglicherweise kostenpflichtig sind.

Workflow-Informationen
Schwierigkeitsgrad
Fortgeschritten
Anzahl der Nodes14
Kategorie1
Node-Typen9
Schwierigkeitsbeschreibung

Für erfahrene Benutzer, mittelkomplexe Workflows mit 6-15 Nodes

Autor
Oneclick AI Squad

Oneclick AI Squad

@oneclick-ai

The AI Squad Initiative is a pioneering effort to build, automate and scale AI-powered workflows using n8n.io. Our mission is to help individuals and businesses integrate AI agents seamlessly into their daily operations from automating tasks and enhancing productivity to creating innovative, intelligent solutions. We design modular, reusable AI workflow templates that empower creators, developers and teams to supercharge their automation with minimal effort and maximum impact.

Externe Links
Auf n8n.io ansehen

Diesen Workflow teilen

Kategorien

Kategorien: 34