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

如何在aiogram(Telegram API)中正确触发指定handler函数

问题根源

你当前的写法存在两个核心问题:

  • 动态注册的handler匹配优先级问题:你在start函数中先注册了绑定state="*"的options handler,aiogram会按照handler注册的先后顺序匹配,用户后续输入内容会优先命中更早注册的options,根本不会走到后续注册的workingWithLinks
  • 动态注册handler的逻辑缺陷:每次触发start、options函数都会重复注册对应的handler,多用户使用时会出现严重的逻辑混乱,aiogram本身不推荐这种在业务函数内动态注册handler的写法

解决方法

所有handler统一在程序启动阶段注册,通过状态机的状态切换来区分不同业务阶段的触发条件,具体修改如下:

  1. 首先修改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()
  1. 调整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,提问作者Рэм Кудусов

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 08:36:03