0

0

Discord.py 交互按钮回调参数错误及数据传递指南

聖光之護

聖光之護

发布时间:2025-11-23 12:34:07

|

229人浏览过

|

来源于php中文网

原创

Discord.py 交互按钮回调参数错误及数据传递指南

本文旨在解决 discord.py 中交互按钮常见的“interaction error”问题。核心在于理解按钮回调函数(如 `agree_btn`)的正确参数签名,即只应包含 `self`、`interaction` 和 `button`。文章将详细解释错误原因,并提供两种安全有效的数据传递方法:通过 `view` 类的 `__init__` 方法传递数据,以及更高级的持久化存储方案,确保按钮功能正常且数据可访问。

理解 Discord.py 交互按钮回调函数

在使用 Discord.py 2.0+ 版本开发机器人时,交互按钮(discord.ui.Button)是实现丰富用户体验的重要组件。然而,开发者常会遇到一个“interaction error”,尤其是在尝试向按钮的回调函数中传递额外参数时。

按钮回调函数的标准签名

discord.ui.button 装饰器修饰的方法,其回调函数签名是严格规定的。它通常期望接收三个参数:

  1. self: 类的实例本身。
  2. interaction: discord.Interaction 对象,代表了用户与按钮的交互事件。
  3. button: discord.ui.Button 对象,代表被点击的按钮实例。

任何额外的参数,如 user: discord.Member,都会导致 Discord 无法正确解析回调,从而引发“interaction error”。

常见错误分析

考虑以下一个模拟求婚系统的代码片段,它试图在按钮回调中直接使用 user 参数:

import discord

# 假设 client 已初始化并与 Discord 连接
# client = discord.Client(intents=discord.Intents.default())
# tree = discord.app_commands.CommandTree(client)

class MarryButtons(discord.ui.View):
    def __init__(self):
        super().__init__()

    @discord.ui.button(label="Yes", style=discord.ButtonStyle.success)
    async def agree_btn(self, interaction: discord.Interaction, button: discord.ui.Button, user: discord.Member):
        # 错误:这里多了一个 user 参数
        embed_agree = discord.Embed(title=f'{user.mention} answered YES', description=f'{user.mention} now married to {interaction.user.mention}')
        await interaction.response.send_message(embed=embed_agree)

    @discord.ui.button(label="No", style=discord.ButtonStyle.danger)
    async def disagree_btn(self, interaction: discord.Interaction, button: discord.ui.Button, user: discord.Member):
        # 错误:这里多了一个 user 参数
        embed_disagree = discord.Embed(title=f'{user.mention} answered NO', description=f'{user.mention} declined propose from {interaction.user.mention}')
        await interaction.response.send_message(embed=embed_disagree)

    @discord.ui.button(label="?", style=discord.ButtonStyle.gray)
    async def emoji_btn(self, interaction: discord.Interaction, button: discord.ui.Button, user: discord.Member):
        # 错误:这里多了一个 user 参数
        embed_emoji = discord.Embed(title=f'{user.mention} canceled propose', description=f'Nothing changed')
        await interaction.response.send_message(embed=embed_emoji)

# 假设这是一个 slash command
# @tree.command(name='marry', description="Suggest to marry")
# async def marry(interaction: discord.Interaction, user: discord.Member):
#     if interaction.user == user:
#         await interaction.response.send_message(content=f"{interaction.user.mention} you can't marry yourself :(")
#         return
#     else:
#         embed_marry = discord.Embed(title='WOW.....', description=f'{interaction.user.mention} suggest a marry to {user.mention}', color=0x774dea)
#         await interaction.response.send_message(embed=embed_marry, view=MarryButtons())

当用户点击上述代码中的按钮时,Discord 会尝试调用 agree_btn 等方法,但由于其签名与预期不符(多了一个 user 参数),就会导致“interaction error”。

解决方案:正确传递数据到按钮回调

要解决这个问题,我们需要移除回调函数中多余的参数,并采用其他机制来传递数据。

白瓜AI
白瓜AI

白瓜AI,一个免费图文AI创作工具,支持 AI 仿写,图文生成,敏感词检测,图片去水印等等。

下载

方法一:通过 View 类的 __init__ 方法传递数据

最常见且推荐的做法是将需要在按钮回调中访问的数据,在 View 类实例化时通过其 __init__ 方法传递进去,并作为实例属性存储。这样,按钮回调函数就可以通过 self. 来访问这些数据。

以下是修正后的求婚系统代码示例:

import discord

# 假设 client 已初始化并与 Discord 连接
# client = discord.Client(intents=discord.Intents.default())
# tree = discord.app_commands.CommandTree(client)

