mcpField
T
telegram-bots

telegram-bots

github.com/ssheleg开发4160 次安装4 天前更新
代码仓库

关于 telegram-bots

面向使用官方 Bot API 构建 Telegram 机器人的开发者参考技能,重点讲解更新投递、限流、支付与运行保障,并附有可运行的 Python 测试夹具。

主要功能

  1. 01更新投递与去重指南
  2. 02Webhook 与轮询的取舍
  3. 03限流和可恢复广播
  4. 04Stars 支付处理
  5. 05可运行的 Python 故障测试夹具

适用场景

  1. 01设计可处理重复更新的处理器
  2. 02选择 Webhook 或长轮询
  3. 03构建可恢复的高流量广播
  4. 04实现 Telegram Stars 购买流程

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ssheleg/telegram-dev --skill telegram-bots

安装后在 Claude Code 中输入/telegram-bots确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

fixtures/
update_delivery.py
update_pump.py
references/
frameworks.md
high-load.md
limits-and-files.md
payments-stars.md
updates-and-delivery.md

详细介绍

Telegram 机器人 — 官方 Bot API

Telegram 保存聊天内容。你的数据库保存之后要处理的一切。 每个严重的机器人缺陷,都是某条更新被处理了两次,或者某条更新从未送达,因为没人去获取它。

Bot API 本身不会导致机器人故障。故障出在衔接处:崩溃后更新再次送达;因为默认设置无人查阅,成员事件从未到达;根据错误信号确认付款;广播在第 31 位用户处触发 429,然后悄无声息地停止。

本文依据 2026-08-25 的 Bot API 文档编写,当时版本为 10.3(于 2026-08-24 发布)。引用版本前,请重新查看 core.telegram.org/bots/api-changelog:此 API 大约每月发布一次。2026-10-19 再次确认:10.3 仍是最新版本。供机器读取时,Telegram 会以 Markdown 格式提供文档,路径不变,末尾加上 .md:core.telegram.org/bots/api.md;每种方法各有一个文件(/bots/api/methods/getupdates.md),另有类型索引(/bots/api/types.md)。这些是官方且可解析的文档,但它们是 Markdown,而不是 JSON schema。

需要时再查阅的深入资料:

阅读适用场景
references/updates-and-delivery.md配置长轮询或 webhook 时——了解交付约定、顺序、去重,以及两者之间的迁移
references/payments-stars.md收款时——了解 Stars、发票、预结账时限、退款和订阅
references/limits-and-files.md大量发送消息或传输文件时——了解所有已公布的限制及相应处理方式
references/frameworks.md选择或审查库时——了解 aiogram、grammY、Telegraf、python-telegram-bot,以及每个库隐藏了哪些细节
references/high-load.md机器人已有实际流量,或 Telegram 变慢时机器人就停止响应所有人——了解每个聊天的队列、上限、队列外的付款处理、关闭和告警

可运行,并与此文件一同发布: fixtures/update_delivery.py — 正确的处理程序必须满足的九项不变量,每项都附有会导致失败的变异版本。运行 python3 fixtures/update_delivery.py --self-test,观察以下故障:重复处理再次送达的更新;在偏移量衔接处崩溃导致更新丢失;确认更新后工作进程死亡导致更新丢失;将再次送达的更新回复为“重复”,但实际工作从未发生;在标记完成前崩溃导致回复重复;再次送达的 outbox 行被发送两次;两条更新导致同一笔扣款被授予两次;429 导致回复被丢弃;一笔付款被授予两次。仅使用标准库。 fixtures/update_pump.py — 防止一个慢处理程序拖住所有聊天的九项规则(轮询不会等待处理程序、每个聊天内保持顺序、上限、付款通道、从到达时开始测量限流、入队时不替换、等待中的回调会得到答复、去重、每个聊天的上限)。运行 --self-test 会逐项移除规则并观察失败。


API 选型只需决定一次

需求使用原因
用户添加的机器人,在它加入的聊天中运行Bot API — 本技能HTTP、令牌、无需电话号码、没有封号风险
读取机器人无法看到的历史记录,或以个人身份操作MTProto — telegram-userbots用户账号,以及随之而来的一切代价
Telegram 内的网页界面Mini App — telegram-miniapps一个页面,以及必须验证的 initData

