1515from botlogging import LogContext , LogLevel
1616from core import auxiliary , cogs , extensionconfig
1717from discord import Color as embed_colors
18+ from discord import app_commands
1819from discord .ext import commands
1920
2021if TYPE_CHECKING :
@@ -106,8 +107,13 @@ class DuckHunt(cogs.LoopCog):
106107 KILL_URL (str): The picture for the kill target
107108 ON_START (bool): ???
108109 CHANNELS_KEY (str): The config item for the channels that the duck hunt should run
110+ duck_group (app_commands.Group): The group for the /duck commands
109111 """
110112
113+ duck_group : app_commands .Group = app_commands .Group (
114+ name = "duck" , description = "..." , extras = {"module" : "duck" }
115+ )
116+
111117 DUCK_PIC_URL : str = (
112118 "https://www.iconarchive.com/download/i107380/google/"
113119 + "noto-emoji-animals-nature/22276-duck.512.png"
@@ -126,20 +132,31 @@ async def loop_preconfig(self: Self) -> None:
126132 """Preconfig for cooldowns"""
127133 self .cooldowns = {}
128134
129- async def wait (self : Self , config : munch .Munch , _ : discord .Guild ) -> None :
135+ # "guild_id": datetime
136+ self .next_duck : dict [str , datetime .datetime ] = {}
137+
138+ async def wait (self : Self , config : munch .Munch , guild : discord .Guild ) -> None :
130139 """Waits a random amount of time before sending another duck
131140 This function shouldn't be manually called
132141
133142 Args:
134143 config (munch.Munch): The guild config to use to determine the min and max wait times
144+ guild (discord.Guild): The guild where the duck is going to appear
135145 """
136- await asyncio .sleep (
137- random .randint (
138- config .extensions .duck .min_wait .value * 3600 ,
139- config .extensions .duck .max_wait .value * 3600 ,
140- )
146+ min_wait = config .extensions .duck .min_wait .value * 3600
147+ max_wait = config .extensions .duck .max_wait .value * 3600
148+
149+ fuzzed_min = int (min_wait * random .uniform (0.9 , 1.1 ))
150+ fuzzed_max = int (max_wait * random .uniform (0.9 , 1.1 ))
151+
152+ wait_time = random .randint (fuzzed_min , fuzzed_max )
153+
154+ self .next_duck [str (guild .id )] = datetime .datetime .now () + datetime .timedelta (
155+ seconds = wait_time
141156 )
142157
158+ await asyncio .sleep (wait_time )
159+
143160 async def execute (
144161 self : Self ,
145162 config : munch .Munch ,
@@ -169,6 +186,7 @@ async def execute(
169186 return
170187
171188 self .cooldowns [guild .id ] = {}
189+ del self .next_duck [str (guild .id )]
172190
173191 embed = discord .Embed (
174192 title = "*Quack Quack*" ,
@@ -453,6 +471,31 @@ async def get_global_record(self: Self, guild_id: int) -> float:
453471
454472 return float (min (speed_records , key = float ))
455473
474+ @app_commands .checks .has_permissions (administrator = True )
475+ @duck_group .command (
476+ name = "next" ,
477+ description = "Displays the time for the next duck for this guild" ,
478+ extras = {"module" : "duck" },
479+ )
480+ async def lookup_next_duck (self : Self , interaction : discord .Interaction ) -> None :
481+ """A simple command to show an admin when the next duck will be spawning
482+
483+ Args:
484+ interaction (discord.Interaction): The interaction that called this command
485+ """
486+ if str (interaction .guild .id ) not in self .next_duck :
487+ embed = auxiliary .prepare_deny_embed (
488+ "Couldn't find a future duck for this guild."
489+ )
490+ await interaction .response .send_message (embed = embed , ephemeral = True )
491+ return
492+
493+ embed = auxiliary .prepare_confirm_embed (
494+ "The next duck in this guild:"
495+ f"<t:{ int (self .next_duck [str (interaction .guild .id )].timestamp ())} >"
496+ )
497+ await interaction .response .send_message (embed = embed , ephemeral = True )
498+
456499 @commands .group (
457500 brief = "Executes a duck command" ,
458501 description = "Executes a duck command" ,
0 commit comments