Skip to content

Fuzz duck time, add admin only next command #1297

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 49 additions & 6 deletions techsupport_bot/commands/duck.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from botlogging import LogContext, LogLevel
from core import auxiliary, cogs, extensionconfig
from discord import Color as embed_colors
from discord import app_commands
from discord.ext import commands

if TYPE_CHECKING:
Expand Down Expand Up @@ -101,13 +102,18 @@ class DuckHunt(cogs.LoopCog):
"""Class for the actual duck commands

Attributes:
duck_group (app_commands.Group): The group for the /duck commands
DUCK_PIC_URL (str): The picture for the duck
BEFRIEND_URL (str): The picture for the befriend target
KILL_URL (str): The picture for the kill target
ON_START (bool): ???
CHANNELS_KEY (str): The config item for the channels that the duck hunt should run
"""

duck_group: app_commands.Group = app_commands.Group(
name="duck", description="...", extras={"module": "duck"}
)

DUCK_PIC_URL: str = (
"https://www.iconarchive.com/download/i107380/google/"
+ "noto-emoji-animals-nature/22276-duck.512.png"
Expand All @@ -126,20 +132,31 @@ async def loop_preconfig(self: Self) -> None:
"""Preconfig for cooldowns"""
self.cooldowns = {}

async def wait(self: Self, config: munch.Munch, _: discord.Guild) -> None:
# "guild_id": datetime
self.next_duck: dict[str, datetime.datetime] = {}

async def wait(self: Self, config: munch.Munch, guild: discord.Guild) -> None:
"""Waits a random amount of time before sending another duck
This function shouldn't be manually called

Args:
config (munch.Munch): The guild config to use to determine the min and max wait times
guild (discord.Guild): The guild where the duck is going to appear
"""
await asyncio.sleep(
random.randint(
config.extensions.duck.min_wait.value * 3600,
config.extensions.duck.max_wait.value * 3600,
)
min_wait = config.extensions.duck.min_wait.value * 3600
max_wait = config.extensions.duck.max_wait.value * 3600

fuzzed_min = int(min_wait * random.uniform(0.9, 1.1))
fuzzed_max = int(max_wait * random.uniform(0.9, 1.1))

Copy link
Preview

Copilot AI May 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The independent fuzzing of min_wait and max_wait may result in fuzzed_min being greater than fuzzed_max, potentially causing random.randint to throw an error. Consider ensuring that the lower bound is always less than or equal to the upper bound.

Suggested change
if fuzzed_min > fuzzed_max:
fuzzed_min, fuzzed_max = fuzzed_max, fuzzed_min

Copilot uses AI. Check for mistakes.

wait_time = random.randint(fuzzed_min, fuzzed_max)

self.next_duck[str(guild.id)] = datetime.datetime.now() + datetime.timedelta(
seconds=wait_time
)

await asyncio.sleep(wait_time)

async def execute(
self: Self,
config: munch.Munch,
Expand Down Expand Up @@ -169,6 +186,7 @@ async def execute(
return

self.cooldowns[guild.id] = {}
del self.next_duck[str(guild.id)]

embed = discord.Embed(
title="*Quack Quack*",
Expand Down Expand Up @@ -453,6 +471,31 @@ async def get_global_record(self: Self, guild_id: int) -> float:

return float(min(speed_records, key=float))

@app_commands.checks.has_permissions(administrator=True)
@duck_group.command(
name="next",
description="Displays the time for the next duck for this guild",
extras={"module": "duck"},
)
async def lookup_next_duck(self: Self, interaction: discord.Interaction) -> None:
"""A simple command to show an admin when the next duck will be spawning

Args:
interaction (discord.Interaction): The interaction that called this command
"""
if str(interaction.guild.id) not in self.next_duck:
embed = auxiliary.prepare_deny_embed(
"Couldn't find a future duck for this guild."
)
await interaction.response.send_message(embed=embed, ephemeral=True)
return

embed = auxiliary.prepare_confirm_embed(
"The next duck in this guild:"
f"<t:{int(self.next_duck[str(interaction.guild.id)].timestamp())}>"
)
await interaction.response.send_message(embed=embed, ephemeral=True)

@commands.group(
brief="Executes a duck command",
description="Executes a duck command",
Expand Down