From 0c9c210dbbbbee531db4475515a17abcc45865b4 Mon Sep 17 00:00:00 2001 From: TrustyJAID Date: Sat, 20 Apr 2024 19:01:28 -0600 Subject: [PATCH] Prevent OverflowError from very large timedeltas in Mutes (#6353) --- redbot/cogs/mutes/converters.py | 5 ++- redbot/cogs/mutes/mutes.py | 65 +++++++++++++++++++++------------ redbot/cogs/mutes/voicemutes.py | 24 ++++++------ 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/redbot/cogs/mutes/converters.py b/redbot/cogs/mutes/converters.py index ea5673069..18a11430d 100644 --- a/redbot/cogs/mutes/converters.py +++ b/redbot/cogs/mutes/converters.py @@ -3,7 +3,7 @@ from __future__ import annotations import logging import re from typing import Optional, TypedDict -from datetime import timedelta +from datetime import timedelta, datetime, timezone from typing_extensions import Annotated from discord.ext.commands.converter import Converter @@ -29,6 +29,7 @@ def _edgematch(pattern: re.Pattern[str], argument: str) -> Optional[re.Match[str class _MuteTime(TypedDict, total=False): duration: timedelta reason: str + until: datetime class _MuteTimeConverter(Converter): @@ -57,6 +58,8 @@ class _MuteTimeConverter(Converter): ) try: result["duration"] = duration = timedelta(**time_data) + result["until"] = ctx.message.created_at + duration + # Catch if using the timedelta with the current date will also result in an Overflow error except OverflowError: raise commands.BadArgument( _("The time provided is too long; use a more reasonable time.") diff --git a/redbot/cogs/mutes/mutes.py b/redbot/cogs/mutes/mutes.py index edf65ee13..eea518af8 100644 --- a/redbot/cogs/mutes/mutes.py +++ b/redbot/cogs/mutes/mutes.py @@ -1005,13 +1005,20 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass): await self.config.guild(ctx.guild).default_time.clear() await ctx.send(_("Default mute time removed.")) else: - data = time.get("duration", {}) - if not data: + duration = time.get("duration", None) + if not duration: return await ctx.send(_("Please provide a valid time format.")) - await self.config.guild(ctx.guild).default_time.set(data.total_seconds()) + if duration >= timedelta(days=365000): + # prevent setting a default time now that might eventually cause an overflow + # later as the date goes up. 1000 years gives us approximately 8000 more years + # of wiggle room. + return await ctx.send( + _("The time provided is too long; use a more reasonable time.") + ) + await self.config.guild(ctx.guild).default_time.set(duration.total_seconds()) await ctx.send( _("Default mute time set to {time}.").format( - time=humanize_timedelta(timedelta=data) + time=humanize_timedelta(timedelta=duration) ) ) @@ -1142,15 +1149,15 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass): return await ctx.send(_("You cannot mute me.")) if ctx.author in users: return await ctx.send(_("You cannot mute yourself.")) - duration = time_and_reason.get("duration", None) - if duration and duration > timedelta(days=28): - await ctx.send(_(MUTE_UNMUTE_ISSUES["mute_is_too_long"])) - return + until = time_and_reason.get("until", None) reason = time_and_reason.get("reason", None) time = "" - until = None - if duration: - until = datetime.now(timezone.utc) + duration + duration = None + if until: + duration = time_and_reason.get("duration") + if duration and duration > timedelta(days=28): + await ctx.send(_(MUTE_UNMUTE_ISSUES["mute_is_too_long"])) + return length = humanize_timedelta(timedelta=duration) time = _(" for {length} until {duration}").format( length=length, duration=discord.utils.format_dt(until) @@ -1159,7 +1166,8 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass): else: default_duration = await self.config.guild(ctx.guild).default_time() if default_duration: - until = datetime.now(timezone.utc) + timedelta(seconds=default_duration) + duration = timedelta(seconds=default_duration) + until = ctx.message.created_at + duration length = humanize_timedelta(seconds=default_duration) time = _(" for {length} until {duration}").format( length=length, duration=discord.utils.format_dt(until) @@ -1227,12 +1235,12 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass): if not await self._check_for_mute_role(ctx): return async with ctx.typing(): - duration = time_and_reason.get("duration", None) + until = time_and_reason.get("until", None) reason = time_and_reason.get("reason", None) time = "" - until = None - if duration: - until = datetime.now(timezone.utc) + duration + duration = None + if until: + duration = time_and_reason.get("duration") length = humanize_timedelta(timedelta=duration) time = _(" for {length} until {duration}").format( length=length, duration=discord.utils.format_dt(until) @@ -1241,7 +1249,8 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass): else: default_duration = await self.config.guild(ctx.guild).default_time() if default_duration: - until = datetime.now(timezone.utc) + timedelta(seconds=default_duration) + duration = timedelta(seconds=default_duration) + until = ctx.message.created_at + duration length = humanize_timedelta(seconds=default_duration) time = _(" for {length} until {duration}").format( length=length, duration=discord.utils.format_dt(until) @@ -1377,18 +1386,26 @@ class Mutes(VoiceMutes, commands.Cog, metaclass=CompositeMetaClass): if ctx.author in users: return await ctx.send(_("You cannot mute yourself.")) async with ctx.typing(): - duration = time_and_reason.get("duration", None) + until = time_and_reason.get("until", None) reason = time_and_reason.get("reason", None) time = "" - until = None - if duration: - until = datetime.now(timezone.utc) + duration - time = _(" until {duration}").format(duration=discord.utils.format_dt(until)) + duration = None + if until: + duration = time_and_reason.get("duration") + length = humanize_timedelta(timedelta=duration) + time = _(" for {length} until {duration}").format( + length=length, duration=discord.utils.format_dt(until) + ) + else: default_duration = await self.config.guild(ctx.guild).default_time() if default_duration: - until = datetime.now(timezone.utc) + timedelta(seconds=default_duration) - time = _(" until {duration}").format(duration=discord.utils.format_dt(until)) + duration = timedelta(seconds=default_duration) + until = ctx.message.created_at + duration + length = humanize_timedelta(seconds=default_duration) + time = _(" for {length} until {duration}").format( + length=length, duration=discord.utils.format_dt(until) + ) author = ctx.message.author channel = ctx.message.channel if isinstance(channel, discord.Thread): diff --git a/redbot/cogs/mutes/voicemutes.py b/redbot/cogs/mutes/voicemutes.py index 075c5339d..57cf23606 100644 --- a/redbot/cogs/mutes/voicemutes.py +++ b/redbot/cogs/mutes/voicemutes.py @@ -99,23 +99,25 @@ class VoiceMutes(MixinMeta): if not can_move: issue_list.append((user, perm_reason)) continue - duration = time_and_reason.get("duration", None) + until = time_and_reason.get("until", None) reason = time_and_reason.get("reason", None) time = "" - until = None - if duration: - until = datetime.now(timezone.utc) + duration - time = _(" for {duration}").format( - duration=humanize_timedelta(timedelta=duration) + duration = None + if until: + duration = time_and_reason.get("duration") + length = humanize_timedelta(timedelta=duration) + time = _(" for {length} until {duration}").format( + length=length, duration=discord.utils.format_dt(until) ) + else: default_duration = await self.config.guild(ctx.guild).default_time() if default_duration: - until = datetime.now(timezone.utc) + timedelta(seconds=default_duration) - time = _(" for {duration}").format( - duration=humanize_timedelta( - timedelta=timedelta(seconds=default_duration) - ) + duration = timedelta(seconds=default_duration) + until = ctx.message.created_at + duration + length = humanize_timedelta(seconds=default_duration) + time = _(" for {length} until {duration}").format( + length=length, duration=discord.utils.format_dt(until) ) guild = ctx.guild author = ctx.author