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

discord.py多选择菜单交互数据混淆问题及优化咨询

模拟赛车Discord举报系统问题与解决方案

问题背景

正在为模拟赛车Discord社区搭建举报交互功能,流程如下:

  1. 展示带有4个选择菜单的View,用户每完成一个菜单选择,对应检查值设为1
  2. 当所有4个检查值均为1时,弹出Modal让用户输入补充文本信息
  3. 用户提交Modal后,所有输入内容以Embed形式发布到论坛频道的线程中

核心问题:多用户同时填写时输入内容会混淆,推测是全局变量存储用户输入导致被其他用户操作覆盖。

咨询问题

  1. 如何避免多用户输入数据混淆?
  2. 实现多选择菜单的最佳方案是什么?不想用select_check这类全局变量。
  3. 有没有无需冷却机制的方法,能在用户A未完成表单时锁定命令,禁止用户B使用?

解决方案

1. 避免数据混淆的核心思路

彻底抛弃全局变量,将用户输入数据绑定到每个用户独立的View实例上。每个用户触发/report命令时,都会生成专属的View实例,该实例内部维护自身的选择状态和输入数据,不同用户的实例完全隔离,不会互相覆盖。

2. 多Select Menu的最佳实现方案

将所有Select组件整合到同一个自定义View类中,在View内部维护选择状态和数据:

  • 自定义View类,内部添加所有需要的Select组件
  • 在View类中定义属性存储每个选择的结果(如reported_users、selected_track)
  • 每个Select的回调方法仅更新当前View实例的属性,而非全局变量
  • 通过View实例内部的方法判断是否所有必填选择已完成,触发Modal

3. 锁定命令禁止多用户同时使用(无需冷却)

维护一个全局集合记录正在进行举报流程的用户ID:

  • 用户触发/report时,先检查自身ID是否在集合中
  • 若存在则提示已有未完成举报;若不存在则加入集合并启动流程
  • 当用户完成提交、流程超时或出错时,将用户ID从集合中移除

修改后的完整代码

import discord
from discord.ext import commands
from discord.ui import View, UserSelect, Select
from discord import app_commands
import json

with open('krfconfig.json') as config_file:
    config = json.load(config_file)

# 全局集合:记录正在进行举报的用户ID
active_reports = set()

class SurveyModal(discord.ui.Modal, title='Report'):
    def __init__(self, view):
        super().__init__()
        self.view = view  # 关联对应的View实例,获取选择数据

    wann = discord.ui.TextInput(
        label='When did it happen?', 
        placeholder="z.B. Rennen Runde 15", 
        max_length=40
    )
    was = discord.ui.TextInput(
        label='Give us a short description', 
        style=discord.TextStyle.paragraph, 
        max_length=1000
    )
    media = discord.ui.TextInput(
        label='Media', 
        style=discord.TextStyle.paragraph, 
        max_length=500, 
        placeholder="Videos/Screenshots vorher hochladen und hier als Link posten, z.B Youtube, Streamable etc.."
    )

    async def on_submit(self, interaction: discord.Interaction):
        # 从关联的View实例获取用户选择数据
        reported_users = self.view.reported_users
        selected_track = self.view.selected_track
        channel = self.view.channel

        author = interaction.user.name
        auticon = interaction.user.display_avatar
        repuser_string = ', '.join([user.name for user in reported_users])

        embed = discord.Embed(title=f'Report-Ticket', color=discord.Color.magenta())
        embed.set_author(name=author, icon_url=auticon)
        embed.add_field(name='Report against:', value=f'{repuser_string} \n\u200b', inline=False)
        embed.add_field(name='When it happened:', value=f'{self.wann.value} \n\u200b', inline=False)
        embed.add_field(name='Short description:', value=f'{self.was.value} \n\u200b', inline=False)
        embed.add_field(name='Media:', value=self.media.value, inline=False)

        thr_name = f"{selected_track}-Report: {author} vs. {repuser_string}"
        await interaction.response.send_message(f'Report gesendet!', ephemeral=True)
        await channel.create_thread(
            name=thr_name, 
            content=f'Reportverfahren: {interaction.user.mention} vs. {repuser_string}', 
            embed=embed
        )

        # 完成举报后移除活跃记录
        active_reports.discard(interaction.user.id)

    async def on_error(self, interaction: discord.Interaction, error: Exception) -> None:
        # 提交出错时也移除活跃记录
        active_reports.discard(interaction.user.id)
        await interaction.response.send_message('提交时出错,请重试', ephemeral=True)

class ReportView(View):
    def __init__(self, channel, user_id):
        super().__init__(timeout=300)
        self.channel = channel
        self.user_id = user_id  # 记录当前使用的用户ID
        # 初始化选择数据存储
        self.reported_users = None
        self.selected_track = None
        # 添加组件到View
        self.add_item(self.ReportedUserSelect())
        self.add_item(self.TrackSelect())

    # 检查是否所有必填选择已完成
    def all_selections_completed(self):
        return self.reported_users is not None and self.selected_track is not None

    # 被举报用户选择组件
    class ReportedUserSelect(UserSelect):
        def __init__(self):
            super().__init__(placeholder="Who do you want to Report?", max_values=19, min_values=1)

        async def callback(self, interaction: discord.Interaction):
            self.view.reported_users = self.values
            if self.view.all_selections_completed():
                await interaction.response.send_modal(SurveyModal(self.view))
                await interaction.delete_original_response()
                self.view.stop()  # 停止View监听
            else:
                await interaction.response.defer()

    # 赛道选择组件
    class TrackSelect(Select):
        def __init__(self):
            options = [
                discord.SelectOption(label="Australia"),
                discord.SelectOption(label="Bahrain"),
                discord.SelectOption(label="Belgium")
            ]
            super().__init__(placeholder="Which Track?", max_values=1, min_values=1, options=options)

        async def callback(self, interaction: discord.Interaction):
            self.view.selected_track = self.values[0]
            if self.view.all_selections_completed():
                await interaction.response.send_modal(SurveyModal(self.view))
                await interaction.delete_original_response()
                self.view.stop()  # 停止View监听
            else:
                await interaction.response.defer()

    async def on_timeout(self):
        # 流程超时后移除活跃记录
        active_reports.discard(self.user_id)

class Report(commands.Cog):
    def __init__(self, bot: commands.Bot):
        self.bot = bot

    @commands.Cog.listener()
    async def on_ready(self):
        print('Report cog loaded.')

    @app_commands.command(name="report", description='Use this to Report others!')
    @app_commands.checks.has_any_role('Liga A', 'Liga B', 'Liga C', 'Liga A Reserve', 'Liga B Reserve', 'Liga C Reserve')
    async def fillsurvey(self, interaction: discord.Interaction):
        # 检查用户是否已有未完成的举报
        if interaction.user.id in active_reports:
            await interaction.response.send_message('你已有一个未完成的举报流程,请先完成或等待超时', ephemeral=True)
            return

        channel = interaction.guild.get_channel(config["REPORT_CHANNEL"])
        # 创建用户专属的View实例
        report_view = ReportView(channel, interaction.user.id)
        # 添加到活跃记录集合
        active_reports.add(interaction.user.id)
        await interaction.response.send_message(view=report_view, ephemeral=True)

async def setup(bot: commands.Bot):
    await bot.add_cog(Report(bot))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 06:12:45