如何在aiogram中从键盘按钮向callback_query传递kwargs?
解决aiogram中Callback Query传递参数的问题
你的代码存在两个核心问题:
InlineKeyboardButton不存在kwargs参数,无法通过该方式传递自定义数据;- Callback Query的处理函数仅能接收
call参数,无法直接声明额外的text、link参数。
下面提供两种可行的解决方案:
方案1:将参数序列化到callback_data中
Telegram允许callback_data存储字符串,我们可以把需要传递的参数序列化为JSON字符串(注意:callback_data长度不能超过64字节,过长数据请用方案2)。
修改后的代码:
import json from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton async def handler(message: types.Message): user_id = message.from_user.id keyboard = InlineKeyboardMarkup() try: parser = Parser(message=message) for i in parser.parse(): # 序列化参数为JSON字符串,添加类型前缀方便识别 callback_data = json.dumps({ "type": "answer", "text": i[1][0], "link": hlink(i[0], i[1][1]) }) # 检查长度,避免超过Telegram的64字节限制 if len(callback_data) > 64: raise ValueError("Callback data too long, use state storage instead") keyboard.add(InlineKeyboardButton(text=i[0], callback_data=callback_data)) await message.answer("Here are the results:", reply_markup=keyboard) except (PageNotAccessible, TermNotFound, TooManyResults, ValueError) as e: await bot.send_message(chat_id=user_id, text=str(e)) @dp.callback_query_handler(lambda call: json.loads(call.data).get("type") == "answer") async def answer(call): # 解析JSON字符串获取参数 data = json.loads(call.data) text = data["text"] link = data["link"] # 发送消息,记得指定parse_mode渲染HTML标签 await call.message.answer(f"{text}\n<i>{link}</i>", parse_mode="HTML") # 必须调用call.answer(),否则按钮会一直处于加载状态 await call.answer()
方案2:使用状态存储传递长数据
如果参数过长超过callback_data的长度限制,可以用aiogram的状态管理(FSM)或外部存储(如Redis)保存数据,callback_data仅传递数据的标识。
示例(使用FSM):
首先定义状态:
from aiogram.dispatcher.filters.state import State, StatesGroup from aiogram.dispatcher import FSMContext class AnswerStates(StatesGroup): waiting_for_answer = State()
然后修改消息处理函数:
async def handler(message: types.Message, state: FSMContext): user_id = message.from_user.id keyboard = InlineKeyboardMarkup() try: parser = Parser(message=message) results = parser.parse() # 将所有结果存入用户状态,用索引作为标识 answer_results = { str(idx): {"text": item[1][0], "link": hlink(item[0], item[1][1])} for idx, item in enumerate(results) } await state.update_data(answer_results=answer_results) for idx, item in enumerate(results): keyboard.add(InlineKeyboardButton(text=item[0], callback_data=f"answer:{idx}")) await message.answer("Here are the results:", reply_markup=keyboard) # 设置状态,确保回调函数能获取到存储的数据 await AnswerStates.waiting_for_answer.set() except (PageNotAccessible, TermNotFound, TooManyResults) as e: await bot.send_message(chat_id=user_id, text=str(e))
最后修改回调处理函数:
@dp.callback_query_handler(lambda call: call.data.startswith("answer:"), state=AnswerStates.waiting_for_answer) async def answer(call, state: FSMContext): # 从callback_data中提取索引 idx = call.data.split(":")[1] # 从状态中取出对应的数据 data = await state.get_data() answer_data = data["answer_results"][idx] await call.message.answer(f"{answer_data['text']}\n<i>{answer_data['link']}</i>", parse_mode="HTML") await call.answer() # 结束状态(可选,根据需求决定是否保留) await state.finish()
无回调的替代方案
如果不想使用Inline按钮的回调,可以使用ReplyKeyboardMarkup:
- 按钮点击后会将文本发送到聊天窗口,你需要编写对应的
message_handler来处理这些文本; - 缺点是会在聊天中产生多余消息,用户体验不如Inline按钮。
内容的提问来源于stack exchange,提问作者Алексей Василенко
相关产品推荐
相关产品推荐

