Discord机器人反应角色命令故障:添加反应后无法赋予角色
我在Discord机器人中实现简易的reaction roles(反应角色)功能,现有代码能为指定消息添加表情,但触发表情反应时,机器人并未添加对应角色,且无任何报错信息。代码如下:
@bot.slash_command(name="rr") async def rr_setup(ctx, message_link: str, role: discord.Role, emoji: str, dm: str, dm_rem: str): if not ctx.author.guild_permissions.administrator: await ctx.respond("You don't have permissions for this command.", ephemeral=True) return try: message_id = message_link.split('/')[-1] message = await ctx.channel.fetch_message(message_id) if not message: await ctx.send("<:no__:974587445138038794> Incorrect syntax!", ephemeral=True) return await message.add_reaction(emoji) def check(reaction, user): return str(reaction.emoji) == str(emoji) and user != bot.user and reaction.message.id == message.id while True: reaction, user = await bot.wait_for('reaction_add', check=check) if role not in user.roles: await user.add_roles(role) await user.send(dm) reaction, user = await bot.wait_for('reaction_remove', check=check) if role in user.roles: await user.remove_roles(role) await user.send(dm_rem) await ctx.respond("Successfully added reaction to role.", ephemeral=True) except Exception as e: await ctx.respond("<:no__:974587445138038794> Incorrect syntax!", ephemeral=True)
问题分析与修复
1. 无限循环阻塞协程,监听直接失效
代码里的while True死循环会卡死slash命令的协程,而Discord的slash命令有执行超时限制,一旦协程被系统回收,wait_for的监听就会终止,自然无法响应后续的表情操作。另外,循环后的成功提示await ctx.respond永远不会执行。
修复:把监听逻辑拆成独立异步函数,用asyncio.create_task启动,让命令协程正常结束并返回提示:
import asyncio async def reaction_role_listener(bot, message, emoji, role, dm, dm_rem, guild_me): def check(reaction, user): # 兼容自定义表情的匹配逻辑 if isinstance(reaction.emoji, discord.PartialEmoji): return reaction.emoji.id == emoji.id or str(reaction.emoji) == str(emoji) return str(reaction.emoji) == str(emoji) and user != bot.user and reaction.message.id == message.id while True: try: # 监听添加反应 reaction, user = await bot.wait_for('reaction_add', check=check, timeout=None) if role not in user.roles: # 检查机器人权限和角色位置 if guild_me.guild_permissions.manage_roles and role.position < guild_me.top_role.position: await user.add_roles(role) # 处理DM发送失败的情况 try: await user.send(dm) except discord.Forbidden: print(f"无法给用户{user.name}发送DM") # 监听移除反应 reaction, user = await bot.wait_for('reaction_remove', check=check, timeout=None) if role in user.roles: if guild_me.guild_permissions.manage_roles and role.position < guild_me.top_role.position: await user.remove_roles(role) try: await user.send(dm_rem) except discord.Forbidden: print(f"无法给用户{user.name}发送DM") except Exception as e: print(f"监听反应时出错: {e}") continue @bot.slash_command(name="rr") async def rr_setup(ctx, message_link: str, role: discord.Role, emoji: str, dm: str, dm_rem: str): if not ctx.author.guild_permissions.administrator: await ctx.respond("You don't have permissions for this command.", ephemeral=True) return try: message_id = message_link.split('/')[-1] message = await ctx.channel.fetch_message(message_id) if not message: await ctx.respond("<:no__:974587445138038794> Incorrect syntax!", ephemeral=True) return # 解析表情,兼容自定义表情 parsed_emoji = None try: parsed_emoji = discord.PartialEmoji.from_str(emoji) await message.add_reaction(parsed_emoji) except: parsed_emoji = emoji await message.add_reaction(emoji) # 启动异步监听任务 asyncio.create_task(reaction_role_listener(bot, message, parsed_emoji, role, dm, dm_rem, ctx.guild.me)) await ctx.respond("Successfully added reaction to role.", ephemeral=True) except Exception as e: print(f"设置反应角色时出错: {e}") await ctx.respond("<:no__:974587445138038794> Incorrect syntax!", ephemeral=True)
2. 表情匹配逻辑不兼容自定义表情
如果用户输入的是自定义表情(格式为<:name:id>),直接用str(reaction.emoji)对比输入字符串可能不匹配——因为reaction.emoji是PartialEmoji对象,若用户输入的是表情名称或直接粘贴表情,就会匹配失败。
修复:添加自定义表情解析逻辑,优先用ID匹配,再回退到字符串匹配。
3. 缺少机器人权限与角色位置检查
机器人必须拥有**管理角色(Manage Roles)**权限,且目标角色的位置必须低于机器人的最高角色,否则无法添加/移除角色。原代码没有这部分检查,失败时无任何提示。
修复:在添加/移除角色前,先验证权限和角色位置。
4. DM发送失败导致监听崩溃
若用户关闭了服务器DM权限,await user.send()会抛出discord.Forbidden异常,原代码未处理该异常,会导致监听协程直接崩溃,后续反应无法被处理。
修复:给DM发送代码套上try-except,捕获异常并打印日志,不中断监听逻辑。
内容的提问来源于stack exchange,提问作者QwertyCode

