Python Discord机器人Select Menu超时失效问题求助
问题根源
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
相关产品推荐
相关产品推荐

