{
  "nodes": [
    {
      "id": "16da12c1-464c-4cde-ab5f-2f4c25601cf0",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1008,
        2000
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7b74a669-ea51-4c33-98fa-4673845a5237",
      "name": "Create a session",
      "type": "n8n-nodes-base.airtop",
      "position": [
        -784,
        2000
      ],
      "parameters": {
        "profileName": "udemy",
        "timeoutMinutes": 30,
        "additionalFields": {}
      },
      "typeVersion": 1
    },
    {
      "id": "50dcf645-8983-4c08-88d3-0fe81850016b",
      "name": "Create a window",
      "type": "n8n-nodes-base.airtop",
      "position": [
        -560,
        2000
      ],
      "parameters": {
        "url": "https://www.udemy.com/courses/search/?q=n8n&src=sac",
        "resource": "window",
        "getLiveView": true,
        "additionalFields": {}
      },
      "typeVersion": 1
    },
    {
      "id": "698d4048-0288-4929-81d6-a0f1b29691df",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        336,
        1904
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "34766cef-6108-4506-aee3-149c606de4b9",
      "name": "Close a window",
      "type": "n8n-nodes-base.airtop",
      "position": [
        560,
        1600
      ],
      "parameters": {
        "resource": "window",
        "windowId": "={{ $('Create a window').item.json.data.windowId }}",
        "operation": "close",
        "sessionId": "={{ $('Create a session').item.json.sessionId }}"
      },
      "typeVersion": 1
    },
    {
      "id": "9d73187e-c02f-4db3-a3e3-00e52b62aff4",
      "name": "Terminate a session",
      "type": "n8n-nodes-base.airtop",
      "position": [
        768,
        1600
      ],
      "parameters": {
        "operation": "terminate",
        "sessionId": "={{ $('Create a session').item.json.sessionId }}"
      },
      "typeVersion": 1
    },
    {
      "id": "7881e7e1-e5ea-457b-b077-10cbfa0be6a3",
      "name": "Split course data",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -112,
        2000
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "output.products"
      },
      "typeVersion": 1
    },
    {
      "id": "ce3b5661-4f56-492d-86cd-ccad8d2417ca",
      "name": "Scrape course",
      "type": "n8n-nodes-base.airtop",
      "position": [
        -336,
        2000
      ],
      "parameters": {
        "prompt": "title, Current price, Original price, url, instructors name, rating, offerLeftTime(provided offerLeftTime is e.g,20 hours left at this price!)\n\nOriginal price is not available to \"\" blank output provide",
        "resource": "extraction",
        "operation": "query",
        "additionalFields": {
          "outputSchema": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"products\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"title\": { \"type\": \"string\" },\n        \"current_price\": { \"type\": \"string\" },\n        \"original_price\": { \"type\": \"string\" },\n        \"url\": { \"type\": \"string\" },\n        \"instructors_name\": { \"type\": \"string\" },\n          \"rating\": { \"type\": \"string\" },\n    \"offerLeftTime\": {\n      \"type\": \"string\"\n    }\n        },\n        \"required\": [\"title\", \"current_price\",\"original_price\", \"url\", \"instructors_name\", \"rating\",\"offerLeftTime\"]\n      }\n    }\n  },\n  \"required\": [\"products\"]\n}",
          "parseJsonOutput": true
        }
      },
      "typeVersion": 1
    },
    {
      "id": "48caad7c-34e6-4403-83b3-1c980174d131",
      "name": "Check offer available or not",
      "type": "n8n-nodes-base.if",
      "position": [
        112,
        2000
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "d9b964ec-cb67-4c1f-aba5-0e8e4c5d241c",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.original_price }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8fe3ce14-965d-4377-b68f-b224c300e6b6",
      "name": "Find discount",
      "type": "n8n-nodes-base.code",
      "position": [
        560,
        1920
      ],
      "parameters": {
        "jsCode": "// Get values from the input item\nconst currentPriceStr = $input.first().json.current_price;\nconst originalPriceStr = $input.first().json.original_price;\n\n// Detect currency symbol (assumes same for both)\nconst currencySymbol = currentPriceStr.trim().charAt(0);\n\n// Clean currency symbols and commas, then parse to float\nconst currentPrice = parseFloat(currentPriceStr.replace(/[^0-9.]/g, '').replace(/,/g, ''));\nconst originalPrice = parseFloat(originalPriceStr.replace(/[^0-9.]/g, '').replace(/,/g, ''));\n\n// Calculate discount\nconst discount = ((originalPrice - currentPrice) / originalPrice) * 100;\nconst discountPercentage = discount.toFixed(2);\n\n// Return output with original currency symbol\nreturn [\n  {\n    json: {\n      current_price: `${currencySymbol}${currentPrice}`,\n      original_price: `${currencySymbol}${originalPrice}`,\n      discount_percentage: `${discountPercentage}`\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "23b12285-eaf3-425a-9976-461aaa2b0c18",
      "name": "Append 50% up disc data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1008,
        1904
      ],
      "parameters": {
        "columns": {
          "value": {
            "Url": "={{ $('Loop Over Items').item.json.url }}",
            "Title": "={{ $('Loop Over Items').item.json.title }}",
            "Rating": "={{ $('Loop Over Items').item.json.rating }}",
            "Discount": "={{ $json.discount_percentage }}% OFF",
            "Offer left": "={{ $('Loop Over Items').item.json.offerLeftTime }}",
            "Current price": "={{ $json.current_price }}",
            "Original price": "={{ $json.original_price }}",
            "Instructor Name": "={{ $('Loop Over Items').item.json.instructors_name }}"
          },
          "schema": [
            {
              "id": "Instructor Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Instructor Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Original price",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Original price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Discount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Discount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Current price",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Current price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Offer left",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Offer left",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rating",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Rating",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JQE5aW11UriY-jtybq9LTXqu-Un3khZuNp7SzSjXnVU/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1JQE5aW11UriY-jtybq9LTXqu-Un3khZuNp7SzSjXnVU",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1JQE5aW11UriY-jtybq9LTXqu-Un3khZuNp7SzSjXnVU/edit?usp=drivesdk",
          "cachedResultName": "Online Course Price Tracker with Coupon Detection"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "8d578a9c-6950-467f-8bf8-2c7a48804dc4",
      "name": "Send notify course deal",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1216,
        1904
      ],
      "parameters": {
        "text": "=\ud83e\udde0 Top Udemy Course Deal\n\n\ud83d\udc68\u200d\ud83c\udfeb InstructorName: {{ $json[\"Instructor Name\"] }}\n\ud83c\udf93 Course: {{ $json.Title }}   \n\ud83d\udcb8 OriginalPrice: {{ $('Loop Over Items').item.json.original_price }} | {{ $json.Discount }} | CurrentPrice: {{ $json[\"Current price\"] }} \n\ud83c\udf10 {{ $json.Url }}\n\u2b50 Rating: {{ $json.Rating }}\n\u23f0 {{ $json[\"Offer left\"] }}",
        "chatId": "chat_id",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "508d795b-2cf2-4abf-a91e-16cc2a30bc5f",
      "name": "Check discount percentage 50%>",
      "type": "n8n-nodes-base.if",
      "position": [
        784,
        1920
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ff84d4e1-b9ac-4896-be90-2e5903d8d8b9",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.discount_percentage }}",
              "rightValue": 50
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "abd3cac0-21af-43f9-808c-3bbb64c5439d",
      "name": "No Operation, do nothing",
      "type": "n8n-nodes-base.noOp",
      "position": [
        336,
        2208
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "fb1da1a4-adde-4bfb-8edb-62b1f994023b",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1584,
        1776
      ],
      "parameters": {
        "width": 528,
        "height": 528,
        "content": "## How it works\nThis workflow monitors Udemy courses for deep discounts (50%+) and alerts you via Telegram.\n\nEvery day, it launches a browser session using Airtop, scrapes course listings from Udemy search results, and extracts pricing data. Each course is checked for active offers\u2014if the original price exists, the workflow calculates the discount percentage. Courses with 50%+ discounts are logged to Google Sheets and sent as formatted Telegram notifications with instructor, rating, and time-limited offer details.\n\n## Setup steps\n1. Connect your Airtop account (for browser automation)\n2. Update the search URL in \"Create a window\" to match your course interests\n3. Connect Google Sheets and select your tracking spreadsheet\n4. Add your Telegram bot token and chat ID\n5. Adjust the schedule trigger frequency (default: daily at midnight)\n\n\n### Sample sheet:\nhttps://docs.google.com/spreadsheets/d/1JQE5aW11UriY-jtybq9LTXqu-Un3khZuNp7SzSjXnVU/edit?gid=0#gid=0\n"
      },
      "typeVersion": 1
    },
    {
      "id": "28836b1d-6303-4496-8367-ecb5979bfe0e",
      "name": "Browser Automation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -848,
        1888
      ],
      "parameters": {
        "color": 7,
        "width": 680,
        "height": 308,
        "content": "## Browser Automation\nLaunches Airtop session, opens Udemy search page, and scrapes course data using AI extraction."
      },
      "typeVersion": 1
    },
    {
      "id": "b99e0060-9983-40ed-b3a7-8f438e226596",
      "name": "Data Processing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -160,
        1792
      ],
      "parameters": {
        "color": 7,
        "width": 840,
        "height": 400,
        "content": "## Data Processing\nSplits scraped courses into individual items and filters for those with active discount offers."
      },
      "typeVersion": 1
    },
    {
      "id": "7140fc2b-4231-47fb-813a-404cfec2f299",
      "name": "Discount Detection",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        688,
        1792
      ],
      "parameters": {
        "color": 7,
        "width": 720,
        "height": 404,
        "content": "## Discount Detection\nCalculates discount percentage and saves courses with 50%+ discounts to Google Sheets, then sends Telegram alerts."
      },
      "typeVersion": 1
    },
    {
      "id": "f7da6aca-6ff1-41d2-8cae-ec3ddeff1ead",
      "name": "Cleanup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        400,
        1504
      ],
      "parameters": {
        "color": 7,
        "width": 620,
        "height": 272,
        "content": "## Cleanup\nCloses browser window and terminates Airtop session after processing all courses."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Find discount": {
      "main": [
        [
          {
            "node": "Check discount percentage 50%>",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape course": {
      "main": [
        [
          {
            "node": "Split course data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Close a window": {
      "main": [
        [
          {
            "node": "Terminate a session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create a window": {
      "main": [
        [
          {
            "node": "Scrape course",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Close a window",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Find discount",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create a session": {
      "main": [
        [
          {
            "node": "Create a window",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Create a session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split course data": {
      "main": [
        [
          {
            "node": "Check offer available or not",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append 50% up disc data": {
      "main": [
        [
          {
            "node": "Send notify course deal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send notify course deal": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check offer available or not": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check discount percentage 50%>": {
      "main": [
        [
          {
            "node": "Append 50% up disc data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}