第 5 期:首个实战流 — Webhook 触发与 Telegram 机器人双向交互
本期目标
完成一个完整的实战工作流:用户在 Telegram 中发送一个城市名,n8n 自动调用天气 API 获取实时天气,然后将格式化后的结果回复给用户。
graph LR
User[👤 用户] -->|"发送: 上海"| TG[📱 Telegram]
TG -->|"Webhook 推送"| N8N[🤖 n8n Workflow]
N8N -->|"调用天气 API"| API[🌤️ OpenWeather API]
API -->|"返回 JSON"| N8N
N8N -->|"格式化回复"| TG
TG -->|"上海: 晴 28°C"| User
style N8N fill:#ff6d5b,stroke:#e55a4e,color:#fff准备工作
1. 创建 Telegram Bot
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 在 Telegram 中创建 Bot(通过 BotFather)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 第一步: 打开 Telegram,搜索 @BotFather
# 第二步: 发送 /newbot
# 第三步: 输入 Bot 名称,例如: "My n8n Weather Bot"
# 第四步: 输入 Bot 用户名(必须以 bot 结尾),例如: my_n8n_weather_bot
# 第五步: BotFather 返回 Bot Token:
# 例: 7123456789:AAHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# ⚠️ 这个 Token 就是 Bot 的"密码",务必保密!
2. 获取 OpenWeather API Key
# 访问 https://openweathermap.org/api 注册免费账号
# 免费版额度: 1000 次/天,足够学习使用
# 获取 API Key 后保存到 n8n 的 Credentials 中
工作流架构详解
整个工作流包含 5 个节点,数据按以下路径流转:
graph TB
subgraph "完整工作流拓扑"
T[🔔 Telegram Trigger
监听用户消息]
Extract[⚙️ Set 节点
提取城市名]
API[🌐 HTTP Request
调用天气 API]
Format[💻 Code 节点
格式化回复文案]
Reply[📤 Telegram 节点
发送回复]
T --> Extract --> API --> Format --> Reply
end
style T fill:#0088cc,stroke:#006699,color:#fff
style API fill:#f59e0b,stroke:#d97706,color:#fff
style Format fill:#22c55e,stroke:#16a34a,color:#fff
style Reply fill:#0088cc,stroke:#006699,color:#fff逐节点详细配置
节点 1: Telegram Trigger
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Telegram Trigger 节点配置
// 作用: 监听 Bot 收到的所有消息
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{
"node": "Telegram Trigger",
"parameters": {
"updates": ["message"] // 只监听普通文字消息 (忽略 callback_query 等)
},
"credentials": {
"telegramApi": {
"accessToken": "7123456789:AAHxxxxxxx" // 从 BotFather 获取的 Token
}
}
}
// 📤 该节点输出的 Item 结构:
// {
// "json": {
// "message": {
// "message_id": 42,
// "from": { "id": 123456, "first_name": "张三" },
// "chat": { "id": 123456, "type": "private" },
// "text": "上海" ← 用户发送的城市名
// }
// }
// }
节点 2: Set 节点 (提取城市名)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Set 节点: 从 Telegram 消息中提取关键字段
// 目的: 让下游节点只关注"干净"的数据,而非原始的 Telegram API 结构
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 在 Set 节点中设置以下字段:
// 字段名: city
// 值 (表达式): {{ $json.message.text }}
// ↑ 这是 n8n 表达式语法,$json 指向当前 Item 的 json 属性
// 从 Telegram 消息中提取用户发送的文本 (即城市名)
// 字段名: chatId
// 值 (表达式): {{ $json.message.chat.id }}
// ↑ 用于后续回复时指定目标聊天窗口
// 📤 输出 Item:
// { "json": { "city": "上海", "chatId": 123456 } }
节点 3: HTTP Request (调用天气 API)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// HTTP Request 节点配置: 调用 OpenWeather API
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{
"node": "HTTP Request",
"parameters": {
"method": "GET",
"url": "https://api.openweathermap.org/data/2.5/weather",
"queryParameters": {
"q": "={{ $json.city }}", // 动态引用上游的城市名
"appid": "your_api_key_here", // OpenWeather API Key
"units": "metric", // 使用摄氏度
"lang": "zh_cn" // 中文描述
},
"options": {
"timeout": 10000 // 超时 10 秒
}
}
}
// 📤 API 返回的 Item (已被 n8n 自动包装):
// {
// "json": {
// "name": "Shanghai", // 城市英文名
// "main": {
// "temp": 28.5, // 当前温度 (°C)
// "humidity": 65, // 湿度 (%)
// "feels_like": 31.2 // 体感温度
// },
// "weather": [
// { "description": "晴" } // 天气描述 (中文)
// ],
// "wind": { "speed": 3.5 } // 风速 (m/s)
// }
// }
节点 4: Code 节点 (格式化回复)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Code 节点: 将 API 原始数据转换为用户友好的回复文案
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 获取天气 API 的返回数据
const weather = $input.first().json;
// 获取上上游节点 (Set) 中保存的 chatId
// $('Set').first().json 可以跨节点引用数据!(n8n 的强大特性)
const chatId = $('Set').first().json.chatId;
// 构建格式化的回复消息
const message = [
`🌍 ${weather.name} 实时天气`, // 城市名
`━━━━━━━━━━━━━━━━`,
`🌡️ 温度: ${Math.round(weather.main.temp)}°C`, // 四舍五入温度
`🤒 体感: ${Math.round(weather.main.feels_like)}°C`,
`☁️ 天气: ${weather.weather[0].description}`, // 天气描述
`💧 湿度: ${weather.main.humidity}%`,
`🌬️ 风速: ${weather.wind.speed} m/s`,
`━━━━━━━━━━━━━━━━`,
`⏰ 查询时间: ${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}`
].join('\n'); // 用换行符连接每一行
// 返回格式化后的 Item
return [{
json: {
chatId: chatId, // Telegram 聊天 ID (回复目标)
text: message // 格式化后的文本
}
}];
节点 5: Telegram 发送回复
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Telegram 节点: 将处理结果回复给用户
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{
"node": "Telegram",
"parameters": {
"operation": "sendMessage",
"chatId": "={{ $json.chatId }}", // 动态引用: 回复给发消息的那个人
"text": "={{ $json.text }}", // 动态引用: 格式化后的天气信息
"additionalFields": {
"parse_mode": "Markdown" // 支持 Markdown 粗体、斜体等
}
}
}
完整数据流时序图
sequenceDiagram
participant User as 👤 张三 (Telegram)
participant Bot as 🤖 Telegram Bot
participant Trigger as 🔔 Telegram Trigger
participant Set as ⚙️ Set 节点
participant HTTP as 🌐 HTTP Request
participant Code as 💻 Code 节点
participant Reply as 📤 Telegram Send
User->>Bot: 发送 "上海"
Bot->>Trigger: 推送 Webhook 消息
Note over Trigger: 产出 Item:
{message: {text: "上海", chat: {id: 123}}}
Trigger->>Set: 传递 Item
Note over Set: 提取: city="上海", chatId=123
Set->>HTTP: 传递 {city: "上海"}
Note over HTTP: GET openweathermap.org?q=上海
HTTP->>Code: 传递天气 JSON
Note over Code: 格式化为用户友好文本
Code->>Reply: 传递 {chatId: 123, text: "🌍上海...28°C"}
Reply->>Bot: 调用 Telegram API
Bot->>User: "🌍 上海 实时天气
🌡️ 温度: 28°C
☁️ 天气: 晴"错误处理增强 (进阶)
graph TB
T[Telegram Trigger] --> Validate{城市名是否为空?}
Validate -->|"空消息"| ErrReply1[回复: 请输入城市名称]
Validate -->|"有内容"| API[HTTP Request]
API --> Check{HTTP 状态码?}
Check -->|"200 成功"| Format[Code: 格式化]
Check -->|"404 城市不存在"| ErrReply2[回复: 未找到该城市]
Check -->|"其他错误"| ErrReply3[回复: 服务暂时不可用]
Format --> Reply[Telegram 回复]
style Validate fill:#f59e0b,stroke:#d97706
style Check fill:#f59e0b,stroke:#d97706// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 增强版 Code 节点: 加入错误处理
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const weatherData = $input.first().json;
const chatId = $('Set').first().json.chatId;
const city = $('Set').first().json.city;
// 检查 API 是否返回了有效数据
if (!weatherData.main) {
// API 返回了错误 (如: 城市名拼写错误)
return [{
json: {
chatId: chatId,
text: `❌ 抱歉,未能找到城市 "${city}" 的天气信息。\n请检查城市名称是否正确。`
}
}];
}
// 正常流程: 格式化天气数据
const message = [
`🌍 ${weatherData.name} 实时天气`,
`🌡️ ${Math.round(weatherData.main.temp)}°C | ${weatherData.weather[0].description}`,
`💧 湿度 ${weatherData.main.humidity}%`
].join('\n');
return [{ json: { chatId, text: message } }];
激活工作流
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 工作流构建完成后,需要激活才能在生产环境中运行
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 在 n8n 编辑器中:
# 1. 点击右上角的 "Inactive" 开关,切换为 "Active"
# 2. 此时 Telegram Trigger 会开始监听消息
# 3. 在 Telegram 中给你的 Bot 发送任意城市名测试
# 如果使用 n8n CLI 激活:
# n8n workflow:activate --id=1
本期小结
mindmap
root((Ep 05 知识图谱))
Telegram Bot 创建
BotFather
Access Token
Webhook 触发
自动注册 Webhook URL
消息结构解析
数据提取
表达式语法 {{ $json.xxx }}
跨节点引用 $('NodeName')
HTTP Request
GET 请求
查询参数 (动态)
超时设置
格式化输出
字符串模板
错误处理
Telegram 回复
sendMessage
动态 chatId下一步
恭喜你完成了模块一的全部 5 期!你已经掌握了 n8n 的底层概念与第一个可用的实战工作流。在模块二中,我们将深入 If/Switch 条件分支、Data Tables 持久化存储 等高级数据操控技巧。