0

0

Discord.py 交互式按钮实现随机响应与指令重触发教程

碧海醫心

碧海醫心

发布时间:2025-11-16 11:09:01

|

795人浏览过

|

来源于php中文网

原创

discord.py 交互式按钮实现随机响应与指令重触发教程

本教程详细指导如何在 Discord.py 机器人中创建一个带有随机回复功能的指令,并添加一个交互式按钮。用户点击按钮后,无需重复输入指令即可重新触发随机回复,同时文章还将探讨如何实现特定角色访问限制,并解决常见的交互失败问题,提升用户体验。

引言:提升 Discord 机器人交互性

在 Discord 机器人开发中,为用户提供更丰富的交互体验是提升机器人实用性的关键。传统的指令模式需要用户反复输入文本,而利用 Discord.py 提供的 UI 组件(如按钮),我们可以创建更直观、更便捷的交互方式。本教程将聚焦于如何实现一个带有随机回复的指令,并为其添加一个“重生成”按钮,允许用户无需重新输入指令即可获取新的随机回复,同时还会讲解如何限制特定角色才能使用这些功能。

核心组件:discord.ui.View 与 discord.ui.Button

Discord.py 1.7 版本及以上引入了 discord.ui 模块,它允许开发者创建复杂的交互式组件。其中,View 是一个容器,用于管理多个 Button 或其他组件。Button 则是可点击的交互元素。

使用 discord.ui.View 的类式方法(Class-based View)是管理组件状态和回调的最佳实践,它比使用全局变量更加健壮和易于维护。

构建随机回复指令

首先,我们需要一个能够生成并发送随机 discord.Embed 的基础指令。

import discord
from discord.ext import commands
import random

# 假设 bot 已经初始化
# intents = discord.Intents.default()
# intents.message_content = True # 如果需要读取消息内容,请启用此意图
# bot = commands.Bot(command_prefix='!', intents=intents)

# 随机 Embed 列表
EMBED_ITEMS = [
    discord.Embed(title="这是一个测试", description="第一条随机消息。"),
    discord.Embed(title="这是第二个测试", description="第二条随机消息。"),
    discord.Embed(title="这是第三个测试", description="第三条随机消息。"),
    discord.Embed(title="这是第四个测试", description="第四条随机消息。"),
    discord.Embed(title="这是第五个测试", description="第五条随机消息。"),
    discord.Embed(title="这是第六个测试", description="第六条随机消息。"),
    discord.Embed(title="这是第七个测试", description="第七条随机消息。"),
    discord.Embed(title="这是第八个测试", description="第八条随机消息。")
]

@bot.command(name="random_message")
async def random_message_command(ctx: commands.Context):
    """发送一条随机的嵌入消息。"""
    initial_embed = random.choice(EMBED_ITEMS)
    await ctx.reply(embed=initial_embed)

这段代码定义了一个名为 random_message 的指令,当被调用时,它会从 EMBED_ITEMS 列表中随机选择一个 discord.Embed 并发送回复。

添加交互式重触发按钮

现在,我们将增强上述指令,使其在发送随机 Embed 的同时,附带一个按钮,用户点击该按钮即可重新生成并更新 Embed。我们将使用一个继承自 discord.ui.View 的自定义类来管理按钮及其回调。

创建自定义 View 类

这个类将包含我们的按钮逻辑,并且能够存储 EMBED_ITEMS 列表以及对原始消息的引用,以便在按钮点击时进行编辑。

LongCat AI
LongCat AI

美团推出的AI对话问答工具

下载
class RandomEmbedView(discord.ui.View):
    def __init__(self, items: list[discord.Embed], original_message: discord.Message, allowed_role_id: int = None):
        super().__init__(timeout=180) # 视图在180秒(3分钟)后失效
        self.items = items
        self.original_message = original_message
        self.allowed_role_id = allowed_role_id
        self.add_item(discord.ui.Button(label="重新生成", style=discord.ButtonStyle.primary, custom_id="reroll_button"))

    async def interaction_check(self, interaction: discord.Interaction) -> bool:
        """
        在处理任何按钮点击之前,检查交互是否被允许。
        这里用于实现角色限制。
        """
        if self.allowed_role_id:
            # 检查用户是否拥有指定角色
            if not any(role.id == self.allowed_role_id for role in interaction.user.roles):
                await interaction.response.send_message("您没有权限使用此按钮。", ephemeral=True)
                return False
        return True

    @discord.ui.button(label="重新生成", style=discord.ButtonStyle.primary, custom_id="reroll_button_callback")
    async def reroll_button_callback(self, interaction: discord.Interaction, button: discord.ui.Button):
        """
        当“重新生成”按钮被点击时执行的回调函数。
        """
        # 立即响应交互,避免“Interaction Failed”错误
        await interaction.response.defer() 

        current_embed = self.original_message.embeds[0] if self.original_message.embeds else None

        next_embed = random.choice(self.items)
        # 循环确保新生成的 Embed 与当前显示的不同,除非列表中只有一个选项
        if current_embed and len(self.items) > 1:
            while next_embed.title == current_embed.title: # 简单地比较标题来判断是否相同
                next_embed = random.choice(self.items)

        # 编辑原始消息,更新 Embed 并保持视图活跃
        await self.original_message.edit(embed=next_embed, view=self)

    async def on_timeout(self):
        """
        当视图超时时执行。
        """
        # 可以选择移除按钮或禁用它们
        for item in self.children:
            item.disabled = True
        await self.original_message.edit(view=self)

