login.py 讲解

这个文件用于首次登录 Telegram,获取 Session String。


原始代码

import asyncio
from telethon import TelegramClient
from telethon.errors import SessionPasswordNeededError
from telethon.sessions import StringSession

from config import API_ID, API_HASH, PHONE, PASSWORD, PROXY


async def main():
    print("=" * 50)
    print("Telegram Userbot 登录工具")
    print("=" * 50)

    client = TelegramClient(
        StringSession(),
        API_ID,
        API_HASH,
        proxy=PROXY
    )

    await client.connect()

    if await client.is_user_authorized():
        print("已经登录!")
        session_string = client.session.save()
        print(f"\n你的 Session String:\n{session_string}\n")
        print("请保存上面的 Session String 到 config.py 中")
    else:
        print(f"正在向 {PHONE} 发送验证码...")

        try:
            sent_code = await client.send_code_request(PHONE)
            print("验证码已发送!")
            print("- 检查 Telegram App 内通知")
            print("- 检查短信")
            print("- 等待语音电话")

            code = input("\n请输入验证码: ").strip()

            try:
                await client.sign_in(PHONE, code, phone_code_hash=sent_code.phone_code_hash)
                print("\n登录成功!")

                session_string = client.session.save()
                print(f"\n你的 Session String:\n{session_string}\n")
                print("请保存上面的 Session String 到 config.py 中")

            except SessionPasswordNeededError:
                print("\n该账号开启了两步验证")
                pwd = input("请输入两步验证密码: ").strip()
                await client.sign_in(password=pwd)

                session_string = client.session.save()
                print(f"\n登录成功!\nSession String:\n{session_string}\n")

        except Exception as e:
            print(f"登录失败: {e}")

    await client.disconnect()


if __name__ == "__main__":
    asyncio.run(main())

逐行讲解

第 1-4 行:导入模块

import asyncio
from telethon import TelegramClient
from telethon.errors import SessionPasswordNeededError
from telethon.sessions import StringSession

拆解:

语句 说明
import asyncio 导入 Python 内置的异步模块
from telethon import TelegramClient 从 telethon 库导入 Telegram 客户端类
from telethon.errors import SessionPasswordNeededError 导入特定的错误类型
from telethon.sessions import StringSession 导入字符串会话类

telethon 是什么?

  • 一个第三方 Telegram 客户端库
  • 让你用代码控制 Telegram 账号
  • 需要先安装:pip install telethon

第 6 行:从 config 导入

from config import API_ID, API_HASH, PHONE, PASSWORD, PROXY
  • config.py 文件导入多个变量
  • 这样就可以使用配置文件中的凭证

第 9 行:异步函数定义

async def main():

关键概念:async def

  • def 定义普通函数
  • async def 定义异步函数
  • 异步函数可以在等待网络请求时做其他事,不阻塞程序

为什么用异步?

  • 网络操作很慢(可能需要几秒)
  • 异步让程序在等待时可以处理其他任务
  • 类似 JavaScript 的 Promise/async-await

第 10-12 行:打印分隔线

print("=" * 50)
print("Telegram Userbot 登录工具")
print("=" * 50)

"=" * 50 是什么?

  • Python 中字符串可以和数字相乘
  • 结果是重复 50 次:"=================================================="
  • 这是一个很实用的技巧

第 14-19 行:创建客户端

client = TelegramClient(
    StringSession(),
    API_ID,
    API_HASH,
    proxy=PROXY
)

拆解:

  • TelegramClient(...) - 创建一个 Telegram 客户端实例
  • StringSession() - 创建一个空的字符串会话(首次登录用)
  • API_ID, API_HASH - 你的 API 凭证
  • proxy=PROXY - 代理设置(可选参数,用参数名传递)

多行括号:

  • Python 允许在括号内换行
  • 这样代码更易读

第 21 行:连接服务器

await client.connect()

await 是什么?

  • await 表示"等待这个异步操作完成"
  • 只能在 async def 函数内使用
  • 程序会在这里暂停,直到连接成功或失败

流程:

调用 connect() → 等待网络连接 → 连接成功后继续

第 23-28 行:检查是否已登录

if await client.is_user_authorized():
    print("已经登录!")
    session_string = client.session.save()
    print(f"\n你的 Session String:\n{session_string}\n")
    print("请保存上面的 Session String 到 config.py 中")

