telegram-userbots

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Telegram userbots — MTProto, and what it costs

Telegram userbots——MTProto,以及它付出的代价

A bot token is issued to software. An MTProto session is a logged-in human being — the same authority the person has, held in a file, with none of the protections a token has. Everything in this skill follows from that one difference.
Read against Telethon 1.44.0 and
core.telegram.org
on 2026-08-25. Telethon minor releases change session and entity-cache behaviour; the version you are on is a fact to check, not to assume.
Deep material, loaded on demand:
ReadWhen
references/sessions-and-auth.md
logging in, storing a session, rotating one, or moving between machines
references/rate-and-flood.md
anything that loops over chats, users or messages — FloodWait, pacing, takeout
references/entities-and-history.md
resolving peers, iterating history, downloading media at scale

bot token 是签发给软件的。一个 MTProto session 就是一个已登录的真人——它拥有与该用户本人相同的权限,而且这些权限被保存在一个文件里,不具备 token 所具有的任何保护。本 skill 中的一切都源于这一差异。
依据 Telethon 1.44.0 与
core.telegram.org
,核对日期为 2026-08-25。Telethon 的次要版本发布会改变 session 和 entity-cache 行为;你当前使用的版本是需要核实的事实,而不是可以想当然的假设。
深度资料,按需加载:
阅读何时
references/sessions-and-auth.md
登录、存储 session、轮换 session,或在不同机器之间迁移
references/rate-and-flood.md
任何遍历 chats、users 或 messages 的操作——FloodWait、节奏控制、takeout
references/entities-and-history.md
解析 peers、遍历历史记录、大规模下载媒体

First: do you actually need one?

首先:你真的需要 userbot 吗?

Answer this before writing a line, and write the answer down.
The jobBot API canVerdict
React to messages in chats the bot is inyesuse a bot
Send to users who started the botyesuse a bot
Read a public channel it is admin ofyesuse a bot
Read history of a channel it does not administernouserbot
Read a chat's past, from before it joinednouserbot
Act as a specific personnouserbot
Download a file over 20 MBnouserbot, or a local Bot API server
Enumerate a group's members at scalenouserbot, and see the risk below
The local Bot API server closes the file ceiling without an account. Reaching for a user session because of a 21 MB video is trading a permanent liability for an infrastructure task you were going to have anyway.
The refusal, and it is not a formality: a user account can be limited or banned, and Telegram does not explain, appeal quickly, or restore what was in it. If the account is a real person's, the blast radius is their messages, their groups and their logins. Automating a personal account is a decision with a named owner or it is not a decision.
在写第一行代码前先回答这个问题,并把答案写下来。
任务Bot API 能做到吗结论
对 bot 所在 chats 中的消息作出反应是使用 bot
向启动 bot 的用户发送消息是使用 bot
读取 bot 担任管理员的公开 channel是使用 bot
读取 bot 未管理的 channel 历史否userbot
读取 chat 加入之前的历史否userbot
以特定人员身份操作否userbot
下载超过 20 MB 的文件否userbot,或使用本地 Bot API server
大规模枚举群组成员否userbot,并参阅下方风险
本地 Bot API server 无需账户即可突破文件大小上限。 仅仅因为要处理一个 21 MB 的视频就改用 user session,就是拿一项永久性责任去换一项你本来也总要搭建的基础设施工作。
要拒绝这种做法,而且这不是走形式: 用户账户可能受到限制或封禁,而 Telegram 不会解释原因、不支持快速申诉,也不会恢复其中原有的数据。如果该账户属于真人,影响范围会涵盖此人的消息、群组和登录状态。自动化个人账户必须有一位明确的责任人,否则就不该做这个决定。

The session file is the credential

session 文件就是凭据

python
import os
from telethon import TelegramClient
from telethon.sessions import StringSession

client = TelegramClient(
    session=StringSession(os.environ["TG_SESSION"]),   # from the secret store
    api_id=int(os.environ["TG_API_ID"]),
    api_hash=os.environ["TG_API_HASH"],
)
  • A
    .session
    file grants full access to the account, without the password and without the 2FA prompt.
    Copying one to another machine logs that machine in. It belongs in
    .gitignore
    , in a secret store, in backups you would give a password the same treatment — and never in an image layer or a log.
  • api_id
    /
    api_hash
    come from
    my.telegram.org
    and identify the application, not the account. They are not a session and not a secret of the same weight, but they are still not public.
  • Losing a session is recoverable; leaking one is an incident. Revoke from the account's own Devices screen, which ends that session everywhere.
  • Log Out is not a lockout, and termination comes from an older session.
    auth.logOut
    can return a
    future_auth_token
    that skips the next login code, so the two-step password is the real gate; a session under 24 hours old cannot terminate others (
    FRESH_RESET_AUTHORISATION_FORBIDDEN
    ). Accepting a
    tg://login?token=
    link logs in whoever generated it. List, watch (
    UpdateNewAuthorization
    ) and terminate sessions:
    references/sessions-and-auth.md
    .
  • Verified on this machine on 2026-08-25: five of five Telethon projects gitignore the session and none has one tracked in git — the one trap this estate has actually closed. Keep it closed.