机器人不能在未关闭隐私模式或未被提及的情况下读取群组消息,不能查看从未加入的聊天,不能读取加入前的历史记录,不能代表用户行事,也不能下载超过 20 MB 的文件。如果你需要这些功能,就该查看 telegram-userbots——并先阅读其中的拒绝说明,因为用户账号带来的风险是机器人令牌没有的。

更新就是事件,并且至少会送达一次

# aiogram 3.x — the shape, not the framework
async def webhook(update: Update, db) -> web.Response:
    await db.inbox_put(update.update_id, raw_body)    # INSERT on a primary key,
    return web.Response(status=200)                   # durable BEFORE the ack

# off the request path: a worker takes a pending row, does the work, marks it
# done — and a crashed attempt leaves the row pending for the next sweep
  • update_id 是你唯一可用的幂等键。 它在重新投递时保持稳定,用于标识一条更新——但它只表示身份,并不保证永远单调递增:闲置约一周后,计数器可能从一个新的随机基数重新开始,因此切勿在长时间间隔后比较 update_id 来判断哪条“更新”。更新中的其他内容都无法标识这条更新:相隔一秒的两条完全相同的消息是两个事件,而同一事件投递两次仍是一个事件。
  • 先写入收件箱,再确认——认领表示已收到,而非已完成。 通过主键上的 INSERT 写入一条持久化记录(绝不要先 SELECT 再 INSERT:在 webhook 下,最多 max_connections 个连接(默认 40)可以同时处理同一条更新的投递),只有写入后才允许返回 200。任务在请求之外通过工作器租约运行:工作器退出后,记录会保持待处理状态,扫描任务会重试。先认领、再在请求内处理,是一种悄无声息的数据丢失模式——崩溃后认领记录仍在,Telegram 重新投递时读到“重复”,更新就此丢失,而所有测试样例依然通过。
  • 更新保留 24 小时,不会更久。机器人停机一天,就会丢失这些更新,而且不会收到任何提示——应根据自己的状态进行对账,绝不要假定队列已排空。
  • 根据 2026-08-25 在本机上对 8 个正在运行的 Telegram 机器人进行的测量:8 个中没有一个按 update_id 去重。 长轮询会掩盖这个问题,直到首次在“处理完成”和“确认偏移量”之间发生崩溃。

allowed_updates 默认会丢弃三种类型

await bot.set_webhook(
    url=f"{origin}/tg/{SECRET_PATH}",
    secret_token=WEBHOOK_SECRET,                 # 1-256 chars
    allowed_updates=["message", "callback_query", "chat_member",
                     "pre_checkout_query", "my_chat_member"],
    drop_pending_updates=False,
)

默认值为空列表;不传此参数时也会使用该值——其含义是*“所有更新类型,但不包括 chat_member、message_reaction 和 message_reaction_count”*。因此,跟踪用户加入和离开的机器人什么也收不到,请求却返回 ok: true,日志也没有异常。明确列出你要处理的每种类型,新增类型时重新运行 setWebhook:allowed_updates 是在订阅时设置的,而不是在处理程序中设置的。

使用 webhook 或轮询,绝不要两者并用

设置 webhook 后,getUpdates 将无法工作。这正是迁移时的风险:使用生产令牌在本地运行 getUpdates,可能会悄无声息地接管,也可能失败,取决于哪一方最后进行了切换。一个令牌只能有一个消费者。

  • webhook 是回调,因此需要采用与支付 webhook 相同的防护措施:以恒定时间比较 X-Telegram-Bot-Api-Secret-Token 与通过 secret_token 设置的值;验证失败时返回不带详细信息的 401。URL 不是秘密,标头才是。
  • 快速响应,稍后处理。 Telegram 会在收到非 2xx 响应或响应过慢时重试,重试即是再次投递同一个 update_id。先确认,再处理——上文的认领机制正是安全性的保障。
  • drop_pending_updates=True 是一项决策,不是清理操作。 它会丢弃所有已排队的更新,包括已经完成支付的更新。
  • 长轮询适用于开发和低流量机器人;如果你有公开的 HTTPS 端点并且关注延迟,则适合使用 webhook。两种方式都见 references/updates-and-delivery.md。

限制是设计约束,而不是错误路径

限制数值对设计的影响
单个聊天~1 条消息/秒可容忍突发流量,之后会被拒绝
单个群组20 条消息/分钟聊天频繁的群组机器人需要队列
批量发送,面向所有用户~30 条消息/秒向 100k 名用户广播需要数小时,而不是一个循环
付费广播最多 1000 条/秒超出免费的 30 条/秒后,每条消息收费 0.1 Stars

