第 5 期:首个实战流 — Webhook 触发与 Telegram 机器人双向交互

⏱ 预计阅读 17 分钟 更新于 2026/4/10

本期目标

完成一个完整的实战工作流:用户在 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 持久化存储 等高级数据操控技巧。