Search code examples
pythonpython-3.xdiscorddiscord.pybots

Extracting time multiplier from time limit in mute command [ Solved ]


I am trying to make a mute command and I want it to be able to take time limit with space and without space both. So what I wanted to do is that I wanted to take the timelimit and make two parts of it one int and another one str in the str part I will check the first two alphabets and check for the multiplier now it works without space but with space it takes the unit part as reason. Can someone please tell me how to fix this?

def parse_time_input(time_input: str) -> tuple[int, str]:
    match = re.match(r'(\d+)\s*([a-z]+)', time_input)
    if match:
        return int(match.group(1)), match.group(2)
    else:
        match = re.match(r'(\d+)', time_input)
        if match:
            return int(match.group(1)), "s"
        else:
            return -1, ""

@bot.command(aliases=["shut", "zip"])
async def mute(ctx, member: Union[discord.Member, int], timelimit: str = '28d', *, 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)

    time_value, time_unit = parse_time_input(timelimit.lower())

    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}")

# My mute logging part

I posted the solved code below


Solution

  • Solved

    I found a way of doing it, but it's kind of different then what I did previously.

    Thanks to anyone who tried to help me. I will leave this code here if anyone wants they can use it.

    Explanation

    What I did here is I took the timelimit variable and checked if there is time unit in it if not then I will check the next part [ which is in reason part ] then check for the first part of reason to get the time_unit. After that for logging I will use the remove_unit function to remove the unit part from the reason and if it was in the timelimit var then we can just directly use it.

    Sorry but my explanation is not that good, hope you will understand it

    def extract_time_parts(time_input: str) -> tuple[str, str]:
        match = re.match(r'(\d+)\s*([a-z]+)', time_input)
        if match:
            return match.group(1), match.group(2)
        
        match = re.match(r'(\d+)([a-z]+)', time_input, re.IGNORECASE)
        if match:
            return match.group(1), match.group(2)
        
        return "", ""
    
    def remove_unit(input_str):
        words = input_str.split()
        if len(words) > 1:
            return ' '.join(words[1:])
        
        return ''
    
    @bot.command(aliases=["shut", "zip"])
    async def mute(ctx, member: commands.MemberConverter, timelimit: str = '28d', *, reason: str = None):
        if isinstance(member, int):
            member = await bot.fetch_user(member)
        
        time_value, time_unit = extract_time_parts(timelimit)
        time_unit = time_unit[:1]
        main_reason = reason
        
        if not time_value or not time_unit:
            time_value = timelimit
            
            if reason and reason[0].lower() in ['s', 'm', 'h', 'd', 'w']:
                time_unit = reason[0].lower()
                reason = reason[1:].strip()
                
                reason_with_unit = reason
                main_reason = remove_unit(reason_with_unit)
                
                if main_reason == "":
                    main_reason = "No reason provided"
                
            else:
                await ctx.send("Invalid time format | `?mute @user 5s reason`")
                return
        
        unit_multipliers = {
            's': 1,
            'm': 60,
            'h': 3600,
            'd': 86400,
            'w': 604800
        }
        
        multiplier = unit_multipliers.get(time_unit, 1)
        mute_time = timedelta(seconds=int(time_value) * multiplier)
        
        if mute_time.total_seconds() > 2419200:
            await ctx.send("Timeout limit can't be over 28 days!")
            return
        
        await member.edit(timed_out_until=discord.utils.utcnow() + mute_time)
        
        if reason is None:
            reason = "No reason provided"
        
        if time_unit == 's':
            log_unit = 'second(s)'
        
        elif time_unit == 'm':
            log_unit = 'minute(s)'
            
        elif time_unit == 'h':
            log_unit = 'hour(s)'
            
        elif time_unit == 'd':
            log_unit = 'day(s)'
            
        elif time_unit == 'w':
            log_unit = 'week(s)'
        
        await ctx.send(f"{member.mention} has been muted for {time_value} {log_unit} by {ctx.author.mention}\nReason: {main_reason}")
        
    # Your logging part