PAYATHON 2026

如何使用 Telethon 强制通过 SMS 接收验证码,而非 Telegram 应用通知?

技术老齐

结论

Telethon 没有保证强制发送 SMS 验证码的通用方法,发送渠道由 Telegram 服务端决定。

  • ResendCodeRequest 用于请求切换到下一种验证渠道。调用前应检查上一次响应中的 next_type,下一种渠道未必是 SMS。
  • 即使服务端允许重发 SMS,也可能受到请求限制或遇到短信投递失败,手机不一定能收到验证码。
  • 首次调用可能直接返回 SentCodeTypeSms,但客户端没有通用参数可以保证首次就发送 SMS。

Telegram 将 auth.resendCode 定义为按前一次响应指定的渠道重发。Telethon 的 force_sms 已被弃用,当前实现会忽略其强制短信含义,因此不能用它来强制发送短信。Telethon 文档

如何判断能否重发 SMS

send_code_request() 通常返回 types.auth.SentCode,需要关注以下字段:

字段含义
type本次使用的验证码渠道
next_type未收到验证码时,下一次重发使用的渠道
timeout等待本次验证码的时间,单位为秒
phone_code_hash后续重发和提交验证码时使用的标识

以下判断用于检查本次验证码是否通过 Telegram 应用发送:

isinstance(sent.type, types.auth.SentCodeTypeApp)

要判断下一次重发渠道是否为 SMS,则应检查:

isinstance(sent.next_type, types.auth.CodeTypeSms)

这里分别使用 SentCodeTypeApp 和 CodeTypeSms,要注意 type 与 next_type 的类型区别。

如果 next_type 是 None,说明响应没有提供下一种重发渠道;如果是其他类型,客户端也无法通过调用重发接口将其改成 SMS。timeout 到期后仍需主动调用重发接口,等待本身不会触发短信发送。auth.sentCode 字段说明

按服务端允许的渠道请求短信

按以下顺序处理:

  1. 调用 send_code_request()。
  2. 如果本次已经使用 SMS,直接等待验证码。
  3. 如果 next_type 是 CodeTypeSms,等待返回的 timeout 后调用 ResendCodeRequest。
  4. 检查重发结果,并保存最新的 phone_code_hash。

以下示例使用 Telethon 1.x 接口,处理普通数字短信验证码。传入的 client 应已连接且尚未登录,phone 使用带国家代码的国际格式。

import asyncio

from telethon import functions, types


async def request_sms_code(client, phone):
    sent = await client.send_code_request(phone)

    if not isinstance(sent, types.auth.SentCode):
        raise RuntimeError("返回结果需要其他授权流程,不能继续普通短信验证")

    if isinstance(sent.type, types.auth.SentCodeTypeSms):
        return sent

    if not isinstance(sent.next_type, types.auth.CodeTypeSms):
        next_name = (
            type(sent.next_type).__name__
            if sent.next_type is not None
            else "None"
        )
        raise RuntimeError(
            f"服务端没有提供下一步 SMS 渠道:next_type={next_name}"
        )

    if sent.timeout is not None and sent.timeout > 0:
        await asyncio.sleep(sent.timeout)

    sent = await client(
        functions.auth.ResendCodeRequest(
            phone_number=phone,
            phone_code_hash=sent.phone_code_hash,
        )
    )

    if not isinstance(sent, types.auth.SentCode):
        raise RuntimeError("重发结果需要其他授权流程")

    if not isinstance(sent.type, types.auth.SentCodeTypeSms):
        raise RuntimeError(
            f"重发后渠道为 {type(sent.type).__name__},请按实际结果处理"
        )

    return sent


async def login_with_sms(client, phone):
    sent = await request_sms_code(client, phone)
    code = input("请输入收到的短信验证码:").strip()

    return await client.sign_in(
        phone=phone,
        code=code,
        phone_code_hash=sent.phone_code_hash,
    )

直接调用原始 ResendCodeRequest 后,不应依赖高层封装先前缓存的 phone_code_hash,因此这里显式传入最新值。Telethon 授权实现

示例没有实现自动重试,也没有覆盖 SentCodeTypeSmsWord、SentCodeTypeSmsPhrase 等短信变体。遇到其他返回类型时,需要补充相应处理,不能一律按普通数字验证码处理。

哪些情况下重发仍然无效

Telegram 没有开放可用的短信渠道

Telegram 官方说明,在某些登录或注册条件下,短信或电话验证只能使用 Firebase SMS 流程,而目前只有官方移动客户端可以完成该流程。普通 Telethon 脚本无法通过再次调用重发接口解除这一限制。其他号码曾经重发成功,也不代表所有账号状态下都能获得 SMS。Telegram 用户授权文档

请求受到限制,或验证流程已经失效

重发接口可能返回 PHONE_CODE_EXPIRED、SEND_CODE_UNAVAILABLE 等错误,其中 SEND_CODE_UNAVAILABLE 表示该号码当前可用的发送选项已经用尽。auth.resendCode 错误说明

发送验证码时还可能遇到 PHONE_NUMBER_FLOOD、PHONE_NUMBER_BANNED 或 SMS_CODE_CREATE_FAILED。这些情况需要根据具体错误处理,连续重发不能保证恢复。auth.sendCode 错误说明

如果 Telethon 抛出 FloodWaitError,应按异常给出的时间等待,不要在异常处理中立即循环请求。

服务端报告发送 SMS,但手机没有收到

SentCodeTypeSms 表示服务端报告验证码已通过 SMS 发送,不能证明短信已送达手机。运营商延迟、短信拦截或号码的接收能力都可能影响实际收件。

此时应检查手机和运营商侧的接收情况,并继续遵守服务端返回的重发条件。

尚未注册的号码需要单独处理

号码尚未注册 Telegram,与账号已经注册但无法访问现有会话,需要分开处理。

SentCodeTypeApp 的协议含义是验证码已作为 Telegram 服务通知发送到其他已登录会话,不能据此判断号码未注册。如果结果与预期不符,应先核对号码和账号状态。Telegram 验证码类型说明

对于确实未注册的号码,收到验证码也不等于完成注册。Telethon 的 sign_up() 已被弃用,当前实现会直接抛出 ValueError。应先通过官方移动客户端按提示完成注册,再使用 Telethon 登录。Telethon sign_up() 实现

多个号码应各自使用独立的 session。成功登录后,可以复用对应的 session,减少重复请求验证码。若账号启用了两步验证,提交验证码后还需要处理 SessionPasswordNeededError 并输入账号密码。Telethon 登录文档

首次请求能否直接指定 SMS

首次请求的 CodeSettings 没有通用的 force_sms 字段。allow_flashcall、allow_missed_call 等参数描述客户端支持的能力,关闭它们也不能保证服务端发送 SMS。allow_app_hash 用于短信自动读取相关功能,不能用来禁止应用内验证码。CodeSettings 参数说明

如果服务端首次选择 SMS,就无需重发。首次返回应用内验证码时,只能根据 next_type 使用服务端允许的后续渠道。没有 SMS 选项时,需要通过官方客户端完成可用的验证流程。

备注:内容仅供参考。