逻辑:

  1. is_user_authorized() - 检查是否已登录
  2. 如果已登录,获取并打印 Session String
  3. client.session.save() - 保存会话为字符串

f"\n..." 中的 \n

  • \n 是换行符
  • 在打印时会产生空行

第 29-59 行:未登录时的处理

else:
    print(f"正在向 {PHONE} 发送验证码...")

    try:
        sent_code = await client.send_code_request(PHONE)
        # ... 更多代码
    except Exception as e:
        print(f"登录失败: {e}")

try-except 是什么?

  • 尝试执行某些代码
  • 如果出错,跳转到 except 块处理
  • 类似其他语言的 try-catch

流程图:

try:
    发送验证码
    ↓
    输入验证码
    ↓
    登录
    ↓
    成功!
except:
    处理错误

第 32 行:发送验证码请求

sent_code = await client.send_code_request(PHONE)
  • 向手机号发送验证码
  • 返回的 sent_code 包含验证码的哈希值(后续登录需要)

第 36-39 行:打印提示信息

print("验证码已发送!")
print("- 检查 Telegram App 内通知")
print("- 检查短信")
print("- 等待语音电话")

简单的打印语句,提示用户在哪里查看验证码。


第 41 行:用户输入

code = input("\n请输入验证码: ").strip()

input() 函数:

  • 程序暂停,等待用户输入
  • 用户按回车后,返回输入的字符串

.strip() 方法:

  • 去除字符串首尾的空白字符
  • 防止用户不小心输入了多余的空格

第 43-48 行:用验证码登录

try:
    await client.sign_in(PHONE, code, phone_code_hash=sent_code.phone_code_hash)
    print("\n登录成功!")

    session_string = client.session.save()
    print(f"\n你的 Session String:\n{session_string}\n")
    print("请保存上面的 Session String 到 config.py 中")

sign_in() 参数:

  • PHONE - 手机号
  • code - 用户输入的验证码
  • phone_code_hash - 发送验证码时返回的哈希值

第 50-56 行:处理两步验证

except SessionPasswordNeededError:
    print("\n该账号开启了两步验证")
    pwd = input("请输入两步验证密码: ").strip()
    await client.sign_in(password=pwd)

    session_string = client.session.save()
    print(f"\n登录成功!\nSession String:\n{session_string}\n")

逻辑:

  1. 如果账号开启了两步验证,sign_in 会抛出 SessionPasswordNeededError
  2. 捕获这个错误,要求用户输入密码
  3. 用密码再次尝试登录

第 58 行:断开连接

await client.disconnect()
  • 关闭与 Telegram 服务器的连接
  • 这是良好的编程习惯:用完资源要释放

第 61-62 行:程序入口

if __name__ == "__main__":
    asyncio.run(main())

__name__ == "__main__" 是什么?

  • 每个 Python 文件都有一个内置变量 __name__
  • 如果直接运行这个文件,__name__ 等于 "__main__"
  • 如果是被其他文件导入,__name__ 等于文件名

作用:

  • 这段代码只在直接运行时执行
  • 被导入时不会执行

asyncio.run(main())

  • 运行异步函数 main()
  • asyncio.run() 是启动异步程序的入口

执行流程图

开始
  ↓
打印标题
  ↓
创建 TelegramClient
  ↓
连接服务器 ──────→ 等待...
  ↓
已登录? ──是──→ 获取 Session String → 打印 → 结束
  │
  否
  ↓
发送验证码请求
  ↓
等待用户输入验证码
  ↓
尝试登录
  ↓
需要两步验证? ──是──→ 输入密码 → 登录
  │
  否
  ↓
获取 Session String
  ↓
打印
  ↓
断开连接
  ↓
结束

关键知识点总结

概念 语法 说明
异步函数 async def 可以使用 await 的函数
等待异步操作 await 暂停直到操作完成
运行异步函数 asyncio.run() 程序入口
错误处理 try-except 捕获并处理异常
用户输入 input() 获取用户输入
字符串乘法 "=" * 50 重复字符串
去空白 .strip() 去除首尾空白

练习

  1. 尝试运行这个文件:python login.py
  2. 思考:为什么网络操作要用 await
  3. 修改打印信息,添加你的名字