Serverless Framework 实战:在 AWS Lambda 上部署 Python Telegram 回声机器人(Echo Bot)
Serverless Framework 实战:在 AWS Lambda 上部署 Python Telegram 回声机器人(Echo Bot)
本指南基于仓库中的 aws-python-telegram-bot 示例,完整讲解如何用 Serverless Framework 将基于 python-telegram-bot v21+ 的异步 Telegram 回声机器人部署到 AWS Lambda + API Gateway。读完本文,你将掌握 Telegram Bot 的 Webhook 接入原理、异步 Bot API 与同步 Lambda 处理器的桥接方式,并能从零开始完成「申请 Token → 部署 → 配置 Webhook → 对话验证」的完整闭环。
示例概览:一个基于 Webhook 的异步回声机器人
该示例实现的是一个经典 Telegram「回声机器人」(echo bot):用户向机器人发送任意文本,机器人把同样的文本原样回复给用户;当收到 /start 命令时,则返回一段定制的欢迎语。整个服务按 Serverless 方式构建,运行在 AWS Lambda 上,由 API Gateway 通过 Webhook 模式把 Telegram 的更新事件推送给 Lambda 处理,无需常驻进程、无需固定公网 IP。
示例有两个关键设计点:
- 完全异步的 SDK:依赖 requirements.txt 中声明的
python-telegram-bot>=21。该版本 SDK 全面拥抱async,Bot 实例需作为异步上下文管理器使用,所有 Bot API 调用都需要await。 - 异步与同步的桥接:Lambda 的处理器入口是普通同步函数,示例通过在处理器内部调用
asyncio.run(...)来驱动异步的 Bot API 调用,从而把两套编程模型优雅地衔接起来。
项目结构一览
整个示例文件极少、结构清晰,适合作为学习 Telegram Serverless 集成的起点:
| 文件 | 作用 |
|---|---|
| handler.py | Lambda 处理器,包含 Webhook 接收、消息回显、Webhook 配置三个核心逻辑 |
| serverless.yml | Serverless Framework 服务定义:函数、事件、运行时、环境变量 |
| requirements.txt | Python 依赖声明:python-telegram-bot>=21 |
| package.json | npm 元数据(当前未声明运行时依赖) |
| README.md | 官方使用说明(本文的依据文档) |
| LICENSE | MIT 许可证 |
该示例在仓库中的定位是 Python 语言族的一个「即时可用(ready-to-deploy)」模板,其 README 头部的 frontmatter 注释(framework: v4、platform: AWS、language: Python)会被仓库的文档生成脚本自动读取,用于在根目录 README.md 的示例清单中登记。
核心代码解析:handler.py
整个服务的业务逻辑都集中在 handler.py 中,可以拆解为五个职责明确的函数。
1. 日志初始化与统一响应结构
import asyncio
import json
import logging
import os
import telegram
logger = logging.getLogger()
if logger.handlers:
for handler in logger.handlers:
logger.removeHandler(handler)
logging.basicConfig(level=logging.INFO)
OK_RESPONSE = {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps('ok')
}
ERROR_RESPONSE = {
'statusCode': 400,
'body': json.dumps('Oops, something went wrong!')
}
代码先重置 Logger 的已有 handler 并设置 INFO 级别,保证 Lambda 的 CloudWatch 日志清晰可读;随后定义了两个统一的 API Gateway 响应结构:OK_RESPONSE(200,Content-Type: application/json,body 为 ok)和 ERROR_RESPONSE(400,body 为错误提示)。后续所有分支都复用这两个常量,让返回路径保持简洁一致。
2. 读取 Token:get_telegram_token
def get_telegram_token():
telegram_token = os.environ.get('TELEGRAM_TOKEN')
if not telegram_token:
logger.error('The TELEGRAM_TOKEN must be set')
raise NotImplementedError
return telegram_token
Token 通过环境变量 TELEGRAM_TOKEN 注入(具体注入方式见下文 serverless.yml 的 environment 配置)。函数在 Token 缺失时记录错误日志并抛出 NotImplementedError,属于快速失败(fail-fast)策略,避免带着空 Token 继续请求 Telegram Bot API。
3. 异步发送消息与设置 Webhook
async def send_message(chat_id, text):
async with telegram.Bot(get_telegram_token()) as bot:
await bot.send_message(chat_id=chat_id, text=text)
async def configure_webhook(url):
async with telegram.Bot(get_telegram_token()) as bot:
return await bot.set_webhook(url)
这是示例中最能体现 python-telegram-bot v21+ 异步特性的部分:
telegram.Bot(token)被用作异步上下文管理器(async with),其职责是自动完成底层 HTTP 会话的打开与关闭;- 每一次 Bot API 调用(
send_message、set_webhook)都必须显式await; - 两个函数都是短生命周期设计:每次调用创建一个临时 Bot 实例,用后即弃,不需要在 Lambda 全局维护长连接(从源码结构看,这也意味着每次调用都会重建 HTTP 会话,对冷启动延迟有一定影响——这是示例为了简洁所做的取舍,生产环境可考虑在全局复用 Bot 实例)。
4. 入口一:webhook —— 接收并回显消息
def webhook(event, context):
logger.info('Event: {}'.format(event))
if event.get('httpMethod') == 'POST' and event.get('body'):
logger.info('Message received')
update = telegram.Update.de_json(json.loads(event.get('body')), None)
chat_id = update.message.chat.id
text = update.message.text
if text == '/start':
text = """Hello, human! ..."""
asyncio.run(send_message(chat_id, text))
logger.info('Message sent')
return OK_RESPONSE
return ERROR_RESPONSE
webhook 对应 serverless.yml 中绑定的 POST / 事件,处理流程如下:
- 校验请求必须为
POST且携带 body,否则直接返回ERROR_RESPONSE; - 用
telegram.Update.de_json把 API Gateway 传入的 JSON 请求体解析为Update对象; - 从
update.message中取出chat.id(会话标识)和text(用户消息文本); - 若文本是
/start,替换为一段欢迎语(这也是机器人对外展示的「自我介绍」入口); - 通过
asyncio.run(send_message(chat_id, text))把普通同步函数桥接到异步调用,向同一个 chat 回显文本; - 成功则返回
OK_RESPONSE。
5. 入口二:set_webhook —— 动态配置 Webhook
def set_webhook(event, context):
logger.info('Event: {}'.format(event))
url = 'https://{}/{}/'.format(
event.get('headers').get('Host'),
event.get('requestContext').get('stage'),
)
webhook_set = asyncio.run(configure_webhook(url))
if webhook_set:
return OK_RESPONSE
return ERROR_RESPONSE
set_webhook 对应 serverless.yml 中绑定的 POST /set_webhook 事件。它的巧妙之处在于不需要手动粘贴部署后生成的 URL:函数从 API Gateway 事件中读取 headers.Host(即网关的公共域名)和 requestContext.stage(部署阶段,默认 dev),自动拼出形如 https://<your_url>.amazonaws.com/dev/ 的 Webhook 地址,再调用 Telegram Bot API 的 set_webhook 接口完成注册。
6. 完整调用链
Telegram 用户发消息
│ (Telegram Bot API 回调)
▼
API Gateway POST / (公开 HTTPS 端点)
▼
Lambda webhook(event, context)
├─ 校验 POST 与 body
├─ Update.de_json 解析消息
├─ asyncio.run(send_message(chat_id, text))
└─ 返回 OK_RESPONSE (200)
部署时手动触发:
curl -X POST https://<your_url>.amazonaws.com/dev/set_webhook
▼
Lambda set_webhook(event, context)
├─ 由 Host + stage 拼出 Webhook URL
├─ asyncio.run(configure_webhook(url))
└─ 返回 OK_RESPONSE (200)
serverless.yml 服务配置逐项解读
serverless.yml 是 Serverless Framework 的服务定义文件,全文如下:
service: serverless-telegram-bot
frameworkVersion: '4'
package:
patterns:
- '!node_modules/**'
provider:
name: aws
runtime: python3.14
architecture: arm64
environment:
TELEGRAM_TOKEN: ${file(./serverless.env.yml):TELEGRAM_TOKEN, ''}
functions:
webhook:
handler: handler.webhook
events:
- http: POST /
set_webhook:
handler: handler.set_webhook
events:
- http: POST /set_webhook
custom:
# Built-in python requirements packaging activates on this key.
pythonRequirements: {}
逐项说明:
- service / frameworkVersion:服务名
serverless-telegram-bot,且显式声明使用 Serverless Framework'4'。 - package.patterns:排除
node_modules/**,避免无关文件被打进 Lambda 部署包。 - provider.runtime:
python3.14,即 Lambda 的 Python 运行时(部署时需确认目标区域已提供该运行时版本)。 - provider.architecture:
arm64,Lambda 采用 Arm 架构(Graviton),通常成本更低。 - provider.environment.TELEGRAM_TOKEN:这里用到了 Serverless 的变量解析语法
${file(./serverless.env.yml):TELEGRAM_TOKEN, ''}——从同目录的serverless.env.yml读取TELEGRAM_TOKEN键的值注入环境变量,''是兜底默认值(文件或键缺失时不报错)。这就是 handler.py 中os.environ.get('TELEGRAM_TOKEN')的数据来源。注意serverless.env.yml应纳入.gitignore,避免 Token 泄漏到版本库。 - functions.webhook:处理器
handler.webhook,事件为POST /,即 API Gateway 的根路径,用于接收 Telegram 的消息回调。 - functions.set_webhook:处理器
handler.set_webhook,事件为POST /set_webhook,用于部署后注册 Webhook。 - custom.pythonRequirements:按文件内注释,Serverless Framework v4 的内置 Python 依赖打包能力在检测到该键时自动激活,会依据 requirements.txt 把
python-telegram-bot等依赖打入部署包。这一点与 package.json 未声明任何 dependencies 的现状一致——README 中的npm install步骤在当前版本下实际不会安装额外插件,保留它主要是兼容旧版serverless-python-requirements插件的工作流。
部署前置条件
按照 README.md 的要求,开始前需要准备:
- 本地配置好的 AWS 凭证:确保本地环境已配置 AWS Key(可参考 Serverless Framework 官方的 AWS 凭证指南),否则
serverless deploy无法创建 CloudFormation 栈。 - Node.js:用于安装 Serverless Framework CLI。
- 一个 Telegram 账号:用于通过 @BotFather 创建机器人并获取 Token。
从零到一的部署实战
以下步骤完整继承自示例的 使用说明,并补充了必要的背景解释。
步骤 1:安装 Serverless Framework
npm install serverless -g
全局安装 Serverless Framework CLI。
步骤 2:安装依赖
npm install
进入示例目录执行(如 aws-python-telegram-bot),如上一节所述,当前 package.json 未声明 npm 依赖,该步骤主要是兼容旧版插件流程,执行无副作用。
步骤 3:向 @BotFather 申请机器人
在 Telegram 中向 @BotFather 发送:
/newbot
按提示为机器人命名,随后会收到一个形如 123456789:ABCdef... 的 Bot Token,请妥善保存。
步骤 4:把 Token 写入 serverless.env.yml
在 aws-python-telegram-bot 目录下创建 serverless.env.yml:
cat serverless.env.yml
TELEGRAM_TOKEN: <your_token>
该文件会被 serverless.yml 中的 ${file(...)} 语法读取,把 Token 注入 Lambda 环境变量。
步骤 5:部署服务
serverless deploy
部署完成后,Serverless CLI 会在输出中返回 API Gateway 的端点 URL,形如 https://<your_url>.amazonaws.com/dev。以该 URL 作为 <your_url> 参与下一步。
步骤 6:配置 Webhook
curl -X POST https://<your_url>.amazonaws.com/dev/set_webhook
这一步调用我们部署的第二个 Lambda 函数。它内部会自动读取 API Gateway 的 Host 与 stage,把 Webhook 注册到 Telegram 指向部署好的端点。
步骤 7:开始对话验证
在 Telegram 中给机器人发任意文本,机器人会原样回显;发送 /start,则收到定制欢迎语。至此整个闭环打通。
Webhook 自动配置原理
在常见的 Telegram Serverless 教程里,开发者往往需要手动把 API Gateway 的完整 URL 粘贴到 Telegram Bot API 的 setWebhook 接口。本示例则把这一步自动化了,值得单独理解:
- API Gateway 为每次 HTTP 请求注入包含
headers.Host(网关公共域名)和requestContext.stage(部署阶段名)的 event; - set_webhook 函数 据此拼出
https://{Host}/{stage}/,例如https://xxxx.execute-api.us-east-1.amazonaws.com/dev/(末尾带斜杠,对应POST /根路径端点); - 再通过
asyncio.run(configure_webhook(url))调用bot.set_webhook(url)完成注册,并根据返回值决定返回OK_RESPONSE还是ERROR_RESPONSE。
由于 API Gateway 天然以 HTTPS 暴露端点,无需额外配置即可满足 Telegram Bot API 对 Webhook 使用 HTTPS 的要求。这一设计让「部署 + 注册」两步操作都可以脚本化,适合 CI/CD 流程。
扩展与生产化建议
从源码结构看,本示例是一个最小可用(minimal viable)演示,若将其用于生产环境,可考虑以下增强方向:
- 请求来源校验:
webhook端点对外完全公开,且未校验 Telegram 官方签名。Telegram Bot API 支持在设置 Webhook 时指定secret_token,回调请求会附带X-Telegram-Bot-Api-Secret-Token请求头,可在 handler.py 的入口处校验该头以拒绝伪造请求。 - 消息类型覆盖:当前逻辑只读取
update.message.text,对图片、语音、回调按钮等消息类型未做分支处理;若业务需要,可按update.message下的不同字段扩展。 - 健壮性加固:
set_webhook直接链式调用event.get('headers').get('Host'),在缺头场景下会抛异常,可增加取值兜底;同时建议为函数配置 CloudWatch 告警,及时发现异常返回。 - 本地调试:可结合
serverless-offline插件在本地模拟 API Gateway 环境,快速验证 Webhook 解析与回显逻辑,无需每次改动都完整部署。
如果你更熟悉 Node.js,本仓库还提供了同主题的兄弟示例 aws-node-telegram-echo-bot,两者架构与部署流程完全对应,可对照学习。
总结
aws-python-telegram-bot 用极少的代码和配置,展示了 Serverless 与聊天机器人集成的完整范式:API Gateway 暴露 HTTPS 端点接收 Webhook → Lambda 同步处理器通过 asyncio.run 桥接异步 Bot API → set_webhook 函数自动完成端点注册。其中环境变量注入(${file(...)})、内置 Python 依赖打包(custom.pythonRequirements)、以及异步 SDK 在 Lambda 中的桥接写法,都是可以直接复用到其他 Telegram/Serverless 项目的关键技能点。