python-telegram-bot多输入处理异常求助:输入均存入同一变量
问题解决:用ConversationHandler实现Telegram机器人的分步设置流程
问题根源是你没有跟踪用户当前的操作状态,所有非命令消息都会被第一个匹配的MessageHandler处理。解决这个问题的核心是使用ConversationHandler,它能帮你管理用户和机器人的对话流程,精准跟踪用户当前处于哪个设置步骤。
修改后的完整代码
#!/usr/bin/env python import logging from telegram import Update from telegram.ext import ( filters, ConversationHandler, Application, MessageHandler, CommandHandler, ContextTypes, ) import config BOT_API_TOKEN = config.BOT_API_TOKEN logging.basicConfig( format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logging.getLogger("httpx").setLevel(logging.WARNING) logger = logging.getLogger(__name__) # 定义对话状态常量,每个状态对应一个设置流程 SET_FILE_PATH, SET_USERNAME, SET_PASSWORD, SET_HOST = range(4) class TelegramBot: def __init__(self) -> None: # 注意:多用户场景下建议改用context.user_data存储,避免不同用户数据冲突 self.file_path: str = "" self.username: str = "" self.password: str = "" self.host: str = "" async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: user = update.effective_user await update.message.reply_html(f"Hi {user.mention_html()}!") async def set_file_path_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: self.file_path = update.message.text await update.message.reply_html( f"File address that you set is:\n{self.file_path}" ) return ConversationHandler.END async def set_username_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: self.username = update.message.text await update.message.reply_html(f"Username that you set is:\n{self.username}") return ConversationHandler.END async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: self.password = update.message.text await update.message.reply_html(f"Password that you set is:\n{self.password}") return ConversationHandler.END async def set_host_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: self.host = update.message.text await update.message.reply_html(f"Host that you set is:\n{self.host}") return ConversationHandler.END async def echo_set_file_path(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: await update.message.reply_text("Enter the file path:") # 返回对应状态,告知ConversationHandler接下来监听该状态的输入 return SET_FILE_PATH async def echo_set_username(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: await update.message.reply_text("Enter the username:") return SET_USERNAME async def echo_set_password(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: await update.message.reply_text("Enter the password:") return SET_PASSWORD async def echo_set_host(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: await update.message.reply_text( "Enter the host(IP or URL e.g. 192.168.1.4 or test.com):" ) return SET_HOST async def end_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: await update.message.reply_html( f"You Enter '{self.file_path}' as a file path.\nYou Enter '{self.username}' as a username.\nYou Enter '{self.password}' as a password.\nYou Enter '{self.host}' as a host." ) # 可选:添加取消命令,用户可随时终止当前操作 async def cancel_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: await update.message.reply_text("Operation cancelled.") return ConversationHandler.END def main() -> None: application = Application.builder().token(BOT_API_TOKEN).build() bot = TelegramBot() # 创建对话处理器,统一管理所有设置流程 conv_handler = ConversationHandler( entry_points=[ CommandHandler("set_file_path", bot.echo_set_file_path), CommandHandler("set_username", bot.echo_set_username), CommandHandler("set_password", bot.echo_set_password), CommandHandler("set_host", bot.echo_set_host), ], states={ SET_FILE_PATH: [MessageHandler(filters.TEXT & ~filters.COMMAND, bot.set_file_path_command)], SET_USERNAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, bot.set_username_command)], SET_PASSWORD: [MessageHandler(filters.TEXT & ~filters.COMMAND, bot.set_password_command)], SET_HOST: [MessageHandler(filters.TEXT & ~filters.COMMAND, bot.set_host_command)], }, fallbacks=[CommandHandler("cancel", bot.cancel_command)], per_user=True # 确保每个用户的对话状态独立,互不干扰 ) application.add_handlers( [ CommandHandler("start", bot.start_command), CommandHandler("end", bot.end_command), conv_handler, # 添加对话处理器到应用 ] ) application.run_polling(allowed_updates=Update.ALL_TYPES) if __name__ == "__main__": main()
关键修改说明
- 对话状态标记:用
SET_FILE_PATH等常量区分不同的设置流程,ConversationHandler通过这些状态跟踪用户当前操作阶段。 - 命令函数返回状态:原
echo_set_xxx函数现在返回对应状态,让ConversationHandler知道接下来要监听该状态下的用户输入。 - ConversationHandler核心配置:
entry_points:触发各个设置流程的命令(如/set_username)。states:每个状态绑定专属的MessageHandler,只有用户处于对应状态时,该处理器才会响应输入。fallbacks:提供取消命令,用户发送/cancel可随时终止当前流程。per_user=True:保证多用户使用时,各自的对话状态独立,不会互相覆盖数据。
- 多用户优化提示:当前用类变量存储数据,多用户场景下会出现数据冲突。建议改用
context.user_data存储每个用户的信息,例如将self.file_path替换为context.user_data['file_path']。
内容的提问来源于stack exchange,提问作者negar amiri
相关产品推荐
相关产品推荐

