You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python Telegram Bot中ConversationHandler在Webhook模式下无法正常工作的问题排查

问题分析

你的核心问题出在Webhook模式下每次请求都新建Dispatcher实例,导致ConversationHandler的会话状态无法持久化。

在Polling模式中,Dispatcher是长期运行的单例,会话状态会保存在它的存储中;但在你的Webhook代码里,每个POST请求都会调用setup()创建新的Dispatcher,之前的会话状态完全丢失,所以ConversationHandler无法跟踪用户当前处于哪个状态,自然不会触发后续的状态函数。

另外,你的回调函数里还有两个小错误,也会导致点击按钮无响应:

  1. 处理CallbackQuery时没有调用query.answer(),Telegram会认为回调未处理,按钮会一直处于加载状态。
  2. 在CallbackQueryHandler对应的函数中错误地使用update.message,而不是从update.callback_query中获取数据和操作消息。
解决方案

1. 全局初始化Dispatcher,避免每次请求重建

修改你的代码,在应用启动时就创建一次Dispatcher并注册所有处理程序,而不是每次请求都新建。

首先,调整全局变量和初始化逻辑:

from telegram.ext import Dispatcher, MemoryStorage

# 全局初始化Bot和Dispatcher
bot = telegram.Bot(token=TOKEN)
storage = MemoryStorage()  # 开发用,生产环境建议用持久化存储比如RedisStorage
dispatcher = Dispatcher(bot, storage=storage, workers=1)

def setup_dispatcher():
    """全局注册所有处理程序"""
    conv_handler = ConversationHandler(
        entry_points=[CommandHandler("start", start)],
        states={
            NEW_PROJECT: [CallbackQueryHandler(project_name)],
            PROJECT_NAME: [MessageHandler(Filters.regex(".*"), store_name_maybe_project_type)],
            PROJECT_TYPE: [CallbackQueryHandler(store_type_maybe_admin)]
        },
        fallbacks=[CommandHandler('cancel', cancel)],
    )
    dispatcher.add_handler(conv_handler)
    # 可以添加其他handler

# 应用启动时注册处理程序
setup_dispatcher()

然后修改respond函数,直接使用全局的Dispatcher处理更新:

@app.route(f"/{TOKEN}", methods=["POST"])
def respond():
    update = telegram.Update.de_json(request.get_json(force=True), bot)
    dispatcher.process_update(update)
    return "ok"

2. 修复CallbackQuery处理函数的错误

修正project_name函数:

必须调用query.answer(),并且使用query.message来回复消息:

def project_name(update, context):
    query = update.callback_query
    # 必须调用answer(),告知Telegram回调已处理
    query.answer()
    # 使用query.message来回复,而不是update.message
    query.message.reply_text(text="Okay, Please enter your project name:")
    return PROJECT_NAME

修正store_type_maybe_admin函数:

同样需要调用query.answer(),并且从query.data获取回调数据(而不是错误地用update.message.text):

def store_type_maybe_admin(update, context):
    query = update.callback_query
    query.answer()
    # 从callback_query中获取项目类型
    project_type = query.data
    keyboard = [[InlineKeyboardButton("Done", callback_data="done")]]
    reply_markup = InlineKeyboardMarkup(keyboard)
    query.message.reply_text(f"Make a private {project_type} and make this bot the admin", reply_markup=reply_markup)
    return ConversationHandler.END

3. 验证Webhook配置

确保你的set_webhook路由正确设置了Webhook地址,并且服务器可以被Telegram访问(公网IP、正确的端口,HTTPS等,Telegram要求Webhook必须用HTTPS)。

为什么这样能解决问题?
  • 全局Dispatcher:确保会话状态(比如用户当前处于哪个Conversation状态)被持久化存储在MemoryStorage(或其他存储)中,每次请求处理时都能读取到之前的状态。
  • CallbackQuery的正确处理:调用query.answer()是Telegram API的要求,否则会导致按钮无响应;同时正确使用update.callback_query的属性来获取数据和操作消息,避免报错。

内容的提问来源于stack exchange,提问作者Palash Singh Raghuwanshi

火山引擎 最新活动