python
import os
from telethon import TelegramClient
from telethon.sessions import StringSession

client = TelegramClient(
    session=StringSession(os.environ["TG_SESSION"]),   # from the secret store
    api_id=int(os.environ["TG_API_ID"]),
    api_hash=os.environ["TG_API_HASH"],
)
  • 一个
    .session
    文件可在不需要密码、也不需要 2FA 提示的情况下授予账户的完整访问权限。
    将它复制到另一台机器,就等于让那台机器登录。它应放在
    .gitignore
    中、存于 secret store、备份时也要像密码一样受到同等保护——绝不能进入 image layer 或日志。
  • api_id
    /
    api_hash
    来自
    my.telegram.org
    ,标识的是 application,而非账户。
    它们不是 session,敏感程度也不相同,但仍不应公开。
  • 丢失 session 可以恢复;泄露 session 就是安全事件。 在账户自身的 Devices 页面中撤销它,会在所有设备上终止该 session。
  • Log Out 不是锁定,而终止其他 session 必须来自一个更老的 session。
    auth.logOut
    可以返回一个
    future_auth_token
    ,从而跳过下一次登录验证码,因此两步验证密码才是真正的关卡;创建时间不足 24 小时的 session 无法终止其他 session(
    FRESH_RESET_AUTHORISATION_FORBIDDEN
    )。接受
    tg://login?token=
    链接会登录生成该链接的一方。列出、监听(
    UpdateNewAuthorization
    )并终止 session:
    references/sessions-and-auth.md
    。
  • 本机于 2026-08-25 验证:五个 Telethon 项目全部将 session 加入了 gitignore,且没有任何一个在 git 中追踪 session 文件——这是这组代码库实际已经堵住的一个陷阱。保持它被堵住。

FloodWaitError
is the API working

FloodWaitError
是 API 正常运作的信号

python
from telethon.errors import FloodWaitError

try:
    await client.send_message(peer, text)
except FloodWaitError as e:
    if e.seconds > MAX_ACCEPTABLE:      # a decision, not a sleep
        raise
    await asyncio.sleep(e.seconds + 1)
    await client.send_message(peer, text)
  • It is not a failure to swallow and not a signal to retry blindly. It names exactly how long to wait; anything else is guessing at a number Telegram already told you.
  • A very large
    seconds
    is a different event.
    Minutes mean you are pacing too hard; hours mean the account is being limited, and continuing is how a limit becomes a ban. Cap it, alert, stop.
  • Sleeping inside a request handler is how one flood becomes an outage. The wait belongs in a worker with a queue, not in the path a user is waiting on.
  • Measured across six Telethon projects here on 2026-08-25: three of six handle
    FloodWaitError
    at all.
    The other three run until the first limit and then stop, in whatever state they were in.
python
from telethon.errors import FloodWaitError

try:
    await client.send_message(peer, text)
except FloodWaitError as e:
    if e.seconds > MAX_ACCEPTABLE:      # a decision, not a sleep
        raise
    await asyncio.sleep(e.seconds + 1)
    await client.send_message(peer, text)
  • 它不是可以吞掉的失败,也不是可以盲目重试的信号。 它会准确告诉你需要等待多久;换成其他做法,就是在猜测 Telegram 已经明确给出的数字。
  • seconds
    非常大意味着发生了另一类事件。
    以分钟计,说明节奏过快;以小时计,说明账户正受到限制,而继续运行正是限制升级为封禁的方式。应设置等待上限、发出告警并停止。
  • 在请求处理器内部 sleep,是一次 flood 演变成服务中断的方式。 等待应放在带队列的 worker 中,而不能位于用户正在等待的请求路径上。
  • 于 2026-08-25 对这里的六个 Telethon 项目进行测量:六个项目中有三个处理了
    FloodWaitError
    。
    另外三个会一直运行到首次触发限制,然后停下,并停留在各自当时的状态中。

Pin the minor, and know why

锁定次要版本,并知道原因

Four bots in this estate pin
telethon==1.37.0
while a fifth runs
1.44.0
, and the pin carries its reason in
requirements.txt
: minor releases change session and entity-cache behaviour, and a jump of seven minors on a live bot needs its own change with its own verification.
That is the right shape and it is worth stating as doctrine. A userbot's dependency is not a library, it is a protocol client holding a login. An upgrade can invalidate a session format, change what
get_entity
costs, or move an exception's module. Upgrade deliberately, one bot at a time, with a session you can recreate.
这组代码库中有四个 bot 固定使用
telethon==1.37.0
,第五个使用
1.44.0
,并且该固定版本在
requirements.txt
中附有理由:次要版本发布会改变 session 和 entity-cache 行为,而线上 bot 一次跨越七个次要版本,需要作为一项独立变更并配套自己的验证。
这是正确的做法,也值得明确为原则。userbot 的依赖项不是一个库,而是一个持有登录态的协议客户端。 升级可能使 session 格式失效、改变
get_entity
的成本,或移动异常所在的模块。升级应有计划地逐个 bot 进行,并使用一个能够重新创建的 session。

