要在本地主机上测试 Telegram 机器人 Webhook,请使用公共 HTTPS 隧道公开本地服务器,使用该 URL 调用 setWebhook,并在每个请求上验证 Telegram 的秘密令牌标头。 这将为您提供真实的消息、回调查询和成员资格更新,而无需每次代码更改后进行部署。完整的循环是:运行机器人处理程序,启动npx portpreview 3000,注册生成的 URL,向机器人发送消息,然后在本地检查请求。
为什么 Telegram 无法直接发送更新到本地主机
Telegram 的 Bot API 将 Webhook 更新从 Telegram 基础设施发送到互联网可访问的 URL。 localhost、127.0.0.1 和专用 LAN 地址无法从该基础设施进行路由。 localhost 隧道 在公共地址终止 HTTPS,并将未更改的 HTTP 请求转发到本地端口。
Telegram 机器人可以通过两种互斥的方式接收更新:通过 getUpdates 进行长轮询,或 webhooks。 官方setWebhook参考指出,在配置传出Webhook时,getUpdates不可用。如果轮询进程仍在运行,请先将其停止,然后再判断 webhook 流程。
构建本地Webhook端点
此 Express 示例有意使处理程序保持较小。它在接触更新之前检查共享密钥,快速确认,并将工作移出响应路径。
import express from 'express';
import crypto from 'node:crypto';
const app = express();
app.use(express.json({ limit: '1mb' }));
function sameSecret(received = '', expected = '') {
const a = Buffer.from(received);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
app.post('/webhooks/telegram', (req, res) => {
const received = req.get('x-telegram-bot-api-secret-token') || '';
if (!sameSecret(received, process.env.TELEGRAM_WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
const update = req.body;
res.sendStatus(200);
queueMicrotask(() => handleUpdate(update));
});
app.listen(3000, () => console.log('Listening on http://localhost:3000'));
Telegram 发送 JSON 序列化的Update。与基于 HMAC 的提供商不同,Telegram 的 secret_token 功能不会对正文进行签名。它将您选择的值放入X-Telegram-Bot-Api-Secret-Token。该令牌证明发送者知道注册 Webhook 时使用的值,但它不提供有效负载摘要。 TLS 保护传输中的请求。
使用 HTTPS 公开端点
- 启动应用程序并确认
curl -i http://localhost:3000/webhooks/telegram到达服务器,即使GET返回404。 - 打开第二个终端并运行
npx portpreview 3000。 - 复制公共HTTPS源并追加
/webhooks/telegram. - 在 Telegram 传送更新时保持隧道进程运行。
Bot API 接受 HTTPS Webhook URL。 Telegram 记录了端口 443、80、88 和 8443 上的 Webhook 支持;即使转发的本地进程侦听 3000,托管隧道的公共端点通常也使用 443。
安全注册 Telegram webhook
创建一个仅包含字母、数字、下划线或连字符的随机秘密。 Telegram 允许 1–256 个字符。请勿重复使用机器人令牌作为此值。
export TELEGRAM_WEBHOOK_SECRET="$(openssl rand -hex 32)"
curl -sS -X POST \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/setWebhook" \
-d "url=https://YOUR-TUNNEL.portpreview.dev/webhooks/telegram" \
-d "secret_token=${TELEGRAM_WEBHOOK_SECRET}" \
-d 'allowed_updates=["message","callback_query"]' \
-d "drop_pending_updates=true"
allowed_updates 减少噪音,并应仅列出机器人处理的更新类型。 drop_pending_updates=true 在启动新的本地会话时很有用,但它会永久丢弃排队的更新,因此当这些事件很重要时请忽略它。 Telegram 的 更新文档 描述了 message、callback_query 和 my_chat_member.
调试代码前确认注册
使用getWebhookInfo将配置失败与处理程序失败分开:
curl -sS "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getWebhookInfo"
检查url、pending_update_count、last_error_message和last_error_date。空 URL 表示注册未成功。待处理计数不断增加通常意味着 Telegram 无法连接或您的端点返回非 2xx 状态。注册后直接向机器人发送消息;仅仅打开聊天并不一定会创建更新。
处理更新而不导致重试
慢工之前先确认
请求经过身份验证并持久接受后,立即返回 2xx 响应。数据库导出、AI 调用和第三方 API 应异步运行。 Telegram 在非 2xx 响应后重试不成功的请求,因此缓慢的同步工作可能会创建重复项。
使用 update_id 进行重复数据删除
每次更新都有一个update_id。使用过期窗口存储已处理的 ID 或强制使用唯一的数据库密钥。重试不得发送第二个付款收据、创建重复的票证或执行相同的回调两次。
显式建模每个更新类型
并非每个更新都包含message.text。回调按钮位于callback_query下;频道帖子和会员变更还有其他字段。在当前顶级字段上进行分支,并将未知类型视为有效的无操作而不是抛出。
本地 Telegram 机器人测试的安全规则
- 首先验证秘密标头。在记录或解析敏感字段之前拒绝丢失或不正确的值。
- 将令牌排除在 URL 和日志之外。 注册命令中的 Bot API 令牌是凭证。避免共享系统上的 shell 历史记录并通过 BotFather 轮换暴露的令牌。
- 使用不可猜测的路线和秘密。该路线是纵深防御;秘密标头是实际应用程序检查。
- 限制捕获的数据。消息可以包含姓名、用户名、电话号码、文件和私人对话文本。完成后编辑日志并删除本地捕获。
- 永远不要在开发中禁用身份验证。公共隧道是公共的。本地代码应执行与生产相同的检查。
有关访问控制和数据保留实践,请参阅更广泛的localhost 隧道安全指南。
排查常见的 Telegram webhook 故障
Telegram 报告证书或连接错误
使用隧道的 HTTPS URL,而不是其本地 HTTP 目标。确认隧道处于活动状态并且 URL 未更改。如果您提供自己的自签名证书,Telegram 需要将公共证书作为文件上传;托管 TLS 端点可以避免这种设置。
端点返回401
将传递给setWebhook的秘密与进程使用的环境变量进行比较。标头名称不区分大小写,但代理或中间件可以删除自定义标头。检查传入的标头而不打印秘密值。
没有请求到达
运行getWebhookInfo,验证注册的路径与您的路由完全匹配,并确保没有防火墙阻止隧道的本地连接。如果您最近使用过轮询,请确认 Webhook URL 现已填充。通过向机器人发送消息来触发实际更新。
更新反复到来
日志状态和响应时间。收到请求后出现异常可能会将预期的 200 变成 500。立即返回 200,使处理幂等,并使用控制的 webhook 重播,而不是在调试期间等待提供者重试。
测试回调查询和文件,而不仅仅是文本
有用的机器人测试矩阵涵盖的内容不止message.text。发送带有标题的照片、共享联系人、编辑消息,然后按内联键盘按钮。对于回调查询,请立即调用answerCallbackQuery,以便客户端停止显示其进度指示器,然后单独执行较慢的工作。文件更新包含标识符;下载字节是第二个 Bot API 操作,不应延迟 Webhook 响应。
保留由经过净化的更新制成的固定装置用于单元测试,但保留每种受支持类型的至少一个测试的完整传输路径。固定装置证明您的调度员了解有效负载;真正的隧道传输还可以证明注册、TLS、标头、正文解析和确认行为。添加新的allowed_updates条目时,再次调用setWebhook并验证getWebhookInfo反映了预期的配置。
在本地会话后删除 webhook
curl -sS -X POST \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/deleteWebhook" \
-d "drop_pending_updates=false"
删除 Webhook 可让您返回到 getUpdates。如果下次会话时隧道 URL 发生变化,请再次拨打setWebhook。如需其他诊断,请遵循常规 本地 Webhook 调试工作流程。