class MarryButtons(discord.ui.View):
    def __init__(self, proposer: discord.Member, target: discord.Member):
        super().__init__(timeout=180) # 设置一个超时时间,防止视图永久存在
        self.proposer = proposer
        self.target = target

    @discord.ui.button(label="Yes", style=discord.ButtonStyle.success)
    async def agree_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
        # 确保只有被求婚者才能点击“Yes”或“No”
        if interaction.user != self.target:
            await interaction.response.send_message("你不是被求婚者,无法操作。", ephemeral=True)
            return

        embed_agree = discord.Embed(
            title=f'{self.target.display_name} 接受了求婚!',
            description=f'{self.target.mention} 现在与 {self.proposer.mention} 喜结连理。',
            color=discord.Color.green()
        )
        # 禁用所有按钮,防止重复点击
        self.stop() # 停止视图,防止进一步交互
        await interaction.response.edit_message(embed=embed_agree, view=None) # 移除按钮
        # 也可以发送新的消息
        # await interaction.response.send_message(embed=embed_agree)

    @discord.ui.button(label="No", style=discord.ButtonStyle.danger)
    async def disagree_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
        if interaction.user != self.target:
            await interaction.response.send_message("你不是被求婚者,无法操作。", ephemeral=True)
            return

        embed_disagree = discord.Embed(
            title=f'{self.target.display_name} 拒绝了求婚。',
            description=f'{self.target.mention} 拒绝了 {self.proposer.mention} 的求婚。',
            color=discord.Color.red()
        )
        self.stop() # 停止视图
        await interaction.response.edit_message(embed=embed_disagree, view=None) # 移除按钮

    @discord.ui.button(label="?", style=discord.ButtonStyle.gray)
    async def emoji_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
        # 允许求婚者或被求婚者取消
        if interaction.user not in [self.proposer, self.target]:
            await interaction.response.send_message("你无权取消此求婚。", ephemeral=True)
            return

        embed_emoji = discord.Embed(
            title='求婚已取消',
            description='求婚被发起人或被求婚者取消,状态未改变。',
            color=discord.Color.light_gray()
        )
        self.stop() # 停止视图
        await interaction.response.edit_message(embed=embed_emoji, view=None) # 移除按钮

# 假设 client 和 tree 已初始化
# client = discord.Client(intents=discord.Intents.default())
# tree = discord.app_commands.CommandTree(client)

@tree.command(name='marry', description="向某人求婚")
async def marry(interaction: discord.Interaction, user: discord.Member):
    if interaction.user == user:
        await interaction.response.send_message(content=f"{interaction.user.mention} 你不能向自己求婚 :(", ephemeral=True)
        return

    # 实例化 MarryButtons 时传入求婚者和被求婚者
    view = MarryButtons(proposer=interaction.user, target=user)
    embed_marry = discord.Embed(
        title='? 惊喜求婚!',
        description=f'{interaction.user.mention} 向 {user.mention} 发起了求婚!',
        color=0x774dea
    )
    await interaction.response.send_message(embed=embed_marry, view=view)

# 在机器人启动时同步命令
# @client.event
# async def on_ready():
#     print(f'Logged in as {client.user}')
#     await tree.sync()
#     print('Commands synced')

# client.run('YOUR_BOT_TOKEN')

代码改进点说明:

  • MarryButtons.__init__(self, proposer: discord.Member, target: discord.Member): 在初始化 View 时,传入了发起求婚者 (proposer) 和被求婚者 (target)。
  • self.proposer 和 self.target: 这些成员变量在 __init__ 中被赋值,使得 View 实例的任何方法(包括按钮回调)都可以访问它们。
  • 回调函数签名修正: agree_btn, disagree_btn, emoji_btn 的签名现在只包含 self, interaction, button,符合 Discord.py 的要求。
  • 权限检查: 在按钮回调中增加了 if interaction.user != self.target: 等检查,确保只有相关用户才能执行操作。ephemeral=True 用于发送只有用户自己能看到的错误消息。
  • timeout: 为 View 设置了超时时间,防止视图在长时间无人交互后仍然存在。
  • self.stop() 和 interaction.response.edit_message(view=None): 在按钮被点击后,调用 self.stop() 可以停止 View 的监听,防止其他按钮被点击。同时,通过 edit_message(view=None) 移除消息上的按钮,可以提供更好的用户体验。

方法二:使用持久化存储(适用于复杂场景)

对于需要管理多个并发请求或更复杂状态的系统,仅仅通过 View 实例传递数据可能不够。在这种情况下,可以考虑使用:

  • 数据库(如 SQLite, PostgreSQL): 将求婚请求的状态(发起人、被求婚者、状态等)存储在数据库中,每个请求分配一个唯一 ID。按钮回调时,可以通过 interaction.message.id 或 custom_id 关联到数据库中的记录,获取所需信息。
  • 内存缓存/JSON 文件: 对于非持久化或较小规模的数据,可以在内存中维护一个字典或将状态序列化到 JSON 文件中。

这种方法更健壮,但实现起来也更复杂,需要额外的逻辑来管理数据的存储、检索和清理。

总结与最佳实践

  1. 遵守回调签名: 始终确保 discord.ui.button 装饰器修饰的回调函数签名严格为 async def callback(self, interaction: discord.Interaction, button: discord.ui.Button)。
  2. 通过 View 实例传递数据: 将回调函数中所需的数据作为 View 类的实例属性存储,并在 __init__ 方法中初始化。
  3. 添加超时机制: 为 discord.ui.View 设置 timeout 参数,防止视图无限期存在。
  4. 处理交互结束: 在用户完成交互后,通过 self.stop() 停止视图,并考虑使用 interaction.response.edit_message(view=None) 移除消息上的按钮,以避免用户进行无效操作。
  5. 权限验证: 在按钮回调中进行严格的用户权限验证,确保只有授权用户才能执行操作。
  6. 考虑并发和持久化: 对于高并发或需要数据持久化的场景,应考虑使用数据库或更高级的状态管理方案。

通过遵循这些原则,开发者可以有效地利用 Discord.py 的交互按钮功能,构建稳定且用户友好的机器人应用。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

412

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

533

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

309

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

74

2025.09.10

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

743

2023.08.22

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

187

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

280

2023.10.25

postgresql常用命令
postgresql常用命令

postgresql常用命令psql、createdb、dropdb、createuser、dropuser、l、c、dt、d table_name、du、i file_name、e和q等。本专题为大家提供postgresql相关的文章、下载、课程内容,供大家免费下载体验。

158

2023.10.10

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

27

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WEB前端教程【HTML5+CSS3+JS】
WEB前端教程【HTML5+CSS3+JS】

共101课时 | 8.3万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 3.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号