如何在discord.py中实现动态持久化按钮?
解决Discord.py动态投票按钮持久化问题
要实现机器人重启后动态生成的投票按钮仍能响应点击,核心是把投票的关键数据持久化存储,重启时重新构建对应的View并注册。以下是具体实现步骤:
1. 数据库设计(以SQLite为例)
创建数据库表存储投票核心信息,确保重启后能还原投票结构:
CREATE TABLE IF NOT EXISTS polls ( message_id INTEGER PRIMARY KEY, guild_id INTEGER, channel_id INTEGER, question TEXT, options TEXT -- 存储选项与custom_id的JSON字符串,格式如[{"label":"选项1","custom_id":"xxx"},...] );
2. 修改投票创建逻辑,存入数据库
在创建投票时,将投票消息ID、选项及对应custom_id存入数据库,确保数据不丢失:
import json import sqlite3 class polls(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot # 新增数据库连接方法 def get_db_connection(self): conn = sqlite3.connect('polls.db') conn.row_factory = sqlite3.Row return conn async def create_poll(self, interaction: discord.Interaction, question: str, choices: str): text_options = split_text(choices) id_list = make_id(text_options) names_and_ids = list(zip(text_options, id_list)) # 整理数据库存储的选项数据 options_data = [{"label": opt, "custom_id": cid} for opt, cid in names_and_ids] view = MyView(names_and_ids) # 发送投票消息并获取message对象 poll_message = await interaction.channel.send(embed=embed, view=view) # 写入数据库 conn = self.get_db_connection() conn.execute( 'INSERT INTO polls (message_id, guild_id, channel_id, question, options) VALUES (?, ?, ?, ?, ?)', (poll_message.id, interaction.guild_id, interaction.channel_id, question, json.dumps(options_data)) ) conn.commit() conn.close() await interaction.response.send_message("投票已创建", ephemeral=True)
3. 调整View类,支持从存储数据重建
修改MyView,使其既能在创建投票时生成按钮,也能从数据库加载的数据重建按钮:
class MyView(discord.ui.View): def __init__(self, options_and_ids=None, options_data=None): super().__init__(timeout=None) # 两种初始化方式:创建投票时传入选项列表,重启时传入数据库加载的选项数据 if options_and_ids: for option, button_id in options_and_ids: self.add_item(MyButton(label=option, custom_id=button_id)) elif options_data: for opt in options_data: self.add_item(MyButton(label=opt['label'], custom_id=opt['custom_id']))
4. 机器人启动时加载所有投票,注册View
在setup_hook中读取数据库内的所有投票,重建对应的View并注册到机器人,确保重启后按钮能响应:
async def setup_hook(self) -> None: conn = self.get_db_connection() polls_data = conn.execute('SELECT * FROM polls').fetchall() conn.close() for poll in polls_data: options_data = json.loads(poll['options']) # 重建View并注册到对应消息ID view = MyView(options_data=options_data) self.bot.add_view(view, message_id=poll['message_id'])
5. 完善按钮回调逻辑(可选)
如果需要实现投票计数功能,可在按钮回调中从数据库读取投票信息并更新:
class MyButton(discord.ui.Button): async def callback(self, interaction: discord.Interaction): button_id = self.custom_id message_id = interaction.message.id # 从数据库获取投票信息 conn = sqlite3.connect('polls.db') conn.row_factory = sqlite3.Row poll = conn.execute('SELECT * FROM polls WHERE message_id = ?', (message_id,)).fetchone() conn.close() if not poll: await interaction.response.send_message("该投票已失效", ephemeral=True) return # 此处可添加投票计数逻辑(例如新增计数字段并更新数据库) # ... await interaction.response.send_message(f"你已投票给:{self.label}", ephemeral=True)
关键注意事项
- 确保
make_id生成的custom_id全局唯一,避免不同投票的按钮ID冲突; - 数据库操作需添加异常处理,防止因连接问题导致机器人崩溃;
- 若需要投票过期功能,可在数据库中新增
expires_at字段,启动时过滤已过期投票。
内容的提问来源于stack exchange,提问作者Andrew Prince
相关产品推荐
相关产品推荐

