如何在aiogram机器人中处理城市选择环节的文本输入异常
问题解决:Telegram机器人城市选择的输入校验与流程维持
问题分析
你当前代码的核心问题是误解了aiogram的事件驱动+状态机工作模式:
- 在
button_press回调里用while True循环是错误的,aiogram是异步事件模型,循环会阻塞整个Dispatcher的事件处理 - 直接调用
get_answer_correct函数无效,这个函数是消息处理器,由Dispatcher在收到对应状态的消息时自动触发,不能直接调用获取返回值 one_time_keyboard=True会导致用户输入后键盘自动收起,不符合重复显示键盘的需求
修正后的代码
from aiogram import Bot, Dispatcher, executor, types from aiogram.dispatcher.filters.state import StatesGroup, State from aiogram.contrib.fsm_storage.memory import MemoryStorage from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton import os bot = Bot(token=os.getenv("TOKEN"), parse_mode=types.ParseMode.HTML) dp = Dispatcher(bot, storage=MemoryStorage()) # 提取城市列表为全局常量,方便复用 ALLOWED_CITIES = ['Washington', 'Montgomery', 'Phoenix', 'Atlanta', 'Boise', 'Frankfort', 'Boston', 'Springfield', 'Helena', 'Nashville'] # 状态类定义 class UserAnswer(StatesGroup): City = State() @dp.message_handler(commands="start") async def start(message: types.Message): markup = InlineKeyboardMarkup() start_button = InlineKeyboardButton(text='Select a city', callback_data="town_id") markup.add(start_button) await bot.send_message(message.chat.id, "Hello from the bot!", reply_markup=markup) # 生成城市选择键盘的工具函数,避免重复代码 def get_city_keyboard(): keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True) # 移除one_time_keyboard,保持键盘显示 for name in ALLOWED_CITIES: keyboard.row(types.KeyboardButton(name)) return keyboard @dp.callback_query_handler(lambda c: c.data == "town_id") async def button_press(call: types.CallbackQuery): await bot.answer_callback_query(call.id) # 发送城市选择提示并进入City状态 await bot.send_message(call.message.chat.id, "Your city:", reply_markup=get_city_keyboard()) await UserAnswer.City.set() @dp.message_handler(state=UserAnswer.City) async def process_city_input(message: types.Message, state: dp.FSMContext): user_input = message.text.strip() if user_input in ALLOWED_CITIES: # 输入正确:收起键盘、确认选择、结束状态 await bot.send_message(message.chat.id, f"You selected: {user_input}", reply_markup=types.ReplyKeyboardRemove()) # 此处可添加后续业务逻辑(如保存城市信息) await state.finish() else: # 输入错误:提示用户并重新显示键盘,保持在City状态等待输入 await bot.send_message(message.chat.id, "Invalid city! Please select from the list or enter the exact city name:", reply_markup=get_city_keyboard()) def main(): executor.start_polling(dp, skip_updates=True) if __name__ == '__main__': main()
关键改进点
- 移除同步循环:利用FSM状态维持实现"循环等待正确输入"的逻辑,完全适配aiogram的异步事件模型
- 状态机正确使用:在
process_city_input处理器中直接处理用户输入,根据合法性决定结束状态或继续等待 - 键盘优化:去掉
one_time_keyboard=True,让键盘保持显示直到用户输入正确后主动移除;提取键盘生成逻辑为工具函数,减少冗余代码 - 精确输入校验:直接判断输入是否在允许的城市列表中(若需要模糊匹配,可调整校验逻辑为子串匹配)
额外扩展提示
如果需要支持模糊匹配(比如输入"wash"自动匹配"Washington"),可修改校验逻辑为:
matched_cities = [city for city in ALLOWED_CITIES if user_input.lower() in city.lower()] if matched_cities: if len(matched_cities) == 1: selected_city = matched_cities[0] # 执行正确选择后的逻辑 else: # 若匹配多个城市,可发送列表让用户再次选择 await bot.send_message(message.chat.id, f"Multiple matches found: {', '.join(matched_cities)}. Please enter the exact name.") else: # 无匹配时提示错误 await bot.send_message(message.chat.id, "Invalid city! Please select from the list or enter the exact city name:", reply_markup=get_city_keyboard())
内容的提问来源于stack exchange,提问作者Vlad
相关产品推荐
相关产品推荐

