如何让Discord机器人mute命令兼容带空格与无空格时间格式?
解决Discord机器人Mute命令支持带空格时间格式的问题
你的问题根源在于Discord.py的参数解析逻辑:timelimit: str只能捕获单个无空格字符串,输入带空格的时间(比如10 minutes)时,10会被赋值给timelimit,minutes会被归入后面的reason参数,导致时间解析失败。下面提供两种可行的解决方法:
方案1:合并参数后手动解析时间
修改命令参数定义,先接收所有后续参数,再从中分离时间部分和理由:
import re from datetime import timedelta import discord from discord.ext import commands from typing import Union def parse_time_input(time_input: str) -> tuple[int, str]: # 匹配带/不带空格的时间格式,例如"10d"、"10 days" match = re.match(r'(\d+)\s*([a-z]+)?', time_input.lower()) if match: time_value = int(match.group(1)) # 无单位时默认用秒 time_unit = match.group(2) if match.group(2) else "s" return time_value, time_unit else: return -1, "" @bot.command(aliases=["shut", "zip"]) async def mute(ctx, member: Union[discord.Member, int], *args): # 处理成员对象 if isinstance(member, int): member = await bot.fetch_user(member) elif isinstance(member, str): member = discord.utils.get(ctx.guild.members, name=member) # 从参数中分离时间和理由 time_input = "" reason = "No reason provided" # 遍历参数,找到第一个能解析的时间输入 for i, arg in enumerate(args): temp_input = " ".join(args[:i+1]) time_val, _ = parse_time_input(temp_input) if time_val != -1: time_input = temp_input # 剩余参数拼接成理由 if i+1 < len(args): reason = " ".join(args[i+1:]) break # 无有效时间时用默认值28天 if not time_input: time_input = "28d" time_value, time_unit = parse_time_input(time_input) if time_value == -1: await ctx.send("<:error:1131175466049470505> | Invalid time format") return unit_multipliers = { 's': 1, 'sec': 1, 'secs': 1, 'second': 1, 'seconds': 1, 'm': 60, 'min': 60, 'mins': 60, 'minute': 60, 'minutes': 60, 'h': 3600, 'hour': 3600, 'hours': 3600, 'd': 86400, 'day': 86400, 'days': 86400, 'w': 604800, 'week': 604800, 'weeks': 604800, } multiplier = unit_multipliers.get(time_unit, 1) total_seconds = time_value * multiplier mute_time = timedelta(seconds=total_seconds) if mute_time.total_seconds() > 2419200: await ctx.send("<:error:1131175466049470505> | Timeout limit can't be over 28 days!") return await member.edit(timed_out_until=discord.utils.utcnow() + mute_time) await ctx.send(f"<:check:1132263275770429471> | {member.mention} has been muted for {mute_time} by {ctx.author.mention}\nReason: {reason}")
方案2:使用自定义时间转换器
这种方式更符合Discord.py的命令规范,让框架自动处理带空格的时间输入:
import re from datetime import timedelta import discord from discord.ext import commands from typing import Union, Optional class TimeConverter(commands.Converter): async def convert(self, ctx, argument): # 匹配带/不带空格的时间格式 match = re.match(r'(\d+)\s*([a-z]+)?', argument.lower()) if not match: raise commands.BadArgument("Invalid time format") time_value = int(match.group(1)) time_unit = match.group(2) if match.group(2) else "s" unit_multipliers = { 's': 1, 'sec': 1, 'secs': 1, 'second': 1, 'seconds': 1, 'm': 60, 'min': 60, 'mins': 60, 'minute': 60, 'minutes': 60, 'h': 3600, 'hour': 3600, 'hours': 3600, 'd': 86400, 'day': 86400, 'days': 86400, 'w': 604800, 'week': 604800, 'weeks': 604800, } multiplier = unit_multipliers.get(time_unit, 1) total_seconds = time_value * multiplier if total_seconds > 2419200: raise commands.BadArgument("Timeout limit can't be over 28 days!") return timedelta(seconds=total_seconds) @bot.command(aliases=["shut", "zip"]) async def mute(ctx, member: Union[discord.Member, int], timelimit: Optional[TimeConverter] = timedelta(days=28), *, reason: str = "No reason provided"): # 处理成员对象 if isinstance(member, int): member = await bot.fetch_user(member) elif isinstance(member, str): member = discord.utils.get(ctx.guild.members, name=member) await member.edit(timed_out_until=discord.utils.utcnow() + timelimit) await ctx.send(f"<:check:1132263275770429471> | {member.mention} has been muted for {timelimit} by {ctx.author.mention}\nReason: {reason}")
方案对比
- 方案1无需额外定义转换器,适合快速修改现有代码,兼容性强。
- 方案2代码结构更清晰,出错时自动触发Discord.py的错误处理机制,更符合框架规范。
内容的提问来源于stack exchange,提问作者Kaze
相关产品推荐
相关产品推荐

