You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Telebot与Telethon结合异常:输入验证码后移动端崩溃

问题描述

我正在尝试将Telegram Bot与Telethon库结合,实现获取指定聊天的所有用户。流程为:用户发送/send命令启动Bot,Bot请求输入手机号,向该账号发送验证码,用户输入验证码后登录并获取对话列表。目前代码能正常读取手机号、发送验证码,但输入验证码后程序停滞,且移动端Telegram应用崩溃,需重启登录。

以下是原代码:

from telethon import TelegramClient                            
from telebot import TeleBot                                     
import asyncio                                                  

api_id = *******
api_hash = '******'
client=TelegramClient('anon', api_id, api_hash)                 

bot_token='********'
bot = TeleBot(token:=bot_token)

@bot.message_handler(commands=['send'])                          # 用户解析器
def send_adv(message): 
    bot.send_message(message.chat.id, '请输入您的手机号')
    bot.register_next_step_handler(message, get_phone)

def get_phone(message):

    phone=message.text                                          

    use_async(1, phone, None)

    bot.send_message(message.chat.id, '请输入您的验证码')

    bot.register_next_step_handler(message, lambda m: get_code(m, phone))
                                                            
def get_code(message, phone_num):

    chat=message.chat.id
    code=message.text
    print(message.text)
   
    use_async(2, phone_num, code)

    bot.send_message(chat, '正在获取您的对话列表')

    dial=use_async(3, None, None)

    bot.delete_message(chat, message.id+1)

    dials=dial[0]
    dial=dial[1:]

    bot.send_message(chat, dials)
    bot.send_message(chat, "请选择要解析用户的对话编号")

    bot.register_next_step_handler(message, lambda m: choose_dial(m, dialogs:=dial))        
                                                            
def choose_dial(message, dialogs):

    num=message.text

    if num>len(dialogs) or isinstance(num, int)==False:
        bot.send_message(message.chat.id, '请检查对话编号')
        bot.register_next_step_handler(message, lambda m: choose_dial(m, dialogs))  
        return None 

    users=use_async(4, dialogs, num)
    for user in users:
        print(user)

async def async_fun(i, phone_num, code_pas):                    # 代码中调用的异步函数

    if i==1:
        await client.connect()                                  # 连接Telegram
        await client.sign_in (phone_num)                        # 请求发送验证码
        return None

    elif i==2:                                                  
        if client.is_connected==False:
            await client.connect()                              # 未连接则连接Telegram
        await client.sign_in(phone_num, code_pas)               # 登录
        return None

    elif i==3:                                                  # 收集对话名称
        j=1
        dials=''
        dial=[]
        async for dialog in client.iter_dialogs():
            if dialog.is_group==True:                           
                dials+=(str(j)+' - '+dialog.name+"\n")          # Bot消息用的对话列表字符串
                dial.append(dialog.id)                          # 对话ID数组
                j+=1
        return dial.insert(0, dials)

    elif i==4:
        users=[]
        async for user in client.iter_participants(phone_num[code_pas-1]):
            if user.id!="":
                users.append (user.id)
        return users

    else:                                                      
        await client.log_out()                                  # 登出
        await client.disconnect()                               # 断开Telegram连接
        return None

def use_async(i, phone_num, code_pas):                          # 通过索引获取异步函数结果
     loop = asyncio.new_event_loop()                                                          
     asyncio.set_event_loop(loop)                                
     coroutine = async_fun(i, phone_num, code_pas)               # 获取函数结果
     loop.run_until_complete(coroutine)                          # 终止进程

bot.polling()

核心问题分析

  1. 多事件循环冲突:use_async每次创建新asyncio事件循环,Telethon客户端绑定初始循环,反复切换会导致状态混乱,甚至触发Telegram安全机制,挤掉移动端登录。
  2. 方法调用错误:client.is_connected是方法,需加括号调用client.is_connected(),原代码直接判断属性会导致逻辑错误。
  3. 返回值无效:dial.insert(0, dials)返回None(insert无返回值),导致后续dial[0]索引报错,程序停滞。
  4. 参数传递混乱:async_fun的i=4分支中,参数名与实际用途不符,且未将用户输入的编号转为整数,导致索引逻辑错误。
  5. 同步异步阻塞冲突:bot.polling()是阻塞式运行,与Telethon的异步操作在同线程中会互相阻塞,导致程序无响应。

