Telegram Bot嵌入带链接按钮遇KeyError:问题排查与求助
Telegram Bot添加带链接按钮功能的KeyError问题解决
问题描述
我正在Telegram Bot中开发一个可为任意文本消息添加带链接按钮的功能,核心代码如下:
message_user_send = {} name_of_url = {} url_user_send = {} ############################ @bot.message_handler(commands=['aaa']) def send(message): msg1 = bot.send_message(message.chat.id, 'Put main text') bot.register_next_step_handler(msg1, name_of_link) def name_of_link(message): message_user_send[message.text] = message.text msg1 = bot.send_message(message.chat.id, 'Put name of url') bot.register_next_step_handler(msg1, send_alll) def send_alll(message): name_of_url[message.text] = message.text msg1 = bot.send_message(message.chat.id, 'Put url') bot.register_next_step_handler(msg1, send_all_userss) def send_all_userss(message): url_user_send[message.text] = message.text markup = types.InlineKeyboardMarkup() b1 = types.InlineKeyboardButton(name_of_url[message.text], url= url_user_send[message.text]) markup.add(b1) keyboard_markup = types.ReplyKeyboardMarkup(resize_keyboard=True) bot.send_message(message.chat.id, message_user_send, reply_markup=markup) ############################
但尝试使用name_of_url[message.text]作为按钮文本时出现错误,报错信息:
line 85, in send_all_userss b1 = types.InlineKeyboardButton(name_of_url[message.text], url= url_user_send[message.text]) ~~~~~~~~~~~^^^^^^^^^^^^^^ KeyError: 'https://stackoverflow.com/questions/1871524/how-can-i-convert-json-to-csv'
若直接使用固定字符串作为按钮文本则可正常运行,预期实现效果是消息下方显示一个按钮,按钮文本为用户此前输入的内容。
问题原因
你用全局字典存储用户输入的逻辑完全错误:
- 在
name_of_link中,你把用户输入的主文本同时作为键和值存入message_user_send,后续也没有正确取出该值; - 在
send_alll中,你把用户输入的按钮文本作为键和值存入name_of_url; - 到
send_all_userss时,你拿当前用户输入的URL去name_of_url中查找键——但name_of_url的键是按钮文本,不是URL,自然找不到对应值,触发KeyError。
另外,全局字典还存在并发问题,多个用户同时使用时数据会互相覆盖。
正确解决方案
推荐使用用户会话存储每一步输入,或者通过register_next_step_handler传递参数,这里用会话的方式更规范:
修改后的代码(标准方案)
from telegram import InlineKeyboardButton, InlineKeyboardMarkup from telegram.ext import CommandHandler, MessageHandler, Filters, ConversationHandler # 定义会话状态 MAIN_TEXT, BUTTON_TEXT, BUTTON_URL = range(3) @bot.message_handler(commands=['aaa']) def start_add_button(message, update): msg = bot.send_message(message.chat.id, '输入消息主文本') return MAIN_TEXT @bot.message_handler(func=lambda msg: True, state=MAIN_TEXT) def get_main_text(message, update): # 将主文本存入当前用户的会话数据 user_data = update.user_data user_data['main_text'] = message.text msg = bot.send_message(message.chat.id, '输入按钮显示文本') return BUTTON_TEXT @bot.message_handler(func=lambda msg: True, state=BUTTON_TEXT) def get_button_text(message, update): user_data = update.user_data user_data['button_text'] = message.text msg = bot.send_message(message.chat.id, '输入按钮跳转链接') return BUTTON_URL @bot.message_handler(func=lambda msg: True, state=BUTTON_URL) def send_final_message(message, update): user_data = update.user_data button_url = message.text # 构建带链接的按钮 markup = InlineKeyboardMarkup() button = InlineKeyboardButton(user_data['button_text'], url=button_url) markup.add(button) # 发送最终消息 bot.send_message( message.chat.id, user_data['main_text'], reply_markup=markup ) # 结束会话 return ConversationHandler.END # 注册会话处理器 conv_handler = ConversationHandler( entry_points=[CommandHandler('aaa', start_add_button)], states={ MAIN_TEXT: [MessageHandler(Filters.text & ~Filters.command, get_main_text)], BUTTON_TEXT: [MessageHandler(Filters.text & ~Filters.command, get_button_text)], BUTTON_URL: [MessageHandler(Filters.text & ~Filters.command, send_final_message)], }, fallbacks=[], ) bot.add_handler(conv_handler)
关键修改点
- 使用
ConversationHandler管理多步输入流程,每个用户的输入独立存储在user_data中,不会互相干扰; - 每一步输入都存入会话数据,最后统一取出使用,彻底避免KeyError;
- 流程符合Telegram Bot的标准开发模式,可读性和可维护性更强。
临时修复方案(无需重构架构)
如果暂时不想改用会话处理器,也可以调整全局字典的存储逻辑,用用户ID作为区分键:
# 全局字典,以用户ID为键存储各用户的输入 user_inputs = {} @bot.message_handler(commands=['aaa']) def send(message): user_id = message.chat.id user_inputs[user_id] = {} # 初始化当前用户的存储容器 msg1 = bot.send_message(user_id, 'Put main text') bot.register_next_step_handler(msg1, name_of_link) def name_of_link(message): user_id = message.chat.id user_inputs[user_id]['main_text'] = message.text msg1 = bot.send_message(user_id, 'Put name of url') bot.register_next_step_handler(msg1, send_alll) def send_alll(message): user_id = message.chat.id user_inputs[user_id]['button_text'] = message.text msg1 = bot.send_message(user_id, 'Put url') bot.register_next_step_handler(msg1, send_all_userss) def send_all_userss(message): user_id = message.chat.id user_inputs[user_id]['button_url'] = message.text markup = types.InlineKeyboardMarkup() b1 = types.InlineKeyboardButton(user_inputs[user_id]['button_text'], url=user_inputs[user_id]['button_url']) markup.add(b1) bot.send_message(user_id, user_inputs[user_id]['main_text'], reply_markup=markup) # 可选:删除用户数据,避免内存占用 del user_inputs[user_id]
这个方案通过用户ID区分不同用户的输入,所有数据都存在对应用户的子字典中,最后直接取出对应字段即可,不会出现KeyError。
内容的提问来源于stack exchange,提问作者WinnieNotThePooh
相关产品推荐
相关产品推荐

