discord.py 如何获取命令所需全部权限 实现类似Dank Memer的帮助命令权限展示
自定义帮助命令提取命令所需权限的正确实现方案
你当前使用的传入假ctx触发检查异常、遍历异常栈读取本地权限变量的方案属于依赖源码细节的黑魔法,存在两个核心缺陷:
- 强依赖discord.py内置权限检查的内部实现,版本迭代调整源码变量名后会直接失效
- 完全无法兼容自定义装饰器:自定义检查的判断逻辑没有内置的异常抛出流程,也不存在约定命名的
perms本地变量,自然无法通过异常栈解析拿到数据
可靠实现思路
不要靠运行时触发异常解析变量,要利用检查函数的可挂载属性,在定义检查阶段就把需要展示的权限元数据显式存储,运行时直接读取即可。
1. 改造检查的元数据存储逻辑
- 对discord.py内置的
commands.has_permissions系列装饰器:不需要手动传值触发异常,这类装饰器生成的检查函数本身会把权限参数存在闭包的perms变量中,可以直接从闭包读取,无需侵入式触发异常。 - 对自定义装饰器:编写自定义检查时,直接把需要展示的权限、特殊规则作为属性挂载在检查谓词函数上,从根源解决元数据读取的兼容性问题。示例:
def require_custom_role(role_id: int): async def role_checker(ctx): # 实际运行时的权限判断逻辑 return ctx.author.get_role(role_id) is not None # 把帮助页需要展示的元信息直接挂在函数上 role_checker.required_perms = {} role_checker.required_display_text = f"需要持有ID为{role_id}的指定角色" return commands.check(role_checker)
2. 跨检查统一收集权限的工具函数
遍历命令本身、所属父分组的全部检查,逐一对接内置检查、自定义检查的元数据来源,汇总得到完整的权限要求:
def fetch_command_requirements(command: commands.Command): required_perms = set() extra_notes = [] # 合并命令自身、所有上级父分组的检查规则 all_checks = command.checks.copy() parent_group = command.parent while parent_group: all_checks.extend(parent_group.checks) parent_group = parent_group.parent for check in all_checks: # 读取内置权限检查的闭包变量 if hasattr(check, "__closure__"): for closure_var in check.__closure__: if closure_var.name == "perms" and isinstance(closure_var.cell_contents, dict): required_perms.update(closure_var.cell_contents.keys()) # 读取自定义检查挂载的元数据 if hasattr(check, "required_perms"): required_perms.update(check.required_perms.keys()) if hasattr(check, "required_display_text"): extra_notes.append(check.required_display_text) return { "server_permissions": list(required_perms), "custom_requirements": extra_notes }
方案优势
- 全兼容:不管是discord.py内置权限检查,还是自定义装饰器写的校验规则,都能稳定读取到需要展示的权限信息
- 低侵入:不需要靠触发异常的黑魔法流程,不会因为版本迭代、自定义逻辑不同导致解析失败
- 易扩展:后续新增任意自定义校验规则时,只需要在检查函数上挂载对应元数据,就能自动被帮助命令读取展示,可实现和Dank Memer一致的命令权限展示效果
内容的提问来源于stack exchange,提问作者Sudip Roy
相关产品推荐
相关产品推荐