修复后的代码示例

from telethon import TelegramClient
from telebot.async_telebot import AsyncTeleBot
import asyncio

api_id = *******
api_hash = '******'
client = TelegramClient('anon', api_id, api_hash)

bot_token = '********'
bot = AsyncTeleBot(bot_token)

# 存储用户临时数据:手机号、对话ID列表等
user_temp_data = {}

@bot.message_handler(commands=['send'])
async def send_adv(message):
    await bot.send_message(message.chat.id, '请输入您的手机号')
    bot.register_next_step_handler(message, get_phone)

async def get_phone(message):
    chat_id = message.chat.id
    phone = message.text.strip()
    user_temp_data[chat_id] = {'phone': phone}
    
    # 连接并请求验证码
    await client.connect()
    await client.sign_in(phone)
    
    await bot.send_message(chat_id, '请输入您的验证码')
    bot.register_next_step_handler(message, get_code)

async def get_code(message):
    chat_id = message.chat.id
    code = message.text.strip()
    phone_num = user_temp_data[chat_id]['phone']
    
    try:
        # 验证登录
        if not await client.is_connected():
            await client.connect()
        await client.sign_in(phone_num, code)
        
        await bot.send_message(chat_id, '正在获取您的对话列表')
        
        # 筛选群组对话
        index = 1
        dialog_str = ''
        dialog_ids = []
        async for dialog in client.iter_dialogs():
            if dialog.is_group:
                dialog_str += f"{index} - {dialog.name}\n"
                dialog_ids.append(dialog.id)
                index += 1
        
        user_temp_data[chat_id]['dialog_ids'] = dialog_ids
        await bot.send_message(chat_id, dialog_str)
        await bot.send_message(chat_id, "请选择要解析用户的对话编号")
        bot.register_next_step_handler(message, choose_dial)
        
    except Exception as e:
        await bot.send_message(chat_id, f"登录失败:{str(e)}")
        await client.disconnect()

async def choose_dial(message):
    chat_id = message.chat.id
    num_str = message.text.strip()
    
    try:
        num = int(num_str)
        dialog_ids = user_temp_data[chat_id]['dialog_ids']
        if num < 1 or num > len(dialog_ids):
            await bot.send_message(chat_id, '请输入有效的对话编号')
            bot.register_next_step_handler(message, choose_dial)
            return
        
        target_dialog_id = dialog_ids[num - 1]
        user_ids = []
        async for user in client.iter_participants(target_dialog_id):
            if user.id:
                user_ids.append(str(user.id))
        
        await bot.send_message(chat_id, f"获取到{len(user_ids)}个用户ID:\n{', '.join(user_ids)}")
        
    except ValueError:
        await bot.send_message(chat_id, '请输入数字编号')
        bot.register_next_step_handler(message, choose_dial)
    except Exception as e:
        await bot.send_message(chat_id, f"获取用户失败:{str(e)}")
    finally:
        # 清理连接与用户数据
        await client.log_out()
        await client.disconnect()
        user_temp_data.pop(chat_id, None)

async def main():
    # 统一异步环境,同时运行Telethon与Bot
    await asyncio.gather(
        client.start(),
        bot.polling(non_stop=True)
    )

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

关键修复说明

  • 统一异步环境:改用AsyncTeleBot,所有Bot处理函数改为异步,通过asyncio.gather同时运行Telethon与Bot的事件循环,避免多循环冲突。
  • 修正方法调用:将client.is_connected改为await client.is_connected(),正确判断连接状态。
  • 修复返回值逻辑:直接构建对话字符串与ID列表,避免insert导致的无效返回值。
  • 规范参数处理:用户输入的编号先转为整数,再做合法性检查,确保对话ID索引正确。
  • 用户数据管理:用字典存储用户临时数据,避免全局变量混乱。
  • 异常捕获处理:增加异常捕获,避免程序崩溃,同时在出错时清理连接与用户数据。

内容的提问来源于stack exchange,提问作者Alexander Odnoromanenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 00:50:38