8
n8n 中文网amn8n.com

行程天气自动预报

高级

这是一个Personal Productivity领域的自动化工作流,包含 17 个节点。主要使用 If, Code, Wait, Telegram, HttpRequest 等节点。 从Google Calendar自动推送行程天气预报到Telegram

前置要求
  • Telegram Bot Token
  • 可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "meta": {
    "instanceId": "e0abec5d9b13345e424c0dbbc512271f89fb17de3b268697e13a797de2271404"
  },
  "nodes": [
    {
      "id": "7df92b27-05c7-4c4a-9b2f-76883f0d8974",
      "name": "条件判断1",
      "type": "n8n-nodes-base.if",
      "position": [
        624,
        -64
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "8e217cc9-d842-4b92-a62b-b79e99a059df",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.sequence }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "40fe3671-d5ed-49fd-a342-f89df42f5657",
      "name": "识别行程",
      "type": "n8n-nodes-base.code",
      "position": [
        400,
        -64
      ],
      "parameters": {
        "jsCode": "const travelKeywords = ['trip', 'travel', 'vacation', 'flight', 'holiday'];\n\nreturn items.filter(item => {\n  const title = (item.json.summary || '').toLowerCase();\n  const description = (item.json.description || '').toLowerCase();\n\n  return travelKeywords.some(keyword => title.includes(keyword) || description.includes(keyword));\n});\n"
      },
      "typeVersion": 2
    },
    {
      "id": "41df49be-15c4-4c83-8c69-6c8cc109ac34",
      "name": "提取位置",
      "type": "n8n-nodes-base.code",
      "position": [
        848,
        -64
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  const startDate = item.json.start.dateTime || item.json.start.date;\n  const endDate = item.json.end.dateTime || item.json.end.date;\n\n  // Initialize location variable\n  let location = null;\n\n  // Extract destination from summary first (e.g. \"Flight to Istanbul\")\n  const summary = (item.json.summary || '').toLowerCase();\n  const matchSummary = summary.match(/to ([a-zA-Z\\s,]+)/);\n  if (matchSummary && matchSummary[1]) {\n    location = matchSummary[1].trim();\n  }\n\n  // If no destination yet, try description (e.g. \"Flight from Mauritius (port Louis) to Istanbul\")\n  if (!location) {\n    const description = (item.json.description || '').toLowerCase();\n    // Match pattern after \"to \"\n    const matchDescription = description.match(/to ([a-zA-Z\\s,]+)/);\n    if (matchDescription && matchDescription[1]) {\n      location = matchDescription[1].trim();\n    }\n  }\n\n  // Only use the location field if above patterns fail or if location is not a \"from\" location\n  if (!location && item.json.location) {\n    const locLower = item.json.location.toLowerCase();\n    if (!locLower.startsWith('from ')) {  // Avoid departure locations starting with \"from\"\n      location = item.json.location;\n    }\n  }\n\n  // Final fallback if no location found\n  if (!location) {\n    location = null; // or 'YOUR_DEFAULT_TRIP_LOCATION'\n  }\n\n  return {\n    json: {\n      startDate,\n      endDate,\n      location\n    }\n  };\n});\n"
      },
      "typeVersion": 2
    },
    {
      "id": "1c3d2277-df1f-4178-bdba-bff40a182c2d",
      "name": "等待",
      "type": "n8n-nodes-base.wait",
      "position": [
        1104,
        320
      ],
      "webhookId": "f1f55baf-71bd-4fc9-b81d-b3f0b8092d03",
      "parameters": {
        "resume": "specificTime",
        "dateTime": "={{ new Date(new Date($('Extract locations').item.json.startDate).getTime() - 24 * 60 * 60 * 1000).toISOString() }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "0d3f1ec2-2377-46e3-84bc-c303adddb825",
      "name": "便签4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -448,
        -400
      ],
      "parameters": {
        "width": 560,
        "height": 752,
        "content": "## 工作原理"
      },
      "typeVersion": 1
    },
    {
      "id": "79b81985-724d-4208-a225-25ecde0851e7",
      "name": "事件已创建",
      "type": "n8n-nodes-base.googleCalendarTrigger",
      "position": [
        176,
        -160
      ],
      "parameters": {
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "triggerOn": "eventCreated",
        "calendarId": {
          "__rl": true,
          "mode": "list",
          "value": "bara.razvan@gmail.com",
          "cachedResultName": "bara.razvan@gmail.com"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "38413099-7d19-4c06-bec7-a2d542607474",
      "name": "事件已更新",
      "type": "n8n-nodes-base.googleCalendarTrigger",
      "position": [
        176,
        32
      ],
      "parameters": {
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "triggerOn": "eventUpdated",
        "calendarId": {
          "__rl": true,
          "mode": "list",
          "value": "bara.razvan@gmail.com",
          "cachedResultName": "bara.razvan@gmail.com"
        }
      },
      "credentials": {},
      "typeVersion": 1
    },
    {
      "id": "1cac0052-349b-4ae2-8c4e-b1a63bd834f4",
      "name": "发送预报",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1808,
        -64
      ],
      "webhookId": "7eb54f22-cbed-498d-b1b7-ccabe38b4a06",
      "parameters": {
        "operation": "send"
      },
      "notesInFlow": [
        {
          "note": "Sends the trip weather forecast summary to user via Telegram."
        }
      ],
      "typeVersion": 1
    },
    {
      "id": "bb15bccd-f857-4de6-907f-7a48457a125f",
      "name": "构建查询 URL",
      "type": "n8n-nodes-base.code",
      "position": [
        1072,
        -64
      ],
      "parameters": {
        "jsCode": "const location = encodeURIComponent($json.location);\nconst startDate = $json.startDate.split('T')[0]; // date only\nconst endDate = $json.endDate.split('T')[0]; // date only\nconst apiKey = '[your API]';\n\nconst url = `https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/${location}/${startDate}/${endDate}?unitGroup=metric&key=${apiKey}&include=days,alerts`;\n\nreturn [{ json: { url } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e29e2ff1-46fd-44c5-ab8f-02d345586e41",
      "name": "获取更新的目的地天气预报",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "One day before the trip",
      "position": [
        1328,
        320
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {}
      },
      "notesInFlow": [
        {
          "note": "Fetches weather forecast and alerts for trip location and dates from Visual Crossing."
        }
      ],
      "typeVersion": 1
    },
    {
      "id": "c69a55de-bbf3-4921-bd89-24d4b8826111",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        368,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 608,
        "height": 240,
        "content": "## 识别"
      },
      "typeVersion": 1
    },
    {
      "id": "50def3d9-67bc-4f5c-b2a4-18f4235fffec",
      "name": "便签8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        208
      ],
      "parameters": {
        "color": 6,
        "width": 448,
        "height": 288,
        "content": "## 在行程前一天更新预报"
      },
      "typeVersion": 1
    },
    {
      "id": "8aec9d53-2af5-4e3f-87a2-f0f67a2497e3",
      "name": "便签6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1008,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 672,
        "content": "## 获取预报"
      },
      "typeVersion": 1
    },
    {
      "id": "57b375da-2354-4b04-a6ee-b510bed576d0",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1536,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 256,
        "content": "## 发送通知"
      },
      "typeVersion": 1
    },
    {
      "id": "b4b8e95d-3ea8-4ea2-ab88-a889a74d6818",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        -272
      ],
      "parameters": {
        "color": 7,
        "width": 192,
        "height": 464,
        "content": "## 触发器:"
      },
      "typeVersion": 1
    },
    {
      "id": "5c65c141-7527-43b9-8e85-bd8f1291b54e",
      "name": "获取目的地天气预报",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "At the event creation/update",
      "position": [
        1296,
        -64
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {}
      },
      "notesInFlow": [
        {
          "note": "Fetches weather forecast and alerts for trip location and dates from Visual Crossing."
        }
      ],
      "typeVersion": 1
    },
    {
      "id": "b92af965-eecf-4019-b5cf-ca38e8ee9928",
      "name": "格式化消息",
      "type": "n8n-nodes-base.code",
      "position": [
        1584,
        -64
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  let forecastSummary = `Weather forecast for your trip to ${item.json.address || 'destination'} from ${item.json.days[0].datetime} to ${item.json.days[item.json.days.length - 1].datetime}:\\n`;\n\n  item.json.days.forEach(day => {\n    const precipprob = day.precipprob !== undefined ? `, Precip Prob: ${day.precipprob}%` : '';\n    const preciptype = day.preciptype ? `, Precip Type: ${day.preciptype.join(\", \")}` : '';\n    const conditions = day.conditions ? `, Conditions: ${day.conditions}` : '';\n    const description = day.description ? `,  ${day.description}` : '';\n    forecastSummary += `${day.datetime}: Max ${day.tempmax}°C, Min ${day.tempmin}°C${precipprob}${preciptype}${conditions}${description}\\n`;\n  });\n\n  if (item.json.alerts && item.json.alerts.length > 0) {\n    forecastSummary += `\\nALERTS:\\n`;\n    item.json.alerts.forEach(alert => {\n      forecastSummary += `${alert.description}\\n`;\n    });\n  }\n\n  return {\n    json: { forecastSummary }\n  };\n});\n"
      },
      "typeVersion": 2
    }
  ],
  "pinData": {},
  "connections": {
    "If1": {
      "main": [
        [
          {
            "node": "Extract locations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Get updated Destination Weather Forecast",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Event created": {
      "main": [
        [
          {
            "node": "Identify trips",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Event updated": {
      "main": [
        [
          {
            "node": "Identify trips",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format message": {
      "main": [
        [
          {
            "node": "Send Forecast",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Identify trips": {
      "main": [
        [
          {
            "node": "If1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract locations": {
      "main": [
        [
          {
            "node": "Build interogation URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build interogation URL": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Destination Weather forecast",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Destination Weather forecast": {
      "main": [
        [
          {
            "node": "Format message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get updated Destination Weather Forecast": {
      "main": [
        [
          {
            "node": "Format message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 个人效率

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量17
分类1
节点类型7
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者
Razvan Bara

Razvan Bara

@amzneer

Whether you're launching your first product or scaling, I'll help you build an AI-powered, data-driven system that runs your business while you focus on strategic growth. Let's discuss how strategic optimization combined with business intelligence and custom AI agents can transform your operations and accelerate your success. Ready to scale smarter with AI and data? Let's talk.

外部链接
在 n8n.io 查看

分享此工作流