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

Aiogram FSM状态异常求助:菜单按钮需双击触发功能

解决Aiogram FSM状态下点击功能按钮需双击的问题

问题原因

当前代码中,处于Test.Q1状态时,用户点击ReplyKeyboard的cancel或Photo按钮,消息会被通用的stop_state处理器捕获,仅执行state.finish()终止状态,但未触发按钮对应的功能逻辑。而原有的photo和cancel处理器仅在state=None时生效,第一次点击时状态尚未终止,因此无法触发功能,第二次点击时状态已变为None,才会执行对应逻辑,导致需要双击。

解决方案

移除通用的stop_state处理器,为cancel和Photo按钮的处理器添加状态兼容(支持state=None和state=Test.Q1),在处理器内部先终止当前状态(如果存在),再执行功能逻辑。

修改后的完整代码

poster = ['any url', 'any url', 'any url']
name = ['test1', 'test2', 'test3']

storage = MemoryStorage()
bot = Bot(TOKEN)
dp = Dispatcher(bot=bot,
                storage=storage)


class Test(StatesGroup):
    Q1 = State()


next_button = InlineKeyboardButton(text='next', callback_data='next')
back_button = InlineKeyboardButton(text='back', callback_data='back')

nbtn_callback = [next_button.callback_data, back_button.callback_data]

nbtn = InlineKeyboardMarkup(row_width=True)
nbtn_next = InlineKeyboardMarkup(row_width=True)
nbtn_next.add(next_button)
nbtn.row(back_button, next_button)

a = KeyboardButton('cancel')
b = KeyboardButton('Photo')

menu = ReplyKeyboardMarkup(resize_keyboard=True)
menu.row(a, b)


@dp.message_handler(commands=['start'])
async def start_bot(message: types.Message):
    txt = "Hello!\n"
    await message.answer(txt, reply_markup=menu)
    await message.delete()


@dp.message_handler(text='Photo', state=[None, Test.Q1])
async def photo(message: types.Message, state: FSMContext):
    # 如果当前处于状态,先终止
    if await state.get_state() is not None:
        await state.finish()
    reply_markup = nbtn_next
    await Test.Q1.set()
    await bot.send_photo(
        message.chat.id,
        photo=poster[0],
        reply_markup=reply_markup,
        caption=name[0],
    )


@dp.message_handler(text='cancel', state=[None, Test.Q1])
async def cancel(message: types.Message, state: FSMContext):
    # 如果当前处于状态,先终止
    if await state.get_state() is not None:
        await state.finish()
    ph = "any url photo"
    await bot.send_photo(chat_id=message.chat.id,
                         photo=ph)


@dp.callback_query_handler(state=Test.Q1)
async def photo_update(callback: types.CallbackQuery, state: FSMContext):
    current_data = await state.get_data()
    current_index = current_data.get("answer", 0)
    
    if callback.data == "next":
        current_index = min(current_index + 1, len(poster)-1)
    elif callback.data == "back":
        current_index = max(current_index - 1, 0)
    
    await state.update_data({"answer": current_index})
    file_path = poster[current_index]
    file = InputMedia(media=file_path, caption=name[current_index])
    # 根据当前索引决定显示的按钮
    reply_markup = nbtn_next if current_index == 0 else nbtn
    await callback.message.edit_media(file, reply_markup=reply_markup)
    await callback.answer()

关键改动说明

  1. 移除通用stop_state处理器:避免拦截所有文本消息,导致按钮功能无法触发。
  2. 扩展按钮处理器的状态范围:将photo和cancel的处理器改为state=[None, Test.Q1],使其在无状态和Test.Q1状态下都能响应。
  3. 处理器内终止状态:在photo和cancel函数开头判断当前状态,若存在则先执行state.finish(),再执行功能逻辑,确保一次点击完成状态切换和功能触发。
  4. 完善分页逻辑:补充了back按钮的处理逻辑,修复原代码中索引计算的潜在问题(比如使用poster.index(file_path)可能因重复URL导致错误,改为直接维护索引),并根据当前索引动态切换按钮布局。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 20:35:16