遇到 429 时,响应会携带以秒为单位的 parameters.retry_after。严格等待这么长时间,然后重试相同的调用——固定退避时间要么浪费等待时间,要么触发下一次限制。广播是一项带有速率限制器和恢复点的任务,而不是一个 for 循环;如果无法恢复,那么在用户 60 000 处崩溃后就会从头开始,之前的每位用户都会收到两次消息。

金钱:支付成功才算付款

数字商品和服务只能使用 Telegram Stars销售,货币代码为 XTR,且其 provider_token 留空。

sendInvoice / createInvoiceLink  →  pre_checkout_query  →  successful_payment
                                    answer within 10s        deliver here
  • **answerPreCheckoutQuery 的处理窗口为十秒。**错过窗口,交易就会取消——因此该处理程序应基于你自己的状态进行验证,不做任何耗时操作。任何可能耗时一秒的操作都应放在 successful_payment 之后。
  • **在 successful_payment 时交付,绝不要在预结账时交付。**预结账只是一个询问;此时付款尚未完成。
  • **refundStarPayment 可用于退款,且退款不等于删除。**按扣款 ID 撤回已授予的权益,处理方式与银行卡退款完全相同——其中的计算和顺序问题与 stripe-billing 涵盖的问题相同。

完整流程、订阅和交易账本: references/payments-stars.md。

文件有大小上限,而且比你想的更低

  • **通过 getFile 下载:20 MB。**超过此大小,Bot API 会拒绝,任何框架都无法绕过这一限制。
  • **上传:**通过云端 Bot API 上传上限为 50 MB。
  • 本地 Bot API 服务可取消这两项上限,并会改变代码收到的文件路径。这是基础设施层面的决策,而不是一个标志—— references/limits-and-files.md。
  • **file_id 既不是 URL,也不会在不同机器人之间保持稳定。**它仅对你的机器人有效;可以保存以便低成本重发,但绝不能将其用作存档引用。

哪些内容应存入数据库

事实存储位置原因
已见过的 update_id你自己的数据库,且唯一唯一的幂等键
chat_id、user_id你自己的数据库Telegram 不会替你列出用户
你发送的消息你自己的数据库,包含 message_id之后编辑消息需要这两个 ID
用 Stars 购买的权益你自己的数据库,按扣款 ID 关联退款时必须能找到它
file_id你自己的数据库,作为缓存方便低成本重发,不用于存档

Telegram 不是数据库,也没有办法枚举启动过你的机器人的用户。如果当时没有记录下来,这些信息就永远丢失了。

**当受众可能无法使用 Telegram 时,增加第二个渠道。**机器人所在的渠道并不归你所有。如果用户所在地区限制 Telegram 访问,或未来可能实施此类限制,就要为无法再联系到他们的那一天做好准备:使用你自己的 ID 而非仅使用 chat_id 来标识用户,在对用户有帮助的时机索取第二种联系方式(电子邮件、Web 账号),并确保用户可以通过 Telegram 以外的渠道访问产品。这样,即便用户拉黑机器人,你失去的也只是一个渠道,而不是用户列表。

发布前检查

  1. 每个处理程序都根据 update_id 保持幂等(§ 更新就是事件)。
  2. Webhook 会验证密钥请求头,并在验证失败时返回不含详细信息的 401 (§ Webhook 还是轮询)。
  3. allowed_updates 列出了你处理的所有类型——默认值并非“全部” (§ allowed_updates 会丢弃三种类型)。
  4. 遇到 429 时,会等待 retry_after 指定的时长,并且广播可以恢复(§ 限制)。
  5. 商品在收到 successful_payment 时交付,而不是在预结账时 (§ 资金)。
  6. 轮询不会等待处理程序完成:每个聊天一个队列,所有聊天共用一个上限,支付则在队列外处理(references/high-load.md)。

触发词

还有一些会指向此处的短语:“grammy”、“telegraf”、“python-telegram-bot”、“getUpdates”、 “pre_checkout_query”、“高负载机器人”、“机器人卡顿”、“机器人 API”、“Telegram 机器人”、“Telegram 机器人”、“Telegram 机器人”。

从 0.5.4 的描述移至此处:“Telegram webhook”、“Telegram Stars”。

评论

支持 Markdown