更新指令以使用 View

现在,我们将修改 random_message_command 指令,使其能够创建并发送带有这个自定义 View 的消息。

# 假设 bot 已经初始化
# intents = discord.Intents.default()
# intents.message_content = True
# bot = commands.Bot(command_prefix='!', intents=intents)

# ... EMBED_ITEMS 定义 ...
# ... RandomEmbedView 类定义 ...

@bot.command(name="random_message")
async def random_message_command(ctx: commands.Context, role_id: int = None):
    """
    发送一条随机的嵌入消息,并附带一个按钮用于重新生成。
    可选参数 role_id 用于限制按钮的使用权限。
    """
    initial_embed = random.choice(EMBED_ITEMS)

    # 首先发送消息,以便获取消息对象
    msg = await ctx.reply(embed=initial_embed)

    # 创建视图实例,传入 Embed 列表、消息对象和可选的角色ID
    view = RandomEmbedView(EMBED_ITEMS, msg, allowed_role_id=role_id)

    # 编辑原始消息,将视图添加到其中
    await msg.edit(view=view)

处理 Discord 交互响应:避免 "Interaction Failed" 错误

当用户点击按钮时,Discord 会向你的机器人发送一个交互事件。机器人必须在 3 秒内对这个交互做出响应,否则 Discord 客户端会显示“Interaction Failed”错误。

在 reroll_button_callback 方法中,我们通过 await interaction.response.defer() 来解决这个问题。defer() 方法会立即向 Discord 发送一个“思考”状态,表示机器人已收到交互请求并正在处理,从而避免超时。之后,我们可以继续执行耗时操作(如生成新的 Embed),最后通过 self.original_message.edit() 更新消息。

如果不需要显示“思考”状态,也可以直接使用 await interaction.response.edit_message(embed=next_embed, view=self) 来同时响应交互并更新消息,但这要求更新操作本身在 3 秒内完成。对于我们的场景,defer() 后再 edit() 更为灵活。

实现特定角色访问限制

教程中提供了两种实现角色限制的方法:

  1. 针对指令本身的限制(使用装饰器): 如果你希望整个 random_message 指令只能由特定角色执行,可以使用 discord.ext.commands.has_role 装饰器。

    # 替换为你的角色名称或ID
    REQUIRED_ROLE_NAME = "管理员" 
    # 或 REQUIRED_ROLE_ID = 123456789012345678 
    
    @bot.command(name="random_message")
    @commands.has_role(REQUIRED_ROLE_NAME) # 或者 @commands.has_role(REQUIRED_ROLE_ID)
    async def random_message_command(ctx: commands.Context):
        # ... 指令内容 ...
        pass
    
    # 添加一个错误处理,以便在没有权限时给出反馈
    @random_message_command.error
    async def random_message_command_error(ctx, error):
        if isinstance(error, commands.MissingRole):
            await ctx.send(f"抱歉,{ctx.author.mention},您没有 '{REQUIRED_ROLE_NAME}' 角色来执行此指令。", ephemeral=True)
        else:
            raise error
  2. 针对按钮点击的限制(在 View 中实现): 如果你希望指令可以被所有人调用,但只有特定角色才能点击“重新生成”按钮,那么在 RandomEmbedView 的 interaction_check 方法中实现角色检查是最佳选择。我们已经在 RandomEmbedView 类中实现了这一点,通过 allowed_role_id 参数传入允许的角色 ID。当用户点击按钮时,interaction_check 会首先验证用户是否拥有该角色。

    如何获取角色 ID: 在 Discord 客户端中,启用开发者模式(用户设置 -> 高级 -> 开发者模式)。然后右键点击服务器中的角色,选择“复制 ID”。

完整代码示例

import discord
from discord.ext import commands
import random

# 配置你的机器人
# 请替换为你的机器人Token
TOKEN = "YOUR_BOT_TOKEN" 
# 确保你的机器人具有消息内容意图(如果需要)和默认意图
intents = discord.Intents.default()
intents.message_content = True 
intents.members = True # 如果需要检查成员的角色,需要启用此意图

bot = commands.Bot(command_prefix='!', intents=intents)

