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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 09:02:49