如何基于python-telegram-bot与asyncio实现可顺序等待用户输入的组件?
实现支持顺序await用户输入的Telegram机器人组件库
需求与问题描述
想要构建一套Telegram机器人组件库,支持按顺序执行组件,每个组件可await用户输入后再继续后续代码,示例用法如下:
fname = await Question().run('Enter first name') lname = await Question().run('Enter last name')
同时需保证代码中其他协程可正常运行。但当前实现的Question类中,run方法通过while循环轮询self.result并配合asyncio.sleep(1)等待输入,会导致无法接收用户消息(事件循环被无意义的轮询占用,无法及时处理on_message回调)。
解决方案:使用asyncio.Future替代轮询
核心思路是利用asyncio.Future对象异步等待结果,而非通过循环sleep轮询。当用户发送消息时,在回调中直接设置Future的结果,run方法只需await该Future即可,既不会阻塞事件循环,也能高效响应输入。
以下是改进后的Question类实现(基于python-telegram-bot v13+):
import asyncio from telegram import Update from telegram.ext import CallbackContext, MessageHandler, Filters class Question: def __init__(self): self.future = asyncio.Future() self.handler = None async def on_message(self, update: Update, context: CallbackContext): # 仅在Future未完成时设置结果,避免重复触发 if not self.future.done(): self.future.set_result(update.message.text) # 移除临时注册的handler,防止后续消息干扰 context.application.remove_handler(self.handler) async def run(self, prompt: str, update: Update, context: CallbackContext): # 创建仅监听当前用户文本消息的handler self.handler = MessageHandler( Filters.user(user_id=update.effective_user.id) & Filters.text, self.on_message ) # 注册handler到机器人应用 context.application.add_handler(self.handler) # 发送提示消息给用户 await update.message.reply_text(prompt) # 等待用户输入,此操作会让出事件循环,不阻塞其他协程 result = await self.future return result
使用示例
async def start_conversation(update: Update, context: CallbackContext): # 按顺序发起提问 fname = await Question().run('请输入你的名字', update, context) lname = await Question().run('请输入你的姓氏', update, context) await update.message.reply_text(f"你好,{fname} {lname}!")
额外优化:添加超时处理
若需要限制用户输入的时间,可通过asyncio.wait_for为Future添加超时机制:
async def run(self, prompt: str, update: Update, context: CallbackContext, timeout: int = 60): self.handler = MessageHandler( Filters.user(user_id=update.effective_user.id) & Filters.text, self.on_message ) context.application.add_handler(self.handler) await update.message.reply_text(prompt) try: result = await asyncio.wait_for(self.future, timeout=timeout) return result except asyncio.TimeoutError: # 超时后移除handler并提示用户 context.application.remove_handler(self.handler) await update.message.reply_text("输入超时,请重新发起请求。") raise # 抛出异常让调用方处理
注意事项
- 确保使用的Telegram机器人库支持动态添加/移除handler(如python-telegram-bot v13+及以上版本)。
- 若使用python-telegram-bot v20+版本,API结构有调整,需对应修改handler的注册方式,但核心的Future等待逻辑保持一致。
内容的提问来源于stack exchange,提问作者Lex Podgorny
相关产品推荐
相关产品推荐

