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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 06:27:09