Entities are resolved, and resolution is not free

Entities 需要解析,而解析并非零成本

get_entity
may hit the network, and doing it in a loop is the most common way to earn a FloodWait that looks unexplained.
  • Prefer ids you already have. A cached
    InputPeer
    costs nothing; a username costs a request.
  • PeerIdInvalidError
    usually means the account has never seen that peer
    , not that the id is wrong. A user account can only address what it has encountered.
  • Iterate with the library's own iterators (
    iter_messages
    ,
    iter_dialogs
    ) and let them page; hand-rolled offsets re-request and re-trip the limits.
  • For a bulk export,
    takeout
    exists and is the sanctioned path
    — it raises the limits for exactly this and tells Telegram what you are doing.
Detail in
references/entities-and-history.md
.
get_entity
可能会发起网络请求,在循环中不断调用它是招致看似无法解释的 FloodWait 最常见的方式。
  • 优先使用你已有的 ids。 已缓存的
    InputPeer
    不产生成本;username 则需要一次请求。
  • PeerIdInvalidError
    通常意味着账户从未见过该 peer,而不是 id 错误。
    用户账户只能寻址它已经遇到过的对象。
  • 使用库自带的迭代器(
    iter_messages
    、
    iter_dialogs
    )并让它们自行分页。
    手写的 offset 会重复发起请求并再次触发限制。
  • 对于批量导出,
    takeout
    已经存在,而且是官方认可的路径
    ——它会针对此用途提高限制,并让 Telegram 知道你正在执行此操作。
详见
references/entities-and-history.md
。

Two accounts, two lifetimes

两个账户,两种生命周期

A userbot has a second failure mode a bot does not: the human logs in somewhere, changes the password, or terminates sessions, and your process dies holding a session that is no longer valid. This is a terminal / revoked auth state, not a transient one: the process may still be LIVE, but it is NOT READY, and a restart just re-loops a login that can never succeed. Surface it as an alert with the account named, stop work until FRESH auth (a re-login that needs the phone), and never a crash loop — the same three-state contract
sheleg-dev
's error-tracking states for any long-lived authenticated connection: liveness, readiness, degraded_auth (transient retries, terminal stops). A supervisor restart is for the
recoverable
policy only
; a revoked session is
terminal
and lands on the STOP-and-alert row of that skill's recovery-contract table, so the identical event cannot draw opposite actions in the two packs. Never put a userbot on the critical path of something a bot could serve.
userbot 有一个 bot 没有的第二种故障模式:真人会在某处登录、更改密码或终止 session,而你的进程会带着一个已失效的 session 死亡。这是一种 terminal / revoked 认证状态,而非瞬时状态:进程可能仍为 LIVE,但不是 READY,而重启只会让一次永远不可能成功的登录重新循环。应以包含账户名称的告警将其暴露出来,停止工作,直到获得 FRESH auth(需要手机号码的重新登录),绝不能形成崩溃循环——这与
sheleg-dev
的错误跟踪为任何长生命周期认证连接定义的三状态契约完全一致:liveness、readiness、degraded_auth(瞬时重试,终止性停止)。supervisor 重启只适用于
recoverable
策略;被撤销的 session 属于
terminal
,应落到该 skill 恢复契约表的 STOP-and-alert 行,因此两个技能包中的同一事件不能触发相反的处理方式。
绝不要把 userbot 放在 bot 本可胜任任务的关键路径上。

Before you ship

上线前

  1. The reason a user account is required is written down, and the bot-API alternative was checked (§ First: do you actually need one?).
  2. The session is in a secret store, gitignored, and revocable (§ The session file is the credential).
  3. FloodWaitError
    is caught, capped and alerted on
    — never slept off unbounded (§
    FloodWaitError
    is the API working
    ).
  4. The client version is pinned, and the upgrade is its own change (§ Pin the minor).
  5. A dead session pages a human rather than restarting forever (§ Two accounts, two lifetimes).
  6. The account has a two-step password, and someone watches its sessions: a new
    UpdateNewAuthorization
    reaches a human, and the oldest trusted session is the one that terminates (§ The session file is the credential).
  1. 已写明必须使用用户账户的原因,并已检查 Bot API 替代方案(§ 首先:你真的需要 userbot 吗?)。
  2. session 位于 secret store 中、已加入 gitignore,并且可撤销(§ session 文件就是凭据)。
  3. 已捕获
    FloodWaitError
    ,设置上限并告警
    ——绝不无限制地 sleep 等待(§
    FloodWaitError
    是 API 正常运作的信号
    )。
  4. 客户端版本已固定,且升级是一项独立变更(§ 锁定次要版本)。
  5. session 失效时会呼叫人工处理,而不是无限重启(§ 两个账户,两种生命周期)。
  6. 账户已设置两步验证密码,并且有人监看其 session:新的
    UpdateNewAuthorization
    会通知人工,由最老的受信任 session 执行终止操作(§ session 文件就是凭据)。

Triggers

触发词

More phrases that route here: "user account", "read channel history", "api_hash", "телетон".
更多会路由到这里的短语:"user account"、"read channel history"、"api_hash"、"телетон"。