如何在aiogram(Telegram API)中正确触发指定handler函数
问题根源
你当前的写法存在两个核心问题:
- 动态注册的handler匹配优先级问题:你在
start函数中先注册了绑定state="*"的optionshandler,aiogram会按照handler注册的先后顺序匹配,用户后续输入内容会优先命中更早注册的options,根本不会走到后续注册的workingWithLinks - 动态注册handler的逻辑缺陷:每次触发
start、options函数都会重复注册对应的handler,多用户使用时会出现严重的逻辑混乱,aiogram本身不推荐这种在业务函数内动态注册handler的写法
解决方法
所有handler统一在程序启动阶段注册,通过状态机的状态切换来区分不同业务阶段的触发条件,具体修改如下:
- 首先修改
states.py,新增通用流程状态组:
from aiogram.dispatcher.filters.state import State, StatesGroup # 新增通用流程状态组 class CommonStates(StatesGroup): waiting_for_first_action = State() waiting_for_secret_choice = State() class SecretOne(StatesGroup): step_one = State() step_two = State() step_three = State() class SecretTwo(StatesGroup): step_one = State() step_two = State() step_three = State() class SecretThree(StatesGroup): step_one = State() step_two = State() step_three = State()
- 调整
main.py的逻辑,删除所有业务函数内的动态注册代码,为每个handler绑定对应状态:
from aiogram.dispatcher.filters import Text from aiogram.dispatcher import FSMContext from database import Database from states import * from aiogram import Bot, Dispatcher, executor, types bot = Bot(token="my_token") dp = Dispatcher(bot) @dp.message_handler(commands=["start", "update"], state="*") async def start(message: types.Message, state: FSMContext): # 进入前先重置状态,避免旧状态影响 await state.finish() keyboard1 = types.InlineKeyboardMarkup() links = ["one", "two", "three"] for row in links: # 补充callback_data,后续可新增回调处理逻辑响应按钮点击 button = types.InlineKeyboardButton(text=row, callback_data=f"select_{row}") keyboard1.add(button) await message.answer("Chose the phrase", reply_markup=keyboard1) # 切换到等待首次输入状态 await CommonStates.waiting_for_first_action.set() # 绑定首次输入状态,只有在该状态下的输入才会进入该handler @dp.message_handler(state=CommonStates.waiting_for_first_action) async def options(message: types.Message, state: FSMContext): if message.text == "Secret phrase": keyboard = types.ReplyKeyboardMarkup(one_time_keyboard=True) keyboard.add(types.KeyboardButton(text="Secret 1"), types.KeyboardButton(text="Secret 2"), types.KeyboardButton(text="Secret 3"), types.KeyboardButton(text="Main menu")) await message.answer("Chose the phrase", reply_markup=keyboard) # 切换到等待选择秘密选项状态 await CommonStates.waiting_for_secret_choice.set() else: await message.answer("This command is error, for phrases update call command /update") # 绑定秘密选项选择状态,只有在该状态下的输入才会进入该handler @dp.message_handler(state=CommonStates.waiting_for_secret_choice) async def workingWithLinks(message: types.Message, state: FSMContext): if message.text == "Secret 1": await message.answer("This is secret number 1") await SecretOne.step_one.set() elif message.text == "Secret 2": await SecretTwo.step_one.set() await message.answer("This is secret 2") elif message.text == "Secret 3": await SecretThree.step_one.set() await message.answer("This is secret 3") elif message.text == "Main menu": # 返回主菜单逻辑 await start(message, state) else: await message.answer("This command is error, for phrases update call command /update") if __name__ == "__main__": executor.start_polling(dp, skip_updates=True)
补充说明
你之前写的register_handlers_common函数已经可以废弃,直接用装饰器绑定命令和状态即可,代码更清晰易维护。另外你之前的内联按钮没有配置callback_data,如果需要响应按钮点击,额外加callback_query_handler处理对应回调即可。
内容的提问来源于stack exchange,提问作者Рэм Кудусов
相关产品推荐
相关产品推荐

