Discord机器人踢/ban报403权限错误,@has_permissions装饰器失效
踢人/封禁指令报403权限缺失的解决方法
这个报错是Discord API判定机器人本身没有执行对应操作的权限,和你作为服务器所有者的个人权限无关,常见触发原因为:
- 角色层级优先级问题:Discord核心规则为「仅能操作角色层级低于自身的成员」。哪怕机器人绑定了全权限的管理员角色,如果该角色在服务器角色列表中的排序位置低于被操作用户的角色,依然会直接被拦截报权限缺失。解决方式:将机器人的专属角色拖动到服务器角色列表的最顶端区域,确保其层级高于所有普通成员、非管理类角色。
- 权限被全局/频道级配置覆盖:如果服务器的「成员权限」全局设置里禁用了「踢除成员」「封禁成员」,或是在执行指令的频道权限设置中单独给机器人关闭了这两个权限,这类配置会优先覆盖角色层面给机器人配置的权限。
- 邀请流程的权限授权问题:如果当初邀请机器人进服务器时,OAuth2授权页面没有勾选对应的操作权限范围,哪怕后续在后台给角色配置了全权限,机器人实际从API拿到的权限依然是邀请时授权的最小集合,需要重新通过带管理员权限的邀请链接拉机器人进服务器,覆盖原有权限配置。
快速排查工具
可在代码中临时加入一段指令,直接打印机器人当前的实际权限和角色层级,不用反复手动核对后台设置:
@client.command() async def check_my_perm(ctx): bot_perms = ctx.guild.me.guild_permissions await ctx.send( f"机器人当前生效权限:\n" f"踢人权限:{bot_perms.kick_members}\n" f"封禁权限:{bot_perms.ban_members}\n" f"管理员权限:{bot_perms.administrator}\n" f"机器人最高角色排序位置:{ctx.guild.me.top_role.position}\n" f"你的最高角色排序位置:{ctx.author.top_role.position}" )
运行该指令即可看到机器人实际生效的权限值,定位配置错误的位置。
实现「拥有管理员 或 对应操作权限即可执行指令」的校验逻辑
discord.py内置的@commands.has_permissions()装饰器默认校验所有列出的权限,无法直接实现“或”的逻辑,需要编写自定义权限校验器:
自定义校验器实现
- 对应kick指令:只要用户拥有
administrator或kick_members任意一个权限,即可执行指令
def can_kick(): async def check_perm(ctx): # 两个条件满足任意一个即通过校验 return ctx.author.guild_permissions.administrator or ctx.author.guild_permissions.kick_members return commands.check(check_perm)
- 对应ban指令:只要用户拥有
administrator或ban_members任意一个权限,即可执行指令
def can_ban(): async def check_perm(ctx): return ctx.author.guild_permissions.administrator or ctx.author.guild_permissions.ban_members return commands.check(check_perm)
指令替换示例
将原来指令上挂载的内置权限装饰器,替换为自定义校验器,同时补上之前代码中缺失的参数空值校验,避免无目标用户时报错:
@client.command() @can_kick() # 替换原来的 @commands.has_permissions(administrator=True) async def kick(ctx, user: discord.Member = None, *, reason="No reason provided"): # 空值判断 if not user: await ctx.send("请指定要踢除的服务器成员") return # 禁止操作机器人自身 if user == ctx.guild.me: await ctx.send("我无法踢除自己") return # 禁止操作层级与自己同级/更高的用户 if user.top_role >= ctx.author.top_role and not ctx.author.guild_permissions.administrator: await ctx.send("你没有权限操作层级和你相同或更高的成员") return await user.kick(reason=reason) await ctx.send(f"已将 {user.mention} 移出服务器,操作理由:{reason}") @client.command() @can_ban() # 替换原来的 @commands.has_permissions(administrator=True) async def ban(ctx, user: discord.Member = None, *, reason="No reason provided"): if not user: await ctx.send("请指定要封禁的服务器成员") return if user == ctx.guild.me: await ctx.send("我无法封禁自己") return if user.top_role >= ctx.author.top_role and not ctx.author.guild_permissions.administrator: await ctx.send("你没有权限操作层级和你相同或更高的成员") return await user.ban(reason=reason) await ctx.send(f"已将 {user.mention} 封禁,操作理由:{reason}")
内容的提问来源于stack exchange,提问作者Aum Singhal
相关产品推荐
相关产品推荐

