sendmessage 是 telegram bot 推送最常用但易踩坑的接口,需结合 sendphoto、editmessagetext 和错误重试策略;手写 http.client 可精准控制超时、重试与 user-agent,并正确处理 429 和 utf-8 编码。

sendMessage 是 Go 实现 Telegram Bot 推送最直接、最常用的接口,但仅靠它容易踩坑——比如中文乱码、超长消息截断、并发发送失败、群组权限被拒。真正稳定推送,得结合 sendPhoto、editMessageText 和错误重试策略。
为什么用 http.Client 而不是第三方 SDK?
Go 生态里没有官方 SDK,主流选择是自己封装 HTTP 请求或用 telebot / tgbot 等第三方库。但很多项目(尤其是 ZeroBot 类框架或轻量监控 Bot)倾向手写 http.Client,原因很实际:
-
telebot默认启用长轮询,对服务端资源占用高,且不支持sendMessageDraft这类 9.5 新 API - 手写请求能精确控制超时(建议设
Timeout: 10 * time.Second)、重试(最多 2 次)、User-Agent(避免被限频) - Telegram Bot API 返回的
429 Too Many Requests错误必须解析Retry-Afterheader,而多数 SDK 默认忽略该字段 - 中文推送需显式设置
Content-Type: application/json; charset=utf-8,否则部分代理或旧版网关会丢掉非 ASCII 字符
sendMessage 的三个关键参数陷阱
调用 sendMessage 时,chat_id、text、parse_mode 看似简单,实则最容易出错:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
chat_id必须是整数类型(如-1001234567890),不能传字符串;群组和频道 ID 均以-100开头,私聊 ID 是纯数字正整数 -
text超过 4096 字符会直接返回400 Bad Request,需提前切分;切分时不能在 HTML 标签中间断开(如<b>xxx</b>后直接截断会导致渲染异常) -
parse_mode=HTML时,&、、<code>>必须手动转义为&、<、>;用parse_mode=MarkdownV2则需对_ * [ ] ( ) ~ ` > # + - = | { } . !全部反斜杠转义
如何安全实现流式推送(sendMessageDraft)
Telegram Bot API 9.5 已全面开放 sendMessageDraft,但它不是“发一条消息”,而是“创建一个可编辑的草稿”。真实流式体验需要两步配合:
- 首次调用
sendMessageDraft,传入空text或占位符(如"…"),获取返回的message_id - 后续用
editMessageText更新该message_id,每次追加文本片段(注意:单次更新仍受 4096 字符限制) - 必须检查每次
editMessageText的响应,若返回"Bad Request: message to edit not found",说明草稿已被用户手动发送或过期(草稿有效期约 5 分钟) - 不建议在群组中对非管理员 bot 使用
sendMessageDraft,部分群组禁用了 bot 编辑他人消息的权限,会静默失败
并发推送时怎么避免 429 和消息乱序
Telegram 对每个 bot 每秒最多允许约 30 条请求,但实际阈值动态浮动。硬上 goroutine 并发极易触发限频,且 editMessageText 依赖前序 message_id,乱序等于失败:
- 用带缓冲的 channel 控制并发数(如
sem := make(chan struct{}, 5)),每发一条前sem ,发完后 <code> - 对同一
chat_id的连续编辑操作,必须串行;可用sync.Map按chat_id维护独立的编辑队列 - 收到
429时,不要立即重试,应 sleepRetry-After秒数(单位是秒,不是毫秒);若 header 不存在,则退避 1 秒再试 - 日志中务必记录每次请求的
chat_id、message_id(如有)、HTTP 状态码和响应体,否则线上排查几乎不可能
真正难的不是发消息,而是让消息“稳稳抵达”——尤其当你要推的是告警、订单、审核结果这类不可丢失的信息。别迷信自动重试,先搞清哪个环节会丢:是网络超时?是群组权限?还是你没处理 Retry-After?这些细节,比选什么框架重要得多。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