# 随机 Embed 列表
EMBED_ITEMS = [
    discord.Embed(title="这是一个测试", description="第一条随机消息。", color=discord.Color.blue()),
    discord.Embed(title="这是第二个测试", description="第二条随机消息。", color=discord.Color.green()),
    discord.Embed(title="这是第三个测试", description="第三条随机消息。", color=discord.Color.red()),
    discord.Embed(title="这是第四个测试", description="第四条随机消息。", color=discord.Color.purple()),
    discord.Embed(title="这是第五个测试", description="第五条随机消息。", color=discord.Color.orange()),
    discord.Embed(title="这是第六个测试", description="第六条随机消息。", color=discord.Color.teal()),
    discord.Embed(title="这是第七个测试", description="第七条随机消息。", color=discord.Color.dark_gold()),
    discord.Embed(title="这是第八个测试", description="第八条随机消息。", color=discord.Color.dark_magenta())
]

class RandomEmbedView(discord.ui.View):
    def __init__(self, items: list[discord.Embed], original_message: discord.Message, allowed_role_id: int = None):
        super().__init__(timeout=180) # 视图在180秒(3分钟)后失效
        self.items = items
        self.original_message = original_message
        self.allowed_role_id = allowed_role_id
        # 添加按钮
        self.add_item(discord.ui.Button(label="重新生成", style=discord.ButtonStyle.primary, custom_id="reroll_button_callback"))

    async def interaction_check(self, interaction: discord.Interaction) -> bool:
        """
        在处理任何按钮点击之前,检查交互是否被允许。
        这里用于实现角色限制。
        """
        if self.allowed_role_id:
            # 检查用户是否拥有指定角色
            # interaction.user.roles 是一个列表,包含 discord.Role 对象
            if not any(role.id == self.allowed_role_id for role in interaction.user.roles):
                await interaction.response.send_message("您没有权限使用此按钮。", ephemeral=True)
                return False
        return True

    @discord.ui.button(label="重新生成", style=discord.ButtonStyle.primary, custom_id="reroll_button_callback")
    async def reroll_button_callback(self, interaction: discord.Interaction, button: discord.ui.Button):
        """
        当“重新生成”按钮被点击时执行的回调函数。
        """
        # 立即响应交互,避免“Interaction Failed”错误
        await interaction.response.defer() 

        current_embed = self.original_message.embeds[0] if self.original_message.embeds else None

        next_embed = random.choice(self.items)
        # 循环确保新生成的 Embed 与当前显示的不同,除非列表中只有一个选项
        if current_embed and len(self.items) > 1:
            while next_embed.title == current_embed.title: # 简单地比较标题来判断是否相同
                next_embed = random.choice(self.items)

        # 编辑原始消息,更新 Embed 并保持视图活跃
        await self.original_message.edit(embed=next_embed, view=self)

    async def on_timeout(self):
        """
        当视图超时时执行。
        超时后,按钮将变为禁用状态。
        """
        for item in self.children:
            if isinstance(item, discord.ui.Button):
                item.disabled = True
        await self.original_message.edit(view=self)

@bot.event
async def on_ready():
    print(f'Bot {bot.user} 已上线!')
    print(f'Discord.py 版本: {discord.__version__}')

@bot.command(name="random_message")
async def random_message_command(ctx: commands.Context, allowed_role_id: int = None):
    """
    发送一条随机的嵌入消息,并附带一个按钮用于重新生成。
    用法: !random_message [允许的角色ID]
    例如: !random_message 123456789012345678 (只有ID为123...的角色才能点击按钮)
    """

相关专题

更多
全局变量怎么定义
全局变量怎么定义

本专题整合了全局变量相关内容,阅读专题下面的文章了解更多详细内容。

75

2025.09.18

python 全局变量
python 全局变量

本专题整合了python中全局变量定义相关教程,阅读专题下面的文章了解更多详细内容。

96

2025.09.18

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

463

2024.01.03

python中class的含义
python中class的含义

本专题整合了python中class的相关内容,阅读专题下面的文章了解更多详细内容。

12

2025.12.06

Java 项目构建与依赖管理(Maven / Gradle)
Java 项目构建与依赖管理(Maven / Gradle)

本专题系统讲解 Java 项目构建与依赖管理的完整体系,重点覆盖 Maven 与 Gradle 的核心概念、项目生命周期、依赖冲突解决、多模块项目管理、构建加速与版本发布规范。通过真实项目结构示例,帮助学习者掌握 从零搭建、维护到发布 Java 工程的标准化流程,提升在实际团队开发中的工程能力与协作效率。

10

2026.01.12

c++主流开发框架汇总
c++主流开发框架汇总

本专题整合了c++开发框架推荐,阅读专题下面的文章了解更多详细内容。

106

2026.01.09

c++框架学习教程汇总
c++框架学习教程汇总

本专题整合了c++框架学习教程汇总,阅读专题下面的文章了解更多详细内容。

64

2026.01.09

学python好用的网站推荐
学python好用的网站推荐

本专题整合了python学习教程汇总,阅读专题下面的文章了解更多详细内容。

139

2026.01.09

学python网站汇总
学python网站汇总

本专题整合了学python网站汇总,阅读专题下面的文章了解更多详细内容。

13

2026.01.09

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3.6万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

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

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