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

Discord.py:如何让on_raw_reaction_add按顺序处理避免误删最新反应

解决Discord投票机器人快速重复反应误删问题

核心问题是on_raw_reaction_add事件的并发处理导致同一用户的多个反应操作互相干扰,解决思路是给每个用户加异步锁,确保同一用户的反应事件串行执行,避免并发冲突。

具体实现步骤

  • 维护一个用户锁字典,为每个用户分配独立的异步锁,保证同一用户的事件排队处理。
  • 在反应事件处理逻辑外层包裹锁,确保同一时间只有一个该用户的反应事件在执行。
  • 处理时获取该用户在投票消息上的所有有效反应,仅保留最新添加的那个,移除其他。

代码示例(基于discord.py)

import asyncio
from collections import defaultdict
import discord

bot = discord.Bot(intents=discord.Intents.all())
user_locks = defaultdict(asyncio.Lock())  # 存储每个用户的异步锁
VOTE_MESSAGE_ID = 1234567890123456789  # 替换成你的投票消息ID
ALLOWED_EMOJIS = {'1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟'}

@bot.event
async def on_raw_reaction_add(payload):
    # 过滤非投票消息和非允许表情
    if payload.message_id != VOTE_MESSAGE_ID or str(payload.emoji) not in ALLOWED_EMOJIS:
        return
    # 忽略机器人自己的反应
    if payload.user_id == bot.user.id:
        return

    # 获取当前用户的锁,串行处理该用户的所有反应事件
    lock = user_locks[payload.user_id]
    async with lock:
        try:
            # 获取投票消息对象
            channel = await bot.fetch_channel(payload.channel_id)
            message = await channel.fetch_message(payload.message_id)
            user = bot.get_user(payload.user_id)
            if not user:
                user = await bot.fetch_user(payload.user_id)

            # 收集该用户的所有有效反应
            user_active_reactions = []
            for reaction in message.reactions:
                emoji_str = str(reaction.emoji)
                if emoji_str in ALLOWED_EMOJIS:
                    # 检查该反应是否包含当前用户
                    async for reactor in reaction.users():
                        if reactor.id == payload.user_id:
                            user_active_reactions.append(emoji_str)
                            break

            # 如果用户有多个有效反应,仅保留最新的(当前触发事件的表情)
            if len(user_active_reactions) > 1:
                for emoji in user_active_reactions:
                    if emoji != str(payload.emoji):
                        await message.remove_reaction(emoji, user)
        except discord.NotFound:
            # 消息或用户不存在时跳过
            pass
        except discord.Forbidden:
            # 机器人无权限移除反应时记录日志
            print(f"无权限移除用户 {payload.user_id} 的反应")

bot.run("YOUR_BOT_TOKEN")

关键说明

  • 异步锁的作用:同一用户的所有on_raw_reaction_add事件会等待前一个事件处理完成后再执行,彻底避免了并发检查时的逻辑冲突。比如用户快速点击多个表情,每个反应事件都会排队,处理时能准确获取当前用户的所有反应状态,只保留最新的那个。
  • 性能优化:defaultdict会自动为新用户创建锁,锁会被复用直到机器人重启,无需额外清理(如果需要可以在投票结束后清空对应用户的锁)。
  • 边界处理:加入了消息/用户不存在、权限不足的异常捕获,避免机器人崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 19:30:55