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()
核心问题分析
- 多事件循环冲突:
use_async每次创建新asyncio事件循环,Telethon客户端绑定初始循环,反复切换会导致状态混乱,甚至触发Telegram安全机制,挤掉移动端登录。 - 方法调用错误:
client.is_connected是方法,需加括号调用client.is_connected(),原代码直接判断属性会导致逻辑错误。 - 返回值无效:
dial.insert(0, dials)返回None(insert无返回值),导致后续dial[0]索引报错,程序停滞。 - 参数传递混乱:
async_fun的i=4分支中,参数名与实际用途不符,且未将用户输入的编号转为整数,导致索引逻辑错误。 - 同步异步阻塞冲突:
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
相关产品推荐
相关产品推荐

