用n8n+JavaScript打造智能天气机器人:从钉钉通知到异常预警全实现
用n8n+JavaScript打造智能天气机器人:从钉钉通知到异常预警全实现
最近在折腾一个有意思的项目:如何让团队每天早上不再被动地查看手机上的天气App,而是让天气信息主动、智能地“找上门”来,并且在出现恶劣天气时能第一时间发出预警。听起来像是需要写一堆后端服务和定时任务?其实,借助一个强大的开源工具,再配合一些JavaScript脚本,我们完全可以像搭积木一样,构建出一个既稳定又灵活的智能天气机器人。这个工具就是n8n。
对于需要将多个系统串联起来、实现自动化流程的中级开发者而言,n8n提供了一个绝佳的画布。它远不止是一个简单的“如果-那么”触发器。通过其内置的Function节点,我们可以注入自定义的JavaScript逻辑,处理复杂的API响应、设计健壮的错误重试机制,甚至实现简单的决策树。本文将带你从零开始,实现一个超越基础通知的工程化天气机器人。我们将重点关注如何在真实、不完美的网络环境和API服务下,构建一个可靠、可维护的自动化解决方案,最终将信息精准推送到钉钉工作群。
1. 项目蓝图与核心架构设计
在动手拖拽节点之前,花点时间思考整体架构是避免后期混乱的关键。一个健壮的智能天气机器人,不应该只是一个简单的“定时获取-发送”管道。我们需要考虑以下几个核心问题:
- 数据源稳定性:免费的天气API可能有调用频率限制,偶尔会超时或返回错误格式的数据。
- 信息价值密度:发送“上海,晴,25℃”是基础功能。我们能否附加穿衣建议、紫外线指数、未来几小时降水概率?
- 异常处理与预警:当气温超过35℃、或预报有暴雨、大风时,系统能否立即触发告警,而不是等到下一次定时任务?
- 流程可观测性:工作流运行失败时,我们如何快速定位是API问题、网络问题,还是消息格式问题?
基于这些思考,我设计了如下核心架构流程:
[定时触发器] -> [获取天气API数据] -> [数据清洗与校验] -> [核心逻辑判断]
| |
| v
| [是异常天气?] -> [是] -> [发送预警通知]
| |
| + -> [否] -> [生成日常简报]
| |
+------------------------------------------------------> [发送至钉钉]
这个流程的骨架在n8n中可以通过节点连线直观呈现。但其中的血肉——数据校验、逻辑判断、消息生成——则需要我们使用JavaScript来填充。接下来,我们将进入n8n的实际操作界面,从搭建基础工作流开始。
2. 搭建基础工作流与Function节点初探
首先,确保你有一个运行中的n8n实例。你可以通过Docker快速部署:
docker run -it --rm \
--name n8n \
-p 5678:5678 \
-v ~/.n8n:/home/node/.n8n \
n8nio/n8n
访问 http://localhost:5678 即可进入控制台。我们创建一个新的工作流。
2.1 设置可靠的触发器
第一个节点我们选择 Schedule Trigger。这是工作流的心脏起搏器。对于日常天气简报,我们可以设置为每天早晨7点触发。但为了测试方便,可以先设置为每分钟触发一次。
在节点的配置中,关键字段是 Rule。使用Cron表达式来定义时间规则。例如:
0 7 * * *表示每天UTC时间7点整运行。注意时区问题,你可以在n8n的设置或节点高级选项中调整时区。*/5 * * * *表示每5分钟运行一次,适合调试。
提示:在生产环境中,建议触发频率不要高于天气API提供商的免费套餐限制,通常每小时1-2次是安全的。
2.2 获取天气数据并处理不确定性
接下来,添加一个 HTTP Request 节点。这里我们以和风天气(HeWeather,需注册获取Key)或Open-Meteo(免费无需Key)为例。
关键配置步骤:
- URL:填入API端点,例如
https://api.open-meteo.com/v1/forecast。 - Method:选择
GET。 - Query Parameters:这是传递参数的地方。我们需要添加:
latitude: 纬度(如上海的31.23)longitude: 经度(如上海的121.47)current_weather:true(获取当前天气)hourly:temperature_2m,precipitation_probability(获取未来小时数据)timezone:auto
- Authentication:选择
Generic Credential,在Type中选择Query Parameter,Name填apikey(如果使用需要Key的API)。
点击 Execute Node 进行测试。你应该能看到一个包含当前天气和未来预报的JSON响应。
然而,网络世界从不完美。这个HTTP节点可能会失败。因此,我们需要配置错误处理机制。在HTTP Request节点的设置中,找到 Error Handling 部分:
- Retry On Fail: 勾选。设置
Max Attempts为3,Timeout为5000毫秒。这样,在第一次请求失败后,n8n会自动重试最多3次。 - Continue On Fail: 通常不勾选,因为我们希望流程在获取数据失败时停止,而不是传递错误数据下去。
但这还不够。有时API会返回HTTP 200成功状态码,但内容却是错误信息(如 {“code”:”500”, “msg”:”Internal Error”})。这时,我们需要下一个节点——Function节点来进行数据清洗和校验。
2.3 使用JavaScript进行数据校验与格式化
添加一个 Function 节点。这个节点允许我们编写自定义JavaScript代码来处理上游节点传来的数据。
假设上游HTTP节点返回的数据结构如下:
{
"current_weather": {
"temperature": 22.5,
"windspeed": 10.3,
"weathercode": 3
},
"hourly": {
"time": ["2023-10-01T00:00", ...],
"temperature_2m": [22.1, 21.8, ...],
"precipitation_probability": [10, 5, ...]
}
}
我们的Function节点代码需要做以下几件事:
// 从上一个节点获取数据项(items)
const inputData = items[0].json;
// 1. 基础校验:检查必需字段是否存在
if (!inputData.current_weather || !inputData.hourly) {
// 如果数据不完整,抛出一个错误,这将导致工作流在此失败并记录日志
throw new Error('从天气API获取的数据结构不完整,缺少关键字段。');
}
const current = inputData.current_weather;
const hourly = inputData.hourly;
// 2. 数据增强:将天气代码转换为可读描述
const weatherCodeMap = {
0: '晴朗',
1: '大部晴朗',
2: '局部多云',
3: '阴天',
// ... 可根据API文档补充更多
};
const weatherDescription = weatherCodeMap[current.weathercode] || `未知代码(${current.weathercode})`;
// 3. 提取关键预警信息:计算未来6小时内最高降水概率
const nextSixHoursPrecip = hourly.precipitation_probability.slice(0, 6);
const maxPrecipProb = Math.max(...nextSixHoursPrecip);
// 4. 判断是否需要预警(逻辑可以根据需求调整)
const isHighTempAlert = current.temperature > 35; // 高温预警
const isRainAlert = maxPrecipProb > 70; // 高降水概率预警
// 5. 构建一个包含所有加工后数据的对象,传递给下游节点
const enrichedWeatherData = {
location: '上海',
currentTemp: current.temperature,
currentWindSpeed: current.windspeed,
currentWeather: weatherDescription,
maxPrecipitationProbNext6h: maxPrecipProb,
alerts: {
highTemperature: isHighTempAlert,
heavyRain: isRainAlert
},
rawHourlyData: hourly // 保留原始数据,供后续节点可能使用
};
// 将处理后的数据作为新项返回
return [{
json: enrichedWeatherData
}];
这段代码是工作流的“大脑”。它不仅仅传递数据,还进行了校验、转换和初步的逻辑判断,输出了一个结构清晰、富含业务语义的对象。下游节点将基于这个对象来决定是发送日常简报还是紧急预警。
3. 实现条件分支与智能消息路由
经过Function节点的处理,我们的数据已经包含了预警标志 (alerts.highTemperature 和 alerts.heavyRain)。现在,我们需要根据这些标志来决定工作流的走向。这里会用到 IF 节点。
添加一个 IF 节点。它的配置是基于表达式(Expression)进行条件判断。
- 条件设置:选择
Add Condition->Expression。 - 表达式:这里我们需要判断是否有任何预警被触发。表达式可以写为:
{{ $json.alerts.highTemperature || $json.alerts.heavyRain }}这个表达式会检查alerts对象下的两个布尔值,只要有一个为true,整个表达式结果即为true。
IF节点会根据表达式结果,将数据流导向两个不同的分支:true(有预警)和 false(无预警,日常情况)。
3.1 预警分支:构建紧急通知消息
如果进入 true 分支,意味着出现了需要立即关注的天气情况。我们添加一个 Function 节点来专门生成预警消息。
const data = items[0].json;
const alertMessages = [];
if (data.alerts.highTemperature) {
alertMessages.push(`🔥 **高温预警**:当前气温 ${data.currentTemp}°C,请注意防暑降温,避免户外长时间活动。`);
}
if (data.alerts.heavyRain) {
alertMessages.push(`🌧️ **强降水预警**:未来6小时内降水概率高达 ${data.maxPrecipitationProbNext6h}%,建议携带雨具,注意出行安全。`);
}
const finalAlertMessage = `## ⚠️ 天气异常预警通知\n\n` +
`**地点**:${data.location}\n` +
`**时间**:${new Date().toLocaleString('zh-CN')}\n` +
`**当前天气**:${data.currentWeather},温度 ${data.currentTemp}°C,风速 ${data.currentWindSpeed} km/h\n\n` +
`**预警详情**:\n` +
alertMessages.join('\n') + `\n\n请相关同事注意防范。`;
// 输出一个专门用于预警的消息对象
return [{
json: {
dingtalkMessage: {
msgtype: 'markdown',
markdown: {
title: '天气异常预警',
text: finalAlertMessage
},
at: {
isAtAll: true // @所有人,可根据需要调整
}
},
isAlert: true // 打上标记,便于后续可能的统一处理
}
}];
3.2 日常分支:生成友好天气简报
如果进入 false 分支,则是风平浪静的日常。我们添加另一个 Function 节点来生成更友好、信息丰富的简报。
const data = items[0].json;
// 从原始数据中提取未来几小时温度趋势
const hourlyTemps = data.rawHourlyData.temperature_2m.slice(0, 12); // 取未来12小时
const tempTrend = hourlyTemps.map((t, i) => `${i+1}时后: ${t}°C`).join(' | ');
// 生成一些趣味建议
let suggestion = '';
if (data.currentTemp > 28) {
suggestion = '天气较热,建议穿着短袖、薄裙等清凉夏季服装。';
} else if (data.currentTemp < 10) {
suggestion = '天气较凉,建议穿外套、毛衣等春秋过渡装。';
} else {
suggestion = '气温适宜,可穿着衬衫、薄外套等舒适衣物。';
}
const dailyMessage = `## 🌤️ 早安天气简报\n\n` +
`**${data.location}** · ${new Date().toLocaleDateString('zh-CN', { weekday: 'long', month: 'long', day: 'numeric' })}\n\n` +
`**现在**:${data.currentWeather},${data.currentTemp}°C,风速 ${data.currentWindSpeed} km/h\n` +
`**体感**:${suggestion}\n\n` +
`**今日趋势**:${tempTrend}\n\n` +
`祝您有愉快的一天!☕`;
return [{
json: {
dingtalkMessage: {
msgtype: 'markdown',
markdown: {
title: '每日天气',
text: dailyMessage
}
// 日常通知不需要@所有人
},
isAlert: false
}
}];
至此,我们有了两条并行的消息生产流水线,它们产出结构相似但内容不同的消息对象。接下来,需要将它们汇聚并发送出去。
4. 连接钉钉与消息发送优化
无论来自哪个分支,消息最终都要发送到钉钉群。我们使用 DingTalk 节点(如果官方节点库没有,可以使用 Webhook 节点模拟)。这里以Webhook节点为例,因为它更通用。
4.1 配置钉钉机器人Webhook
首先,在钉钉群中添加一个自定义机器人,获取其Webhook地址。格式类似:https://oapi.dingtalk.com/robot/send?access_token=XXXXXX。
在n8n中添加一个 HTTP Request 节点,用于发送消息。
- URL:填入你的钉钉机器人Webhook地址。
- Method:
POST - Headers:添加
Content-Type: application/json - Body:选择
JSON,然后填入表达式{{ $json.dingtalkMessage }}。这样,无论是预警消息还是日常简报,其dingtalkMessage属性都会被作为请求体发送。
4.2 处理发送失败与重试
消息发送也可能失败(网络波动、钉钉限流等)。除了在HTTP Request节点设置重试,我们还可以增加一个“守门员”节点来确保关键预警不丢失。
在预警分支的Function节点和发送节点之间,可以插入一个 Wait 节点(设置等待几秒)和一个 IF 节点进行简易的“发送状态检查”(这需要更复杂的设计,例如将发送状态写入临时存储)。一个更简单的工程化实践是:利用n8n的错误触发机制。
你可以创建两个独立的工作流:
- 主工作流:负责获取、处理、判断天气。如果判断为预警,它不仅生成消息,还通过 Webhook 节点触发第二个工作流。
- 预警专用工作流:由Webhook触发,专门负责发送预警消息,并配置更激进的重试策略(如重试5次,每次间隔1分钟)。这样可以将预警发送的逻辑与主业务逻辑解耦,提高可靠性。
4.3 消息格式与@功能优化
钉钉Markdown支持有限但有用的语法。我们已经在上面的Function节点中使用了Markdown格式。对于@功能:
at.isAtAll: true会@所有人,慎用。- 可以指定具体手机号:
at.atMobiles: ["138xxxx8888"]。你可以尝试从n8n的数据库或其他服务中动态获取需要被@的成员列表,注入到消息对象中。
一个更清晰的发送节点配置参数表示例如下:
| 参数项 | 值/表达式 | 说明 |
|---|---|---|
| URL | https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN | 钉钉机器人Webhook |
| Method | POST | |
| Header | Content-Type: application/json | |
| Body | {{ $json.dingtalkMessage }} | 直接使用上游构建好的消息对象 |
| Options | Ignore SSL Issues | 根据你的证书情况可选 |
| Error Handling | Retry On Fail (Max: 3) | 配置发送失败重试 |
5. 工程化扩展:监控、日志与故障排查
一个投入生产使用的自动化流程,必须具备可观测性。n8n在这方面提供了良好的内置支持。
5.1 利用执行历史(Executions)进行调试
工作流每次运行都会在 Executions 页面留下完整记录。你可以看到:
- 每个节点的输入和输出数据。
- 每个节点的执行状态(成功、失败、错误信息)。
- 整个工作流的执行时间线。
这是排查问题的第一现场。例如,如果发送到钉钉失败,你可以查看HTTP Request节点的输出,确认是网络错误、认证错误还是消息格式错误。
5.2 添加日志节点(Debug Node)
在关键节点之后,特别是Function节点之后,可以添加 Debug 节点。它不会影响数据流,但会将当前节点的输入数据完整地输出到执行日志中。这对于开发阶段验证数据处理逻辑是否正确极其有用。
5.3 实现简单的状态监控
你可以创建一个辅助工作流,定期检查主天气机器人的“心跳”。例如:
- 在主工作流的最后,添加一个 HTTP Request 节点,调用一个可以记录最后成功运行时间的服务(甚至可以是一个简单的Google Sheet或Airtable)。
- 创建另一个监控工作流,定时(比如每小时)检查这个“最后成功时间”。如果超过24小时没有更新,则通过钉钉或其他渠道发送告警:“天气机器人可能已停止工作”。
5.4 凭证(Credentials)的安全管理
在HTTP Request节点中使用的API Key,务必通过n8n的 Credentials 功能来管理。不要将密钥硬编码在节点参数中。在Credential中选择“Create New”,然后选择对应的认证类型(如Query Auth),将密钥填入并命名保存。之后在所有需要该API的工作流中,都可以安全地引用这个凭证名称。这样既方便轮换密钥,也避免了密钥泄露在配置中。
整个项目搭建下来,你会发现,n8n配合JavaScript提供的灵活性,足以应对许多中小型自动化场景。它降低了集成多个服务的门槛,但又通过代码节点保留了处理复杂逻辑的能力。这个天气机器人只是一个起点,你可以基于此模式,轻松扩展出监控服务器状态、同步跨平台数据、处理表单审批等各类自动化流程,真正把重复性的数字劳动交给机器。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)