From c319fdeab91120b2266b84a0d4e1ab29952fd328 Mon Sep 17 00:00:00 2001 From: pacnpal <183241239+pacnpal@users.noreply.github.com> Date: Sun, 29 Sep 2024 00:18:10 -0400 Subject: [PATCH] yeah I changed some stuff --- birthday/README.md | 18 ++++++++- birthday/birthday.py | 90 ++++++++++++++++++++++++++++---------------- 2 files changed, 74 insertions(+), 34 deletions(-) diff --git a/birthday/README.md b/birthday/README.md index 9bcb647..5ebd86c 100644 --- a/birthday/README.md +++ b/birthday/README.md @@ -8,14 +8,19 @@ To install this cog, follow these steps: 1. Ensure you have Red-DiscordBot V3 installed. 2. Add the repository to your bot: + ``` [p]repo add Pac-cogs https://github.com/pacnpal/Pac-cogs ``` + 3. Install the Birthday cog: + ``` [p]cog install Pac-cogs birthday ``` + 4. Load the cog: + ``` [p]load birthday ``` @@ -27,28 +32,37 @@ Replace `[p]` with your bot's prefix. Before using the cog, you need to set it up: 1. Set the birthday role: + ``` [p]birthdayset role @Birthday ``` + **Note:** The bot's role must be above the birthday role in the server's role hierarchy, but users assigning the birthday role do not need to have a role above it. 2. Add roles that can use the birthday command: + ``` [p]birthdayset addrole @Moderator ``` + 3. (Optional) Set the timezone for role expiration: + ``` [p]birthdayset timezone America/New_York ``` + 4. (Optional) Set a specific channel for birthday announcements: + ``` [p]birthdayset channel #birthdays ``` + If not set, the birthday message will be sent in the channel where the command is used. ## Usage To assign the birthday role to a user: + ``` [p]birthday @User ``` @@ -59,7 +73,7 @@ This will assign the birthday role to the user and send a celebratory message wi - Assigns a special birthday role to users - Sends a celebratory message with random cake (or pie) emojis -- Automatically removes the birthday role at midnight +- Automatically removes the birthday role at midnight, temporarily stores so tasks will complete even if cog is reloaded - Configurable timezone for role expiration - Option to set a specific channel for birthday announcements (defaults to the channel where the command is used) - Restricts usage of the birthday command to specified roles @@ -76,4 +90,4 @@ This will assign the birthday role to the user and send a celebratory message wi ## Support -If you encounter any issues or have questions, please open an issue on the [GitHub repository](https://github.com/pacnpal/Pac-cogs). \ No newline at end of file +If you encounter any issues or have questions, please open an issue on the [GitHub repository](https://github.com/pacnpal/Pac-cogs). diff --git a/birthday/birthday.py b/birthday/birthday.py index 5cc2459..f2b16dd 100644 --- a/birthday/birthday.py +++ b/birthday/birthday.py @@ -16,7 +16,8 @@ class Birthday(commands.Cog): "birthday_role": None, "allowed_roles": [], "timezone": "UTC", - "birthday_channel": None + "birthday_channel": None, + "scheduled_tasks": {} } self.config.register_guild(**default_guild) self.birthday_tasks = {} @@ -28,27 +29,12 @@ class Birthday(commands.Cog): pass @birthdayset.command() + @checks.is_owner() async def role(self, ctx, role: discord.Role): """Set the birthday role.""" await self.config.guild(ctx.guild).birthday_role.set(role.id) await ctx.send(f"Birthday role set to {role.name}") - @birthdayset.command() - async def addrole(self, ctx, role: discord.Role): - """Add a role that can use the birthday command.""" - async with self.config.guild(ctx.guild).allowed_roles() as allowed_roles: - if role.id not in allowed_roles: - allowed_roles.append(role.id) - await ctx.send(f"Added {role.name} to the list of roles that can use the birthday command.") - - @birthdayset.command() - async def removerole(self, ctx, role: discord.Role): - """Remove a role from using the birthday command.""" - async with self.config.guild(ctx.guild).allowed_roles() as allowed_roles: - if role.id in allowed_roles: - allowed_roles.remove(role.id) - await ctx.send(f"Removed {role.name} from the list of roles that can use the birthday command.") - @birthdayset.command() @checks.is_owner() async def timezone(self, ctx, tz: str): @@ -67,6 +53,22 @@ class Birthday(commands.Cog): await self.config.guild(ctx.guild).birthday_channel.set(channel.id) await ctx.send(f"Birthday announcement channel set to {channel.mention}") + @birthdayset.command() + async def addrole(self, ctx, role: discord.Role): + """Add a role that can use the birthday command.""" + async with self.config.guild(ctx.guild).allowed_roles() as allowed_roles: + if role.id not in allowed_roles: + allowed_roles.append(role.id) + await ctx.send(f"Added {role.name} to the list of roles that can use the birthday command.") + + @birthdayset.command() + async def removerole(self, ctx, role: discord.Role): + """Remove a role from using the birthday command.""" + async with self.config.guild(ctx.guild).allowed_roles() as allowed_roles: + if role.id in allowed_roles: + allowed_roles.remove(role.id) + await ctx.send(f"Removed {role.name} from the list of roles that can use the birthday command.") + @commands.command() async def birthday(self, ctx, member: discord.Member): """Assign the birthday role to a user until midnight in the set timezone.""" @@ -78,7 +80,7 @@ class Birthday(commands.Cog): birthday_role_id = await self.config.guild(ctx.guild).birthday_role() if not birthday_role_id: return await ctx.send("The birthday role hasn't been set. An admin needs to set it using `[p]birthdayset role`.") - + birthday_role = ctx.guild.get_role(birthday_role_id) if not birthday_role: return await ctx.send("The birthday role doesn't exist anymore. Please ask an admin to set it again.") @@ -89,8 +91,13 @@ class Birthday(commands.Cog): except discord.Forbidden: return await ctx.send("I don't have permission to assign that role.") - timezone = await self.config.guild(ctx.guild).timezone() - + # Generate birthday message with random cakes (or pie) + cakes = random.randint(0, 5) + if cakes == 0: + message = f"🎉 Happy Birthday, {member.mention}! Sorry, out of cake today! Here's pie instead: 🥧" + else: + message = f"🎉 Happy Birthday, {member.mention}! Here's your cake{'s' if cakes > 1 else ''}: " + "🎂" * cakes + # Get the birthday announcement channel birthday_channel_id = await self.config.guild(ctx.guild).birthday_channel() if birthday_channel_id: @@ -100,16 +107,10 @@ class Birthday(commands.Cog): else: channel = ctx.channel - # Generate birthday message with random cakes (or pie) - cakes = random.randint(0, 5) - if cakes == 0: - message = f"🎉 Happy Birthday, {member.mention}! Sorry, out of cake today! Here's pie instead: 🥧" - else: - message = f"🎉 Happy Birthday, {member.mention}! Here's your cake{'s' if cakes > 1 else ''}: " + "🎂" * cakes - await channel.send(message) # Schedule role removal + timezone = await self.config.guild(ctx.guild).timezone() try: tz = ZoneInfo(timezone) except ZoneInfoNotFoundError: @@ -119,10 +120,15 @@ class Birthday(commands.Cog): now = datetime.now(tz) midnight = datetime.combine(now.date() + timedelta(days=1), time.min).replace(tzinfo=tz) - if ctx.guild.id in self.birthday_tasks: - self.birthday_tasks[ctx.guild.id].cancel() - - self.birthday_tasks[ctx.guild.id] = self.bot.loop.create_task(self.remove_birthday_role(ctx.guild, member, birthday_role, midnight)) + await self.schedule_birthday_role_removal(ctx.guild, member, birthday_role, midnight) + + async def schedule_birthday_role_removal(self, guild, member, role, when): + """Schedule the removal of the birthday role.""" + await self.config.guild(guild).scheduled_tasks.set_raw(str(member.id), value={ + "role_id": role.id, + "remove_at": when.isoformat() + }) + self.birthday_tasks[guild.id] = self.bot.loop.create_task(self.remove_birthday_role(guild, member, role, when)) async def remove_birthday_role(self, guild, member, role, when): """Remove the birthday role at the specified time.""" @@ -132,4 +138,24 @@ class Birthday(commands.Cog): except (discord.Forbidden, discord.HTTPException): pass # If we can't remove the role, we'll just let it be finally: - del self.birthday_tasks[guild.id] \ No newline at end of file + del self.birthday_tasks[guild.id] + await self.config.guild(guild).scheduled_tasks.clear_raw(str(member.id)) + + async def reload_scheduled_tasks(self): + """Reload and reschedule tasks from the configuration.""" + for guild in self.bot.guilds: + scheduled_tasks = await self.config.guild(guild).scheduled_tasks() + for member_id, task_info in scheduled_tasks.items(): + member = guild.get_member(int(member_id)) + if not member: + continue + role = guild.get_role(task_info["role_id"]) + if not role: + continue + remove_at = datetime.fromisoformat(task_info["remove_at"]).replace(tzinfo=ZoneInfo(await self.config.guild(guild).timezone())) + self.birthday_tasks[guild.id] = self.bot.loop.create_task(self.remove_birthday_role(guild, member, role, remove_at)) + +async def setup(bot): + cog = Birthday(bot) + await bot.add_cog(cog) + await cog.reload_scheduled_tasks() \ No newline at end of file