{
  "id": "qivwnBWR4Vi172As",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Automated Malaysian Weather Alerts with Perplexity AI, Firecrawl and Telegram",
  "tags": [],
  "nodes": [
    {
      "id": "3111f7be-eeb3-4abf-9355-aa4e77ee562d",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        656,
        352
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7c96180a-5ac3-45e8-94b4-a1ad6fb4027e",
      "name": "Get Weather Warnings",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        880,
        352
      ],
      "parameters": {
        "url": "https://api.data.gov.my/weather/warning",
        "options": {}
      },
      "typeVersion": 4.1
    },
    {
      "id": "f6a01b30-5df6-44d7-968b-c37f64a68cdf",
      "name": "Send to Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        3072,
        928
      ],
      "parameters": {
        "text": "={{ $json.output[0].content[0].text }}",
        "chatId": "=INSERT YOUR TELEGRAM GROUP OR CHANNEL ID HERE",
        "additionalFields": {
          "appendAttribution": false,
          "disable_notification": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "a1e946f7-5519-412a-a5e6-e4a776937ac8",
      "name": "Make a summary",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        2592,
        928
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "=gpt-4.1-mini"
        },
        "options": {
          "temperature": 0.3
        },
        "responses": {
          "values": [
            {
              "role": "system",
              "content": "=You are an expert weather and flood forecaster for Malaysia.\n\nYou have received the weather news in Malaysia:\n\nSummary: \n{{ $json.summary }}\n\nand this is URL {{ $json.sourceURL }} sequenlty\n\nIMPORTANT:\nYou need to categorized each of it and make the summary message to be sent to telegram. \nGreet and Just give the result.\nInsert the source URL too.\nGive the report in English."
            }
          ]
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "8ad09077-a831-4915-91bf-42f78069e4d1",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        -80
      ],
      "parameters": {
        "width": 608,
        "height": 1008,
        "content": "## Automated Malaysian Weather Alerts with Perplexity AI and Telegram\n\n### How it works\n1. Schedule trigger runs daily at 9 AM to fetch weather warnings from Malaysia's official data.gov.my API.\n2. JavaScript code processes weather data to extract warning types, severity levels, and affected locations.\n3. Search queries are combined with location information and sent to Perplexity Sonar Pro AI Agent.\n4. AI Agent searches for recent news articles (within 3 days) from major Malaysian news channels like Utusan, Harian Metro, Berita Harian, and Kosmo.\n5. URLs are cleaned and processed one by one through Firecrawl to scrape article summaries.\n6. All summaries and source URLs are combined and sent to OpenAI for final report generation.\n7. The polished weather report is delivered to your Telegram channel in English.\n\n### How to Set Up\n1. Get an OpenAI API key from [https://platform.openai.com](https://platform.openai.com).\n2. Get a Perplexity API key via OpenRouter from [https://openrouter.ai](https://openrouter.ai).\n3. Get a free Firecrawl API key from [https://firecrawl.dev](https://firecrawl.dev). Free tier has limited requests per hour.\n4. Create a Telegram bot and get your bot token from [@BotFather](https://t.me/botfather).\n5. Insert your OpenAI API key in the \"Make a summary\" node credentials.\n6. Insert your OpenRouter API key in the \"Perplexity Sonar Pro Model\" node credentials.\n7. Insert your Firecrawl API key in the \"Scrape Website with Firecrawl\" node Authorization header (Bearer fc-YOUR-KEY).\n8. Update the Telegram chat ID in the \"Send to Telegram\" node with your channel or group ID.\n\n### Customize\n1. **Change schedule time**: Adjust the \"Schedule Trigger\" node to run at your preferred time instead of 9 AM.\n2. **Language options**: Modify the system prompt in the \"Make a summary\" node to generate reports in Bahasa Malaysia instead of English.\n3. **Filter warnings**: Edit the JavaScript code to focus only on severe warnings or specific states you want to monitor.\n4. **Expand news sources**: Update the AI Agent prompt to include additional Malaysian news outlets or social media platforms.\n5. **Add storage**: Connect Supabase or Google Sheets nodes after the summary to maintain a historical database of weather warnings.\n6. **Multi-channel delivery**: Add email, WhatsApp, or SMS notification nodes alongside Telegram for wider alert distribution.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "48e00a57-e93d-4f0e-adf7-642c9975d9d7",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1664,
        352
      ],
      "parameters": {
        "text": "=Find latest news about {{ $json.searchQuery }}, \nThe location is: {{ $json.locations }} from the Malaysia News channel such as Utusan, Harian Metro, Berita Harian, Kosmo and etcs. Malaysia New Channel in Youtube and Dailymotion if have.\n\nJust give the link of the news without explanation.\nMake sure the published date of the news is not exceed 3 days from {{ $now }}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "c8c84420-881a-45a7-8194-fc77ba382ed7",
      "name": "Replace Me",
      "type": "n8n-nodes-base.noOp",
      "position": [
        2704,
        400
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "56fa7bbd-7a25-49fa-b971-2a93fc153739",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 320,
        "content": "## 1. Fetch weather warning from the official government API"
      },
      "typeVersion": 1
    },
    {
      "id": "808efdd5-636c-4983-926d-9fe21bfe61b3",
      "name": "Separate Search Query, Warning Type and other information",
      "type": "n8n-nodes-base.code",
      "position": [
        1168,
        352
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst searchQueries = [];\nconst locations = new Set();\nconst warningTypes = new Set();\n\nitems.forEach(item => {\n  const data = item.json;\n  \n  if (data.heading_en === \"No Advisory\") {\n    return;\n  }\n  \n  const warningType = data.heading_en;\n  warningTypes.add(warningType);\n  \n  const locationMatches = data.text_en.match(/(?:states of|over the waters of|waters of)\\s+([^.]+?)(?:until|;)/gi);\n  \n  if (locationMatches) {\n    locationMatches.forEach(match => {\n      const locationText = match.replace(/(?:states of|over the waters of|waters of)/gi, '').replace(/until|;/gi, '').trim();\n      const stateMatches = locationText.match(/([A-Z][a-z]+(?:\\s+[A-Z][a-z]+)*)/g);\n      \n      if (stateMatches) {\n        stateMatches.forEach(state => {\n          if (state.length > 2) {\n            locations.add(state.trim());\n          }\n        });\n      }\n    });\n  }\n  \n  const severity = data.heading_en.includes('Severe') ? 'severe' : \n                   data.heading_en.includes('Alert') ? 'alert' : '';\n  \n  const baseQuery = warningType.replace(/\\(.*?\\)/g, '').trim();\n  \n  searchQueries.push({\n    json: {\n      searchQuery: `${baseQuery} Malaysia news`,\n      warningType: warningType,\n      severity: severity,\n      locations: Array.from(locations).join(', '),\n      dateRange: 'past 24 hours'\n    }\n  });\n});\n\nconst uniqueQueries = [];\nconst seen = new Set();\n\nsearchQueries.forEach(query => {\n  const key = query.json.searchQuery;\n  if (!seen.has(key)) {\n    seen.add(key);\n    uniqueQueries.push(query);\n  }\n});\n\nreturn uniqueQueries.slice(0, 3);\n"
      },
      "typeVersion": 2
    },
    {
      "id": "5f5a4e6a-7848-4ba0-924c-8008e24fb3e1",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1104,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 320,
        "content": "## 2. Combine the Search Query and Location"
      },
      "typeVersion": 1
    },
    {
      "id": "025fb749-d3e6-4c96-a9a8-9e4052af681a",
      "name": "Combine Search and Location information",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        1360,
        352
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "searchQuery"
            },
            {
              "fieldToAggregate": "locations"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b1b1e555-8309-406c-9b9b-f0c723bb0c33",
      "name": "Clean the URLs",
      "type": "n8n-nodes-base.code",
      "position": [
        1936,
        352
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst urls = [];\n\nitems.forEach(item => {\n  const urlMatches = item.json.output?.match(/https?:\\/\\/[^\\s\\)]+/g);\n  if (urlMatches) {\n    urlMatches.forEach(url => {\n      const cleanUrl = url.replace(/[,.\\)]+$/, '');\n      if (!urls.includes(cleanUrl)) {\n        urls.push(cleanUrl);\n      }\n    });\n  }\n});\n\nreturn urls.map(url => ({ json: { url: url } }));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "54c82616-1aad-4b61-8e59-9593375cb727",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1600,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 528,
        "height": 464,
        "content": "## 3.Crawl the internet to search all possible related weather news"
      },
      "typeVersion": 1
    },
    {
      "id": "f2bf6a77-e17e-42a8-a7f9-3d329ae52aab",
      "name": "Scrape Website with Firecrawl",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2512,
        400
      ],
      "parameters": {
        "url": "https://api.firecrawl.dev/v1/scrape",
        "method": "POST",
        "options": {
          "timeout": 30000
        },
        "jsonBody": "={\n  \"url\": \"{{ $json.url }}\",\n  \"formats\": [\"summary\"],\n  \"onlyMainContent\": true\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer fc-YOUR-KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "8b01251a-e19e-4b0c-9de3-c108662cc027",
      "name": "Combine the Summary and Sources",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        2400,
        928
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "data.summary"
            },
            {
              "fieldToAggregate": "data.metadata.sourceURL"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b83ac285-6598-4ebc-bc28-14f3d513540a",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2224,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 864,
        "height": 464,
        "content": "## 4. Process the news and get the summary of the articles"
      },
      "typeVersion": 1
    },
    {
      "id": "43c81baf-f992-417e-8d9a-75363361a42d",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2240,
        816
      ],
      "parameters": {
        "color": 7,
        "width": 608,
        "height": 352,
        "content": "## 5. Refine the weather report and arrange the URL for the spesific report"
      },
      "typeVersion": 1
    },
    {
      "id": "c79c2410-e623-4b6f-beff-58cee259d97e",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2944,
        816
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 352,
        "content": "## 6.Send the report to the Telegram channel"
      },
      "typeVersion": 1
    },
    {
      "id": "2767e8e8-21ee-4c8f-8783-951da0743ef7",
      "name": "Interval to avoid Firecrawl Free API Limit",
      "type": "n8n-nodes-base.wait",
      "position": [
        2880,
        400
      ],
      "parameters": {
        "amount": 3
      },
      "typeVersion": 1.1
    },
    {
      "id": "b1c19a70-1fa6-4ec5-9868-181bab5cb859",
      "name": "Perplexity Sonar Pro Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        1664,
        544
      ],
      "parameters": {
        "model": "perplexity/sonar-pro-search",
        "options": {
          "temperature": 0.3
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0ccaafe4-737e-46a3-b738-bd8beab17a5f",
      "name": "Loop URL One by One",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        2320,
        400
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "fccdcec9-30db-4cdc-a8d6-18e778e403e6",
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Clean the URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Replace Me": {
      "main": [
        [
          {
            "node": "Interval to avoid Firecrawl Free API Limit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean the URLs": {
      "main": [
        [
          {
            "node": "Loop URL One by One",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Make a summary": {
      "main": [
        [
          {
            "node": "Send to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get Weather Warnings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop URL One by One": {
      "main": [
        [
          {
            "node": "Combine the Summary and Sources",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Scrape Website with Firecrawl",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Weather Warnings": {
      "main": [
        [
          {
            "node": "Separate Search Query, Warning Type and other information",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Perplexity Sonar Pro Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Website with Firecrawl": {
      "main": [
        [
          {
            "node": "Replace Me",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine the Summary and Sources": {
      "main": [
        [
          {
            "node": "Make a summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Search and Location information": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Interval to avoid Firecrawl Free API Limit": {
      "main": [
        [
          {
            "node": "Loop URL One by One",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Separate Search Query, Warning Type and other information": {
      "main": [
        [
          {
            "node": "Combine Search and Location information",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}