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

如何在aiogram中从键盘按钮向callback_query传递kwargs?

解决aiogram中Callback Query传递参数的问题

你的代码存在两个核心问题:

  1. InlineKeyboardButton不存在kwargs参数,无法通过该方式传递自定义数据;
  2. 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,提问作者Алексей Василенко

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 12:15:42