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

Python Discord机器人Select Menu超时失效问题求助

解决Discord机器人Select Menu/Button几分钟后失效的问题

问题根源

Discord.py的View类默认设置了**180秒(3分钟)**的超时时间,超时后所有绑定的组件(Select Menu、Button)都会停止响应,导致点击时出现"interaction failed"提示。

基础解决方案:禁用View超时(机器人在线期间永久可用)

只需给所有创建的View实例设置timeout=None,即可让组件在机器人运行期间一直保持可用。

修改点1:Menu类初始化

class Menu(discord.ui.View):
    def __init__(self, user_id):
        # 禁用超时,让按钮永久有效
        super().__init__(timeout=None)
        self.user_id = user_id

修改点2:命令中的Select Menu所在的View

在/giftcardsend命令里创建View时,同样设置timeout=None:

# 原代码:view = View()
view = View(timeout=None)  # 添加timeout=None
view.add_item(select)

这样修改后,只要机器人保持在线,发送的消息里的Select Menu和按钮就不会过期失效。

进阶方案:持久化视图(机器人重启后仍能响应旧消息)

如果需要机器人重启后,之前发送的消息组件依然能正常工作,需要使用持久化视图,步骤如下:

1. 给组件添加固定custom_id

每个需要持久化的组件必须有唯一的custom_id,同时调整逻辑,将需要传递的参数(如user_id)存储到消息的Embed中。

修改Menu类的按钮:

class Menu(discord.ui.View):
    def __init__(self):
        super().__init__(timeout=None)

    # 给按钮添加custom_id
    @discord.ui.button(label="Worked (gives him buyer)", style=discord.ButtonStyle.green, custom_id="giftcard_btn_worked")
    async def worked(self, interaction: discord.Interaction, button: discord.ui.Button):
        try:
            # 从消息Embed中获取用户ID(需要在发送Embed时添加此字段)
            user_id = int(interaction.message.embeds[0].fields[0].value)
            guild_id = 1150516258316492812
            role_id = 1150517166085509211
            guild = await aclient.fetch_guild(guild_id)
            member = await guild.fetch_member(user_id)
            role = guild.get_role(role_id)
            await member.add_roles(role)
            await interaction.response.send_message(f"Successfully gave {member.name} buyer! if you missclicked, go remove his buyer here is his id : {user_id}", ephemeral=True)

            user = await aclient.fetch_user(user_id)
            try:
                await user.send("you have received buyer role! that means the gift card worked pookie!\n\n\n# -- BUYER INSTRUCTIONS --\n\nhttps://discord.com/channels/1150516258316492812/1169400001441579129 for instructions\n\nhttps://discord.com/channels/1150516258316492812/1189053146631245834 for any issues\n\nhttps://discord.com/channels/1150516258316492812/1203980977366630470 (READ THIS)")
            except discord.Forbidden:
                await interaction.channel.send("Could not DM that one person. he probably has dms closed. still gave buyer tho!", ephemeral=True)
                
        except Exception as e:
            print(e)

    @discord.ui.button(label="didn't work (times him out)", style=discord.ButtonStyle.red, custom_id="giftcard_btn_didntwork")
    async def didntwork(self, interaction: discord.Interaction, button: discord.ui.Button):
        try:
            user_id = int(interaction.message.embeds[0].fields[0].value)
            guild_id = 1150516258316492812
            guild = await aclient.fetch_guild(guild_id)
            member = await guild.fetch_member(user_id)
            timeout_time = datetime.timedelta(weeks=1)
            await member.timeout(timeout_time, reason="giftcard didn't work (troll/already redeemed/invalid)")
            await interaction.response.send_message(f"successfully timed out {member.name} for 1 week bc of troll/already redeemed/invalid gift card.", ephemeral=True)

            user = await aclient.fetch_user(user_id)
            try:
                await user.send(f"You have been timed out for 1 week because of the giftcard you sent. reason : **giftcard didn't work (troll/already redeemed/invalid)**")
            except discord.Forbidden:
                await interaction.channel.send("Could not DM that one person. he probably has dms closed. still timed 'em out tho!", ephemeral=True)
        except Exception as e:
            print(e)

修改Modal发送Embed的逻辑(添加User ID字段)

以robloxus为例,其他Modal同理:

class robloxus(ui.Modal, title = "roblox gift card us"):
    giftcard = ui.TextInput(label = "Enter da giftcard", style = discord.TextStyle.short, placeholder="ur giftcard", required=True)
    user_id = None

    async def on_submit(self, interaction: discord.Interaction):
            self.user_id = interaction.user.id
            embed = discord.Embed(title = self.title, description = f'gift card :\n\n```{self.giftcard}```', timestamp=datetime.datetime.now(), color=discord.Colour.red())
            embed.set_author(name = interaction.user, icon_url=interaction.user.avatar)
            # 添加字段存储用户ID,供按钮回调读取
            embed.add_field(name="User ID", value=self.user_id, inline=False)

            view = Menu()  # 现在不需要传入user_id
            user = await aclient.fetch_user(shibauserid)
            await user.send(embed=embed, view=view)

            await interaction.response.send_message("Your gift card has been sent. wait for it to be redeemed to get buyer.", ephemeral=True)

2. 注册持久化视图

在机器人启动时注册Menu视图,确保重启后能处理旧消息的交互:

class client(discord.Client):
    def __init__(self):
        intents = discord.Intents.all()
        super().__init__(intents=intents)
        self.synced = False

    async def on_ready(self):
        await self.wait_until_ready()
        if not self.synced:
            await tree.sync(guild = None)
            self.synced = True
        # 注册持久化视图
        self.add_view(Menu())
        print(f'logged in as {self.user}')

3. 命令中的Select Menu持久化(可选)

如果需要命令发送的Select Menu在重启后也能使用,同样给Select添加custom_id,并创建单独的持久化View类:

class GiftCardSelectView(discord.ui.View):
    def __init__(self):
        super().__init__(timeout=None)

        @discord.ui.select(
            placeholder="choose a giftcard option",
            options=[
                discord.SelectOption(
                    label="roblox gift card us", 
                    value="0x1", 
                    description="buy gold with a roblox gift card us", 
                    emoji="💀"
                ),
                discord.SelectOption(
                    label="roblox gift card eu", 
                    value="0x2", 
                    description="buy gold with a roblox gift card eu", 
                    emoji="💀"
                ),
                discord.SelectOption(
                    label="steam gift card us", 
                    value="0x3", 
                    description="buy gold with a steam gift card us", 
                    emoji="💀"
                ),
                discord.SelectOption(
                    label="steam gift card eu", 
                    value="0x4", 
                    description="buy gold with a steam gift card eu", 
                    emoji="💀"
                ),
            ],
            custom_id="giftcard_select_menu"
        )
        async def select_callback(self, interaction: discord.Interaction, select: discord.ui.Select):
            if select.values[0] == "0x1":
                await interaction.response.send_modal(robloxus())
            elif select.values[0] == "0x2":
                await interaction.response.send_modal(robloxeu())
            elif select.values[0] == "0x3":
                await interaction.response.send_modal(steamus())
            elif select.values[0] == "0x4":
                await interaction.response.send_modal(steameu())

然后在on_ready里注册这个视图:

self.add_view(GiftCardSelectView())

修改/giftcardsend命令中的发送逻辑:

@tree.command(guild = None, name='giftcardsend', description="giftcard?")
async def p(interaction: discord.Interaction):
    if interaction.user.id == 1166885066102800578:
        embed = discord.Embed(title = "Buy ShibaGT Gold With GiftCards", description = f'To purchase ShibaGT gold with gift cards follow those steps : \n\n   - click that select menu right below\n   - click the giftcard you wanna pay with\n   - enter the giftcard in the input textbox **MAKE SURE THAT THE GIFTCARD IS CORRECT!!!!**\n   - then wait for outspect(steam eu, roblox eu), shiba(roblox us), or polar (steam us) to confirm that the giftcard works to get buyer :thumbsup:\n\nthanks for buying gold!', timestamp=datetime.datetime.now(), color=discord.Colour.red())

        view = GiftCardSelectView()
        await interaction.channel.send(embed=embed, view=view)
    else:
        await interaction.response.send_message("<a:dox:1210691712385679521>")

总结

  • 仅需机器人在线期间组件有效:给所有View设置timeout=None即可。
  • 需要机器人重启后旧消息组件仍有效:使用持久化视图,给组件添加custom_id,并在启动时注册视图,同时将动态参数存储到Embed中。

内容的提问来源于stack exchange,提问作者jiko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 05:35:55