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

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()

关键修改说明

  1. 对话状态标记:用SET_FILE_PATH等常量区分不同的设置流程,ConversationHandler通过这些状态跟踪用户当前操作阶段。
  2. 命令函数返回状态:原echo_set_xxx函数现在返回对应状态,让ConversationHandler知道接下来要监听该状态下的用户输入。
  3. ConversationHandler核心配置:
    • entry_points:触发各个设置流程的命令(如/set_username)。
    • states:每个状态绑定专属的MessageHandler,只有用户处于对应状态时,该处理器才会响应输入。
    • fallbacks:提供取消命令,用户发送/cancel可随时终止当前流程。
    • per_user=True:保证多用户使用时,各自的对话状态独立,不会互相覆盖数据。
  4. 多用户优化提示:当前用类变量存储数据,多用户场景下会出现数据冲突。建议改用context.user_data存储每个用户的信息,例如将self.file_path替换为context.user_data['file_path']。

内容的提问来源于stack exchange,提问作者negar amiri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 03:22:35