Discord机器人重复发送消息与日志问题排查及修复请求
Discord机器人重复发送私信与重复记录反应日志的问题修复
问题原因
1. 重复发送私信
- 未启用Discord.py核心意图:v2.x版本后,必须显式开启成员意图(Members Intent)和消息内容意图(Message Content Intent),才能正常获取服务器成员列表、解析命令文本。缺少这些意图会导致
ctx.guild.members返回的成员列表异常(如重复加载同一成员),进而在循环中重复发送私信。 - 无发送记录去重机制:未记录已发送的成员ID,极端情况下可能因缓存重复触发重复发送。
2. 重复记录反应日志
- 事件重复触发无过滤:Discord偶尔会重复发送同一反应事件,原代码未对反应做唯一标识去重,导致重复记录。
- 变量覆盖逻辑冗余:原代码中先获取用户对象判断是否为机器人,随后又重新赋值成员变量,虽不影响核心逻辑,但增加了不必要的资源消耗。
修复后的完整代码
import discord from discord.ext import commands from collections import defaultdict # 配置Discord机器人核心意图 intents = discord.Intents.default() intents.members = True # 启用成员意图,用于获取服务器成员列表 intents.message_content = True # 启用消息内容意图,用于处理命令文本 bot = commands.Bot(command_prefix="!", intents=intents) allowed_role_id = 1234567890 # 替换为你有权限执行命令的角色ID sent_member_ids = set() # 记录已发送私信的成员ID,避免重复发送 processed_reactions = defaultdict(set) # 记录已处理的反应,格式:(用户ID, 表情名称) @bot.event async def on_ready(): print(f'已登录为 {bot.user} (ID: {bot.user.id})') sent_member_ids.clear() processed_reactions.clear() @bot.command(name='sendmessage') @commands.has_role(allowed_role_id) async def send_message(ctx, *, message: str): sent_member_ids.clear() # 每次执行命令时重置发送记录 for member in ctx.guild.members: # 跳过机器人和已发送过消息的成员 if member.bot or member.id in sent_member_ids: continue try: dm_channel = await member.create_dm() sent_msg = await dm_channel.send(message) # 添加反应 await sent_msg.add_reaction("✅") await sent_msg.add_reaction("❌") await sent_msg.add_reaction("🟡") sent_member_ids.add(member.id) # 标记为已发送 except discord.Forbidden: print(f"无法给 {member.name} ({member.id}) 发私信:用户关闭了私信权限") except Exception as e: print(f"给 {member.name} 发私信时出错:{str(e)}") @bot.event async def on_raw_reaction_add(payload): # 过滤机器人自己的反应 if payload.user_id == bot.user.id: return # 生成唯一反应标识,避免重复处理 reaction_key = (payload.user_id, payload.emoji.name) if reaction_key in processed_reactions[payload.message_id]: return processed_reactions[payload.message_id].add(reaction_key) # 获取目标服务器和成员信息 target_guild = bot.get_guild(1077548297037819985) if not target_guild: print("无法获取目标服务器") return member = target_guild.get_member(payload.user_id) if not member: print(f"无法获取成员 {payload.user_id} 的信息") return # 验证消息是否为机器人发送的私信 try: channel = bot.get_channel(payload.channel_id) if not channel: print(f"无法获取频道 {payload.channel_id}") return msg = await channel.fetch_message(payload.message_id) if msg.author != bot.user: return except Exception as e: print(f"获取消息时出错:{str(e)}") return # 发送反应日志 log_channel = bot.get_channel(1095793300184764477) if not log_channel: print("无法找到日志频道") return embed = discord.Embed(title="反应日志", color=0x00ff00) embed.add_field(name="用户", value=f"{member.mention} ({member.id})", inline=True) embed.add_field(name="反应", value=payload.emoji.name, inline=True) embed.add_field(name="角色", value=" ".join([r.mention for r in member.roles]) if member.roles else "无角色", inline=False) embed.add_field(name="昵称", value=member.nick if member.nick else member.name, inline=True) embed.add_field(name="加入时间", value=member.joined_at.strftime("%Y-%m-%d %H:%M:%S") if member.joined_at else "未知", inline=True) await log_channel.send(embed=embed) # 替换为你的机器人Token bot.run("YOUR_BOT_TOKEN")
关键修复点
- 意图配置:添加
members和message_content意图,确保机器人能正常获取成员列表、解析命令,从根源解决成员重复加载的问题。 - 私信去重:用
sent_member_ids集合记录已发送的成员ID,循环时跳过已发送对象,避免重复私信。同时增加异常捕获,处理无法发送私信的场景。 - 反应日志去重:用
processed_reactions字典记录已处理的反应,以消息ID+用户ID+表情为唯一标识,重复事件直接跳过。 - 逻辑优化:简化机器人反应判断逻辑,增加空值检查和异常处理,提升代码稳定性和容错性。
内容的提问来源于stack exchange,提问作者Technical
相关产品推荐
相关产品推荐

