Discord.py 定时禁言(mute)命令无报错也不生效问题求助
问题排查与修复
你代码里的问题如下,按影响优先级排序:
- time参数类型错误:你将time参数注解为
int类型,但实际需要传入带单位的字符串(如10m),discord.py的参数转换器会直接拒绝这类输入,就算你传纯数字,后续time[:-1]的切片操作也会因为int类型不支持切片直接报错。把参数注解改成time:str=None即可解决。 - reason默认值逻辑位置错误:
if reason == None的判断写在了if time == None的return分支内部,只要传入了有效time,该判断永远不会触发,reason会一直为None,把这段逻辑移到time判断的外面。 - Muted角色无权限限制:这是禁言不生效的核心原因。你只是创建了名为Muted的空角色,没有给这个角色配置所有频道的「禁止发送消息」「禁止连接语音」「禁止发送语音消息」等限制权限,用户带上这个角色没有任何实际限制,看起来就像功能没生效。创建角色后需要遍历服务器所有频道,给Muted角色设置对应禁止权限。
- Bot角色权限不足:Bot的最高角色的排序必须高于Muted角色,也必须高于你要禁言的目标用户的最高角色,否则Bot没有权限给目标用户添加Muted角色。
- 定时逻辑有缺陷:用
asyncio.sleep做定时,只要Bot重启,所有待执行的解除禁言任务都会丢失,用户会被永久禁言,建议改用定时任务配合数据库存储禁言到期时间的方案。
修复后参考代码
import asyncio import discord from discord.ext import commands @commands.command() @commands.guild_only() @commands.has_permissions(kick_members=True) async def mute(self, ctx, member: discord.Member=None, time:str=None, reason=None): # 找Muted角色,不存在就创建并配置权限 muted_role = discord.utils.get(ctx.guild.roles, name="Muted") if not muted_role: muted_role = await ctx.guild.create_role(name="Muted") # 给所有频道配置Muted角色的禁止权限 for channel in ctx.guild.channels: await channel.set_permissions(muted_role, send_messages=False, speak=False, send_messages_in_threads=False, add_reactions=False) # 参数校验 if not member or member == ctx.author: await ctx.send("You cannot mute yourself :angry:") return if not time: return await ctx.send("You have to write the time for which you want to mute the mentioned user!") # 处理默认reason if not reason: reason = "For being a jerk." # 角色权限校验,直接用role.position属性,不用取index if ctx.author.top_role.position <= member.top_role.position: return await ctx.send("You don't have enough permission to mute this user!") # Bot自身权限校验 if ctx.guild.me.top_role.position <= member.top_role.position or ctx.guild.me.top_role.position <= muted_role.position: return await ctx.send("I don't have enough permission to mute this user!") # 时间转换 time_convert = {"s":1, "m":60, "h":3600,"d":86400} unit = time[-1].lower() if unit not in time_convert: return await ctx.send("Invalid time unit! Use s/m/h/d, e.g. 10m") try: num = int(time[:-1]) except ValueError: return await ctx.send("Invalid time format! e.g. 10m") tempmute = num * time_convert[unit] # 执行禁言 await member.add_roles(muted_role, reason=reason) await ctx.send(f'{member.mention} has been muted for {time}. Reason: {reason}.') # 定时解除禁言,优化方案可以用tasks loop代替sleep await asyncio.sleep(tempmute) # 解除前先校验用户还在服务器,还持有Muted角色 if member in ctx.guild.members and muted_role in member.roles: await member.remove_roles(muted_role, reason="Mute expired")
内容的提问来源于stack exchange,提问作者Captain Underpants
相关产品推荐
相关产品推荐

